NERD ALERT: I love databases. God, they’re just fantastic. Few things give me as much joy as an empty whiteboard and a couple of intricate join tables that need to be sketched out. But there’s also an art to understanding how to handle data – particularly when you have a shit ton of it.
The main issue people run into with data is first, how to go about storing it, and second, how to go about getting it when you actually need it! This doesn’t sound super complicated though, right? Wrong. Databases grow vertically, not horizontally, and they grow hella fast. At a certain point, the way that you go about storing your data ends up directly impacting how you go about retrieving it!
Many Rubyists write different helper methods to do their querying for them. But this is Rails Land, which means that we have
black Rails magic at our disposal. And with the advent of Rails 4.1, that magic now has a name: enum.
Get Yo Enum On
When Rails 4.1 was released less than a year ago, it came out with a bunch of new features. One of those was ActiveRecord enums, which essentially cut out a lot of methods and superfluous code.
ActiveRecord enums allow you to manipulate the attributes of an ActiveRecord object in Rails such that an attribute’s values map to integers in the database (as opposed to strings), and yet can also be queried by name. If all this sounds kind of crazy to you, that’s because it is! Enums are pretty cool because they have the flexibility of a string, but the speed and efficiency of an integer. You can look up values in a huge database using an integer, but you can also update and add attributes as string. Oh – you also get a bunch of methods for FREE!
So how does this magic work, exactly? I thought you’d never ask!
Multiple Columns?! Ain’t Nobody Got Time For That!
To demonstrate how to implement ActiveRecord enums, I’ll continue using last week’s ecommerce bookstore app as my example. So, I have these
Book objects in my store, and I want to keep track of what their status is: either
out of stock, or
ordered. For the sake of simplicity, we’ll pretend that I’m not a super sophisticated bookseller, so I don’t have a lot of
Books, and therefore each
Book object can only ever have one of these three states.
I might first start off by having three different columns to represent each of these states:
1 2 3 4 5 6 7
But this seems kind of…ridiculous. I’m always going to have two empty columns. This code has me all like:
Alternatively, I could combine all of these columns into one and use some helper methods that return boolean values when I call them on an instance of a
1 2 3 4 5 6 7 8 9 10 11 12 13
Well, now I have one column, so that’s better. But this is still so repetitive, ugly, and inefficient. Don’t worry, it’s about to get real good.
Query Your Little Heart Out
Implementing enum is pretty simple. First, add the macro to your class:
1 2 3
You’ll notice that I have my
status attribute categorized into my three different options. The fact that they look like symbols in an array is no mistake – each of these symbols is actually associated with its index. So
in_stock will be referenced by its index number
Next, add a column to your migration that will allow ActiveRecord enum to carry out your queries:
1 2 3 4 5
I want all my
Book objects to default to an
in_stock status when they are created, so I added a
default: 0 to my column. Adding a default is always a good way to keep your code defensive.
Now if I want to create a
Book object and check its status:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Go ahead and scroll up if you don’t believe me – I promise I didn’t make any of these methods! Enum gave them to me, FO FREE. So what’s actually going on here? Well, not much more than ActiveRecord mapping the integer corresponding to the indexes of the symbols we provided in the enum macro array.
Our database has only one column storing all this information:
status. That column has rows that are all either
3. ActiveRecord pulls the actual symbols that correspond to these array indexes, and returns them, simultaneously creating an
ordered scope. And the icing on the cake: all the helper methods that are immediately generated for us in the process!
Tips and Tricks
Enums give you a lot of flexibility. For example, I played around with the
in_stock scope of my
Book object to create a specific class method that would order the top three newest books added to my inventory:
Another thing to remember is that you cannot use the same names for different enums of the same class:
1 2 3 4
Definitely don’t do this – this will raise an ActiveRecord error!
A huge upside to using enums is their contribution to your application’s speed and performance. It’s a well-known fact that it’s much cheaper to store data as an integer in memory, rather than as a string value. Enums take advantage of that, yet allow you to use all the ActiveRecord methods you know and love.
So, you can have your human-readable and fun-to-program code without sacrificing any of the speed and performance that you need to save and access your information. I guess dreams really do come true.
- The ActiveRecord enum feature allows you compose a single complex state on your models, and can help avoid using multiple boolean value columns to check the status of different objects.
- There are a bunch more resources out there, so make sure you check them all out.
- Check out the enum release notes and documentation.