Quick Tip: Use Enums in Rails for Mapped Values

When I worked in a call center, we used to mark cases with different statuses. This allowed upper management to get a handle on where cases stood, what the bottlenecks were and flow of calls. Thankfully it has been a long time since I worked in a call center, but I have pondered how I would accomplish this task with Rails. Thankfully, Rails has a solution known as Enums.

When you are adding statuses to a model, you might be tempted to use strings to set the status. This makes perfect sense because different statuses tend to be named things like ‘pending’, ‘under review’, and ‘completed’.

--ADVERTISEMENT--

However, Rails encourages you to take advantage of Enums to replace these strings with integers. It seems counter-intuitive at first, but it is actually a perfect fit for the problem.

Enums allow you to map string values to integers in the database so they can be queried by name:

class CustomerCase < ApplicationRecord
  enum status: [:open, :closed,:under_review, :pending]
end

Now in the Rails console, we can do the following:

$ customer_case.open?

We can even use scopes:

$ CustomerCase.open

Just so we can really practice using Enums, I highly recommend creating a small Rails App:

$ rails new call_center

Change into that directory and generate a quick scaffold with lots of attributes:

$ rails generate scaffold CustomerCase title:string description:text status:integer agent:string

Notice that the status field is an integer.

Migrate the database:

$ rails db:migrate

Open up app/models/customer_case.rb and input the following:

class CustomerCase < ApplicationRecord
  enum status: [:open, :closed, :under_review, :pending]
end

Now we have a small application up and running. Go ahead and create a couple of cases yourself with different attributes in the rails console (rails c):

$ CustomerCase.create(title: "Case 1", description: "Our first case", status: :open, agent: "Me")
=> #<CustomerCase id: 1, title: "Case 1"...status: "open"
$ CustomerCase.create(title: "Case 2", description: "Our second case", status: :pending, agent: "Me")
=> #<CustomerCase id: 2, title: "Case 2"...status: "pending"

It’s also a good idea to change the line in app/views/customercase/form.html.erb that corresponds to the Enums so you can pick the actual statuses:

...
</div>
<div class="field">
  <%= f.label :status %><br>
  <%=  f.collection_select :status, CustomerCase.statuses.map{ |a| [a.first,a.first] },  :first, :second %>
</div>

I suggest creating as many different cases as possible and testing the different possibilities from the Rails console:

$ bundle exec rails c
$ cse = CustomerCase.create(title:"Case Title", description:"what has happend", status: "open" )
$ cse.open?
=> true
$ cse.closed?
=> false
$ cse.pending?
=> false
$ CustomerCase.open.to_a
CustomerCase Load (0.1ms)  SELECT "customer_cases".* FROM "customer_cases" WHERE "customer_cases"."status" = ?  [["status", 0]]
=> #<ActiveRecord::Relation..

The last example calling the open scope shows the SQL used to query the database. The query is using 0 for the value, not open, as expected.

You can also change the status of the case:

$ cse.pending!

(0.1ms) begin transaction
SQL (1.0ms) UPDATE “customercases” SET “status” = ?, “updatedat” = ? WHERE “customercases”.”id” = ? [[“status”, 3], [“updatedat”, 2016-10-05 13:34:28 UTC], [“id”, 3]]
(0.6ms) commit transaction
=> true

Enums, when used properly, can be a great addition to creating readable code. Now that we have explored Enums along with a possible use case, I hope that it will make implementing other Enum-related features into your Rails app easier.