Introducing Literal Enums in Ruby
I just released a new library to make it easier to define and work with enums in Ruby and Rails. An enum is essentially a named enumeration of constant values. For example, we could define an enumeration of colors: Color::Red
, Color::Green
and Color::Blue
.
class Color < Enum
Red("ff0000")
Green("00ff00")
Blue("0000ff")
end
The basics
Each member, e.g.
Color::Red
, is an instance of the classColor
.We can get a list of colors by asking the
Color
class for its members, callingColor.members
, which returns a Set of members.We can also get a list of its member values with
Color.values
which returns a Set of values.Given a value, Color can cast it to a member. For example,
Color.cast("ff0000")
returnsColor::Red
.You can also query a member for its value directly, e.g.
Color::Red.value
returns"ff0000"
.Additionally, the
Color
class itself isEnumerable
.each
,map
, etc. enumerate over members.Each member responds to predicates that map to the member names, so
Color::Red.red?
returnstrue
andColor::Red.blue?
returnsfalse
.
Enum members are objects
Each enum member subclasses its enum class and responds to instance methods defined on that class.
For example, If your users can select their favourite color form the Color
enum, you can add a method to the Color
class that allows you to get a relation of users from any instance.
class Color < Enum
Red("ff0000")
Green("00ff00")
Blue("0000ff")
def users
User.where(favourite_color: self)
end
end
Singletons
You can also define singleton methods on enum members. This Switch
enum, for example has two members: Switch::On
and Switch::Off
, and you can call toggle
on either of them to return the other.
class Switch < Enum
On do
def toggle
Off
end
end
Off do
def toggle
On
end
end
end
State machine
You can set up a simple state machine by defining transitions_to
singleton methods on enum members to return either another member of Array of members.
class State < Enum
Pending do
def transitions_to
[Approved, Rejected]
end
end
Approved do
def transitions_to
Published
end
end
Rejected do
def transitions_to
Deleted
end
end
Deleted()
Published()
end
You can transition between members using one of two methods:
First, you can call transition_to
on a member with the member you wish to transition to. This will return the new member if the transition is valid or raise a LiteralEnums::TransitionError
if the transition is not valid.
Alternatively, you can call the new state as a method on the old state. State::Pending.approved
returns State::Approved
, since this is a valid transition.
Rails support
To use Literal Enums in a Rails app, install the literal_enums-rails Gem. This gem ships with serializers for ActiveJob and ActiveRecord. Just pass enums to jobs like any other value and they will be automatically serialised.
To use literal enums as ActiveRecord attributes, simply define the attribute on your model and set the type to Enum[YourEnumClass]
. Enums are serialised as their value, so make sure the value type matches the type of the database column.
class User < ApplicationRecord
attribute :favourite_color, Enum[Color]
...
end
Additionally, the Rails plugin provides has_many associations so you can go from a member to associated ActiveRecord relations.
class Color < Enum
Red("ff0000")
Green("00ff00")
Blue("0000ff")
has_many :users, as: :favourite_color
end
I hope you found this useful. Please check out literal_enums and literal_enums-rails on GitHub and let me know what you think.