読者です 読者をやめる 読者になる 読者になる

こしごぇ(B)

旧:http://d.hatena.ne.jp/koshigoeb/

ActiveSupport::Inflector::Inflections#acronym が期待通りで無い件

ActiveSupport::Inflector.inflections(:en) do |inflect|
  inflect.acronym 'OAuth'
end

ネームスペースがつくと期待通りの変換をしてくれない模様。

'OAuth'.underscore # => 'oauth'
'oauth'.camelize   # => 'OAuth'
'Auth::OAuth'.underscore # => 'auth/o_auth'
'auth/oauth'.camelize    # => 'Auth::OAuth'

ActiveSupport::Inflector#underscore の実装は以下の通り。

    def underscore(camel_cased_word)
      word = camel_cased_word.to_s.dup
      word.gsub!('::', '/')
      word.gsub!(/(?:([A-Za-z\d])|^)(#{inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
      word.gsub!(/([A-Z\d]+)([A-Z][a-z])/,'\1_\2')
      word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
      word.tr!("-", "_")
      word.downcase!
      word
    end

問題は2個目のgsub!で使っている正規表現。acronym で指定した単語が指定文字列の先頭に登場するか、英数字に続いて登場するかしないとマッチしないため、スラッシュに続いて登場するケースでは対象外となってしまう。

ActiveSupport::Inflector.inflections.acronym_regex
# => /OAuth/
'OAuth'.gsub(/(?:([A-Za-z\d])|^)(#{ActiveSupport::Inflector.inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
# => 'oauth'
'Auth/OAuth'.gsub(/(?:([A-Za-z\d])|^)(#{ActiveSupport::Inflector.inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
# => 'Auth/OAuth'
'Auth/oOAuth'.gsub(/(?:([A-Za-z\d])|^)(#{ActiveSupport::Inflector.inflections.acronym_regex})(?=\b|[^a-z])/) { "#{$1}#{$1 && '_'}#{$2.downcase}" }
# => 'Auth/o_oauth'

これは、以前からこのような結果だっただろうか?