Using ENUMS in Ruby on Rails with ActiveRecord...

Enums, or Enumerated Values have been around for a long time and I first stumbled into the while working with Java and wow, they were a whole big thing with java. Basically mini classes. Super neat but really not super necessary. Rails / AR has a more standard implementation of them, simply mapping types to values with some helper functions to get you going. The standard implementation can be expanded upon though, and the end result is very useful. 

The Problem
I’m working on a Purchase Order system, each Purchase Order (PO) having multiple Purchase Order Lines and one status. Each Purchase Order Line (Line) has its own status.
As POs are created, destroyed, sent, edited, received, back-ordered, etc. etc. the status needs to be changed as a guide for the user and the app. Same goes for the Lines. They all have their own status, and these statuses are useful for displaying information to the user, checking if the user has received something quickly and easily, viewing what stage the PO is in, etc.etc.
Just useful stuff all around.
But how do we define it? There are a number of options;
  1. We can just create our own arbitrary code system and create a new column, ie. 0=ordered, 1=received, 2=destroyed etc.etc. But that can cause a host of problems down the road. Things like now you’ve got a hardcoded, predefined list that all your old orders rely on. So if you need to make changes it’s going to mess stuff up, or we end up adding to the code list and get to 11154=received_instead_of or something like that.
  2. Adding a String column and checking against strings ie. ‘received’ , but no. Just no. Awful idea.
  3. Creating an ENUM hash system that allows us to map strings to symbols in Rails and plop them into our PGSQL database allowing all sorts of benefits and helper functions. 
  4. Number 3? Cool, number 3.
The Simple Solution
So if we want to implement a status column for a Purchase Order, we need some statuses. To keep it simple we’ll start with 3; [:in_progress, :sent, :received] . And we’ll be adding it to our PurchaseOrder object.
First we will generate a migration that will create a new column in the PurchaseOrder table;
rails g migration AddPurchaseOrderStatusToPurchaseOrder purchase_order_status:integer
which creates something like this in our Ruby on Rails project;
#Rails Project db/migrations
class AddPurchaseOrderStatusToPurchaseOrder < ActiveRecord::Migration[5.2]
 def change
    add_column :purchase_order, :purchase_order_status, :integer
  end
end
 Run a quick rails db:migrate and you’ll be ready for step numero b;
Declaring the ENUM in your model!
#Rails Project app/models/purchase_order.rb
class PurchaseOrder < ApplicationRecord
 enum purchase_order_status: [:in_progress, :sent, :received]
 end
That line in your model is what defines the ENUM values, and can save you a heck of a lot of pain. 
Want to set your PO status to received?
purchase_order.received!
Done. Want to check the status?
puts purchase_order.purchase_order_status # received
It’s a very handy function, thanks to Rails and ActiveRecord, that will be super useful for sure, but there’s a few optimisations we can toss into the mix as well;

Using a Hash To Declare It
Rather than using an array, which would cause problems similar to problem numero one above, we can declare the ENUM values using a Ruby Hash like so;
#Rails Project app/models/purchase_order.rbclass PurchaseOrder < ApplicationRecord
 enum purchase_order_status: {in_progress: 0, sent: 1, received: 2}
 end
Perfect, now if one becomes defunct you aren’t relying on an Array index for the values, you are declaring the indexes. 

Index On It
Obviously we are going to want to check on things like ‘all sent POs’, ‘all received POs’, etc. and if we want to optimise that whole process we should huck an index onto the table. That’s an easy one, just run a little migration like so;
#Rails Project db/migrations
class AddIndexToPurchaseOrders < ActiveRecord::Migration
  def change
    add_index :purchase_orders, :purchase_order_status
  end
end
{ Pre / Su } Fixing The Whole Thing
If you have an object with A BUNCH of ENUM attributes, like a Purchase Order with things like :status, :stock_code, :bin, etc. etc. etc. It can be helpful to add prefixes or suffixes to your attributes to delineate them. 

I’m just using an example from the API docs here but you get the idea;
     You can use the :_prefix or :_suffix options when you need to define multiple enums with same values. If the passed value is true, the methods are            prefixed/suffixed with the name of the enum. It is also possible to supply a custom value:

class Conversation < ActiveRecord::Base
  enum status: [:active, :archived], _suffix: true
  enum comments_status: [:active, :inactive], _prefix: :comments
end
         With the above example, the bang and predicate methods along with the associated scopes are now prefixed and/or suffixed accordingly:
conversation.active_status! conversation.archived_status? # => false conversation.comments_inactive! conversation.comments_active? # => false
 Best of luck with all your enumerating! Feel free to drop a line with any questions or comments!

Comments (Coming Soon)