Skip to content

Instantly share code, notes, and snippets.

@jcuervo
Last active June 26, 2024 00:26
Show Gist options
  • Save jcuervo/e9e11e815951933a3e36fa943387831d to your computer and use it in GitHub Desktop.
Save jcuervo/e9e11e815951933a3e36fa943387831d to your computer and use it in GitHub Desktop.
Unveiling the Power of Enums for Scoping Queries in Rails

Unveiling the Power of Enums for Scoping Queries in Rails

Provided in ActiveRecord, enums are used to define a set of named values for an attribute on a model. The basic implementation for enums are for attributes that relates to status, stages and states.

class Task < ApplicationRecord
  enum status: { pending: 0, active: 1, completed: 2, paused: 3, archived: 4 }
end

Enums provide symbolic names for values, making the code more readable and self-explanatory. For the code above, 0, 1, 2, 3 and 4 will be saved in the field status for the Task model and the respective table field in the database. Rather than the numbers, the symbolic names pending, active, completed, paused, and archived will be referred to in the code.

Enums make sure that only the predefined values can be used for the specified attribute, thus reducing errors. The values are stored as integers in the database which is more efficient in storage than strings. Enums will also automatically generate helpful methods and scopes for querying.

Let's walk though several powerful features of enums:

1. Query methods. The generated query methods for each value makes querying based on the enum's state very straightforward. With the code above, we can write the query methods like:

pending_tasks = Task.pending
active_tasks = Task.active
completed_tasks = Task.completed
paused_tasks = Task.paused

2. Scopes for enums. We can build custom scopes for each enum values which in turn provides complex but readable queries.

class Task < ApplicationRecord
  enum status: { pending: 0, active: 1, completed: 2, paused: 3, archived: 4 }
  scope :recent_pending, -> { pending.where('created at > ?', Date.current) }
end
recent_pending_tasks = Task.recent_pending

3. Conditional queries. It is easier to construct conditional queries based on the value of the enum attribute. This is useful in controller actions or services that requires multiple criteria when filtering records.

tasks = Task.where(status: [:paused, :archived])

4. State management. Enums are often used to manage state transitions. This is very useful for workflows or state machine scenarios where filters are required based on the record's state.

class Task < ApplicationRecord
  enum status: { pending: 0, active: 1, completed: 2, paused: 3, archived: 4 }
  scope :not_archived, -> { where.not(status: archived) }
end
workable_tasks = Task.not_archived

5. Integration with form helpers. Enums allows easy creation of select drop downs in forms This makes the values in forms are consistent with the defined enum values.

<%= form_with model: @task do |f| %>
  <%= f.label :status %>
  <%= f.select :status, Task.statuses.keys.map { |s| [s.humanize, s] } %>
  <%= f.submit %>
<% end %>

6. ActiveRecord callbacks. Enums can be combined with ActiveRecord callbacks to perform actions based on the state of the enum attribute.

class Task < ApplicationRecord
  enum status: { pending: 0, active: 1, completed: 2, paused: 3, archived: 4 }
  after_update :notify_managers, if: status_changed?
  
  def notify_managers
    # send notifications to managers about task changes
  end
end

With these powerful implementations of enums, there are some limitations: First is the database support, enum support is built into ActiveRecord and doesn't rely on the database's enum types. Enum will work with any database supported by ActiveRecord. Another is the migration considerations, if there will be changes in the enum values, careful handling is necessary to avoid migration inconsistencies.

Enums in Ruby on Rails provides powerful and expressive means to handle attributes with predefined set of values either in scope queries, state management and thus improving code readability and maintainability. Leveraging this feature also ensures consistency across the application.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment