[Rails5] Active Support::Inflectorの便利な活用形メソッド群

こんにちは、hachi8833です。

Active Support探訪シリーズは、前回のString#pluralizeで扱ったActiveSupport::Inflectorの便利メソッド群を概観します。

今回のメソッド

今回は、初めてcore extension以外のメソッドにお邪魔することになります。

条件

  • Railsバージョン: 5-0-stable(執筆時点では5.0.1)
  • Rubyバージョン: 2.4.0

ActiveSupport::Inflector

Rails 5.0のActiveSupport::Inflectorメソッド一覧

Rails 5.0のActiveSupport::Inflectorには以下の活用形関連メソッドがあります。ActiveSupport::Inflectorのメソッド群はやや雑然としているので、対になっているメソッドなどをグループ化してみました。

  • 英単語の単数形⇔複数形変換
    • #pluralize
    • #singularize
  • 英語の序数
    • #ordinal
    • #ordinalize
  • アルファベットの大文字小文字変換
    • #upcase_first
    • #humanize
    • #titleize
  • 記号の変換
    • #dasherize
  • 定数名・モジュール名の変換
    • #constantize
    • #safe_constantize
    • #deconstantize
    • #demodulize
  • キャメルケース⇔スネークケース変換
    • #camelize
    • #underscore
  • クラス名/モジュール名/テーブル名の変換
    • #classify
    • #tableize
  • パラメータ化
    • #parameterize
  • 活用形のカスタマイズ
    • ActiveSupport::Inflector.inflections
  • 近い英文字への変換
    • ActiveSupport::Inflector.transliterate
  • 外部キー名への変換
    • #foreign_key

多くはStringクラスですが、数値に関連するメソッドもあります。

最初に、各ケースについて以下に簡単にまとめておきます。

スネークケース
active_record」のように小文字のみの単語をアンダースコアで結合(Railsではメソッド名などで使用)
キャメルケース(upper camel case)
ActiveRecord」のように大文字で始まる単語を結合(Railsではクラス名などで使用)
キャメルケース(lower camel case)
activeRecord」のように先頭のみ小文字、残りは大文字で始まる単語を結合

英単語の単数形⇔複数形変換

#pluralize
(Stringクラス) 英語の単数形を複数形に変換します
#singularize
(Stringクラス) 英語の複数形を単数形に変換します

活用形はすべてを網羅しているわけではないのでご注意ください。

#pluralize
'post'.pluralize             # => "posts"
'octopus'.pluralize          # => "octopi"
'sheep'.pluralize            # => "sheep"
'words'.pluralize            # => "words"
'the blue mailman'.pluralize # => "the blue mailmen"
'CamelOctopus'.pluralize     # => "CamelOctopi"
'apple'.pluralize(1)         # => "apple"
'apple'.pluralize(2)         # => "apples"
'ley'.pluralize(:es)         # => "leyes"
'ley'.pluralize(1, :es)      # => "ley"

# singularize
'posts'.singularize            # => "post"
'octopi'.singularize           # => "octopus"
'sheep'.singularize            # => "sheep"
'word'.singularize             # => "word"
'the blue mailmen'.singularize # => "the blue mailman"
'CamelOctopi'.singularize      # => "CamelOctopus"
'leyes'.singularize(:es)       # => "ley"

英語の序数

#ordinal
(Integerクラス)数字に対応する英語の序数(ordinal numbers)の接尾語を返す
#ordinalize
(Integerクラス)数字に英語の序数(ordinal numbers)を追加して返す
# ordinal
1.ordinal     # => "st"
2.ordinal     # => "nd"
1002.ordinal  # => "nd"
1003.ordinal  # => "rd"
-11.ordinal   # => "th"
-1001.ordinal # => "st"

# ordinalize
1.ordinalize     # => "1st"
2.ordinalize     # => "2nd"
1002.ordinalize  # => "1002nd"
1003.ordinalize  # => "1003rd"
-11.ordinalize   # => "-11th"
-1001.ordinalize # => "-1001st"

これらのみIntegerクラスを拡張しています。

アルファベットの大文字小文字変換

#upcase_first
(Stringクラス) 最初の文字のみを大文字に変換
#humanize
(Stringクラス) アンダースコアをスペースにして先頭を大文字にするなど、英文「らしく」整形する(末尾の_idは除去する)
#titleize
(Stringクラス) 小文字の英語フレーズを英語のタイトル「らしく」整形する(Rails内部では使っていない)
# upcase_first
'what a Lovely Day'.upcase_first # => "What a Lovely Day"
'w'.upcase_first                 # => "W"
''.upcase_first                  # => ""

# humanize
'employee_salary'.humanize              # => "Employee salary"
'author_id'.humanize                    # => "Author"
'author_id'.humanize(capitalize: false) # => "author"
'_id'.humanize                          # => "Id"

# titleiize
'man from the boondocks'.titleize # => "Man From The Boondocks"
'x-men: the last stand'.titleize  # => "X Men: The Last Stand"

そして#titleize内部で呼ばれている#humanizeソースを見ると、一律単語の先頭を大文字にしているようです。

追伸: titleizeは英語のタイトルスタイルそのものではない

現実の英文におけるタイトルのスタイルは、「inやtoなどの前置詞は大文字にしない」などルールが複雑で、かつ出版社や新聞社によって異なるので機械的な処理が困難です。

#titleizeに限らず、こうしたメソッドはコーディング支援のためのものであり、実用的な英文に変換するものではないと考えるほうがよいでしょう。

記号の変換

#dasherize
(Stringクラス) アンダースコア_をダッシュ-に変換
'puni_puni'.dasherize # => "puni-puni"

なお、ここでいうダッシュはhyphen-dash(U+002D)(要するにマイナス/ハイフン/ダッシュのどれにも使われている例のASCII文字)のことです。

定数名・モジュール名の変換

#constantize
(Stringクラス) 指定の文字列を定数に変換する(元がキャメルケースかつ該当の定数が存在する場合以外はエラー)
#safe_constantize
(Stringクラス) constantizeと同様だが、無効な文字列の場合にはnilを返す
#deconstantize
(Stringクラス) 定数名(文字列)の最も右の要素を除去する
#demodulize
(Stringクラス) モジュール名の最も右の要素を返す
# constantize
'Module'.constantize  # => Module
'Class'.constantize   # => Class
'blargle'.constantize # => NameError: wrong constant name blargle

# safe_constantize
'Module'.safe_constantize  # => Module
'Class'.safe_constantize   # => Class
'blargle'.safe_constantize # => nil

# deconstantize
'Net::HTTP'.deconstantize   # => "Net"
'::Net::HTTP'.deconstantize # => "::Net"
'String'.deconstantize      # => ""
'::String'.deconstantize    # => ""
''.deconstantize            # => ""

# demodulize
'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
'Inflections'.demodulize                                       # => "Inflections"
'::Inflections'.demodulize                                     # => "Inflections"
''.demodulize  

#constantizeすると文字列ではなく定数オブジェクトになりますので、#deconstantizeで逆変換できません。

#deconstantize#constantizeの逆操作ではないのがなかなか紛らわしいですね。

さらに、#demodulizeはモジュール名ではなく名前空間の方を削除しています。

キャメルケース⇔スネークケース変換

#camelize
(Stringクラス) キャメルケースに変換(デフォルトは:lower)、かつスラッシュ/をコロン2つ::に置き換える
#underscore
(Stringクラス) #camelizeの逆操作
# camelize
'active_record'.camelize                # => "ActiveRecord"
'active_record'.camelize(:lower)        # => "activeRecord"
'active_record/errors'.camelize         # => "ActiveRecord::Errors"
'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"

# underscore
'ActiveModel'.underscore         # => "active_model"
'ActiveModel::Errors'.underscore # => "active_model/errors"

#camelize#underscoreが互いに逆操作というのが名前からはちょっとわかりにくいですね。

クラス名/モジュール名/テーブル名の変換

テーブル名はデータベースのテーブル名を指します。

classify
(Stringクラス) テーブル名(小文字、ハイフン)をクラス名に変換する
tableize
(Stringクラス) キャメルケースのクラス名(や単数形のスネークケース)をテーブル名に変換する
# classify
'ham_and_eggs'.classify # => "HamAndEgg"
'posts'.classify        # => "Post"

