gsub with ActiveSupport::SafeBuffer

12
Rails helper named capture 使ったらハマった話 chezou kawasaki.rb #010

description

Railsのhelperでnamed captureを使ったgsubを使ったらハマった話

Transcript of gsub with ActiveSupport::SafeBuffer

Page 1: gsub with ActiveSupport::SafeBuffer

Railsのhelperでnamed capture使ったらハマった話

chezou kawasaki.rb #010

Page 2: gsub with ActiveSupport::SafeBuffer

正規表現と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

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

現実

= replace_awesome(str)!NoMethodError: undefined method `[]' for nil:NilClass

( д) ゚ ゚

Page 5: gsub with ActiveSupport::SafeBuffer

なぜだろう?

=> 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

AS::SafeBufferとは• RailsのviewではHTMLの特殊文字(<,>,&,")を自動的にエスケープしてくれる

• その時に出力されるクラスがAS::SafeBuffer

"".html_safe + "<" #=> "&lt;"!("".html_safe + "<").class #=> ActiveSupport::SafeBuffer!

http://guides.rubyonrails.org/active_support_core_extensions.html#extensions-to-string

Page 7: gsub with ActiveSupport::SafeBuffer

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

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

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

仕様です

Page 11: gsub with ActiveSupport::SafeBuffer

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

回避方法

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!!!"!