Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: rails | AR queries | enums (#29099) #29116

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

sean-garwood
Copy link
Contributor

Add lesson content: section on enums

Because

Enums are a useful way of storing discrete states and expose useful querying class and instance methods.

This PR

  • use cases
  • benefits
    • readability
    • maintainability
    • speed
    • disk space
  • how to implement
    • model
    • migration
      • warn about changing persisted data after change to enum declaration
  • comparison to scopes
  • class/instance methods exposed

Issue

Closes #29099

Additional Information

Pull Request Requirements

  • I have thoroughly read and understand The Odin Project curriculum contributing guide
  • The title of this PR follows the location of change: brief description of change format, e.g. Intro to HTML and CSS lesson: Fix link text
  • The Because section summarizes the reason for this PR
  • The This PR section has a bullet point list describing the changes in this PR
  • If this PR addresses an open issue, it is linked in the Issue section
  • If any lesson files are included in this PR, they have been previewed with the Markdown preview tool to ensure it is formatted correctly
  • If any lesson files are included in this PR, they follow the Layout Style Guide

Add lesson content: section on enums

* use cases
* benefits
  * readability
  * maintainability
  * speed
  * disk space
* how to implement
  * model
  * migration
    * warn about changing persisted data after
      change to enum declaration
* comparison to scopes
* class/instance methods exposed
@github-actions github-actions bot added the Content: Ruby on Rails Involves the Ruby on Rails course label Nov 23, 2024
Copy link
Contributor Author

@sean-garwood sean-garwood left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I double-linked the enum docs in the assignment and additional resources sections. Will leave it up to reviewer to determine where it fits better.

Copy link
Member

@KevinMulhern KevinMulhern left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for doing this @sean-garwood, lots of great stuff here! I've left a few suggestions, please let me know if I can clarify or help with any of them.


How much do you need to understand or care about scopes? In the early going, you probably won't run into them or see why to use them. Keep them in the back of your mind for when you start working on some slightly more complicated projects that might need them.

### Enums

Enums (short for "enumerations") map symbolic names to integer constants. They
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: It is likely for an enum to be backed by an integer, but some databases will have an enum type built in. We'll likely have this corrected by someone later, best to get ahead of them 😆

Suggested change
Enums (short for "enumerations") map symbolic names to integer constants. They
Enums (short for "enumerations") map a database column, typically an integer, to a set of symbolic names.

make code more readable and maintainable, and they offer a performance boost
since queries involving integers are faster than those involving strings.

Enums are perfect for representing the state of an attribute that has a [discrete
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Can we drop: that has a [discrete value](https://en.wikipedia.org/wiki/Continuous_or_discrete_variable) please? I think the concept should be clear enough without it, and it'll be one less link to sidetrack students 😆

since queries involving integers are faster than those involving strings.

Enums are perfect for representing the state of an attribute that has a [discrete
value](https://en.wikipedia.org/wiki/Continuous_or_discrete_variable). As an
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I like this light switch example, but worry it might be too abstract to give learners a good idea of how they can apply enums to their projects.

Can something more practical/real world be used in the example? heres some ideas off the top of my head:

  • Article model - status of draft and published
  • Order model - status of pending, shipped and delivered
  • Github PR model - status of open, closed and merged


#### How to use enums

To implement `enum`s, we need to declare them in the model and add a column to
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: would it be worth showing the data migration adding the enum column? I think it might help flesh it out a little more and give learners a complete picture of how it works from database to model.

Edit: I just saw you've done this at the bottom of the example. I think it would be worth promoting it to here as the "first step".


```ruby
# Array declaration
class LightSwitch < ApplicationRecord
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think it would be worth focusing on the hash version of the enum syntax and dropping the array approach. It's not used that often in the wild, and focusing on demonstrating one approach should make it simpler and easier to understand.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I told them to include this in the issue, but I think you're actually correct and omitting this keeps things simpler 👍

enum :status, { off: 0, on: 1 } # Value independent of position; explicit
end
```

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: After showing how to define an enum, and before we get into the convenience methods. I think it would be good to come full circle and show learners how enums are used when creating a new record.

For example, if we end up using article status for the example:

class Article < ApplicationRecord
  enum :status, { draft: 0, published: 1 } 
end
article = Article.create(status: :draft)
article.status #=> "draft"

And, just like scopes, this exposes a number of class methods that return
collections:

```ruby
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I think it would be worth switching this example and the scopes example above this one around. First demonstrating the class methods and then tying them back to scopes seems like the better flow.

bin/rails g migration AddToLightSwitch status:integer
```

Importantly, changing the model declaration **will not change** values that have
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: I'm not sure if this will be necessary, but don't mind keeping it if you do. Could we include an example with it to demonstrate it?

details of scopes, but you should understand the concept and when it might be
useful.
1. Read [How to Use Enums in
Rails](https://blog.saeloun.com/2022/01/05/how-to-use-enums-in-rails/).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good find with this article. I think it should be enough for the assignment and the docs will be a better fit in additional resources.

Copy link
Contributor

@JoshDevHub JoshDevHub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stylistic nit, but in a lot of places you've added manual line breaks in the markdown. Paragraphs should all occur on one continuous line -- which makes reviewing and suggesting changes much more intuitive for us. You can still configure your editor to wrap long lines in md files for better readability locally. Hopefully that all makes sense.

Thanks a lot for adding this in btw! I think enums are a great addition to the curriculum, and they're something that pretty much every professional Rails dev works with often.

If you have a formatter that's inserting these, I recommend disabling it while working on the curriculum.

Comment on lines +291 to +292
Importantly, changing the model declaration **will not change** values that have
already been stored in the database; you must update them yourself.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Importantly, changing the model declaration **will not change** values that have
already been stored in the database; you must update them yourself.
Importantly, changing the model declaration **will not change** values that have already been stored in the database; you must update them yourself.

There shouldn't be a line break here.

Comment on lines +253 to +254
And, just like scopes, this exposes a number of class methods that return
collections:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
And, just like scopes, this exposes a number of class methods that return
collections:
And, just like scopes, this exposes a number of class methods that return collections:

Removing the manual line break.

Comment on lines +228 to +230
Enums are declared in the model's class as either an array or a hash. The hash flavor has a slight advantage
in that the integer values mapped to the `enum` are independent of their
position and the values stored in the database are explicitly stated.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Enums are declared in the model's class as either an array or a hash. The hash flavor has a slight advantage
in that the integer values mapped to the `enum` are independent of their
position and the values stored in the database are explicitly stated.
Enums are declared in the model's class as either an array or a hash. The hash flavor has a slight advantage in that the integer values mapped to the `enum` are independent of their position and the values stored in the database are explicitly stated.

Removing manual line breaks.

Comment on lines +214 to +216
Enums (short for "enumerations") map symbolic names to integer constants. They
make code more readable and maintainable, and they offer a performance boost
since queries involving integers are faster than those involving strings.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Enums (short for "enumerations") map symbolic names to integer constants. They
make code more readable and maintainable, and they offer a performance boost
since queries involving integers are faster than those involving strings.
Enums (short for "enumerations") map symbolic names to integer constants. They make code more readable and maintainable, and they offer a performance boost since queries involving integers are faster than those involving strings.

Removing manual line breaks

Comment on lines +218 to +221
Enums are perfect for representing the state of an attribute that has a [discrete
value](https://en.wikipedia.org/wiki/Continuous_or_discrete_variable). As an
example, consider a light switch: it is either in an on or an off state. It is
never between these two states, and it is never both on and off.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
Enums are perfect for representing the state of an attribute that has a [discrete
value](https://en.wikipedia.org/wiki/Continuous_or_discrete_variable). As an
example, consider a light switch: it is either in an on or an off state. It is
never between these two states, and it is never both on and off.
Enums are perfect for representing the state of an attribute that has a [discrete value](https://en.wikipedia.org/wiki/Continuous_or_discrete_variable). As an example, consider a light switch: it is either in an on or an off state. It is never between these two states, and it is never both on and off.

Removing manual line breaks.

Comment on lines +225 to +226
To implement `enum`s, we need to declare them in the model and add a column to
store them in the database table that stores instances of the model.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To implement `enum`s, we need to declare them in the model and add a column to
store them in the database table that stores instances of the model.
To implement `enum`s, we need to declare them in the model and add a column to store them in the database table that stores instances of the model.

Removing manual line break.

Comment on lines +317 to +321
1. Read section 14 in the [Rails Guide on
Querying](https://guides.rubyonrails.org/active_record_querying.html#scopes)
for a look at scopes. Again, you don't necessarily need to memorize all the
details of scopes, but you should understand the concept and when it might be
useful.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. Read section 14 in the [Rails Guide on
Querying](https://guides.rubyonrails.org/active_record_querying.html#scopes)
for a look at scopes. Again, you don't necessarily need to memorize all the
details of scopes, but you should understand the concept and when it might be
useful.
1. Read section 14 in the [Rails Guide on
Querying](https://guides.rubyonrails.org/active_record_querying.html#scopes)
for a look at scopes. Again, you don't necessarily need to memorize all the
details of scopes, but you should understand the concept and when it might be
useful.
Suggested change
1. Read section 14 in the [Rails Guide on
Querying](https://guides.rubyonrails.org/active_record_querying.html#scopes)
for a look at scopes. Again, you don't necessarily need to memorize all the
details of scopes, but you should understand the concept and when it might be
useful.
1. Read section 14 in the [Rails Guide on Querying](https://guides.rubyonrails.org/active_record_querying.html#scopes) for a look at scopes. Again, you don't necessarily need to memorize all the details of scopes, but you should understand the concept and when it might be useful.

Removing line breaks.

Comment on lines +322 to +323
1. Read [How to Use Enums in
Rails](https://blog.saeloun.com/2022/01/05/how-to-use-enums-in-rails/).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
1. Read [How to Use Enums in
Rails](https://blog.saeloun.com/2022/01/05/how-to-use-enums-in-rails/).
1. Read [How to Use Enums in Rails](https://blog.saeloun.com/2022/01/05/how-to-use-enums-in-rails/).

Removing line break.

Comment on lines +354 to +355
- [ActiveRecord::Enum API
docs](https://api.rubyonrails.org/classes/ActiveRecord/Enum.html)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- [ActiveRecord::Enum API
docs](https://api.rubyonrails.org/classes/ActiveRecord/Enum.html)
- [ActiveRecord::Enum API docs](https://api.rubyonrails.org/classes/ActiveRecord/Enum.html)

Removing line break.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Content: Ruby on Rails Involves the Ruby on Rails course
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Ruby on Rails | Add enums section to Active Record Queries lesson
3 participants