2
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]

4

2 回答 2

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
于 2012-07-22T21:40:48.327 回答
0

Sloves in one class_eval 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

于 2018-03-28T06:58:24.003 回答