Rails provides a powerful feature called enum
that allows you to map attribute values to integers in the database while keeping them human-readable in your application. This feature enables you to use symbols instead of raw integers in your code, making it more expressive and maintainable.
In this blog post, we’ll explore how to define and use enum
attributes in Rails, covering everything from basic setup to advanced configurations like scopes, defaults, string persistence, and validations.
Basic Usage
To define an enum attribute, use the enum
method in your model. Here’s a simple example:
class Order < ActiveRecord::Base enum :status, [ :pending, :shipped, :delivered, :cancelled ] end
How It Works:
- The
status
column is stored as an integer in the database. -
:pending
is mapped to0
,:shipped
to1
,:delivered
to2
, and:cancelled
to3
based on their order. - You can update and query the attribute using either integers or symbols.
order = Order.new order.status = :pending order.pending? # => true order.status # => "pending" order.status = 2 order.delivered? # => true order.status # => "delivered"
Built-in Scopes
Using enums automatically creates scopes based on the attribute values:
Order.pending # Fetches all pending orders Order.not_pending # Fetches all non-pending orders Order.shipped # Fetches all shipped orders Order.delivered # Fetches all delivered orders Order.cancelled # Fetches all cancelled orders
If you want to disable these scopes, set :scopes
to false
:
class Order < ActiveRecord::Base enum :status, [ :pending, :shipped, :delivered, :cancelled ], scopes: false end
Setting Default Values
You can set a default value for an enum attribute using the :default
option:
class Order < ActiveRecord::Base enum :status, [ :pending, :shipped, :delivered, :cancelled ], default: :pending end order = Order.new order.status # => "pending"
Explicit Mapping of Enum Values
If you prefer to define specific mappings instead of relying on implicit integer assignments, use a hash:
class Order < ActiveRecord::Base enum :status, pending: 0, shipped: 1, delivered: 2, cancelled: 3 end
You can also use string values instead of integers (though this may impact performance):
class Order < ActiveRecord::Base enum :status, pending: "pending", shipped: "shipped", delivered: "delivered", cancelled: "cancelled" end
Accessing Enum Mappings
To retrieve the integer value mapped to an enum symbol, use:
Order.statuses[:pending] # => 0 Order.statuses["shipped"] # => 1
This is useful when writing raw SQL queries:
Order.where("status <> ?", Order.statuses[:cancelled])
Prefixes and Suffixes
When dealing with multiple enums that share similar values, you can use :prefix
or :suffix
to prevent conflicts:
class Order < ActiveRecord::Base enum :status, [ :pending, :shipped, :delivered, :cancelled ], suffix: true enum :payment_status, [ :pending, :completed, :failed ], prefix: :payment end
This will generate:
order.pending_status! order.shipped_status? # => false order.payment_completed! order.payment_pending? # => false
Disabling Instance Methods
If you don’t need the automatically generated methods, you can disable them:
class Order < ActiveRecord::Base enum :status, [ :pending, :shipped, :delivered, :cancelled ], instance_methods: false end
Validating Enum Values
To ensure only valid enum values are assigned, use :validate
:
class Order < ActiveRecord::Base enum :status, [ :pending, :shipped, :delivered, :cancelled ], validate: true end
Example validation behavior:
order = Order.new order.status = :unknown order.valid? # => false
You can also allow nil
values:
class Order < ActiveRecord::Base enum :status, [ :pending, :shipped, :delivered, :cancelled ], validate: { allow_nil: true } end
Handling Errors
If an invalid value is assigned, an ArgumentError
will be raised:
order.status = :unknown # Raises 'unknown' is not a valid status (ArgumentError)
Conclusion
Using enum
in Rails models simplifies your code, improves query readability, and enforces data integrity. Whether you’re defining simple status fields or complex multi-enum configurations, the flexibility of enum
ensures that your models remain clean and maintainable.
By leveraging scopes, default values, explicit mappings, and validations, you can ensure that your application handles enumerations efficiently while maintaining a high level of clarity in your codebase.
Top comments (0)