Обновлять хэш атрибутов класса Ruby при изменении свойства - PullRequest
5 голосов
/ 15 июля 2010

Я пытаюсь написать класс Ruby, который работает аналогично модели AactiveRecord в Rails способом обработки атрибутов:

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

Я имею в виду, что когда я инициализирую класс, хэш "атрибутов" обновляется соответствующими атрибутами:

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

Пока все хорошо. Я хочу, чтобы хэш атрибутов обновлялся при установке индивидуального свойства:

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

Я мог бы сделать это, написав сеттер и геттер для каждого атрибута вместо использования attr_accessor, но это будет отстой для модели, которая имеет много полей. Есть ли быстрый способ сделать это?

1 Ответ

7 голосов
/ 15 июля 2010

Проблема в том, что вы сохраняете свои атрибуты как в виде отдельных ivars, так и внутри хеша @attributes.Вы должны выбрать и использовать только один путь.

Если вы хотите использовать хеш, вы должны создать свой собственный способ создания методов доступа, который бы «перенаправил» их на один метод, который установил бы и получал из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"}>

Если вы хотите использовать ivars, вы должны снова использовать собственный метод attr_accessor, который, кроме того, запомнит, какие ivars должны быть «атрибутами», и используйте этот список вattributes метод.И метод attributes на лету создаст из них хеш и вернет его.

Здесь вы можете найти хорошую статью о реализации методов доступа.

...