こんにちは、hachi8833です。
Active Support探訪シリーズは、前回のString#pluralizeで扱ったActiveSupport::Inflector
の便利メソッド群を概観します。
今回のメソッド
今回は、初めてcore extension以外のメソッドにお邪魔することになります。
- メソッド: ActiveSupport::Inflectorのメソッド
- ディレクトリ配置: https://github.com/rails/rails/blob/5-0-stable/activesupport/lib/active_support/inflector/methods.rb
条件
- 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#underscore
とString#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仕様の変換に対応していません。
関連記事
- [Rails5] Active Support Core ExtensionsのString#pluralize
- [Rails5] Active Support Core ExtensionsのString#acts_like_string?
- [Rails5] Active Support Core ExtensionsのStringクラス(3)#to_time、#to_date、#to_datetime
- [Rails5] Active Support Core ExtensionsのStringクラス(2)html_safe
- [Rails5] Active Support Core ExtensionsのStringクラス(1)String#blank?
- [Rails5] Active Supportの概要をつかむ
- [Rails5] Railsの主要なライブラリ構成
- [Ruby] module_functionでモジュールの特異メソッドを簡潔に書く
- Active Support core_extのHash#diffはRails 4で非推奨化・廃止された
- RailsビューのHTMLエスケープは#link_toなどのヘルパーメソッドで解除されることがある
- Rails 4.0.2, 3.2.16リリース!重大なセキュリティFIXがあります