Rails 7.1 adds validation to enums
A Rails enum is a way to define a set of allowed values for an attribute in a Rails model. Enums are stored as integers in the database but can be accessed and used in Ruby as symbols.
To define an enum, you use the enum method in your model. The enum method takes two arguments: the name of the attribute and a list of values. The values can be either an array or a hash.
Enums were added to Rails in the 4.1 version, but until Rails 7.1, the ability to validate the enums was missing.
Before Rails 7.1
Let's say you have a Rails application with a User
model with a status
column.
The status
column is an enum which specifies
whether the user is active
,
inactive
,
or
deleted
.
When you assign an incorrect value to the User
status
attribute,
it raises ArgumentError
,
as shown below:
class User < ApplicationRecord
enum status: [:active, :inactive, :deleted]
end
user = User.find_by_email("[email protected]")
user.status = :random
=> `assert_valid_value': 'random' is not a valid status (ArgumentError)
To avoid the above issue,
you can first verify whether the value assigned to the
status
attribute is valid.
user = User.find_by_email("[email protected]")
status_value = :random
if User.statuses[status_value].present?
user.status = status_value
else
# raise custom error
end
In Rails 7.1
With the changes in Rails 7.1, you can validate the enum values without raising any errors. Rails has built-in features to validate ActiveRecord objects. You have methods to validate the presence of an attribute, the length of the attribute, the numericality, or the uniqueness. Rails extended the validation feature on enums as shown below:
class User < ApplicationRecord
enum status: [:active, :inactive, :deleted], validate: true
end
// In code
user = User.find_by_email(user_params[:email])
user.status = user_params[:status]
if user.valid?
user.save
redirect_to :show
else
# render errors or redirect to page and show errors
end
// In Rails console
user = User.find_by_email("[email protected]")
user.status = :random
user.valid?
=> false
user.status = nil
user.valid?
=> false
user.status = :active
user.valid?
=> true
You can also pass additional validation options.
class User < ApplicationRecord
enum status: [:active, :inactive, :deleted], validate: { allow_nil: true }
end
user = User.find_by_email("[email protected]")
user.status = :random
user.valid?
=> false
user.status = nil
user.valid?
=> true
user.status = :active
user.valid?
=> true
If you don't pass the validate
option,
it will raise ArgumentError
as the earlier versions.
class User < ApplicationRecord
enum status: [:active, :inactive, :deleted]
end
user = User.find_by_email("[email protected]")
user.status = :random
=> `assert_valid_value': 'random' is not a valid status (ArgumentError)
To know more about this feature, please refer to this PR.