Обновите хеш атрибутов класса Ruby, когда свойство изменится

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

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

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

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

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

5
задан random 7 July 2013 в 18:41
поделиться

1 ответ

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

Если вы хотите использовать хеш, вы должны создать свой собственный способ создания аксессоров, который «перенаправит» их в один метод, который будет устанавливать и получать из хеша:

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 на лету создает из них хеш и возвращает его.

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

7
ответ дан 14 December 2019 в 08:39
поделиться