K
Kedar Mhaswade
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.newaccepted)
EXPIRED = GenericState.newexpired)
IGNORED = GenericState.newignored)
REJECTED = GenericState.newrejected)
SENT = GenericState.newsent)
UNSURE = GenericState.newunsure)
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
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.newaccepted)
EXPIRED = GenericState.newexpired)
IGNORED = GenericState.newignored)
REJECTED = GenericState.newrejected)
SENT = GenericState.newsent)
UNSURE = GenericState.newunsure)
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