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_many
のdependent
オプションを共通化しましたが、他のパターンでも適用できます。例えば、モデルの例では他にも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 4.1では
実際、Rails HEADのドキュメントにはレシーバ無しで呼べると書かれててて、4.1でそう書いたら動かなくてハマりました。
参考文献
- with_optionsを使う際の注意点
- 4.1から4.2で変わったレシーバ不要の件