如何在 Ruby 中的 class_eval 语句中使用散列
How to use a hash in a class_eval statement in Ruby
当我 运行 遇到一个令人沮丧的问题时,我正在做家庭作业。该赋值是 Ruby 元编程中的一个练习,目标是定义一个 '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 语句中访问该哈希。无论我如何尝试设置它,我总是要么因为 'history_hash' 以某种方式变成 NilClass 类型而得到 []= 方法的 NoMethodError,要么因为它将 'history_hash' 视为未定义的局部变量而发生 NameError变量或方法。如何在 class_eval 语句中使用散列?
or a NameError occurs because it sees 'history_hash' as an undefined local variable or method
我会说你不能,因为它是一个局部变量,在你想要它的上下文中是不可访问的。但是,为什么你甚至需要它?我有理由相信它在 "some code I added in an attempt to complete the assignment" 中,而不是原始分配代码(我假设,它希望您将 @bar
的历史记录存储在 @bar_history
中 - 或者什么是 attr_hist_name
关于什么?)
我也对字符串求值感到不自在;通常没有必要,而且 Ruby 可以做得更好,因为它具有强大的元编程功能。以下是我的做法:
class Class
def attr_accessor_with_history(attr_name)
attr_setter_name = :"#{attr_name}="
attr_getter_name = :"#{attr_name}"
attr_hist_name = :"@#{attr_name}_history"
attr_name = :"@#{attr_name}"
self.class_eval do
define_method(attr_getter_name) do
instance_variable_get(attr_name)
end
define_method(attr_setter_name) do |val|
instance_variable_set(attr_name, val)
history = instance_variable_get(attr_hist_name)
instance_variable_set(attr_hist_name, history = []) unless history
history << val
end
end
end
end
class Object
def history(attr_name)
attr_hist_name = :"@#{attr_name}_history"
instance_variable_get(attr_hist_name)
end
end
最后,由于它是猴子修补基础 类,我宁愿使用改进将其添加到需要的地方,但这对于作业来说可能有点矫枉过正。
当我 运行 遇到一个令人沮丧的问题时,我正在做家庭作业。该赋值是 Ruby 元编程中的一个练习,目标是定义一个 '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 语句中访问该哈希。无论我如何尝试设置它,我总是要么因为 'history_hash' 以某种方式变成 NilClass 类型而得到 []= 方法的 NoMethodError,要么因为它将 'history_hash' 视为未定义的局部变量而发生 NameError变量或方法。如何在 class_eval 语句中使用散列?
or a NameError occurs because it sees 'history_hash' as an undefined local variable or method
我会说你不能,因为它是一个局部变量,在你想要它的上下文中是不可访问的。但是,为什么你甚至需要它?我有理由相信它在 "some code I added in an attempt to complete the assignment" 中,而不是原始分配代码(我假设,它希望您将 @bar
的历史记录存储在 @bar_history
中 - 或者什么是 attr_hist_name
关于什么?)
我也对字符串求值感到不自在;通常没有必要,而且 Ruby 可以做得更好,因为它具有强大的元编程功能。以下是我的做法:
class Class
def attr_accessor_with_history(attr_name)
attr_setter_name = :"#{attr_name}="
attr_getter_name = :"#{attr_name}"
attr_hist_name = :"@#{attr_name}_history"
attr_name = :"@#{attr_name}"
self.class_eval do
define_method(attr_getter_name) do
instance_variable_get(attr_name)
end
define_method(attr_setter_name) do |val|
instance_variable_set(attr_name, val)
history = instance_variable_get(attr_hist_name)
instance_variable_set(attr_hist_name, history = []) unless history
history << val
end
end
end
end
class Object
def history(attr_name)
attr_hist_name = :"@#{attr_name}_history"
instance_variable_get(attr_hist_name)
end
end
最后,由于它是猴子修补基础 类,我宁愿使用改进将其添加到需要的地方,但这对于作业来说可能有点矫枉过正。