Enums in Rails 4

David Buchan-Swanson
Tanda Product Team
Published in
2 min readJun 13, 2017

I was updating some columns today, from boolean to enums. There were two columns that stored very similar data, with (obviously) different column names.

My solution was to have two enums, both with the same options. My model looked like:

enum preferred_item: [:not_applicable, :requested, :processed]
enum default_item: [:not_applicable, :requested, :processed]

This perfectly represented what I wanted. I could see the status of the preferred item, and I could see the status of the default item. And this also gave me a new type, processed to mark when we had handled the request, if it was present.

I restarted my Rails console, and entered a test query to make sure it all worked:

ArgumentError: You tried to define an enum named "default_item" on the model "Order", but this will generate a instance method "not_applicable?", which is already defined by another enum.

Ouch. Rails, in it’s infinite wisdom, decided that defining methods on the model corresponding to the enum options was a good idea.

I had a look online, and found that there existed a _prefix option. When set to true, it prefixed these methods with the column name. Alternatively, when set to some string, it will prefix the methods with that string.

Catch is: we’re running Rails 4 (migration to 5 in progress), and the _prefix option (and the associated _suffix, which works the same way, but on the other side) only exists in Rails 5.

So, we did the only reasonable thing in a Rails application, and monkey-patched the functionality from Rails 5 into our application. Adding an initializer (aptly named ar_5_enum.rb in our case), and copying in the functionality from the new ActiveRecord implementation gave us the requisite features. You can find our implementation here.

I updated my model file to look like:

preferred_item: [:not_applicable, :requested, :processed], _prefix: true
default_item: [:not_applicable, :requested, :processed], _prefix: true

This left me with some handy helpers that don’t get in the way, and I can use multiple enums with the same values. I now get:

> Order.last.preferred_item
"requested"
> Order.last.preferred_item_requested?
true

--

--