method

with_options

v4.2.1 - Show latest stable - Class: Object
with_options(options, &block)
public

An elegant way to factor duplication out of options passed to a series of method calls. Each method called in the block, with the block variable as the receiver, will have its options merged with the default options hash provided. Each method called on the block variable must take an options hash as its final argument.

Without with_options>, this code contains duplication:

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

Using with_options, we can remove the duplication:

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

It can also be used with an explicit receiver:

I18n.with_options locale: user.locale, scope: 'newsletter' do |i18n|
  subject i18n.t :subject
  body    i18n.t :body, user_name: user.name
end

When you don’t pass an explicit receiver, it executes the whole block in merging options context:

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

with_options can also be nested since the call is forwarded to its receiver.

NOTE: Each nesting level will merge inherited defaults in addition to their own.

class Post < ActiveRecord::Base
  with_options if: :persisted?, length: { minimum: 50 } do
    validates :content, if: -> { content.present? }
  end
end

The code is equivalent to:

validates :content, length: { minimum: 50 }, if: -> { content.present? }

Hence the inherited default for `if` key is ignored.

3Notes

Nested with_options

Pilaf · Feb 23, 20096 thanks

You can nest with_options blocks, and you can even use the same name for the block parameter each time. E.g.:

class Product
with_options :dependent => :destroy do |product|
  product.with_options :class_name => 'Media' do |product|
    product.has_many :images, :conditions => {:content_type => 'image'}
    product.has_many :videos, :conditions => {:content_type => 'video'}
  end
  
  product.has_many :comments
end
end

This can be useful for conditional validation

himn1 · Oct 7, 2010

Beware nested with_options clobbers!

kwerle · May 18, 2012

Careful: with_options :foo => :bar do |something| something.with_options :foo => :baz do |inner| what_is(:foo) end end

:foo will be :baz. It will not be [:bar, :baz], for example.

This bit me when trying to do nested with_options for validation where both had :if => something.