Blind Squirrel

Once in a while, even a blind squirrel finds a nut.

Ruby Mixin for the Enum Pattern

Sometimes you just want to use an Enum. Unfortunately, if you’re a Ruby developer, Ruby does not offer a native enum structure.

Here’s a simple approach using a mixin module:

Enum.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
module Enum
  def const_missing(key)
    @enum_hash[key]
  end

  def add_enum(key, value)
    @enum_hash ||= {}
    @enum_hash[key] = NameValuePair.new(value, key.to_s.downcase)
  end

  def each
    @enum_hash.each {|key, value| yield(key, value) }
  end

  def enums
    @enum_hash.keys
  end

  def enum_values
    @enum_hash.values
  end

  def get_enum_hash
    @enum_hash
  end

  def find_by_key(key)
    @enum_hash[key.upcase.to_sym]
  end
end

The Enum mixin depends on a NameValuePair class to hold the data:

NameValuePair.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class NameValuePair
  attr_reader :label, :value

  def initialize(label, value)
    @label = label
    @value = value
  end

  def first
    @label
  end

  def last
    @value
  end
end

I included first and last methods to better support the select and options_for_select helper methods in Rails. Here’s how you might use it:

1
2
3
4
5
6
7
8
9
10
11
12
class FooEnum
  extend Enum

   self.add_enum(:APPLE, "Apple")
   self.add_enum(:PEAR, "Pear")
   self.add_enum(:ALL, "All Fruit")
end

FooEnum::APPLE ==> #<NameValuePair @value="apple", @label="Apple">
FooEnum::ALL.value ==> "all"
FooEnum::ALL.label ==> "All Fruit"
FooEnum.find_by_key('apple') ==> #<NameValuePair @value="apple", @label="Apple">