22

我正在上斯坦福大学的 SaaS 课程,试图完成这项作业的第 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 章节,但我仍然不明白,有人可以帮助我理解这一点吗?

4

3 回答 3

41

这很有趣!

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.

于 2012-03-05T03:51:26.113 回答
6

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

有几种方法可以做到这一点。一种方法是 if defined? @#{attr_name}_history DoStuffHere

于 2012-03-05T03:57:06.113 回答
0

你必须注意到这#{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
于 2013-07-30T14:51:46.013 回答