我正在尝试给一个使用 Ruby 特殊方法的方法起别名$&
(返回最后一个正则表达式匹配 http://jimneath.org/2010/01/04/cryptic-ruby-global-variables-and-their-meanings.html#_dollar_ampersand)。我可以手动执行此操作并且有效:
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"
但是,如果我尝试编写一个为我执行此操作的方法,则会失败:
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'
看起来全局状态正在受到范围的影响programatic_alias
方法,但我不确定这是否是发生的事情。问题是这样的:我如何以编程方式别名String#sub
这样它仍然可以与 Ruby 的特殊全局变量一起使用吗?
据我所知,你不能这样做。这docs http://ruby-doc.org/core-2.2.0/Regexp.html say
这些全局变量是线程局部变量和方法局部变量。
如果你深入研究 ruby 源代码,访问$&
calls last_match_getter https://github.com/ruby/ruby/blob/ruby_2_2/re.c#L1643它的数据来自rb_backref_get https://github.com/ruby/ruby/blob/ruby_2_2/vm.c#L944,这称为vm_svar_get https://github.com/ruby/ruby/blob/ruby_2_2/vm.c#L930它(跳过一些内部方法)获取当前控制帧并从那里读取数据。这些数据都不会暴露给 ruby api - 无法将这些数据从一帧传播到您想要访问它的帧。
在您的第二个示例中,对原始方法的调用发生在您的内部programatic_alias
方法等$&
正在该范围内设置。为了同样的原因
'foo'.try(:sub, /f/) {$&.upcase}
也行不通。
你的第一个例子有效,因为这个地方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()
and $&
不再在您的块中定义(如果您捕获由x
你可以看到$&
正在设置在顶层)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)