Question

Say I have a class like:

class Person
  module Health
    GOOD = 10
    SICK = 4
    DEAD = 0
  end
end

I can reference such Health codes like: Person::Health::GOOD. I'd like to dynamically generate a hash that maps from number values back to constant names:

{ 10 => "GOOD",
   4 => "SICK",
   0 => "DEAD" }

To do this dynamically, I've come up with:

Person::Health.constants.inject({}) do |hsh, const|
  hsh.merge!( eval("Person::Health::#{const}") => const.to_s )
end

This works, but I wonder if there's a better/safer way to go about it. It's in a Rails app, and while it's nowhere near any user input, eval still makes me nervous. Is there a better solution?

Was it helpful?

Solution

You can use constants and const_get for this purpose.

ph = Person::Health # Shorthand
Hash[ph.constants(false).map { |c| [ph.const_get(c), c.to_s ] }]
# {10=>:GOOD, 4=>:SICK, 0=>:DEAD}

I added false to .constants to prevent including any inherited constants from included Modules. For example, without false the following scenario would also include a 5 => "X" mapping:

module A
  X = 5
end

class Person
  module Health
    include A        
    # ... 
  end
end

Hash[ph.constants.map { |c| [ph.const_get(c), c.to_s ] }]
# {10=>"GOOD", 4=>"SICK", 0=>"DEAD", 5=>"X"}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top