Enumish
Enumish is "Database-backed Enum for ActiveRecord".
It gives you the ability to create Enum-like properties directly in the database.
Simple Example
Given a model:
class Color < ActiveRecord
include Enumish
end
You could do things such as:
color = Color.blue # gets the color blue from the database
color.blue? # => true
color.red? # => false
color.to_s # => "blue"
color.to_sym # => :blue
color.id # returns the ID from the database
Color.not_a_color # raises MethodNotFound
A-bit-more-advanced-but-still-simple Example
You can combine Enumish with any other ActiveRecord functionality or Gems. For example, here's a model that uses Enumish and RankedModel, as well as some custom code.
class Color < ActiveRecord
include RankedModel
ranks :position
include Enumish
class << self
def select_options
Color.rank(:position).where(enabled: true).map do |s|
{ s.human_description => s.id }
end
end
def all_primary
Color.rank(:position).where(enabled: true).where(primary: true)
end
end
def human_description
"The color #{self.to_s}"
end
end
Such a model allows you, for example, to have a relationship between two models such as:
class Car < ActiveRecord
has_one :color
end
car = Car.new
car.color = Color.blue
car.save!
car.color.blue? # => true
car.color.red? # => false
car.color == Color.blue # => true
car.color_id # => Integer
# NOTE: table `cars` has column `color_id` of type `integer`
The method .select_options
above could be used to populate an HTML select
element.
The value of each element would be the id
of the corresponding Color
record.
Migration
For each model that includes Enumish, add a column named short
of type string
. This
column will be used to store the "short" representation of the record. It will also be used
as a method name, so make sure it only contains characters that are valid in a Ruby method name.
class CreateColors < ActiveRecord::Migration[5.0]
def change
create_table :colors do |t|
t.string :short, null: false # << This!
t.string :long, null: false
t.boolean :enabled, default: true, null: false
t.integer :position, null: false
t.timestamps
end
end
end
If you'd rather name the column something else, you can override the enum_id
method in your
model:
class Color < ActiveRecord
include Enumish
def enum_id
:enum_value
end
end
Why not just use ActiveRecord::Enum?
Glad you asked! ActiveRecord::Enum
(AR::E going forward) is an option you should definitely consider if it fits your needs. I wrote
Enumish because I needed the following capabilities:
-
Ability to have more than one enum field per model.
For example, a
Car
model could have the following Enumish properties:exterior_color
,interior_color
,model
,transmission
. This would not be possible with vanilla AR::E. -
Database-backed properties. There are many benefits to this. Notably, Enumish allows you to:
- Maintain an ever-evolving list of properties directly in the database instead of changing and potentially breaking code. Your marketing team will love this!
- Keep a human-readable version of your Enumish properties in the database for your users
to see, or for your eyes only. Just add a
description
column. - Enable or disable some properties. Just add an
enabled
column. - Sort how properties are displayed to end-users. Pro-tip, just use
RankedModel
. - Limit possible Enumish properties under different circumstances. For example, a car from 2015
might have different color options than a car from 2016. Or in the example above, I have
an
.all_primary
method that only returns primary colors. - Perform database
JOIN
s between your model and Enumish models. - Maintain data integrity between your models and Enumish models. It's all just SQL after all.
Installation
Add this line to your application's Gemfile:
gem 'enumish'
License
The gem is available as open source under the terms of the MIT License.