Skip to content

“Soft” enum columns for Rails

Here’s a quick and easy way to use enum-style columns implemented directly on an active record model.

=begin rdoc
  Usage: 
  class Box < ActiveRecord::Base
    include Enumify
    enumify :state, :pending, :in_progress, :complete
  end

  box = Box.new
  box.state = :wat # raises ValueNotFound error
  box.state = :in_progress # sets column value to 2
  box.state # returns :in_progress
=end
module Enumify
  class ValueNotFound < Exception; end
  
  def self.included base
    base.send :include, InstanceMethods
    base.send :extend,  ClassMethods
  end
  
  module InstanceMethods
    def read_attribute attr_name
      if enumified? attr_name
        self.class.enumified_attrs[attr_name][super(attr_name)]
      else
        super(attr_name)
      end
    end
    
    def write_attribute attr_name, value
      if enumified? attr_name
        vals = self.class.enumified_attrs[attr_name]
        if vals.index(value).nil?
          raise ValueNotFound, "#{value} is not a valid #{attr_name} value"
        else
          attribute_will_change! attr_name.to_s
          super attr_name, vals.index(value)
        end
      else
        super attr_name, value
      end
    end
    
    def enumified? attr_name
      self.class.enumified?(attr_name)
    end
  end
  
  module ClassMethods
    attr_accessor :enumified_attrs
    
    def enumify attr_name, *values
      @enumified_attrs = {} if @enumified_attrs.nil?
      @enumified_attrs[attr_name] = values
      
      define_method(:"#{attr_name}=") do |value|
        write_attribute(attr_name, value)
      end
      
      define_method(attr_name) do |*args|
        read_attribute(attr_name)
      end
    end
    
    def enumified? attr_name
      @enumified_attrs && @enumified_attrs[attr_name].present?
    end
  end
end

Categories: Code.

Tags: ,

Comment Feed

No Responses (yet)



Some HTML is OK

or, reply to this post via trackback.