在 Ruby 中通过 instance_eval 调用块时,动态添加的访问器分配不起作用
我有一个在运行时动态添加属性访问器的类。此类构成 DSL 的一部分,其中块被传递到配置方法并使用 instance_eval 进行调用。这使得在 DSL 中引用类的方法时删除对“self”的引用成为可能。
但是,我发现我可以引用属性来检索它们的值,但无法分配它们,除非显式引用 self,如以下代码示例所示。
class Bar
def add_dynamic_attribute_to_class(name)
Bar.add_dynamic_attribute(name)
end
def invoke_block(&block)
instance_eval &block
end
def self.add_dynamic_attribute(name)
attr_accessor name
end
end
b = Bar.new
b.add_dynamic_attribute_to_class 'dyn_attr'
b.dyn_attr = 'Hello World!'
# dyn_attr behaves like a local variable in this case
b.invoke_block do
dyn_attr = 'Goodbye!'
end
# unchanged!
puts "#{b.dyn_attr} but should be 'Goodbye!'"
# works if explicitly reference self
b.invoke_block do
self.dyn_attr = 'Goodbye!'
end
# changed...
puts "#{b.dyn_attr} = 'Goodbye!"
# using send works
b.invoke_block do
send 'dyn_attr=', 'Hello Again'
end
# changed...
puts "#{b.dyn_attr} = 'Hello Again!"
# explain this... local variable or instance method?
b.invoke_block do
puts "Retrieving... '#{dyn_attr}'"
# doesn't fail... but no effect
dyn_attr = 'Cheers'
end
# unchanged
puts "#{b.dyn_attr} should be 'Cheers'"
谁能解释为什么这没有按预期运行?
I have a class to which I add attribute accessors dynamically at runtime. This class forms part a DSL, whereby blocks get passed to configuration methods and invoked using instance_eval. This makes it possible in the DSL to remove references to 'self' when referencing methods of the class.
However, I've discovered that I can reference the attributes to retrieve their values, but am unable to assign them, unless explicity referencing self, as the following code sample illustrates.
class Bar
def add_dynamic_attribute_to_class(name)
Bar.add_dynamic_attribute(name)
end
def invoke_block(&block)
instance_eval &block
end
def self.add_dynamic_attribute(name)
attr_accessor name
end
end
b = Bar.new
b.add_dynamic_attribute_to_class 'dyn_attr'
b.dyn_attr = 'Hello World!'
# dyn_attr behaves like a local variable in this case
b.invoke_block do
dyn_attr = 'Goodbye!'
end
# unchanged!
puts "#{b.dyn_attr} but should be 'Goodbye!'"
# works if explicitly reference self
b.invoke_block do
self.dyn_attr = 'Goodbye!'
end
# changed...
puts "#{b.dyn_attr} = 'Goodbye!"
# using send works
b.invoke_block do
send 'dyn_attr=', 'Hello Again'
end
# changed...
puts "#{b.dyn_attr} = 'Hello Again!"
# explain this... local variable or instance method?
b.invoke_block do
puts "Retrieving... '#{dyn_attr}'"
# doesn't fail... but no effect
dyn_attr = 'Cheers'
end
# unchanged
puts "#{b.dyn_attr} should be 'Cheers'"
Can anyone explain why this isn't behaving as expected?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这个问题是由 Ruby 处理实例和局部变量的方式引起的。发生的情况是您在 instance_eval 块中设置局部变量,而不是使用 ruby 访问器。
这可能有助于解释它:
bocks 的工作方式是它们区分局部变量和实例变量,但是如果调用读取器时未定义局部变量,则该类默认为实例变量(与调用的情况一样)在我的示例中为 input_instance )。
可通过三种方法获得您想要的行为。
使用实例变量:
使用自变量:
使用 setter 函数:
所有这些示例都将 foo.bar 设置为“instance”。
The issue arrises with the way that Ruby deals with instance and local variables. What is happening is that you are setting a local variable in your instance_eval block, rather than using the ruby accessor.
This might help explain it:
The way bocks work is that they distinguish between local and instance variables, but if a local variable is not defined when it's reader is called, the class defaults to the instance variable (as is the case with the call to input_instance in my example).
There are three ways to get the behavior you want.
Use instance variables:
Use a self variable:
Use setter functions:
All of these examples set foo.bar to "instance".