2009-10-15 12 views
35

Se ho questa classe:C'è un modo per inizializzare un oggetto attraverso un hash?

class A 
    attr_accessor :b,:c,:d 
end 

e questo codice:

a = A.new 
h = {"b"=>10,"c"=>20,"d"=>30} 

è possibile inizializzare l'oggetto direttamente dalla hash, senza di me la necessità di andare oltre ogni coppia e chiamare instance_variable_set? Qualcosa di simile:

a = A.new(h) 

che dovrebbe causare ogni variabile di istanza per essere inizializzato a quello che ha lo stesso nome nella hash.

risposta

50

È possibile definire una funzione di inizializzazione sulla tua classe:

class A 
    attr_accessor :b,:c,:d 
    def initialize(h) 
    h.each {|k,v| public_send("#{k}=",v)} 
    end 
end 

Oppure è possibile creare un modulo e poi "mescolare in"

module HashConstructed 
def initialize(h) 
    h.each {|k,v| public_send("#{k}=",v)} 
end 
end 

class Foo 
include HashConstructed 
attr_accessor :foo, :bar 
end 

In alternativa si può provare qualcosa di simile constructor

+3

+1. A proposito, potresti voler considerare l'uso di 'public_send' invece di' send' per evitare di chiamare scrittori di attributi privati ​​:) – epidemian

+1

+1 per la gemma del costruttore –

8

instance_variable_set è inteso per questo tipo di caso d'uso:

class A 
    def initialize(h) 
    h.each {|k,v| instance_variable_set("@#{k}",v)} 
    end 
end 

Si tratta di un metodo pubblico, così si potrebbe anche chiamare dopo la costruzione:

a = A.new({}) 
a.instance_variable_set(:@foo,1) 

Ma si noti l'avvertimento implicito nella documentation:

Imposta i nomi delle variabili di istanza dal simbolo per oggetto frustrando in tal modo gli sforzi dell'autore della classe per tentare di fornire un incapsulamento adeguato. La variabile non doveva esistere prima di questa chiamata.

+1

Per me, il controllo per assicurarti di poter impostare solo quelli specificati nella definizione è abbastanza importante, che rende questo suggerimento indegno –

10

OpenStruct è opportuno prendere in considerazione:

require 'ostruct' # stdlib, no download 
the_hash = {"b"=>10, "c"=>20, "d"=>30} 
there_you_go = OpenStruct.new(the_hash) 
p there_you_go.C#=> 20 
+1

Sì! E puoi anche convertire JSON in OpenStruct, che è bello. 'JSON.parse ({a: 1, b: 2} .to_json, object_class: OpenStruct)' –

Problemi correlati