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:
-
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
-
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
-
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])
-
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
-
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 %>
-
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.
Ps. if you have any questions
Ask here