Reasonable attempt to do type-safe enum for Ruby?

Perhaps this has been tried before. But I didn’t find it. Google
searches showed many different ways to implement an “enum” data type for
Ruby. Searching on forum suggested using hashes, arrays and Struct. But
none of them seemed type-safe to me. So, I went ahead and did something
like below, for an enum named GenericState which represents a “state” of
some kind.

Please comment on this approach.

An enum kind of model. No persistent equivalent.

I still don’t know the correct way of doing enums in Ruby!

class GenericState
attr_reader :state

def initialize(s)
@state = s
end

def to_s
@state.to_s
end

ACCEPTED = GenericState.new(:accepted)
EXPIRED = GenericState.new(:expired)
IGNORED = GenericState.new(:ignored)
REJECTED = GenericState.new(:rejected)
SENT = GenericState.new(:sent)
UNSURE = GenericState.new(:unsure)
STATES = [ACCEPTED, EXPIRED, IGNORED, REJECTED, SENT, UNSURE]

class << self
include Enumerable
def each(&block)
STATES.each(&block)
end
alias all entries
end

convenience methods

def one_of_my_predicates?(meth)
# meth is a symbol
$1 if meth =~ /(.+)?$/ && STATES.any?{|ss| ss.to_s == $1}
end

def run_predicate(method_name)
method_name == to_s
end

def method_missing(meth, *args, &block)
matched = one_of_my_predicates?(meth)
if matched
run_predicate(matched)
else
super
end
end

end

The method_missing protocol does not have to be implemented, but it
facilitates more readable usage as indicated below.

#usage
puts GenericState.any? {|x| x == GenericState::SENT} # => true
puts GenericState.all # => An array of all GenericState objects!
s1 = GenericState::SENT
s2 = GenericState::ACCEPTED
s3 = GenericState::ACCEPTED
puts s1 == s2 # => false
puts s3 == s2 # => true
puts s2.object_id; puts s3.object_id #=> they are the same objects
puts “Does s1 imply sent? : #{s1.sent?}” # => true
puts “Does s3 imply accepted? : #{s3.accepted?}” # => true
puts s1.to_s #=> “sent”

Best Regards,
Kedar

I need to have constructor private like:
class GenericState
private_class_method :new

reminder …

end

On Fri, May 20, 2011 at 4:35 PM, Kedar M.
[email protected] wrote:

An enum kind of model. No persistent equivalent.

end

ACCEPTED = GenericState.new(:accepted)
EXPIRED = GenericState.new(:expired)
IGNORED = GenericState.new(:ignored)
REJECTED = GenericState.new(:rejected)
SENT = GenericState.new(:sent)
UNSURE = GenericState.new(:unsure)
STATES = [ACCEPTED, EXPIRED, IGNORED, REJECTED, SENT, UNSURE]

There is definitively too much redundancy here. STATES is superfluous
since you can use method constants for that. Also, you repeat the
name.

meth is a symbol

run_predicate(matched)
#usage
puts s1.to_s #=> “sent”
How about

Cheers

robert

Yet another enum... · GitHub

Wow. This is meta programming on steroids :-). Thanks, will study it
more.