2010-07-15 11 views
5

Sto cercando di scrivere una classe di Ruby che funziona in modo simile al modello di Rails AactiveRecord nel modo in cui vengono gestiti gli attributi:classe Aggiornamento Rubino attribuisce hash quando una proprietà cambia

class Person 
    attr_accessor :name, :age 

    # init with Person.new(:name => 'John', :age => 30) 
    def initialize(attributes={}) 
    attributes.each { |key, val| send("#{key}=", val) if respond_to?("#{key}=") } 
    @attributes = attributes 
    end 

    # read attributes 
    def attributes 
    @attributes 
    end 

    # update attributes 
    def attributes=(attributes) 
    attributes.each do |key, val| 
     if respond_to?("#{key}=") 
     send("#{key}=", val) 
     @attributes[key] = name 
     end 
    end 
    end 
end 

Quello che voglio dire è che quando ho init della classe, un "attributi" hash viene aggiornato con i relativi attributi:

>>> p = Person.new(:name => 'John', :age => 30) 
>>> p.attributes 
=> {:age=>30, :name=>"John"} 
>>> p.attributes = { :name => 'charles' } 
>>> p.attributes 
=> {:age=>30, :name=>"charles"} 

Fin qui tutto bene. Quello che voglio che accada è per gli attributi di hash per aggiornare quando ho impostato una proprietà individuale:

>>> p.attributes 
=> {:age=>30, :name=>"John"} 
>>> p.name 
=> "John" 
>>> p.name = 'charles' # <--- update an individual property 
=> "charles" 
>>> p.attributes 
=> {:age=>30, :name=>"John"} # <--- should be {:age=>30, :name=>"charles"} 

potevo farlo scrivendo un setter e getter per ogni attributo invece di utilizzare attr_accessor, ma che ti succhiano per un modello che ha molti campi. Qualche modo veloce per realizzare questo?

+0

Nel metodo 'initialize', non è necessario impostare gli attributi utilizzando' send'. Semplicemente aggiorna l'hash '@ attributes'. Lo stesso vale per il metodo 'attributes ='. –

+0

Credo che questo sarebbe abbastanza comune in cui qualcuno potrebbe trasformarlo in una gemma semplice. – NullVoxPopuli

risposta

7

Il problema è che si mantengono gli attributi sia come ivars separati, sia all'interno di un hash @attributes. Dovresti scegliere e usare solo un modo.

Se si desidera utilizzare un hash, si dovrebbe rendere il vostro modo di creare funzioni di accesso, che avrebbe "reindirizzare" loro di un unico metodo che impostare e ottenere da un hash:

class Class 
def my_attr_accessor(*accessors) 
    accessors.each do |m| 

    define_method(m) do 
     @attributes[m] 
    end   

    define_method("#{m}=") do |val| 
     @attributes[m]=val 
    end 
    end 
end 
end 

class Foo 
    my_attr_accessor :foo, :bar 

    def initialize 
    @attributes = {} 
    end 
end 

foo = Foo.new 

foo.foo = 123 
foo.bar = 'qwe' 
p foo 
#=> #<Foo:0x1f855c @attributes={:foo=>123, :bar=>"qwe"}> 

Se vuoi usare ivars, dovresti, ancora, lanciare il tuo metodo attr_accessor che dovrebbe, inoltre, ricordare quali ivars dovrebbero essere "attributi", e usare quell'elenco nel metodo attributes. E il metodo attributes creerebbe un hash al volo e lo restituirà.

Here è possibile trovare un bell'articolo sull'implementazione degli accessor.

+0

sembra fantastico. ti dispiacerebbe scrivere qualche suggerimento sul codice su come potrei fare tutto questo? grazie – sa125

+0

Ho illustrato il primo metodo, lasciando l'altro per il tuo studio ...;) –

+0

fantastico, questo è esattamente ciò di cui avevo bisogno. – sa125