Метапрограммирование в Ruby: добавление @variables к существующему методу initialize (используя class_eval)

Возможный дубликат:
Ruby.Metaprogramming. class_eval

У меня есть небольшой проект, цель которого - создать метод attr_accessor_with_history, который будет хранить записи о каждом значении, назначенном переменным, созданным им. Вот код:

class Class
  def attr_accessor_with_history(attr_name)
  attr_name = attr_name.to_s   # make sure it's a string
  attr_reader attr_name        # create the attribute's getter
  attr_reader attr_name+"_history" # create bar_history getter

  a = %Q{
        def initialize
        @#{attr_name}_history = [nil]
    end         

    def #{attr_name}
        @#{attr_name} 
    end 

    def #{attr_name}=(new_value)
        @#{attr_name}=new_value 
        @#{attr_name}_history.push(new_value)
    end }
      puts a

      class_eval(a)

  end
end

Теперь, когда я тестирую скрипт на одну переменную. Работает нормально. Но когда я пытаюсь создать две или более переменных (как это)....

class Foo
attr_accessor_with_history :bar
attr_accessor_with_history :lab
end

a = Foo.new
a.bar = 45
a.bar = 5
a.bar = 'taat'
puts a.bar_history

b = Foo.new
b.lab = 4
b.lab = 145
b.lab = 'tatu'
puts b.lab_history

.... Ruby выдает "несуществующий метод" push "для (class_eval) bar_history.push(new_value)". Я думаю, что метод initialize переопределяется при втором вызове attr_accessor_with_history, поэтому запись для первой переменной уничтожается.

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

1 ответ

Решение

В вашем методе установки просто проверьте, инициализирована ли переменная экземпляра истории:

def #{attr_name}=(new_value)
  @#{attr_name}=new_value
  @#{attr_name}_history ||= [nil]
  @#{attr_name}_history.push(new_value)
end

Вам понадобится другой метод получения вашей переменной истории, который устанавливает значение по умолчанию, если оно не было установлено ранее:

def #{attr_name}_history
  @#{attr_name}_history ||= [nil]
end

Затем вы можете удалить свой метод инициализации, который, кстати, был уязвим для перезаписи.

Другие вопросы по тегам