如何将class_eval用于多属性值

时间:2012-03-06 03:23:16

标签: ruby

我尝试扩展code from this question以保留属性值的记录。但是,在多个属性的情况下,我的代码失败了。这是代码:

class Class
  def attr_accessor_with_history(attr_name)
    attr_name = attr_name.to_s
    attr_reader attr_name

    ah=attr_name+"_history"
    attr_reader ah 

    class_eval %Q{          
      def #{attr_name}= (attr_name)
        @attr_name=attr_name

        if @ah == nil
          @ah=[nil]
        end
        @ah.push(attr_name)
      end
      def #{ah}
        @ah
      end  

      def #{attr_name}
        @attr_name
      end
     }
  end
end

这是一个用于测试的虚拟类

class Foo
  attr_accessor_with_history :bar
  attr_accessor_with_history :bar1
end

f = Foo.new
f.bar = 1
f.bar = 2
f.bar1 = 5
p f.bar_history  
p f.bar1_history  

出于某种原因,f.barf.bar1都返回5f.bar_history = f.bar1_history = [nil, 1, 2, 5]。知道为什么会这样吗?

1 个答案:

答案 0 :(得分:2)

在获取/设置方法时,您使用的是@ah@attr_name,而不是@#{ah}@#{attr_name}。这意味着他们总是设置并返回相同的实例变量,而不是动态命名的变量。

class Class
  def attr_accessor_with_history(attr_name)
    class_eval %{
      attr_reader :#{attr_name}, :#{attr_name}_history

      def #{attr_name}=(value)
        @#{attr_name} = value
        @#{attr_name}_history ||= [nil]
        @#{attr_name}_history << value
      end
     }
  end
end

我也经常清理你的代码,以便(我认为)更清晰,更简洁。