动态添加的访问器分配不';在Ruby中通过instance_eval调用block时不起作用
我有一个在运行时动态添加属性访问器的类。该类构成DSL的一部分,通过该部分,块被传递给配置方法,并使用instance_eval调用。这使得DSL在引用类的方法时可以删除对“self”的引用 然而,我发现我可以引用属性来检索它们的值,但是我无法分配它们,除非明确地引用self,如下面的代码示例所示动态添加的访问器分配不';在Ruby中通过instance_eval调用block时不起作用,ruby,dynamic,dsl,instance-eval,Ruby,Dynamic,Dsl,Instance Eval,我有一个在运行时动态添加属性访问器的类。该类构成DSL的一部分,通过该部分,块被传递给配置方法,并使用instance_eval调用。这使得DSL在引用类的方法时可以删除对“self”的引用 然而,我发现我可以引用属性来检索它们的值,但是我无法分配它们,除非明确地引用self,如下面的代码示例所示 class Bar def add_dynamic_attribute_to_class(name) Bar.add_dynamic_attribute(name) end d
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'"
有人能解释一下为什么这样做没有达到预期效果吗?这个问题与Ruby处理实例和局部变量的方式有关。发生的情况是,您正在实例评估块中设置一个局部变量,而不是使用ruby访问器 这可能有助于解释:
class Foo
attr_accessor :bar
def input_local
bar = "local"
[bar, self.bar, @bar, bar()]
end
def input_instance
self.bar = "instance"
[bar, self.bar, @bar, bar()]
end
def input_both
bar = "local"
self.bar = "instance"
[bar, self.bar, @bar, bar()]
end
end
foo = Foo.new
foo.input_local #["local", nil, nil, nil]
foo.input_instance #["instance", "instance", "instance", "instance"]
foo.input_both #["local", "instance", "instance", "instance"]
bocks的工作方式是区分局部变量和实例变量,但如果在调用局部变量的读取器时未定义局部变量,则该类默认为实例变量(在我的示例中,调用input_instance就是这种情况)
有三种方法可以获得你想要的行为
使用实例变量:
class Foo
attr_accessor :bar
def evaluate(&block)
instance_eval &block
end
end
foo = Foo.new
foo.evaluate do
@bar = "instance"
end
foo.bar #"instance"
福班
属性存取器:条
def评估(&block)
实例评估和块
结束
结束
foo=foo.new
foo.do
@bar=“实例”
结束
foo.bar#“实例”
使用自变量:
class Foo
attr_accessor :bar
def evaluate(&block)
block.call(self)
end
end
foo = Foo.new
foo.evaluate do |c|
c.bar = "instance"
end
foo.bar #"instance"
福班
属性存取器:条
def评估(&block)
阻塞呼叫(自我)
结束
结束
foo=foo.new
foo.do|c|
c、 bar=“实例”
结束
foo.bar#“实例”
使用setter函数:
class Foo
attr_reader :bar
def set_bar value
@bar = value
end
def evaluate(&block)
instance_eval &block
end
end
foo = Foo.new
foo.evaluate do
set_bar "instance"
end
foo.bar #"instance"
福班
属性读取器:条
def设置_条值
@bar=值
结束
def评估(&block)
实例评估和块
结束
结束
foo=foo.new
foo.do
设置工具栏“实例”
结束
foo.bar#“实例”
所有这些例子都将foo.bar设置为“instance”。我不完全理解;您说ruby类区分本地变量和实例变量,但问题在于访问器方法。i、 e.酒吧!=@酒吧!=bar()!=bar=()。顺便说一句:我通过在类上实现一个名为“config”的方法来解决这个问题,该方法返回self,这样我的DSL读起来更好(即,不再使用self.*它现在有config.*)。我用更多细节更新了帖子。问题是您在不知不觉中设置了局部变量而不是实例变量。更准确地说,如果未定义名为bar的局部变量,则bar==@bar==bar()==self.bar。否则吧!=@如果定义了名为bar的局部变量,则bar==bar()==self.bar。为了安全起见,不要定义名为“bar”的局部变量,或者只使用其他方法。