Introducing Literal Enums in Ruby
An object-oriented enum pattern for Ruby and Rails with literal-style definition syntax.
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:
class Color < Enum Red("ff0000") Green("00ff00") Blue("0000ff") end
Each member, e.g.
Color::Red, is an instance of the class
We can get a list of colors by asking the
Colorclass for its members, calling
Color.members, which returns a Set of members.
We can also get a list of its member values with
Color.valueswhich returns a Set of values.
Given a value, Color can cast it to a member. For example,
You can also query a member for its value directly, e.g.
Colorclass itself is
map, etc. enumerate over members.
Each member responds to predicates that map to the member names, so
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
You can also define singleton methods on enum members. This
Switch enum, for example has two members:
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
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::Approved, since this is a valid transition.
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