秋の夜長にActive Support

RailsのActive Supportのソースは比較的読みやすいものが多く、お役立ちメソッドもいろいろとあるので、最近酒のつまみ代わりに夜な夜な読み始めた下條です。

特に、Rubyの組み込みクラスを拡張するCore Extensionsのソースを読んでいると、知らなかったお役立ちメソッドがあったり、こんなもの使うんかいなってメソッドもけっこうあったりでおもしろい。実際、非常に役に立つメソッドもある。Object#tryなど。
ガイドにまとまっているのだが、全部の機能が書いてあるわけではないので、ソースを読んでみるのもオツなものである。今回は、ざっと目に留まったものをとりとめなく紹介させていただきたいと思う。

Stringクラス

Stringクラスの拡張。文字列操作は概して汚いコードになりがちだが、ここのメソッドを使うと簡単に書ける場合がある。

rails/activesupport/lib/active_support/core_ext/string/access.rb

String#at

1
2
3
4
'hello'.at(0) # => "h"
'hello'.at(1..3) # => "ell"
'hello'.at(/lo/) # => "lo"
'hello'.at(/ol/) # => nil

指定箇所の文字列を取得する以外にも、正規表現を指定するとマッチしなかったらnilが返ることを利用して、文字列の存在チェックに使うことも可能。

String#from, String#to

1
'hello'.from(1).to(-2) # => "ell"

fromとtoを組み合わせることで、始めから2文字目から、終わりから2文字目をとるといったことがきれいに書ける。

rails/activesupport/lib/active_support/core_ext/string/conversions.rb

String#to_time

1
2
3
4
5
"13-12-2012".to_time               # => 2012-12-13 00:00:00 +0900
"06:12".to_time # => 2014-10-14 06:12:00 +0900
"2012-12-13 06:12".to_time # => 2012-12-13 06:12:00 +0900
"2012-12-13T06:12".to_time # => 2012-12-13 06:12:00 +0000
"2012-12-13T06:12".to_time(:utc) # => 2012-12-13 06:12:00 UTC

いろいろな形式の文字列をよろしくTime型に変換してくれるので、文字列をTime型に変換したくなったときにはこのメソッドが使えるかをチェックするのがよいと思う。
また、Time型のフォーマットメソッド(to_formatted_s)と組み合わせると、

1
"13-12-2012".to_time(:utc).to_formatted_s(:iso8601) # => "2012-12-13T00:00:00Z"

このように、ISO8601形式の文字列にできたりするので、APIなどで日時情報を返すときなど使えそうである。

同様にDate型やDateTime型への変換も可能。

1
2
"1-1-2012".to_date # => Sun, 01 Jan 2012
"1-1-2012".to_datetime # => Sun, 01 Jan 2012 00:00:00 +0000

rails/activesupport/lib/active_support/core_ext/string/filters.rb

String#remove

1
"hoge\r\nhoge".remove!(/\r|\r\n|\n/) # => "hogehoge"

正規表現でマッチした文字列を取り除く。
上記例は改行コードを取り除くコード。str.gsub!(/\r|\r\n|\n/, ‘’)よりもちょっときれい。

1
2
3
'Once upon a time in a world far far away'.truncate(27) # => "Once upon a time in a wo..."
'Once upon a time in a world far far away'.truncate(27, separator: ' ') # => "Once upon a time in a..."
'むかしむかしあるところにおじいさんと'.truncate(15, omission: '。。。') # => "むかしむかしあるところに。。。"

文字列を切り取って、最後に’…’を付加する。セパレータを指定すると、単語の区切りまでで切ってくれる。また、最後に付加する文字列(omission)も変更可能である。けっこう使える場面はあるかもしれないと思う。昔、Twitter投稿用に文字列を’…’終端の140文字に切り詰める処理を書いたことがあるのだが、これを知っていれば一瞬で書けたところである。

1
'Once<br>upon<br>a<br>time<br>in<br>a<br>world'.truncate_words(5, separator: '<br>')   #=> "Once<br>upon<br>a<br>time<br>in..."

4.2で追加されるメソッド。セパレータで単語ごとに区切って指定の数までを返す。使う機会はあるかな。。。

rails/activesupport/lib/active_support/core_ext/string/inflections.rb

Railsっぽい重要なメソッドがいろいろ入っているソース。単数複数を変換するメソッドpuluralize, singularize, 外部キー名を生成するforeign_key, Camel Caseに変換するcamelizeなどなど。

String#pluralize, String#singularize

1
2
3
'car'.pluralize # => "cars"
'person'.pluralize # => "people"
'people'.singularize # => "person"

単語を複数形にするメソッドと、その逆のメソッド。ガイドにも書いてある通り、Active Recordではpluralizeメソッドを使用して、モデルに対応するテーブル名を取得している。

なお、ここの変換ルールは難しそうに思えるけれども意外とシンプルで、以下のソースに書いてある。
https://github.com/rails/rails/blob/master/activesupport/lib/active_support/inflections.rb

