Мета-программирование ruby ​​для - PullRequest
0 голосов
/ 03 марта 2012

Я пытаюсь отслеживать историю каждой переменной класса с помощью метапрограммирования.Я не фанат задавать такие вопросы, но у меня ушло 5 часов, чтобы написать их, и отныне я понятия не имею, как поступить (я новичок в ruby, и я впервыеиграю с метапрограммированием).

В моем понимании;когда attr_accessor_with_history инициализируется в классе, он должен выполнить код, который он содержит.Таким образом, каждый раз, когда этот метод инициализируется, благодаря достоинствам метапрограммирования у каждого класса будет свой собственный метод для описанной мной проблемы.

В представленном мной коде читатели инициализируются правильно, но я не могу сказать,то же самое с кодом в class_eval части.Мне нужно уточнить, почему код не работает, и метапрограммирование в целом.

class Class
  def attr_accessor_with_history(attr_name)
    attr_name = attr_name.to_s
    attr_reader attr_name
    attr_reader attr_name + "_history"

    class_eval "%Q{
    @#{attr_name}_history=[nil] 
    def #{attr_name}=(value)
        #{attr_name}=value
        #{attr_name}_history.push(value)
    end
    }
    "
  end
end

class Klass
  attr_accessor_with_history :kamil
  def initialize(value)
    kamil = value
  end
end

a = Klass.new(5)
a.kamil = 1
puts "#{a.kamil_history}"

Ответы [ 3 ]

4 голосов
/ 03 марта 2012

Саас конечно, а? Вы должны знать, что переменные экземпляра должны начинаться со знака @. Так, например, в вашем методе инициализации, что такое kamil? Если это переменная экземпляра, она должна быть @kamil. Я бы также предложил вам пересмотреть ваш class_eval аргумент в отношении этого соображения.

EDIT:

@#{attr_name}_history=[nil].

Я бы также поместил этот код в некоторый метод, потому что не очень хорошо инициализировать переменную вашего экземпляра из любого метода.

1 голос
/ 05 марта 2012

Код внутри вашего метода def attr_accessor_with_history (attr_name) вызывается каждый раз, когда вы вызываете этот метод. Вы называете это в своем классе, когда написали класс Класс attr_accessor_with_history: Камил ..

Когда Ruby обрабатывает эту строку 'attr_accessor_with_history: kamil', он фактически запускает код из метода Class.attr_accessor_with_history. Строка внутри class_eval интерпретируется как код, который был написан вами напрямую.

Наконец, ваш интерпретированный код будет выглядеть так:

класс Класс ..

@ kamil_history = [ноль]

def kamil=(value)
    kamil=value
    kamil_history.push(value)
end

Видите проблему? это должно быть значение @ kamil =, в противном случае он снова вызовет метод kamil =, не обращаясь к переменной экземпляра @ kamil.

Аналогично, это должно быть '@ kamil_history.push (..)'.

Рабочий код вы можете найти здесь: http://maxivak.com/ruby-metaprogramming-and-own-attr_accessor/

0 голосов
/ 03 марта 2012

Чтобы вызвать метод set для себя, вам нужно написать self.foo = bar.Если вы просто напишите foo = bar, он просто создаст локальную переменную с именем bar и не вызовет какой-либо метод.Поэтому вам нужно соответственно изменить строки 11 и 23.

Кроме того, используя% Q {} внутри кавычек, весь ваш eval фактически будет просто преобразовываться в строку.Вы должны использовать% Q {} или кавычки - не оба.На самом деле вам, вероятно, вообще не следует использовать строку, а вызывайте class_eval с блоком и используйте define_method внутри блока.

...