Rails 7.1 adds validation to enums

railsOctober 26, 2023Dotby Alkesh Ghorpade

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("sam@example.com")

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("sam@example.com")
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("sam@example.com")

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("sam@example.com")

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("sam@example.com")
user.status = :random
=> `assert_valid_value': 'random' is not a valid status (ArgumentError)

To know more about this feature, please refer to this PR.

Closing Remark

Could your team use some help with topics like this and others covered by ShakaCode's blog and open source? We specialize in optimizing Rails applications, especially those with advanced JavaScript frontends, like React. We can also help you optimize your CI processes with lower costs and faster, more reliable tests. Scraping web data and lowering infrastructure costs are two other areas of specialization. Feel free to reach out to ShakaCode's CEO, Justin Gordon, at justin@shakacode.com or schedule an appointment to discuss how ShakaCode can help your project!
Are you looking for a software development partner who can
develop modern, high-performance web apps and sites?
See what we've doneArrow right