Using Enums to Make Everything Better
Okay, maybe not everything. But definitely some of the things.
When I log in to Netflix, after scrolling past the auto-playing trailer of a Netflix original movie that I don’t want to watch right now (though I probably will eventually — I do recommend Set It Up for your rom-com needs), I’m greeted with never-ending rows of options. I could scroll through what’s popular on Netflix or “Trending Now,” find something new in one of the genres Netflix recommends for me (thank you, I do enjoy “TV Shows Featuring a Strong Female Lead”), or, most likely, continue watching a TV show that I’m currently binging. But how does Netflix know that I’ve already finished the new season of Orange Is The New Black? Or that I’m currently on Season 3, Episode 7 of The Office, even through I’ve watched the entire series at least a dozen times? What information does Netflix store that sorts these shows appropriately, and how embarrassed by this data should I be?
I can imagine how Netflix might store this type of data. Let’s say we have a table that stores all of the TV shows on Netflix. We want to have some way of telling what the status of each TV show for the user is: are they currently watching it, have they finished watching it, or have they never watched any of it? So, on our
tv_shows table, along with the title of the show, we could have a status:
This would store a different string as the status for each TV show. To find all of the shows that I have watched, I would look for all of the shows where the status is “watched”:
This is do-able, and I could write methods to return the shows I want, but typing in a string for the status for every single TV show is just asking for typos in your data, and is definitely not DRY, fast, or easy to work with.
So using strings to store this information is probably not the best option. Instead, we could store those three statuses on a separate
Then, on our
tv_shows table, we could use a foreign key called
status_id to identify the status of each show:
Using the information on these two tables, I would be able to appropriately sort the TV shows. I could create a row of shows to “Continue Watching” by selecting all of the shows that have a
status_id of 3, and another row called “Watch Again” by selecting those with a
status_id of 2. To do this, I would probably want to write class methods for the
TvShow model, such as this
I could then call
TvShow.watched_shows and receive a list of all the shows that I have watched.
I could create similar methods for “shows in progress” and “unwatched shows.” That seems like a lot of work.
In addition, let’s say I wanted to find out the status of Parks and Recreation. I can find the TV Show by its title, then return its
2? What does that mean? In order to actually tell what the status of Parks and Recreation is, I would then need to find what a
status_id of 2 means using the
But this returns a status object. If I just want the string “watched,” I have one more step:
And now I finally know that I’ve watched Parks and Rec.
I could make this slightly easier by creating an association between the two classes, where a status
has_many TV shows and a show
belongs_to a status.
Then, I could find the status of a show by calling
status on it to get the status object, then
status again to get the string.
This improves it a little, but it all seems very messy and confusing. We still need to write additional methods to sort the data, and it doesn’t really make sense to have an entire table to store the name of the three statuses.
Enums can make this better! They can help our data become more readable and easier to work with. Instead of a TV show having a foreign key of
status_id , we can use an enum to store the name of the status directly on the
tv_shows table, without using a string or compromising any of the functionality that we need.
An enum can be used when there are only a few possible values for an attribute. In this case, our status can only be “watched,” “not watched,” or “in progress.” We can create an enum for the status of a TV show, which we can store on our
tv_shows table, meaning we no longer need the
statuses table. The statuses will still be stored as integers, but we can use the actual name of each status to find the shows we are after. Cool, right?
Here’s how this works. First, we create the
tv_shows table with columns for a
title stored as a string and a
status stored as an integer:
Next, we define the possible values for the
status enum in the
The order here does matter. Each status is now associated with an integer, starting with 0, which you can see by calling
When we look at our
tv_shows table now, we can see that the
status is actually stored as an integer.
This set-up gives us all of the functionality that we need, without having to write any new methods!
To find all of the shows that I have watched, we now only need to call
Woah! I didn’t have to do anything else, and it returned all of the shows with a status of “watched.” We can also see that the SQL used to retrieve these shows from our database used the integer 1 as the status. So enums are basically acting as both strings and integers at the same time. This means that we can use them in all of the ways that we used the foreign id in our previous set-up, but with simple, readable methods based on the name of each status.
Maybe I want to find out the status of The Office. That’s easy enough. I can find the TV show by its title, then return its status.
Using enums means that I now receive a string as the status, so I can easily see that The Office is
But that’s not all! Enums give you a bunch of other built-in methods, without you having to do anything else.
Have I watched Bojack Horseman?
Sure have! Look at that again. I called
.watched? on the
TvShow object, and it returned
true . I didn’t have to write a method anywhere. The enum did it!
What if I start watching Stranger Things? Do I have to remember the integer “in progress” is associated with to change its status? Of course not! I just need to find the show and set its status to
“in_progress”. Then, I can save the change to the database, where it will update the integer in the status column.
Even easier, I can use a bang method to change the status. Let’s say I finally finish watching all of the episodes of Black Mirror:
This automatically updates the status for the show in the
tv_shows table, as you can see by the
UPDATE SQL. Pretty cool!
These methods can of course be used in conjunction with your other favorite methods, too. To find out how many shows I am currently watching, I could call
Looks like I’m currently watching 3 TV shows. Enums definitely seem like the best way to represent this data, providing me with simple, readable methods that I can quickly use to find the information I need. As I continue to learn and use enums, I’m sure I will discover even more benefits and interesting ways to use them.
But for now, back to Netflix.
Shout out to Justin Weiss, whose post about enums helped me tremendously: https://www.justinweiss.com/articles/creating-easy-readable-attributes-with-activerecord-enums/