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
endIn 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?
=> trueYou 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?
=> trueIf 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.