如何在Ruby中的class_eval语句中使用哈希

时间:2017-03-22 07:15:01

标签: ruby hash metaprogramming class-eval

当我遇到一个令人沮丧的问题时,我正在做家庭作业。赋值是Ruby元编程的一个练习,其目标是定义一个“attr_accessor_with_history”' attr_accessor_with_history'它与&attr_accessor'完成所有相同的事情,但也提供了属性所有值的历史记录。以下是分配中提供的代码以及为完成分配而添加的一些代码:

    class Class

  def attr_accessor_with_history(attr_name)
    attr_name = attr_name.to_s
    attr_hist_name = attr_name+'_history'
    history_hash = {attr_name => []}

    #getter
    self.class_eval("def #{attr_name} ; @#{attr_name} ; end")
    #setter
    self.class_eval %Q{
      def #{attr_name}=(val)
        # add to history
        @#{attr_hist_name} = [nil] if @#{attr_hist_name}.nil?
        @#{attr_hist_name} << val
        history_hash[@#{attr_name}] = @#{attr_hist_name}

        # set the value itself
        @#{attr_name} = val
      end

      def history(attr) ; @history_hash[attr.to_s] ; end
    }
  end
end

class Foo
  attr_accessor_with_history :bar
  attr_accessor_with_history :crud
end
f = Foo.new     # => #<Foo:0x127e678>
f.bar = 3       # => 3
f.bar = :wowzo  # => :wowzo
f.bar = 'boo!'  # => 'boo!'
puts f.history(:bar) # => [3, :wowzo, 'boo!']
f.crud = 42
f.crud = "Hello World!"
puts f.history(:crud)

我想使用哈希来存储不同属性的不同历史记录,但我无法在setter的class_eval语句中访问该哈希值。无论我如何设置它,我总是要么为[] =方法得到NoM​​ethodError,因为&#39; history_hash&#39;不知何故变成类型NilClass,或者因为它看到&#39; history_hash&#39;而发生NameError。作为未定义的局部变量或方法。如何在class_eval语句中使用哈希?

1 个答案:

答案 0 :(得分:1)

  

或发生NameError,因为它将'history_hash'视为未定义的局部变量或方法

我会说你不能,因为它一个局部变量,在你想要的上下文中是不可访问的。但是,为什么你甚至需要它呢?我有理由相信它是在“我试图完成赋值时添加的一些代码”中,而不是原始赋值代码(我假设,我希望您将@bar的历史存储在{{1}中 - 或者什么是@bar_history所有关于?)

我对弦乐的贬低感到不舒服;它通常没有必要,Ruby可以通过其强大的元编程功能做得更好。我就是这样做的:

attr_hist_name

最后,因为它是猴子修补基类,我宁愿使用改进来在需要的地方添加它,但这对于作业来说可能是一种过度杀伤。