Skip to content

Instantly share code, notes, and snippets.

@mdub
Created Aug 1, 2012
Embed
What would you like to do?
Easy enumerated types
# Provide support for defining enumerated types.
#
# Example
#
# class Colour
#
# extend EnumType
#
# define :red do
# # ...
# end
#
# define :green do
# # ...
# end
#
# define :blue do
# # ...
# end
#
# end
#
module EnumType
def self.extended(target)
class << target
protected :new # prevent arbitrary new instances
end
end
# Public: Declare a enumerated value.
#
# code - a unique (String) code that identifies this value
# block - optional block defines behaviour specific to this value
#
# Example
#
# class Colour; extend EnumType; end
#
# Colour.define :red do
#
# def rgb
# "#ff0000"
# end
#
# end
#
def define(code, &block)
value_class = generate_value_subclass(code.to_s, &block)
register_value(value_class.new)
end
# Public: Succintly declare methods to return hardcoded values.
#
# attributes - a Hash; a method will be generated for each key, returning
# the corresponding value
#
# Example
#
# Colour.define :red do
#
# hardcode :rgb => "#ff0000"
#
# end
#
def hardcode(attributes)
attributes.each do |name, value|
define_method(name) { value }
end
end
def get(name)
@all_by_code[name.to_s]
end
alias :[] :get
def fetch(name)
get(name) || raise(ArgumentError, "no such #{self}: #{name.inspect}")
end
def all
@all_by_code.values
end
def codes
@all_by_code.keys
end
private
def generate_value_subclass(code, &block)
const_name = "#{self}::#{code.upcase}"
Class.new(self) do
define_method(:code) { code }
define_method(:to_s) { code }
define_method("#{code}?") { true }
define_method(:inspect) { const_name }
class_eval(&block) if block
end
end
def register_value(value)
code = value.code
const_set(code.upcase, value)
singleton_class.send(:define_method, code) { value }
class_eval "def #{code}?; false; end"
@all_by_code ||= {}
@all_by_code[code] = value
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment