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"
ただし、これを実行するメソッドを作成しようとすると、失敗します。
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の特別なグローバル変数で引き続き機能するようにプログラムでエイリアスを作成するにはどうすればよいですか?
私の知る限り、これはできません。ドキュメントは言います
これらのグローバル変数は、スレッドローカル変数とメソッドローカル変数です。
ルビーソースを掘り下げると、からデータを取得する$&
呼び出しにアクセスします。この呼び出しは、現在の制御フレームを取得し、そこからデータを読み取ります(さらにいくつかの内部メソッドをスキップします)。このデータはいずれもrubyapiに公開されていません。このデータを1つのフレームからアクセスしたいフレームに伝播する方法はありません。last_match_getter
rb_backref_get
vm_svar_get
2番目の例では、元のメソッドの呼び出しがメソッド内で行われているprogramatic_alias
ため$&
、そのスコープに設定されています。同じ理由で
'foo'.try(:sub, /f/) {$&.upcase}
どちらも機能しません。
最初の例の半分sub
は、呼び出される場所$&
と参照される場所(ブロック内)が同じメソッドスコープ(この場合はルビーのトップレベル)にあるため、機能します。次のように変更します。
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()
そして$&
、もはや(あなたがキャッチした場合によってスローされた例外あなたのブロックで定義されていないx
あなたはそれを見ることができる$&
トップレベルに設定されています)
この記事はインターネットから収集されたものであり、転載の際にはソースを示してください。
侵害の場合は、連絡してください[email protected]
コメントを追加