Ruby Pills: Enums Options
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!