How to define Enums in Ruby?

Back | ruby | 3/16/2011 |

This is going to heavily quote http://www.rubyfleebie.com/enumerations-and-ruby/,  please read that first. The proposed implementation lets you iterate over enumerated values, which is quite awesome. But it offers little in terms of reuse. Lets improve upon it and split the methods in a way that lets us include an Enum implementation with all its class methods along the way. Full Enum.rb at the end.

  1. module Enum
  2.  
  3.   ... instance methods ...
  4.   
  5.   def self.included(base)
  6.     base.extend(ClassMethods)    
  7.   end
  8.  
  9.   module ClassMethods
  10.     ... class methods ...
  11.   end
  12. end

The trick here is that when a class includes a module the module self.included method is invoked. The base parameter is the class object for the class that includes the module, so we can extend it with the ClassMethods implementation. Magical.

To define a gender enumeration we can now write the following.

  1. class Gender
  2.   include Enum
  3.   
  4.   Gender.define :MALE, "male"
  5.   Gender.define :FEMALE, "female"
  6. end

You can call Gender.all and Gender::MALE.

What’s next?

I want to be able to write define :MALE = “male” inside Gender class and I want to prevent instances of Gender outside of within Enum. First one to accomplish both gets a beer.

Full Enum.rb

  1. module Enum
  2.  
  3.   def initialize(key, value)
  4.     @key = key
  5.     @value = value
  6.   end
  7.  
  8.   def key
  9.     @key
  10.   end
  11.  
  12.   def value
  13.     @value
  14.   end
  15.   
  16.   def self.included(base)
  17.     base.extend(ClassMethods)    
  18.   end
  19.  
  20.   module ClassMethods
  21.     def define(key, value)
  22.       @hash ||= {}
  23.       @hash[key] = self.new(key, value)
  24.     end
  25.  
  26.     def const_missing(key)
  27.       @hash[key].value
  28.     end    
  29.  
  30.     def each
  31.       @hash.each do |key, value|
  32.         yield key, value
  33.       end
  34.     end
  35.  
  36.     def all
  37.       @hash.values
  38.     end
  39.  
  40.     def all_to_hash
  41.       hash = {}
  42.       each do |key, value|
  43.         hash[key] = value.value
  44.       end
  45.       hash
  46.     end
  47.   end
  48. end