How And Why To Use Enums In Ruby On Rails
By: Chris I.
Tags: Ruby, Learning To Code, Coding, Programming, Rails

How And Why To Use Enums In Ruby On Rails

Photo by Karolina Grabowska from Pexels

Understanding data structures in Ruby/Rails is an easy way to supercharge your development speed.

Enums are designed to whitelist and specify “types” on model instances.

Enums (introduced in Rails 4.1) are a feature of Rails, not Ruby.

But are ARE enums?

Enums are constant mappings of values to integers

They’re constants. They’re defined in advance and don’t change.

They’re used when an instance of a model can be only one of several predefined types.

If an item in a supermarket can only be a fruit or a vegetable (not both), it’s a great case for enums.

An enum example, as defined in a model

enum item_type: {

fruit: 0,

vegetable: 10,

meat: 20,

bread: 30

}

An alternative: constant hashes

I’ve seen constant hashes used to achieve a similar affect.

ITEM_TYPE = {

fruit: 0,

vegetable: 10,

meat: 20,

bread: 30

}

That said, enums are the Rails convention, and make it easier for future developers to understand what you’ve written.

Not only do they imply “type”, but they do some cool magic behind the scenes (which we’ll get to in a moment)

A couple more examples where enums may apply.

pet.rb has an enum called species , which can only be dog , fish or cat .

car_part.rb has an enum called status which can only be one of in_development , in_production , obsolete , or unknown .

Enums can be defined as arrays or hashes

There are 2 ways to define enums, but I recommend the latter.

An array enum

enum status: [:available, :discontinued, :pending]

A hash enum

enum status: {

available: 0,

discontinued: 10,

pending: 20

}

The first is more concise but lacks flexibility. The second is more extensible because you can choose which integers to use.

Enums are integers in migrations

When creating columns in a migration that you want to use with an enum, set the columns to integer .

Below is a migration to create an items table where each item represents a distinct product in a supermarket.

class CreateItems < ActiveRecord::Migration[6.0]

def change

create_table :items do |t|

t.string :name

t.integer :item_type, default: 0

t.integer :status, default: 0

t.timestamps

end

end

end

Corresponding model file, item.rb

class Item < ApplicationRecord



enum status: {

available: 1,

discontinued: 2,

pending: 3}



enum item_type: {

fruit: 10,

vegetable: 20,

meat: 30,

bread: 40}



end

You can now use either the key or the value when saving the record.

Item.new(

name:'Tomato',

status:'available', # KEY

item_type:10) # VALUE

Note an error will be thrown if your input doesn’t match keys/values in the enum.

Active Record auto converts between strings and integers

This is cool.

At the Rails level, values are the enum strings.

#<Item

id: 2,

name: "Tomato",

item_type: "fruit",

status: "available",

created_at: "2020-11-20 18:53:19",

updated_at: "2020-11-20 18:53:19">

But at the db level, values are the enum integers.

The big benefit of this is that you can change the string in your model, and it automatically applies to all records in the database when loaded.

So ifwe change our enum from this.

enum item_type: {fruit: 10...}

To this.

enum item_type: {sweet_fruit: 10...}

The loaded records automatically have the new string value, “sweet_fruit” .

#<Item

id: 2,

name: "Tomato",

item_type: "sweet_fruit",

status: "available",

created_at: "2020-11-20 18:53:19",

updated_at: "2020-11-20 18:53:19">

Some other functionality

A few methods are added by default to any column with an associated enum.

Question marks

Returns true if the current value of the enum matches.

Item.last.sweet_fruit?

=> true Item.last.meat?

=> false

Explanation marks

Updates the value.

Item.last.item_type => "sweet_fruit"

Item.last.meat!

Item.last.item_type => "meat"

Tips on making the best use of enums

Use hashes rather than arrays for defining enums. This allows you to set the integers yourself. Don’t use consecutive integers when defining an enum. Use 10, 20, 30… rather than 1, 2, 3… . This gives more space to add new keys/values in the future. If vegetable is 20 and we want to add root_vegetable , I’d rather add that as 21 , rather than at the end of the list.

Conclusion

That’s enums in a nutshell. They’re perfectly suited for designating types available to instances of a model.

Unlike in other languages, they’re a feature of the framework (Rails), and not the language (Ruby). That said, their coupling with Rails provides unique advantages like automatic conversions between strings and integers at the time of saving/loading.

Let me know in the comments if I missed anything important!

As always, happy coding.