2011-07-19 23 views
77

Digamos que tengo el siguiente almohadilla:Rubí Metaprogramación: dinámica nombres de las variables de instancia

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

¿Cómo podría establecer dinámicamente las claves y valores para convertirse en variables de instancia de un objeto ...

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

... así que termino con la siguiente dentro del modelo ...

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

?

Respuesta

145

El método que está buscando es instance_variable_set. Por lo tanto:

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

O, más brevemente,

hash.each &method(:instance_variable_set) 

Si la instancia de nombres de variables están perdiendo la "@" (como lo son en el ejemplo de la OP), tendrá que añadirlos, por lo que sería más como:

hash.each { |name, value| instance_variable_set("@#{name}", value) } 
+1

Perfecto, gracias! – Andrew

+15

No funcionó para mí para 1.9.3. Usé esto en cambio 'hash.each {| k, v | instance_variable_set ("@ # {k}", v)} ' – Andrei

+0

¡Demasiado! –

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

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

o.baz 
=> "qux" 
o.foo 
=> "bar" 
+1

Eso es bastante interesante ... ¿qué es exactamente el segundo 'encadenado' .new() 'haciendo? – Andrew

+2

@Andrew: 'Struct.new' crea una nueva clase basada en las teclas hash, y luego la segunda' new' crea el primer objeto de la clase recién creada, inicializándolo a los valores de la Hash. Ver http://www.ruby-doc.org/core-1.8.7/classes/Struct.html – DigitalRoss

+2

Esta es realmente una gran manera de hacerlo, ya que es más o menos lo que Struct está hecho para. – Chuck

4

también puede utilizar send, que evita que el usuario ajuste de instancia inexistente variables:

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

Use send cuando en su clase hay un colocador como attr_accessor para sus variables de instancia:

class Example 
    attr_accessor :foo, :baz 
    def initialize(hash) 
    hash.each { |key, value| send("#{key}=", value) } 
    end 
end 
Cuestiones relacionadas