ClassyEnum is a Ruby on Rails gem that adds class-based enumerator functionality to ActiveRecord attributes.
Rails & Ruby Versions Supported
- 3.1.0.rc: Mostly functional, known issue with using uniqueness validation with ClassyEnum attribute as a scope
- 3.0.0 – 3.0.9: Fully tested in a production application, no known issues
- 2.3.x: If you need support for Rails 2.3.x, please install version 0.9.1
Ruby: Ruby 1.8.7 and 1.9.2 both tested and supported
The gem is hosted at rubygems.org
You will also need to add
app/enums as an autoloadable path. This configuration will depend on which version of rails you are using.
The most common use for ClassyEnum is to replace database lookup tables where the content and behavior is mostly static and has multiple “types”. In this example, I have an ActiveRecord model called
Alarm with an attribute called
priority. Priority is stored as a string (VARCHAR) type in the database and is converted to an enum value when requested.
1. Generate the Enum
The fastest way to get up and running with ClassyEnum is to use the built-in Rails generator like so:
rails g classy_enum Priority low medium high
A new enum template file will be created at app/enums/priority.rb that will look like:
class Priority < ClassyEnum::Base enum_classes :low, :medium, :high end class PriorityLow < Priority end class PriorityMedium < Priority end class PriorityHigh < Priority end
enum_classes macro will add all the ClassyEnum behavior, which is described further down in this document.
2. Customize the Enum
The generator creates a default setup, but each enum member can be changed to fit your needs.
enum_classes method, I have defined three priority levels: low, medium, and high. Each priority level can have different properties and methods associated with it.
I would like to add a method called
send_email? that all member subclasses respond to. By default this method will return false, but will be overridden for high priority alarms to return true.
class Priority < ClassyEnum::Base enum_classes :low, :medium, :high def send_email? false end end class PriorityHigh < Priority def send_email? true end end
Note: Defining the subclasses within your enum file is only required when you will be overriding behavior and/or properties. The member subclasses still exist without being defined here because ClassyEnum.enum_classes automatically creates a class for each member. The generator only creates these subclass definitions for convenience, but they can be deleted as shown in this example.
3. Setup the ActiveRecord model
My ActiveRecord Alarm model needs a text field that will store a string representing the enum member. An example model schema might look something like:
create_table "alarms", :force => true do |t| t.string "priority" t.boolean "enabled" end
Then in my model I’ve added a line that calls
classy_enum_attr with a single argument representing the enum I want to associate with my model. I am also delegating the send_email? method to my Priority enum class.
class Alarm < ActiveRecord::Base classy_enum_attr :priority delegate :send_email?, :to => :priority end
With this setup, I can now do the following:
@alarm = Alarm.create(:priority => :medium) @alarm.priority # => PriorityMedium @alarm.priority.is? :medium # => true @alarm.priority.to_s # => 'medium' @alarm.priority.name # => 'Medium' # Should this alarm send an email? @alarm.send_email? # => false @alarm.priority = :high @alarm.send_email? # => true
The enum field works like any other model attribute. It can be mass-assigned using
Back reference to owning object
In some cases you may want an enum class to be able to reference the owning object (an instance of the active record model). Think of it as a
belongs_to relationship, where the enum can reference its owning object.
In order to create the back reference, you must declare how you wish to refer to the owner using the
owner class method.
class Priority < ClassyEnum::Base enum_classes :low, :medium, :high owner :alarm end class PriorityHigh < Priority def send_email? alarm.enabled? end end
In the above example, high priority alarms are only emailed if the owning alarm is enabled.
@alarm = Alarm.create(:priority => :high, :enabled => true) # Should this alarm send an email? @alarm.send_email? # => true @alarm.enabled = false @alarm.send_email? # => false
What if your enum class name is not the same as your model’s attribute name? No problem! Just use a second arugment in
classy_enum_attr to declare the attribute name. In this case, the model’s attribute is called alarm_priority.
class Alarm < ActiveRecord::Base classy_enum_attr :alarm_priority, :enum => :priority end @alarm = Alarm.create(:alarm_priority => :medium) @alarm.alarm_priority # => PriorityMedium
An ActiveRecord validator
validates_inclusion_of :field, :in => ENUM.all is automatically added to your model when you use
If your enum only has members low, medium, and high, then the following validation behavior would be expected:
@alarm = Alarm.new(:priority => :really_high) @alarm.valid? # => false @alarm.priority = :high @alarm.valid? # => true
To allow nil or blank values, you can pass in :allow_nil and :allow_blank as options to classy_enum_attr, like so:
class Alarm < ActiveRecord::Base classy_enum_attr :priority, :allow_nil => true end @alarm = Alarm.new(:priority => nil) @alarm.valid? # => true
Working with ClassyEnum outside of ActiveRecord
While ClassyEnum was designed to be used directly with ActiveRecord, it can also be used outside of it. Here are some examples based on the enum class defined earlier in this document.
Instantiate an enum member subclass PriorityLow
# These statements are all equivalent low = Priority.build(:low) low = Priority.build('low') low = Priority.find(:low) low = PriorityLow.new
Get a list of the valid enum options
Priority.valid_options # => low, medium, high
To add ClassyEnum support to Formtastic, add the following to your formtastic.rb initializer (config/initializers/formtastic.rb):
Then in your Formtastic view forms, use this syntax:
<%= f.input :priority, :as => :enum_select %>
Note: ClassyEnum respects the
:allow_nil options and will include a blank select option in these cases
Copyright © 2011 Peter Brown. See LICENSE for details.