ただ、微妙な変換がされることがあるようで、例えばこのPull Requestは以下のようなおかしな変換を修正するものである。

1
'waves'.singularize # => 'wafe' ※本来は'wave'が返るべき!

しかし、このPull Requestはマージされていない。というのも、Inflectorの単数複数変換のコアルールを変えてしまうと既存アプリケーションへの影響が大きいために、単数複数変換のルールは凍結されているためである。そのため、必要な場合にはアプリケーションごとにカスタムルールで変更してくれとのことである。調べてみると他にも同様にルールを修正するPull Requestが何個もあった。(全てマージされずにクローズ。)

https://github.com/rails/rails/pull/17134
https://github.com/rails/rails/pull/15740
https://github.com/rails/rails/pull/13065
https://github.com/rails/rails/pull/10812

現在のソースでは、「この変換ルールは完全なものでないが、既存アプリケーションへの影響を避けるために修正することはない」ことが以下のようにコメントで記載されている。
https://github.com/rails/rails/commit/07c70245a128cfe42f134be8759963dc98f1a63e

1
2
3
4
# Define the standard inflection rules. These define a starting point for
# new projects and are not considered complete. The current set of inflection
# rules is frozen. This means, we do not change them to become more complete.
# This is a safety measure to keep existing applications from breaking.

まあ、いずれにせよ、開発者としては、不自然な変換がされる場合があるというところを意識しておけばよいとおもう。

Numericクラス

rails/activesupport/lib/active_support/core_ext/numeric/bytes.rb

Numeric#byte, Numeric#kilobyte, …

1
2
3
1.byte                    # => 1
2.kilobytes # => 2048
45.bytes + 2.6.megabytes # => 2726342.6

最後の例はソースのコメントに書いてある例。
なお、1kilobyte = 1024 * 1byteである。

rails/activesupport/lib/active_support/core_ext/numeric/conversions.rb

電話番号、通貨、パーセンテージなどのフォーマットをするメソッドが入っている。例えば、

1
1235551234.to_s(:phone, area_code: true) # => "(123) 555-1234"

電話番号のフォーマット。しかし、Numericクラスのメソッドなので、電話番号についていえば、当然ながら先頭がゼロだと使えない。日本に限らず電話番号がゼロから始まるパターンは多いので、あまり使えないかもしれない。

1
2
3
1234567890.5.to_s(:currency, locale: :ja) # => "1,234,567,891円"
1234567890.4.to_s(:currency, locale: :ja) # => "1,234,567,890円"
1234567890.4.to_s(:currency, locale: :ja, delimiter: '') # => "1234567890円"

通貨の形式に変換。

他にも、例えば

1
2
12345.to_s(:human, locale: :en) # => "12.3 Thousand"
12345.to_s(:human) # => "12.3 千"

日本語の場合は、ちょっと微妙である。ここらへんのフォーマットは欧米系の言語に特化した部分があるので、使いづらい部分もある。
その他、ソースにたくさんの例が書いてあるのでご参照ください。

rails/activesupport/lib/active_support/core_ext/numeric/time.rb

Numeric#second, Numeric#minute, …

1
2
3
4
2.seconds    # => 2
2.minutes # => 120
2.hours # => 7200
1.fortnight # => 1209600

secondsやminutesなどは、全てaliasで単数形とも紐付けられているので、1.secondでも1.secondsでもどっちでもよいけれども、一応1.second、2.seconds、一般的にはsecondsと使うのがRails流だろうか。

また、Numeric#fortnightは2週間の秒数を返す。2.fortnightsだったら4週間。
ていうか2週間のことを英語でfortnightって言うことを知らなかった。。。日本では二週間を一単位にするというのは馴染みがないと思うのだが、欧米だとけっこう当たり前なのかな?

ちなみに、Numeric#ago, Numeric#until, Numeric#since, Numeric#from_nowというメソッドもある。

1
2.ago # => Sun, 12 Oct 2014 14:29:22 JST +09:00

が、Numericクラスのagoなどは、秒だということが分かりにくく誤使用を招くことから、deprecatedとなっており、Rails 4.2では削除されるので、2.agoではなく2.seconds.agoを使うようにとのこと。

参考:
https://github.com/rails/rails/commit/1f1613604925823be4b15893fbb8f957a38dd0b8
https://github.com/rails/rails/commit/f1eddea1e3f6faf93581c43651348f48b2b7d8bb

Arrayクラス

rails/activesupport/lib/active_support/core_ext/array/prepend_and_append.rb

Array#append, Array#prepend

1
2
array << '1'
array.append('1')

配列に要素を追加するメソッド。
個人的には「<<」は分かりにくいなあといつも感じるので、appendはむしろ本体に入って欲しいぐらいの感じである。

まとめ

まだまだ読み始めたばかりですが、IssueやPull Requestを読んでいくのもおもしろいので、Active Supportに限らず引き続き夜な夜な読んでいこうと思います。

Ruby on Railsを活用したWebサービスや業務システム開発をご検討の企業様は、是非MMMにご相談下さいませ!

このエントリーをはてなブックマークに追加