Railserなら知っておくべき地味だけど便利なメソッド

Railsのバージョンは3.2.8時点のものです。(2012/8/21追記)


前回の冒頭で書いてたので、Rails版も書いてみようと思う。
要は、困ったらリファレンスマニュアルのそれっぽいところ見れば、
Ruby/Railsは大体解決してくれるよ(しかもスマートに)って感じなので、
悩んだらハマる前に一回公式に帰るのもいい手段だと思ってます。


でも、RailsAPIリファレンスどこに何書いてるか分かりづらいと思う。
例えば、has_many関連には、実は:after_addとか:before_removeとか、
その手のコールバックをProcで渡せるんだけど、メソッドの説明の所に書いてないんよね。
上の方のクラス全体の説明の所にAssociation Callbackって項目があって、
そこで地味に説明されてたりとか。
この辺、もしかしてpull request候補かな。


メインはActiveSupportです。
細かいところまで書くとキリが無いので、単純にメソッドとして解説できるものだけ。
Rails3レシピブック等で、あまり記述が無かったり
書いてあるけど忘れがちと思ってる所を挙げてます。
同じく突っ込み歓迎。

ActionView

#video_tag(sources, options = {})

配列でファイル名を渡すと、展開して複数ソースを持ったvideoタグに展開してくれる。
audio_tagとか、favicon_link_tagとかもあったりする。

#number_with_delimiter(number, options={})

123456を123,456って感じで表示してくれる。
区切り文字はオプションで変えられる。

#highlight(text, phrases, *args)

textの中でphrasesに合致するものがあれば、ハイライト表示するためのタグで囲んでくれる。
見せ方はcssで調整。
どのタグで包むかは、highlihgerオプションで調整できる。

ActiveRecord

ActiveRecord::Persistence#toggle(attribute)

Booleanな属性名を渡すと、trueとfalseを入れ替えてくれる。

ActiveRecord::Store.store

DBにHashをシリアライズしてデータを保存し、直接データにアクセスできるアクセサメソッドを定義してくれる。
ただシリアライズして欲しいだけなら、serializeメソッドが使える。

class User < ActiveRecord::Base
  store :settings, accessors: [ :color, :homepage ]
end

u = User.new(color: 'black', homepage: '37signals.com')
u.color                          # Accessor stored attribute
u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
ActiveRecord::Relation#first_or_create(attributes = nil, options = {}, &block)

そのクラスのテーブルの最初の要素があればそれを利用し、
無ければattributesを使ってレコードを作成し、それを返す。
スコープと組み合わせたり、ブロックを渡す使い方が便利。

User.where(:first_name => 'Scarlett').first_or_create do |user|
  user.last_name = "O'Hara"
end
ActiveRecord::Relation#to_sql

スコープを使って戻ってきたRelationオブジェクトで実行する。
どういうSQLが帰ってくるかすぐに確認できるので、デバッグに便利。

ActiveSupport

ActiveSupport::Concern#included(base = nil, &block)

自分が何かモジュールを書く時に、includeされたら、呼出側のクラスのコンテキストで実行される処理を
ブロックで記述できる。
ActiveSupport::Concernはモジュール書く時に非常に便利なので、
リファレンスマニュアルを読んでおくことをオススメしたい。

module Activatable
  included do
    scope :active, lambda {where(:activate => true)}
  end
end
ActiveSupport::StringInquirer

厳密にはメソッドじゃないけど、method_missingを使って、問い合わせメソッド的に処理してくれる。
実は、こんな感じで書ける。
String#inquiryで、普通の文字列から変換できる。

Rails.env.production?
# Rails.env == "production" と同じ

ActiveSupportのコア拡張

Array#from(position)

positionで指定したインデックスから配列の最後までを取る。
Array[2..-1]と書くより直感的。

Hash#assert_valid_keys(*valid_keys)

引数で渡したkey以外が含まれていると例外を起こす。
オプションとして受けとったHashの値を検証して、
メソッドのガード条件として使う。

Hash#with_indifferent_access

HashをHashWithIndiferrentAccessに変換する。
Railsではシンボルアクセスが多いので、たまに普通のHashだと
データ取り損ねたりするので、防御的に変換かけたりすることがある。

Module.delegate

Delegatorパターンを簡単に書ける。
Ruby組み込みのDelegatable, Fowardableより楽。
prefixオプションを渡してメソッド名を調節したり、
allow_nilオプションで移譲先が無い場合はnilを返すようにしたり。

class Foo < ActiveRecord::Base
  belongs_to :greeter
  delegate :hello, :goodbye, :to => :greeter
end

Foo.new.goodbye # => "goodbye"
Object#to_s(format = :default)

to_sに引数渡せるようになっていて、
:dbを渡すと、DBクエリとして活用する時の文字列表現が取れる。

Object#present?

Object#blank?の逆。
文字列だったら、空文字列じゃない場合にtrueになる。
nilに対しても呼べるので条件式書く時に楽。

Object#presence
a = hoge.presence
# -> a = hoge.present? ? hoge : nil
Object#try(method_name, args, &block)

既に定番かな。メソッドチェインのどっかでnilになったら、
後全部nilで評価してくれるので、NoMethodErrorにならない。
複数引数も渡せるし、ブロックも渡せる。
最後に || と組み合わせてtryでnilになったら、そっちを返すようにするなど。

first_post_title = user.posts.first.try(:title) || "No Post"
Range#overlaps?(other)

Range同士が被っている場所が無いか比較してくれる。

String#to_time, #to_date

テストコード書く時とか便利。

let(:today) {"2012-12-13".to_date} #=> Thu, 13 Dec 2012