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
Я, наверное, все делаю неправильно, читаю главу о программировании в Ruby и не все понимаю, может кто-нибудь помочь мне понять это?
3 ответа
Это было весело!!!
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
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
,
Что касается того, что вы сделали, вы на самом деле на пороге решения. Это просто #{attr_name}_history
не существует в вашем коде. Вам нужно будет создать переменную экземпляра и установить ее равной nil, если она не существует. То, что у вас уже есть, должно обрабатывать вставку в массив, если он существует.
Есть несколько способов сделать это. Одним из способов является if defined? @#{attr_name}_history DoStuffHere
Вы должны заметить, что #{attr_name}_history
является переменной экземпляра, поэтому используйте @ перед, как @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