Enums in Rails 4
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