Ruby turn values inside a hash into local variables

You can do this with eval, but all operations on those variables must be inside the scope they were defined in.

For example, this will work:

vals = {
  "foo" => "bar",
  "baz" => "qux"
}

eval <<-EOF
  #{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") }
  puts foo
EOF

However, this will not:

vals = {
  "foo" => "bar",
  "baz" => "qux"
}

eval <<-EOF
  #{ vals.map {|k, v| "#{k} = \"#{v}\""}.join("\n") }
EOF

puts foo

as foo goes out of scope at the end of the eval. However, if all your work on the variables can be done inside the scope of the eval, it's quite doable.


Option 1

I can't recommend this except for fun, but it mostly has the effect you are after:

entry.each |k, v|
  singleton_class.send(:attr_accessor, k)
  send("#{k}=", v)
end

director                        # => "Chris Nolan"
self.director = "Wes Anderson"  # Unfortunately, must use self for assignment
director                        # => "Wes Anderson"

Instead of creating local variables it defines acccessor methods on the singleton class of the current object, which you can call as if they were local variables.

To make them more "local" you could use singleton_class.remove_method to remove these methods when you are done with them. You could even try to alias any existing singleton class methods with the same name and restore them afterwards.

Option 2

This is something I use in real code. It requires the ActiveSupport gem which comes with Ruby On Rails but can also be used on its own.

director, producer, writer = entry.values_at('director', 'producer', 'writer')

Unfortunately it requires typing each variable name twice, instead of zero times as you asked for. If you were certain about the order of the values in the hash, you could write:

director, producer, writer = entry.values  # Not so good, IMO.

I feel uneasy about this version because now the code that created the hash has a non-obvious responsibility to ensure the hash is in a certain order.

Note

If your goal is just to reduce typing, here is an approach that only requires two more characters per variable access than if they were true local variables:

e = OpenStruct.new(entry)
e.director     # => "Chris Nolan"

You can't create local variables, because of variable scope.
If you create a local variable inside a block, the variable would be only accessible inside the block itself.
Please refer to this question for more info.
Dynamically set local variables in Ruby

Tags:

Ruby