ブロックを渡す gsub を委譲したとき、$1 が参照できなくなる理由はなんでしょうか?
Rails の SafeBuffer に ブロック付きの gsub を呼び、$1
を参照したところ、nil でした。
同じ gsub を String で使うと、$1
には値が入ります。
SafeBuffer のソースを見ると、to_str
した後に、gsub
を呼んでいましたので、
該当部分を取り出し、同じ挙動を再現しました(コードは末尾にあります)。
主要部分は下記です。
def gsub(*args, &block)
to_str.gsub(*args, &block)
end
メソッドの中で、to_str してから gsub を呼ぶことで挙動が変わる理由はなんでしょうか?
$1 が これらの変数はローカルスコープかつスレッドローカルとあるため、
この現象はスコープが異なる、ということなのでしょうか?
その場合、レシーバーはどこですか?
コード
aタグ の text を抜き出すコードです
HREF_RE = /<a[^>]+>([^<]+)<\/a>/ix
def pick_url(text)
text.gsub(HREF_RE) do |a_tag|
$1
end
end
class StringWrapper < String
def gsub(*args, &block)
to_str.gsub(*args, &block)
end
end
require 'minitest/unit'
require 'minitest/autorun'
class ATest < MiniTest::Unit::TestCase
def setup
@url = 'http://example.com'
@html = "<a href='#{@url}'>#{@url}</a>"
@wrapped = StringWrapper.new(@html)
end
def test_gsub
assert_equal @url, pick_url(@html)
end
def test_wrapped_gsub
assert_equal @url, pick_url(@wrapped)
end
def test_wrapped_to_str_gsub
assert_equal @url, pick_url(@wrapped.to_str)
end
end
結果は下記になります。
1) Failure:
ATest#test_wrapped_gsub [gsub_text_01.rb:30]:
Expected: "http://example.com"
Actual: ""
3 runs, 3 assertions, 1 failures, 0 errors, 0 skips
追記 2015-07-20
参考資料
- How to fix gsub on SafeBuffer objects - makandropedia
- gsub_file with block, no access to $1 etc? · Issue #207 · erikhuda/thor
- ruby - Using $1, $2, etc. global variables inside method definition - Stack Overflow