Ruby Enums

Ruby does not have native support for enum (as a core module/class or keyword). Many coming from other programming languages may miss this feature (myself included, as I came from C#).

Maybe: This does not mean that you cannot represent the enum type in Ruby. There are many ways (like we all like!).

In this Ruby Pill, I’ll cover 3 ways to define enum’s in your Ruby code!

1st - A Simple Module

Take a look at this module and an example of its use:

module AlertType
  Email = 0
  Sms = 1
  Push = 2
  Letter = 3
end

class Notification
  include AlertType
  # AlertType::Push => Using inside the class (You can even omit the module name here)
end

p Notification::Push # => 2

If your main goal is to improve the legibility and organization of your code but aren’t worried about the value of each option (maybe it’s just ids in a domain table of your database), this is the simplest and most straightforward enum that you’ll have!

2nd - Using Constants

If you want to have access to all enum values, as a constant of your class, you can use a trick with class constants:

class Notification
  include AlertType
  ENUM_VALUES = [STRING_VALUE = "value1", SYMBOL_VALUE = :value2]
end

p Notification::STRING_VALUE # => "value1" 
p Notification::SYMBOL_VALUE # => :value2

Even the constants defined inside the ENUM_VALUES array can be used as class constants normally. And, this way you can access all values of the enum at the same time with Notification::ENUM_VALUES.

If the main constant ENUM_VALUES is inside the module that your class includes (like the first example), this works too!

3rd - With Some Metaprogramming

I’ll guide you through the code itself in this example:

module Kernel
  # Defining a method on the Ruby's Kernel module 
  def enum(values)
    # Each time we call it, a new module is created with a block
    Module.new do |mod|
      # first, we iterates on the list of values passed to this method
      values.each_with_index do |value, index|
        # then a constant is defined (using `const_set`), whose name is the capitalized value
        # this assumes that all values that you passed responds to #to_s (be careful!)
        mod.const_set(value.to_s.capitalize, 2**index)
      end

      # Overriding `inspect` for convenience only
      def mod.inspect
        "#{self.name} {#{self.constants.join(', ')}}"
      end
    end
  end
end

You then could use it like this:

Status = enum %w[draft published trashed banished]

class Article
  include Status

  def self.available_statuses
    Status.inspect
  end
end

puts Status.class               # => Module
puts Article::Trashed           # => 4
puts Article.available_statuses # => Status {Banished, Draft, Published, Trashed}

Final Thoughts

Although Ruby doesn’t implement enums natively, there are at least 3 ways to fill in this gap, if you like to organize your code this way!

Are you using enums? Do you define them some other way or maybe using a gem?

Let me know if you liked this post! If you have any suggestions or critics, post a comment below!