Using Enums in Ruby on Rails

For Rails developers, using enums with ActiveRecord is always a good choice when you have to manage the state of an object. For example, for an online shop, you may have three states of an order - i.e. received, dispatched and delivered. One option is to add three boolean fields in the database for received, dispatched and delivered, but this approach looks ugly. A cleaner approach is to add a single string column status that includes values of received, dispatched and delivered, then add three methods in the model file.

1
2
3
4
5
6
7
8
9
10
11
12
13
class Order < ApplicationRecord
 def received?
   status == "received"
 end

 def dispatched?
   status == "dispatched"
 end

 def delivered?
   status == "delivered"
 end
end

Though this is a better approach than using boolean fields, it’s still not the most efficient due to the fact that values are stored as integers in the database and can be queried by name.

With enums, you have to create an integer field in the database named status.

1
rails generate migration add_status_to_orders status:integer

Make sure your newly created migration file looks like this:

1
2
3
4
5
class AddStatusToOrders < ActiveRecord::Migration[6.0]
 def change
   add_column :orders, :status, :integer, default: 0
 end
end

Run rails db:migrate.

In app/models/order.rb, declare your enum.

1
2
3
class Order < ApplicationRecord
 enum status: { received: 0, dispatched: 1, delivered: 2 }
end

Test your implementation.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
order = Order.create
order.received?
=> true 

order.status
=> "received" 

order.dispatched!
(0.2ms)  BEGIN
Order Update (0.6ms)  UPDATE "orders" SET "updated_at" = $1, "status" = $2 WHERE "orders"."id" = $3  [["updated_at", "2020-05-23 12:17:35.782738"], ["status", 1], ["id", 1]]
(1.3ms)  COMMIT
=> true 

order.dispatched?
=> true 

order.status
=> "dispatched" 

That’s it - happy managing!