Rails

Railsのwith_options

More than 1 year has passed since last update.

with_optionsという便利メソッドの存在を知る。

Object.with_options`を使うとオプションパラメタをまとめる事ができます。

例えばこういうのを、

class Account < ActiveRecord::Base
  has_many :customers, dependent: :destroy
  has_many :products,  dependent: :destroy
  has_many :invoices,  dependent: :destroy
  has_many :expenses,  dependent: :destroy
end

こう書けます。

class Account < ActiveRecord::Base
  with_options dependent: :destroy do |assoc|
    assoc.has_many :customers
    assoc.has_many :products
    assoc.has_many :invoices
    assoc.has_many :expenses
  end
end

with_optionsの引数に渡したHashのオプションの値がブロック内に適用されます。上の例ではhash_manydependentオプションを共通化しましたが、他のパターンでも適用できます。例えば、モデルの例では他にもvalidatesメソッドのオプションを共通化など使えます。

動作としては、with_optionsに引数として与えたHashを、ブロック内の各メソッドの引数のオプションHashにdeepマージする挙動なのでモデル以外でも使えます。

定義はactivesupport/lib/active_support/core_ext/object/with_options.rbにある。

ただハマりポイントもあり

  • deepマージする挙動なので、with_optionsとブロック内メソッドで同じkeyでオプションを定義すると片方が適用されない
  • Rails 4.1 以前ではレシーバオブジェクト経由で本来のメソッドを呼ぶことが必須
    • Rails 4.1ではwith_optionsからブロックへ渡されるレシーバオブジェクト(上の例のassoc)経由で本来のメソッドを呼ぶ必要あり(上の例のassoc.has_many。レシーバ省略してメソッドを呼ぶと効かない。
    • Rails 4.2 からはレシーバ省略可能

実際、Rails HEADのドキュメントにはレシーバ無しで呼べると書かれててて、4.1でそう書いたら動かなくてハマりました。

参考文献