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.

NOTE: You cannot call class methods implicitly inside of with_options. You can access these methods using the class name instead:

class Phone < ActiveRecord::Base
  enum phone_number_type: [home: 0, office: 1, mobile: 2]

  with_options presence: true do
    validates :phone_number_type, inclusion: { in: Phone.phone_number_types.keys }
  end
end
Show source
Register or log in to add new notes.
February 23, 2009
6 thanks

Nested with_options

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
May 18, 2012
0 thanks

Beware nested with_options clobbers!

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.