12486


Rails の SafeBuffer に ブロック付きの gsub を呼び、`$1` を参照したところ、nil でした。  
同じ gsub を String で使うと、`$1` には値が入ります。

SafeBuffer のソースを見ると、`to_str` した後に、`gsub` を呼んでいましたので、  
該当部分を取り出し、同じ挙動を再現しました(コードは末尾にあります)。

主要部分は下記です。

[code]

      def gsub(*args, &block)
        to_str.gsub(*args, &block)
      end
    
[/code]

メソッドの中で、to_str してから gsub を呼ぶことで挙動が変わる理由はなんでしょうか？

$1 が [これらの変数はローカルスコープかつスレッドローカル](http://docs.ruby-
lang.org/ja/2.2.0/method/Kernel/v/1.html)とあるため、  
この現象はスコープが異なる、ということなのでしょうか？  
その場合、レシーバーはどこですか？

コード  
aタグ の text を抜き出すコードです

[code]

    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
    
[/code]

結果は下記になります。

[code]

      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
    
[/code]

追記 2015-07-20

参考資料  
\- [How to fix gsub on SafeBuffer objects -
makandropedia](http://makandracards.com/makandra/11171-how-to-fix-gsub-on-
safebuffer-objects)  
\- [gsub_file with block, no access to $1 etc? · Issue #207 ·
erikhuda/thor](https://github.com/erikhuda/thor/issues/207)  
\- [ruby - Using $1, $2, etc. global variables inside method definition -
Stack Overflow](https://stackoverflow.com/questions/18550434/using-1-2-etc-
global-variables-inside-method-definition)


CC BY-SA 3.0
2015-07-19T13:59:46.047
12484
2015-07-20T00:19:27.753
2017-05-23T12:38:55.250
-1
7471
question
ruby
ブロックを渡す gsub を委譲したとき、$1 が参照できなくなる理由はなんでしょうか？


質問のなかですでに答えは出ています。

挙動が変わる理由は`$1`がローカルスコープだからです。  
今回の場合は、`String#gsub`を呼び出した関数内でのみ`$1`を参照できます。

[code]

    def gsub(*args, &block)
      to_str.gsub(*args, &block)
      # $1のスコープ
    end
    
[/code]


CC BY-SA 3.0
2015-07-19T15:06:24.183
12486
2015-07-19T15:06:24.183
3639
12484
answer