Building Maintainable Enums with Rails ActiveRecord

Photo by Veron Wessels on Unsplash

TLDR: Enums are a great user-defined model level validation for stored values. There are many ways to implement enums in Ruby on Rails, but the best way uses hashes with strings for the keys and values.

Ruby on Rails is great for newer developers as it preaches convention over configuration. What this means is that there is a community agreed upon “correct” way to setup models and controllers. This convention includes scaffolding scripts which makes setup quicker, allowing for more time implementing solutions rather than writing boilerplate code.

When creating database migrations there are a number of different types for constraining our data. Some of the most common types are strings, integers, floats and booleans. These primitive data types can have their fields extended through user-defined types. A commonly used user-defined type is the enum.

What is an enum?

An enum is a data type that restricts the possible values for a certain field. If you are working with a constant set of values for a field, you can use enums to simplify and validate inputs.

Along with offering value validation, enums also improve code readability. Enums allow developers to work with the string value instead of an integer. This makes the code more human readable as we will see later.

A final benefit of Rails enums is their built in boolean methods. The values that you declare as your enum options can be used to check for the presence of a value. For example, if you have an enum value “dog”, you can add the“dog?” Method to an object to return true or false depending on whether the value is “dog.”

There are a number of approaches to setting up enums in Rails. They all have pros and cons in their readability and maintainability that I want to explore with you. Throughout this article I will use a stoplight enum with the colors of red, yellow and green as values.

Approach 1 — Arrays

A very common approach for setting up enums on a model to define them as an array:

Figure 1. Array Enum Implementation

In this approach you define your enum and values as an array of string values. While we are using strings in our array, we will set the stoplights field as type of integer in our database table. We do this because we will store the array index of the string, not the value of the string, in our table.

This simple approach may work well to start, but is not very flexible. If we decided that we needed to remove “green” and add a “purple” value, our table would require a bit of updating.

Because the table stores the index of the string instead of the string itself, if we delete any value besides the last value in our array, we will break our index associations. Deleting “green” would cause “yellow” and “red” to shift an index, invalidating all of our table values.

To avoid this we could change “green” to “purple” in our table. This would work, but only with the assumption that we want to replace and not just delete a value from our array. How can we avoid this index issue?

Approach 2 — Hashes with Integer Keys

If your mind went to hashes then you are right! By using hashes instead of arrays, we can store an arbitrary integer in our database as our value for our corresponding color. Here is what our implementation might look like:

Figure 2. Hash Enum Implementation 1

Here we are setting integers for the values of our colors keys so our enum is independent of indexes. However, the values we are storing in our database are still integers. If another developer or business user were to look at this table, there would be no way to tell what any of these integers are. This is a real issue that we will fix with our next approach.

Approach 3 — Hashes with String Keys

To solve the issue of database readability we will make two changes. First we can change the values of keys to strings:

Figure 3. Hash Enum Implementation 2

We will also have to make a change in our database table by making our stoplights field a string field. Making these changes will give our data human readable meaning as returning this column will give users strings that tell a story instead of a set of random integers.

Final Thoughts

Enums offer a lot of power in creating meaningful data with model level validations. Let me know in the comments which of the approaches of enums you have implemented and how you plan to improve your future projects!

Notes

https://naturaily.com/blog/ruby-on-rails-enum

Former Big Beer Engineer turned Full Stack Software Engineer