Posted at

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でそう書いたら動かなくてハマりました。


参考文献