# tableize
'RawScaledScorer'.tableize # => "raw_scaled_scorers"
'ham_and_egg'.tableize     # => "ham_and_eggs"
'fancyCategory'.tableize   # => "fancy_categories"

#tableize#classifyの逆操作にもなっています。この手のメソッド名はスペルを間違えそうで冷や冷やします。

パラメータ化

#parameterize
(Stringクラス) 文字列をURLらしく変換する(記号の除去やスペース->ハイフン置き換え)

ただし全角文字は記号と同様除去されてしまうので、Base64#encode64などで別途変換する必要があります。

活用形のカスタマイズ

Inflector.inflections
(ActiveSupport::Inflector) 登録されていない活用形を追加する

rails generateでコントローラやモデルなどを生成するときの単語が惜しくも活用形に対応していない場合などに、config/initializers/inflections.rbに以下のように単語の活用を追加します。カスタマイズはロケールごとに行えます。

ActiveSupport::Inflector.inflections(:es) do |inflect|
  inflect.irregular 'ley', 'leyes'
end

なお、このメソッドはActiveSupport::Inflectorクラスのシングルインスタンスを生成します。

# File activesupport/lib/active_support/inflector/inflections.rb, line 234
def inflections(locale = :en)
  if block_given?
    yield Inflections.instance(locale)
  else
    Inflections.instance(locale)
  end
end

近い英文字への変換

Inflector.transliterate
(ActiveSupport::Inflector)ø、ñ、é、ß、лなどの英語にないアルファベットを近い英語に変換する

これはStringクラスではなく、ActiveSupport::Inflectorモジュールです。

ActiveSupport::Inflector.transliterate('Ærøskøbing') # => "AEroskobing"

この変換は以下のようにロケールごとにカスタマイズもできます。

# locales/de.ymlで指定する場合
i18n:
  transliterate:
    rule:
      ü: "ue"
      ö: "oe"
# Rubyコードで設定する場合
I18n.backend.store_translations(:de, i18n: {
  transliterate: {
    rule: {
      'ü' => 'ue',
      'ö' => 'oe'
    }
  }
})

外部キー名への変換

#foreign_key
(Stringクラス) クラス名を外部キー名に変換する(小文字化、アンダースコア除去、”_id” の追加)
'Message'.foreign_key        # => "message_id"
'Message'.foreign_key(false) # => "messageid"
'Admin::Post'.foreign_key    # => "post_id"

String#foreign_keyのソースをちょっとのぞいてみると、先に紹介したString#underscoreString#demodulizeを組み合わせて実現しています。

# File activesupport/lib/active_support/inflector/methods.rb, line 235
def foreign_key(class_name, separate_class_name_and_id_with_underscore = true)
  underscore(demodulize(class_name)) + (separate_class_name_and_id_with_underscore ? "_id" : "id")
end

参考: Rubyの活用形関連メソッド

以下はRailsではなくRubyのメソッドですが、上のActiveSupport::Inflectorの内部で呼ばれているなど、Railsとも密接に関連しています。それぞれ、!が末尾につく破壊的メソッドもあります。

これらのメソッドはRuby 2.4で大きく拡張され、トルコ語など多くのアルファベット系言語でUnicode仕様に準じた変換に対応したのは記憶に新しいところです。

なお、String#casecmpはまだUnicode仕様の変換に対応していません。

関連記事

Ruby on RailsによるWEBシステム開発、Android/iPhoneアプリ開発、電子書籍配信のことならお任せください この記事を書いた人と働こう! Ruby on Rails の開発なら実績豊富なBPS

この記事の著者

hachi8833

Twitter: @hachi8833 コボラー、ITコンサル、ローカライズ業界を経てなぜかWeb開発者志願。 これまでにRuby on Rails チュートリアルの大半、Railsガイドのほぼすべてを翻訳。 かと思うと、正規表現の粋を尽くした日本語エラーチェックサービス enno.jpを運営。 仕事に関係ないすっとこブログ「あけてくれ」は2000年頃から多少の中断をはさんで継続、現在はnote.muに移転。

hachi8833の書いた記事

週刊Railsウォッチ

インフラ

Rubyスタイルガイドを読む

BigBinary記事より

ActiveSupport探訪シリーズ