The Abstraction

Share this post

Introducing Literal Enums in Ruby

www.theabstraction.space

Introducing Literal Enums in Ruby

An object-oriented enum pattern for Ruby and Rails with literal-style definition syntax.

Joel Drapper
Mar 14, 2022
1
Share this post

Introducing Literal Enums in Ruby

www.theabstraction.space

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

  1. Each member, e.g. Color::Red, is an instance of the class Color.

  2. We can get a list of colors by asking the Color class for its members, calling Color.members, which returns a Set of members.

  3. We can also get a list of its member values with Color.values which returns a Set of values.

  4. Given a value, Color can cast it to a member. For example, Color.cast("ff0000") returns Color::Red.

  5. You can also query a member for its value directly, e.g. Color::Red.value returns "ff0000".

  6. Additionally, the Color class itself is Enumerable. each, map, etc. enumerate over members.

  7. Each member responds to predicates that map to the member names, so Color::Red.red? returns true and Color::Red.blue? returns false.

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.

Share this post

Introducing Literal Enums in Ruby

www.theabstraction.space
Previous
Next
Comments
TopNewCommunity

No posts

Ready for more?

© 2023 The Abstraction
Privacy ∙ Terms ∙ Collection notice
Start WritingGet the app
Substack is the home for great writing