用历史记录初始化`attr_accessor`类方法

时间:2012-07-22 21:34:50

标签: ruby metaprogramming

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}=(new_value)
        @#{attr_name}_history = [nil] if @#{attr_name}_history.nil?
        @#{attr_name}_history << @#{attr_name} = new_value
      end
    }
  end
end

class Example
  attr_accessor_with_history :foo
  attr_accessor_with_history :bar
end

Class.attr_accessor_with_history方法提供相同的方法 功能为attr_accessor,但也跟踪属性的每个值 曾经有过。

> a = Example.new; a.foo = 2; a.foo = "test"; a.foo_history
=> [nil, 2, "test"]

但是,

> a = Example.new; a.foo_history
=> nil

它应该是[nil

如何为initialize类定义单个Example方法 …_history值将初始化为[nil]

2 个答案:

答案 0 :(得分:10)

我认为,最好的办法是为历史定义一个自定义阅读器(以及自定义编写器)。

class Class
  def attr_accessor_with_history(attr_name)
    attr_name = attr_name.to_s
    attr_reader attr_name
    class_eval %Q{
      def #{attr_name}_history
        @#{attr_name}_history || [nil] # give default value if not assigned
      end

      def #{attr_name}=(new_value)
        @#{attr_name}_history ||= [nil] # shortcut, compare to your line
        @#{attr_name}_history << @#{attr_name} = new_value
      end
    }
  end
end

class Example
  attr_accessor_with_history :foo
  attr_accessor_with_history :bar
end

a = Example.new; a.foo = 2; a.foo = "test"; 
a.foo_history # => [nil, 2, "test"]

a = Example.new
a.foo_history # => [nil]

编辑:

这是一个稍微冗长的片段,但它不使用class_eval(在不必要的情况下使用时不赞成)。

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

    define_method "#{attr_name}_history" do
      instance_variable_get("@#{attr_name}_history") || [nil]
    end

    define_method "#{attr_name}=" do |new_value|
      v = instance_variable_get("@#{attr_name}_history")
      v ||= [nil]
      v << new_value

      instance_variable_set("@#{attr_name}_history", v)
      instance_variable_set("@#{attr_name}", new_value)
    end
  end
end

答案 1 :(得分:0)

一个class_eval中的Sloves 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}=(val) if @#{attr_name}_history @#{attr_name}_history << @#{attr_name} else @#{attr_name}_history = [nil] end @#{attr_name} = val end } end end