Enums and Queries in Rails 4.1, and Understanding Ruby - Rails on Maui

The docs say:

In rare circumstances you might need to access the mapping directly. The mappings are exposed through a class method with the pluralized attribute name

1 Conversation.statuses # => { "active" => 0, "archived" => 1 }

This is not rare! This is critical!

For example, suppose you want to query where the status is not “archived”:

You might be tempted to think that Rails will be smart enough to figure out that

1 Conversation.where("status <> ?", "archived")

Rails is not smart enough to know that the ? is for status and that is an enum. So you have to use this syntax:

1 Conversation.where("status <> ?", Conversation.statuses[:archived])

You might be tempted to think that this would work:

1 Conversation.where.not(status: :archived)

That throws an ArgumentError. Rails wants an integer and not a symbol, and symbol does not define to_i.

What’s worse is this one:

1 Conversation.where.not(status: "archived")

The problem is that ActiveRecord sees that the enum column is of type integer and calls #to_i on the value, so archived.to_i gets converted to zero. In fact, all your enums will get converted to zero! And if you use the value of the enum attribute on an ActiveRecord instance (say a Conversation object), then you’re using a string value!

If you’re curious what the Rails source is, then take a look here: ActiveRecord::Type::Integer.

Here’s a guaranteed broken bit of code:

1 2 # my_conversation.status is a String! Conversation.where.not(status: my_conversation.status)

You’d think that Rails would be clever enough to see that the key maps to an enum and then check if the comparison value is a String, and then it would not call to_i on the String! Instead, we are effectively running this code:

1 Conversation.where.not(status: 0)

An acceptable alternative to the last code example would be:

1 Conversation.where.not(Conersation.statuses[my_conversation.status])

If you left out the not, you could also do:

1 Conversation.send(my_conversation.status)

However, I really would like to simply do these, all of which DO NOT work.:

1 2 3 Conversation.where(status: my_conversation.status) Conversation.where(status: :archived) Conversation.where(status: "archived")

This is a companion discussion topic for the original entry at http://www.railsonmaui.com//blog/2014/10/23/enums-and-queries-in-rails-4-dot-1/