103

Let's say I have the following hash:

{ :foo => 'bar', :baz => 'qux' }

How could I dynamically set the keys and values to become instance variables in an object...

class Example
  def initialize( hash )
    ... magic happens here...
  end
end

... so that I end up with the following inside the model...

@foo = 'bar'
@baz = 'qux'

?

Andrew
  • 42,517
  • 51
  • 181
  • 281

4 Answers4

188

The method you are looking for is instance_variable_set. So:

hash.each { |name, value| instance_variable_set(name, value) }

Or, more briefly,

hash.each &method(:instance_variable_set)

If your instance variable names are missing the "@" (as they are in the OP's example), you'll need to add them, so it would be more like:

hash.each { |name, value| instance_variable_set("@#{name}", value) }
amacy
  • 280
  • 6
  • 14
Chuck
  • 234,037
  • 30
  • 302
  • 389
16
h = { :foo => 'bar', :baz => 'qux' }

o = Struct.new(*h.keys).new(*h.values)

o.baz
 => "qux" 
o.foo
 => "bar" 
DigitalRoss
  • 143,651
  • 25
  • 248
  • 329
  • 1
    That's pretty interesting... what exactly is the second chained `.new()` doing? – Andrew Jul 19 '11 at 04:26
  • 3
    @Andrew: `Struct.new` creates a new class based on the hash keys, and then the second `new` makes the first object of the just-created class, initializing it to the values of the Hash. See http://www.ruby-doc.org/core-1.8.7/classes/Struct.html – DigitalRoss Jul 19 '11 at 04:38
  • 2
    This is actually a really great way to do it since this is pretty much what Struct is made for. – Chuck Jul 19 '11 at 05:37
  • 2
    Or use [OpenStruct](http://www.ruby-doc.org/stdlib-1.9.3/libdoc/ostruct/rdoc/OpenStruct.html). `require 'ostruct'; h = {:foo => 'foo'}; o = OpenStruct.new(h); o.foo == 'foo'` – Justin Force Sep 06 '12 at 20:16
  • I had to map my keys to symbols: `Struct.new(*hash.keys.map { |str| str.to_sym }).new(*hash.values)` – erran Jul 07 '13 at 23:34
7

You can also use send which prevents the user from setting non-existent instance variables:

def initialize(hash)
  hash.each { |key, value| send("#{key}=", value) }
end

Use send when in your class there is a setter like attr_accessor for your instance variables:

class Example
  attr_accessor :foo, :baz
  def initialize(hash)
    hash.each { |key, value| send("#{key}=", value) }
  end
end
Asarluhi
  • 1,280
  • 3
  • 22
  • 43
6

You make we want to cry :)

In any case, see Object#instance_variable_get and Object#instance_variable_set.

Happy coding.

  • er yes, i couldn't help wondering... why? when would be a good time to use this? – Zach Smith Feb 10 '15 at 21:02
  • for example, I might want to have a generic `set_entity` callback for all controllers, and I don’t want to interfere with existing instance variables `def set_entity(name, model); instance_variable_set(name, model.find_by(params[:id])); end;` – user1201917 Feb 18 '20 at 07:36