📑
[Bug #20807] String のサブクラスで gsub を再定義すると Regexp.last_match が取得できないバグ報告
[Bug #20807] String#gsub fails when called from string subclass with a block passed
- 以下のようにサブクラスで
String#gsub
を再定義したときに#gsub
のブロック内でRegexp.last_match
が取得できないというバグ報告
def call_gsub(str)
str.gsub(/^./) {
# ブロック内でマッチした値を取得する
pp Regexp.last_match
}
end
class MyString < String
def gsub(*args, &block)
super(*args, &block)
end
end
text = 'hoge'
call_gsub(String.new(text))
# => #<MatchData "h">
call_gsub(MyString.new(text))
# => nil
- これなんですが
Regexp.last_match
などの正規表現関連のグローバル値は複数のフレームに渡って動作しないのが起因みたいですね - 今回のケースだと
#gsub
を再定義しているのでMyString#gsub
内でのみ参照できるらしい
def call_gsub(str)
str.gsub(/^./)
pp call_gsub: Regexp.last_match
end
class MyString1 < String
end
class MyString2 < String
def gsub(*args, &block)
# このメソッド内で gsub でマッチしたときの正規表現の情報が参照できる
super(*args, &block)
ensure
pp ensure: $~
end
end
text = 'hoge'
# #gsub を再定義していない場合はブロック内でも取得できる
call_gsub(MyString1.new(text))
# {call_gsub: #<MatchData "h">}
# #gsub を再定義している場合はそのメソッド内で値を取得できる
call_gsub(MyString2.new(text))
# {call_gsub: nil}
# {ensure: #<MatchData "h">}
-
call_gsub
メソッドだけ見るとString#gsub
でRegexp.last_match
が利用できる期待値なのでこういうケースはなかなか気づきづらそうですねえ - 相当なエッジケースとはいえ、ハマると一生気づかなさそう…
Discussion