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 3 commits 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.

implement changes suggested during review

* drop array model declaration
* rearrange lesson content
  * move up migrations
  * move down equivalent scopes
* use more pertinent example
  * light_switch => article (draft, published)
* unwrap lines
  * my editor defaulted to 80 char wrapping.
  * unwrapped all lines in enums/modified sections
* drop mention of/link to discrete values
* clarify enum definition
  * typically, not always, an int
@sean-garwood
Copy link
Contributor Author

sean-garwood commented Dec 2, 2024

Thanks for the review! I've implemented the changes, but still pretty new to OSS contributions/PRs, so please let me know if I'm missing anything.

For instance, I did git fetch upstream main but when git checkout enum && git merge main, I get,

$ git merge main
hint: Diverging branches can't be fast-forwarded, you need to either:
hint: 
hint: 	git merge --no-ff
hint: 
hint: or:
hint: 
hint: 	git rebase
hint: 
hint: Disable this message with "git config advice.diverging false"

I did this after pushing to origin/enum, my feature branch for this issue that points to my forked TOP curriculum. Was checking on the using git in the real world workflow and noticed that I missed the merge step.

If passing --no-ff to git merge main, I get what might be an 'evil merge' that just has a merge x into y commit message. I don't think I've reconfigured anything through git config --global or in .gitconfig that would change whatever merge behavior was recommended in Foundations.

@JoshDevHub
Copy link
Contributor

JoshDevHub commented Dec 10, 2024

Thanks for the review! I've implemented the changes, but still pretty new to OSS contributions/PRs, so please let me know if I'm missing anything.

For instance, I did git fetch upstream main but when git checkout enum && git merge main, I get,

You have to fetch AND merge the changes to your local main, and then you can checkout this PR's branch and merge main back in.

So:

# have `main` checked out
git fetch upstream main  # fetch latest changes
git merge upstream main # apply the latest changes to your local main branch

# another option would be to just run this single command
git pull upstream main # combines the fetch/merge

git checkout enum
git merge main

This process should work, but the PR is now saying that there are conflicts with main's version of this file. So you'll be prompted to fix those when you try to merge into your enum branch. Let me know if anything else goes wrong though.

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.

This is looking great @sean-garwood! thanks for making those changes.

Happy for this to be merged if you are @JoshDevHub?

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