gsub with activesupport::safebuffer
DESCRIPTION
Railsのhelperでnamed captureを使ったgsubを使ったらハマった話TRANSCRIPT
![Page 1: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/1.jpg)
Railsのhelperでnamed capture使ったらハマった話
chezou kawasaki.rb #010
![Page 2: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/2.jpg)
正規表現とnamed capture
• 正規表現のグループに名前をつけて、後方参照できる
pat = /(?<good>good|nice|greate) catch/! #=> /(?<good>good|nice|greate) catch/!pat === 'nice catch!!'! #=> true!Regexp.last_match! #=> #<MatchData "nice catch" good:"nice">!Regexp.last_match[:good]! #=> "nice"!
![Page 3: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/3.jpg)
helperでnamed capture
• Railsのhelperでnamed capture使ったgsubしたら…
module FooHelper! def replace_awesome(str)! str.gsub(pat){|m| "#{m}!!!" if Regexp.last_match[:good]}! end!end!!- str = 'nice catch'!= replace_awesome(str) #=> "nice catch!!!"!# こういうのがやりたい!
![Page 4: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/4.jpg)
現実
= replace_awesome(str)!NoMethodError: undefined method `[]' for nil:NilClass
( д) ゚ ゚
![Page 5: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/5.jpg)
なぜだろう?
=> ActiveSupport::SafeBuffer
module FooHelper! def replace_awesome(str)! p(str.class) ! str.gsub(pat){|m| "#{m}!!!" if Regexp.last_match[:good]}! end!end!
![Page 6: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/6.jpg)
AS::SafeBufferとは• RailsのviewではHTMLの特殊文字(<,>,&,")を自動的にエスケープしてくれる
• その時に出力されるクラスがAS::SafeBuffer
"".html_safe + "<" #=> "<"!("".html_safe + "<").class #=> ActiveSupport::SafeBuffer!
http://guides.rubyonrails.org/active_support_core_extensions.html#extensions-to-string
![Page 7: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/7.jpg)
SafeBufferのgsubを見た
rails/activesupport/lib/active_support/core_ext/string/output_safety.rb
module ActiveSupport! class SafeBuffer < String! UNSAFE_STRING_METHODS = %w(! capitalize chomp chop delete downcase gsub lstrip next reverse rstrip! slice squeeze strip sub succ swapcase tr tr_s upcase prepend! )! …!
UNSAFE_STRING_METHODS.each do |unsafe_method|! if unsafe_method.respond_to?(unsafe_method)! class_eval <<-EOT, __FILE__, __LINE__ + 1! def #{unsafe_method}(*args, &block) # def capitalize(*args, &block)! to_str.#{unsafe_method}(*args, &block) # to_str.capitalize(*args, &block)! end # end!! def #{unsafe_method}!(*args) # def capitalize!(*args)! @html_safe = false # @html_safe = false! super # super! end # end! EOT! end! end!
![Page 8: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/8.jpg)
Rubyのみの再現コードdef test(*args, &block)! 'hogehoge'.gsub(/h/, &block)! p Regexp.last_match!end! !test do |matched|! p Regexp.last_match!end!
![Page 9: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/9.jpg)
Rubyのみの再現コードdef test(*args, &block)! 'hogehoge'.gsub(/h/, &block)! p Regexp.last_match #=> <MatchData "h">!end! !test do |matched|! p Regexp.last_match #=> nil!end!
![Page 10: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/10.jpg)
仕様です
![Page 11: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/11.jpg)
Rubyのみの再現コードdef test(*args, &block)! 'hogehoge'.gsub(/h/, &block)! p Regexp.last_match #=> <MatchData "h">!end! !test do |matched|! p Regexp.last_match #=> nil!end!
block内のscopeと違うので取れません
![Page 12: gsub with ActiveSupport::SafeBuffer](https://reader035.vdocuments.mx/reader035/viewer/2022081403/554f831fb4c905d25b8b4b2c/html5/thumbnails/12.jpg)
回避方法
module FooHelper! def replace_awesome(str)! str.to_str.gsub(pat){|m| "#{m}!!!" if $~[:good]}! end!end! !- str = 'nice catch'!= replace_awesome(str) #=> "nice catch!!!"!