What is enum?
Enum (short form of Enumeration) is a data type used to assign names to integral constants. The names are identifiers that behave as constants in language and makes a program easy to read and maintain.
ActiveRecord::Enum
was introduced in Rails 4.1.
An enum is an attribute where the values map to
integers in the database and can be queried by name.
Enums also give us the ability to change the state of the
data very quickly.
This makes it easy to use enums in Rails
and
saves a lot of time by providing dynamic methods.
Creating database column for enum
In Rails, adding an enum to a model is pretty simple as adding an integer column to the table.
Let’s assume we have a Rails application that has a
Post model.
A post can be drafted,
published,
archived,
or trashed.
Instead of using these strings in the Post table
to denote its status,
we can use integers like 0
, 1
, 2
.
Assuming the Post
table already exists in our application
the DB migration to add status
as enum will look as below:
Note:
We have added the default value as 0 in the migration.
This means the post status will be draft
by default.
Various ways to define enum in model
We run the migration using rake db:migrate
and
in our Post model,
we need to define the enum as shown in the below example.
The enum method expects the column attribute name which it should
refer to
and
the second parameter is a list of values a post status can have.
We can also use the %i()
format to declare enums.
Since we have used arrays for declaring enums,
draft
will be mapped to 0 in the database,
published
will be mapped to 1, and so on.
Instead of an array,
we can also pass a hash where the key will be
draft
, published
, etc.,
and
the values can be assigned as per the developer’s choice.
The recommended way is to use a hash instead of an array because if we change the order of the values, we will break the whole logic inside our Rails app.
How enum works
We can fetch all the enum values or a particular enum value using the pluralized form of the column name.
Creating a published post
For the First post where status
is not passed,
it gets set to draft
by default.
Even if the status
column stores integer value,
we can pass the symbol :published
as a value to the status key.
This is because Rails knows that the status
column is an enum,
and it replaces the values internally from symbol to integer.
Verifying the post status
Rails provide dynamic methods to verify the status of a particular post.
If we create a post
and
publish it,
we usually verify by using
post.status == 'published'
.
With an enum,
we can verify using enum helpers provided by Rails.
Updating a post status
Similar to verifying the post status using ?,
Rails enum provides a helper for updating the enum value.
Instead of using
post.update(status: :archived)
,
we can use the ! method.
Working with scopes
Our Rails app
has a Post model with different statuses
and we would want to pull records only with a given status
sooner or later.
Rails has added dynamic methods to resolve this query.
To fetch all published
posts,
we might use Post.where(status: "published")
query in our Rails controllers.
Instead of this,
we can use the published
method as scope on Post.
For every status,
we have in our enum,
Rails add a class method with the same name.
In our case,
we have #draft
, #published
, #archived
, and #trashed
methods
for the Post model.
With Rails 6,
we can also add the negate condition to enum scopes.
To fetch all posts that are not published
,
we can add the not_
prefix to the published
method.
We can also disable these enum scopes by adding the
scopes: false
option to the enum defined in the model.
Using the scope methods then will raise NoMethodError.
Prefixes and Suffixes
Sometimes the enum values might not be meaningful
and
it’s better to apply prefix or suffix to enum name.
Let’s take the User model in this case with the status
column,
defined as enum
in our application.
user.active?
method can be precisely written as
user.active_status?
and similar for other enum names.
To achieve this,
we can add suffix: true
as an option to enum.
We can use the updated dynamic methods for equality check, updating and querying on user object or model.
If we pass prefix: true
,
the above methods will change to:
Prefixes
and
Suffixes are useful when a model has two columns
with enum values.
Let’s say our Post model has a category
column which is an enum
and can take free
or premium
value.
If we define these enums without prefix or suffix,
the methods Post.free
,
post.published?
,
post.premium!
will confuse developers which columns
these methods are referring to.
Instead, we can add prefix and suffix as per our requirement and call the methods accordingly.
Note:
Rails 7 introduced a new syntax for enum. For more details, please refer to our previous blog post.