2010-07-15 15 views
5

Estoy intentando escribir una clase Ruby que funciona de manera similar al modelo de rieles AactiveRecord en la forma en que los atributos son manejados:atributos de clase de actualización de Rubí de hash cuando una propiedad 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 

Lo que quiero decir es que cuando init la clase, un hash de "atributos" se actualiza con los atributos relevantes:

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

Hasta ahora todo bien. Lo que quiero que suceda es para los atributos de picadillo para actualizar cuando me puse una propiedad individual:

>>> 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"} 

que podría hacerlo escribiendo un setter y getter para cada atributo en lugar de utilizar attr_accessor, pero que a chupar de un modelo que tiene muchos campos ¿Alguna forma rápida de lograr esto?

+0

En su método 'initialize', no necesita establecer atributos usando' send'. Simplemente actualice hash '@ attributes' en su lugar. Lo mismo aplica para el método 'attributes ='. –

+0

Siento que esto sería lo suficientemente común como para que alguien pueda convertirlo en una gema simple. – NullVoxPopuli

Respuesta

7

El problema es que mantiene sus atributos como ivars separados, y dentro de un hash @attributes. Debe elegir y usar solo una forma.

Si desea utilizar un hash, usted debe hacer su propia manera de crear métodos de acceso, lo que "redirigir" a un solo método que establecer y obtener a partir de 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"}> 

Si Si desea utilizar ivars, debería, de nuevo, lanzar su propio método attr_accessor que, además, recordaría cuáles ivars deberían ser "atributos" y usar esa lista en el método attributes. Y el método attributes crearía un hash fuera de ellos sobre la marcha, y lo devolvería.

Here puede encontrar un buen artículo sobre la implementación de accesorios.

+0

suena genial. ¿Te importaría escribir alguna sugerencia de código sobre cómo podría hacer todo eso? gracias – sa125

+0

ilustré el primer método, dejando el otro para su práctica ...;) –

+0

impresionante, eso es exactamente lo que necesitaba. – sa125

Cuestiones relacionadas