Sch*_*ems 8 ruby metaprogramming global-variables
我正在尝试使用Ruby的特殊方法$&(返回上一个正则表达式匹配)的别名.我可以手动完成这个工作:
original = String.instance_method(:sub)
String.send(:define_method, :sub) do |*args, &block|
puts "called"
original.bind(self).call(*args, &block)
end
"foo".sub(/f/) { $&.upcase }
called
# => "Foo"
Run Code Online (Sandbox Code Playgroud)
但是,如果我尝试编写一个为我执行此操作的方法,它将失败:
def programatic_alias(klass, method_name)
original = klass.instance_method(method_name)
klass.send(:define_method, method_name) do |*args, &block|
puts "called"
original.bind(self).call(*args, &block)
end
end
programatic_alias(String, :sub)
"foo".sub(/f/) { $&.upcase }
called
NoMethodError: undefined method `upcase' for nil:NilClass
called
called
called
from (irb):19:in `block in irb_binding'
Run Code Online (Sandbox Code Playgroud)
看起来全球状态受到programatic_alias方法范围的影响,但我不确定这是不是正在发生的事情.问题是:我如何以编程方式使用别名,String#sub以便它仍然适用于Ruby的特殊全局变量?
据我所知,你不能这样做。文档说
这些全局变量是线程局部变量和方法局部变量。
如果您深入研究 ruby 源代码,访问从 获取数据的$&调用,调用which (跳过更多内部方法)获取当前控制框架并从那里读取数据。这些数据都不会暴露给 ruby api - 无法将这些数据从一帧传播到您想要访问它的帧。last_match_getterrb_backref_getvm_svar_get
在您的第二个示例中,对原始方法的调用发生在您的programatic_alias方法内部,因此$&是在该范围内设置的。为了同样的原因
'foo'.try(:sub, /f/) {$&.upcase}
Run Code Online (Sandbox Code Playgroud)
也行不通。
您的第一个示例之所以有效,是因为sub调用的位置和引用的位置$&(块内)位于相同的方法范围内(在本例中为 ruby 顶级)。将其更改为:
original = String.instance_method(:sub)
String.send(:define_method, :sub) do |*args, &block|
puts "called"
original.bind(self).call(*args, &block)
end
def x
"foo".sub(/f/) { $&.upcase }
end
x()
Run Code Online (Sandbox Code Playgroud)
并且$&不再在您的块中定义(如果您捕获抛出的异常,x您可以看到它$&正在顶层设置)
| 归档时间: |
|
| 查看次数: |
395 次 |
| 最近记录: |