Подтвердить что ты не робот

Ruby - использование class_eval для определения методов

Я делаю класс SaaS Stanford, пытаясь сделать часть 5 этого задания

Мне очень трудно понять эту концепцию, это то, что я пытался сделать:

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'{def #{attr_name}(a);#{attr_name}_history.push(a) ; end;}'
  end
end

Я, наверное, все делаю неправильно, читаю главу "Книга Руби" о метапрограммировании, и я до сих пор не понимаю, может кто-то поможет мне понять это?

4b9b3361

Ответ 1

Это было весело!!!

class Class
    def attr_accessor_with_history(attr_name)
        attr_name = attr_name.to_s # make sure it a string
        attr_reader attr_name
        attr_reader attr_name+"_history"
        class_eval %Q"
            def #{attr_name}=(value)
                if !defined? @#{attr_name}_history
                    @#{attr_name}_history = [@#{attr_name}]
                end
                @#{attr_name} = value
                @#{attr_name}_history << value
            end
        "
    end
end

class Foo
    attr_accessor_with_history :bar
end

class Foo2
    attr_accessor_with_history :bar
    def initialize()
        @bar = 'init'
    end
end

f = Foo.new
f.bar = 1
f.bar = nil
f.bar = '2'
f.bar = [1,nil,'2',:three]
f.bar = :three
puts "First bar:", f.bar.inspect, f.bar_history.inspect
puts "Correct?", f.bar_history == [f.class.new.bar, 1, nil, '2', [1,nil,'2',:three], :three] ? "yes" : "no"
old_bar_history = f.bar_history.inspect

f2 = Foo2.new
f2.bar = 'baz'
f2.bar = f2
puts "\nSecond bar:", f2.bar.inspect, f2.bar_history.inspect
puts "Correct?", f2.bar_history == [f2.class.new.bar, 'baz', f2] ? "yes" : "no"

puts "\nIs the old f.bar intact?", f.bar_history.inspect == old_bar_history ? "yes" : "no"

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

Ответ 2

Что касается того, что вы сделали, вы на самом деле находитесь на пороге решения. Это просто, что #{attr_name}_history не существует в вашем коде. Вам нужно будет создать переменную экземпляра и установить ее на нуль, если она не существует. То, что вы уже должны обрабатывать нажатием массива, если оно существует.

Существует несколько способов сделать это. Один из способов: if defined? @#{attr_name}_history DoStuffHere

Ответ 3

Вы должны заметить, что #{attr_name}_history - это переменная экземпляра, поэтому используйте @before, например @foo в классе ниже

def #{attr_name}=value, #{attr_name}= - это имя метода, value - параметр, такой же, как def func parameter

def #{attr_name}=value
  (!defined? @#{attr_name}_history) ? @#{attr_name}_history = [nil, value] : @#{attr_name}_history << value
end