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 

我可能做错了各种各样的事情,阅读关于元编程的The Book of Ruby章节,我仍然没有得到它,有人能帮助我理解这一点吗?

这很有趣!!!

 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的字符串的唯一原因是,您可以在定义自定义setter时引用attr_name的 。 否则,通常会将一个块传递给class_eval

关于你所做的事情,你实际上是在解决方案的尖端。 只是#{attr_name}_history在您的代码中不存在。 您将需要创建一个实例变量,如果它不存在则将其设置为nil。 你已经应该处理的数据如果确实存在则推入数组。

有几种方法可以做到这一点。 一种方法是if defined? @#{attr_name}_history DoStuffHere 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