March 31, 2015 by Daniel P. Clark

Rails 4’s Awesome enums

In Rails 4 enum was introduced to support a feature that people were building by hand or by gem.  What enum does for you is it associates a symbol list for you to reference which will be stored in the database as an integer.  So :cow could be 0 and :dog could be 1 in an enum of [:cow, :dog] .  But enum is far more helpful that that.  It builds scopes and boolean checker methods.

For example.  Lets create a Flower object and we’ll have kinds and colors as enums.

First we generate the model.

Then we update app/models/flower.rb

Make sure you migrate.

And now all the hard work has been done for you.

You can create a blue rose in two different ways.  With scopes, or with attributes.

As you can see I used the plural name on the class of Flower for setting the attribute values.  The plural named method is a Hash of the enum symbol names to the index of their placement in the originally defined Array.  So kinds[:rose] will return 0 for the attribute to be written to the database since it was the first item in the enum defined.

And you can access them by the same scopes (and scopes work in any order)

As I had mentioned earlier enum will add boolean checkers for every single enum option.

And you can forcefully change them with a bang method.

If you look at the record’s attributes you can see that the enum‘s are stored as integers.  The integers are the index of the Array in which you declared the enum.  So you can add new items to the end of the enum Array, but don’t change the ordering.

To check whether an enum value has been set for any of the enum fields just use a boolean check on the enum name.

Summary

As you can see enum has saved us a lot of work by defining many scopes and helper methods.  This comes in to be pretty handy.  As a consequence though you can’t use conflicting names between any enum or ActiveRecord methods.  So you may have to be clever for some names to use.  I really like having the convenience that enum adds.  And now you have this power tool in your tool set! ^_^

Please share, I’d love to hear about them.  Please feel free to comment, share, subscribe to my RSS Feed, and follow me on twitter @6ftdan!

God Bless!
-Daniel P. Clark

Image by Hernán García Crespo via the Creative Commons Attribution 2.0 Generic License.

#activerecord#boolean#database#db#enum#helper#rails#rails 4#scope#scopes

14
Leave a Reply

avatar
3 Comment threads
11 Thread replies
0 Followers
 
Most reacted comment
Hottest comment thread
5 Comment authors
Francisco QuinteroBen GrevilleDaniel P. ClarkfritzhutMrChris Recent comment authors
  Subscribe  
newest oldest most voted
Notify of
MrChris
Guest
MrChris

“but don’t change the ordering.”

Bugs be here!

fritzhut
Guest
fritzhut

Yea the gem ‘enumerize’ is way better and provides the same scope/predicate helper methods. I think this is one of the most overhyped features of rails with the worst implementation

Daniel P. Clark
Guest

How do you believe it’s the “worst implementation”?

Daniel P. Clark
Guest

@MrChris:disqus It’s a rookie mistake. But it’ll happen if you don’t know any better.

MrChris
Guest
MrChris

Are your saying that changing the order is a rookie mistake? It’s not. Changing the order of an enumeration shouldn’t affect anything.

The rookie mistake is writing code that easily breakable in the first place.

Daniel P. Clark
Guest

That’s an interesting opinion. In the case of Arrays; accessing by index will always break if the order of the Array changes. So in the same sense this is just an Array (which it is) and the records are the indexes (which they are).

It’s not a “bad” implementation. It’s just “a way” to implement it. One could argue that any code is easily breakable if one doesn’t know what one is doing. So I don’t see why you’d consider it a mistake.

MrChris
Guest
MrChris

So, when adding a new item to the enumeration do you add it to the end or the start of the enumeration array? The only way to know the answer is to know how the underlying implementation of enumeration works. That is just plain wrong and bad code. It’s brittle. Should you expect every new developer on the project to know this small implementation detail? Clearly not. Adding an item to the start of the array will completely break your software.

Daniel P. Clark
Guest

I guess it depends on the situation. A great way to enforce the order is to write a test saying the enum Array must start with these items [:a,:b,:c…] That way if the enum gets changed at the wrong end, the test fails, and the new developer can see the explanation in the test (with a note an order importance). If you have a concern of what new developers may do, the tests should account for that. Again “anything can break”, regardless of enum, based on who’s changing what.

Ben Greville
Guest
Ben Greville

Active enums are all well and good when your rails stack is the only level of business logic “dressing” your data.
However when other business applications like a data warehouse need to process this raw data, they will most likely not have access to the active enum information defined in your Rails model.
In this scenario it’s better to have that information defined inside the database tier of your application.
That way the business definition of what colours a flower can be doesn’t have to be duplicated in both Ruby and your DW.

Daniel P. Clark
Guest

You raise an interesting point. Are you familiar with any external database processors that don’t require a schema? The few I know of you still specify at least a basic schema (such as Googles big data). Haven’t looked at how they’d handle enums. I know that Postgres itself supports enum and has the information of what each enum item is in the DB. I don’t believe Rails ties in with that as Rails’ is DB impartial. I wonder what kind of hack can be implemented to integrate Postgres’ enum with Rails’ to save the field information. I’m also curious as… Read more »

Francisco Quintero
Guest

Hi! This post helped me test my enums in the console. I’m using enums to fill some select inputs, however, if I use the symbol notation(:dog, :cow), the option is printed as it is written in the enum declaration. To solve that, I declare my enums as strings(‘Dog’, ‘Cow’) and it works normal but when testing in the rails console whether they really worked I have to create them passing the exact name: Animal.create(type: Animal.types[‘Dog’]) in order to get the record save. Animal.create(type: Animal.types[:dog]) or Animal.create(type: Animal.types[:Dog]) are not available. Could this cause any trouble later? Is there a better… Read more »

Daniel P. Clark
Guest

Capitalized strings seems to be an issue for the view. So I’d advise using symbols behind the scenes and in the views simply map capitalize

Francisco Quintero
Guest

Will give it a shot. Thanks again.

Francisco Quintero
Guest

I finally worked this around. Created my enums using symbols and in views they’re displayed calling .humanize or .titleize methods.