DEV Community

tjray-dev
tjray-dev

Posted on

An Enum by any other name queries just as sweet

When creating models sometimes you may find yourself in a situation where you have the need for a model to belong to a subgroup. Maybe your making a blogging app and want users to be separated into readers and authors, maybe your building an e-commerce platform, and want to have buyers and sellers share many of the same features, whatever the reason you may have come up with a few ways to do this.

The first way might be to have multiple models one for each type of user. It works...but could quickly get out of hand by simply adding another subgroup, not to mention having nearly identical data sets stored multiple times is non-ideal.

Another, slightly more elegant, way of doing this would be to have a single column in our model that holds a foreign key that in turn corresponds to the subgroup we want that user to belong to. This way is better, but still leaves us with what seems like an unnecessary model in our app.

But what if there was a way to do all of that without the need for any extra tables at all?

What if I told you it was built natively into active record?

what if I told you it was as easy as declaring an array of values in the model file?

Well then, let me introduce you to the wonderful world of enum...

What is an Enum?

Enums where add to rails in version 4.1 when they where added to active record. They allow you to map a list of values to integers in a column in your database. For Example:

enum phone_number_type: [ :home, :work, :mobile ]

enum user_type [ :user, :admin, :seller ]
Enter fullscreen mode Exit fullscreen mode

these values would then be represented in the table as integers but still able to be queried by the value.

Why Enums?

the most basic technical answer to why you should use enums is two-fold; first, integer (especially single digit integers) are faster to query than strings. Second, while it is completely possible to have a model that (and thus a table) that houses this same data, it is a bit...bulky, of a solution for what amounts to a simple array of data.

but there are a few more benefits to using active record enums

for one they give you macros Like these:

# sets the phone_number_type of the contact instance to home saving it as an integer of 0
contact.home!

# evaluates the value of phone_number_type, returns a boolean
contact.home? #=> true

# returns the value of the enum
contact.phone_number_type #=> "home"
Enter fullscreen mode Exit fullscreen mode

Scopes are also provided for each of the enum values. For Instance:

#  queries for all entries with an the given enum value
User.admin
or
Contact.home
Enter fullscreen mode Exit fullscreen mode

And since the database entry is a plain integer you can even set it to a default value. (helpful if your trying to restrict how that value gets changed, looking at you admin privileges)

create_table :enum_example do |t|
  t.integer :enum_type, default: 0
end
Enter fullscreen mode Exit fullscreen mode

You can even map the values yourself, for when you really don't want to have your index to start at zero. Its as simple as declaring the enum as a hash instead of an array, like this:

enum status: { active: 1, away: 2, do_not_disturb: 3 }
Enter fullscreen mode Exit fullscreen mode

In Conclusion:

Enums are a simple and lightweight way to allow a single model to belong to a static sub-group without the need for excess models or relations.

Discussion (0)