Idiomatic Ruby: Enumerations


#1

Hi. I’d appreciate comments/criticisms on the following approach &
code:

For some variables, we need to be able to choose from enumerations.
For instance, we track different choices on a webform - each has an
integer code, as well as some other properties. I want to implement a
class which these are made out of.

How’s this?:

class TimeSlot

# Only use predefined
private_class_method :new

# The text description
attr_reader :text

# The integer code
attr_reader :code

      # The legacy_id (key in a legacy database)
attr_reader :legacy_id

def initialize(text, code, legacy_id)
	@text = text
	@code = code
	@legacy_id = legacy_id
end

MORNING = new('morning', 2, 1001)
AFTERNOON = new('morning', 3, 1002)
EVENING = new('evening', 4, 1003)
NIGHT = new('night', 5, 1004')

      # This next line is unDRY, but I'm not sure how to eliminate

it.
# Perhaps instead of just listing the constansts, use a
register_value method which both defines the constant
# and adds it to the @@values - but I’m not sure how to
implement this
@@members = [MORNING, AFTERNOON, EVENING, NIGHT]

def self.from_code(c)
	@@members.find { |t| t.code == c } || raise ArgumentError, "No

TimeSlot has code #{c}"
end

end

Also - we have several of these - how would I extract a common
Enumeration mixin? I’d like to be able to include Enumeration, much as
you include Singleton, to turn a class into an enumeration type along
these lines.


#2

removed_email_address@domain.invalid wrote:

class TimeSlot
# The legacy_id (key in a legacy database)
EVENING = new(‘evening’, 4, 1003)
NIGHT = new(‘night’, 5, 1004’)

     # This next line is unDRY, but I'm not sure how to eliminate

it.
# Perhaps instead of just listing the constansts, use a
register_value method which both defines the constant
# and adds it to the @@values - but I’m not sure how to
implement this
@@members = [MORNING, AFTERNOON, EVENING, NIGHT]

I would not use a class variable here as it does not seem needed. A
simple
class member will be sufficient. As for the definition method:

def self.enum(*a)
c = new(*a).freeze
(@members || = []) << c
c
end

Then you can do

MORNING = enum ‘morning’, 2, 1001

OTOH, you do not need this at all. You can simply use
TimeSlot.constants to
get all constants defined in this class. If you need to be more
selective
(foe example if you define nested classes in this class) you can do

def self.enums
constants.select {|x| self === x}
end

Which will select only instances of TimeSlot.

along these lines.
I think I’ve seen an enumeration module on RAA. If not it will be quite
easy to put some code in a module and implement YourModule#included or
YourModule#extended to make certain changes (like making new private).
HTH

Kind regards

robert