Today I Learned

with_options in Rails

Active Support in Rails adds a with_option method to the Object (i.e., it’s available everywhere).

It yields a proxy object, which adds given options to each method call:

def log(arg, **kwargs)
  puts(arg: arg, kwargs: kwargs)
end

> with_options a: 123 do |with_a|
>   with_a.log(:called_with_a)
>   log(:called_without_a)
>   with_a.log(:called_with_a_and_b, b: 456)
>   with_a.log(:called_with_overwritten_a, a: 789)
> end
{:arg=>:called_with_a, :kwargs=>{:a=>123}}
{:arg=>:called_without_a, :kwargs=>{}}
{:arg=>:called_with_a_and_b, :kwargs=>{:a=>123, :b=>456}}
{:arg=>:called_with_overwritten_a, :kwargs=>{:a=>789}}

You can use it to group multiple similar calls or to avoid repetition.
E.g., if you need to call I18n multiple times in a place that does not support inferring the scope:

I18n.with_options(scope: 'active_admin.orders.edit.details') do |i18n|
  panel i18n.t('header', order_number: order_number) do
    ...
  end
end

That way, each call to i18n.t will have the correct scope option without you having to repeat it over and over.

Another use case is to group conditional ActiveModel validations:

class EventForm < ApplicationRecord
  with_options if: :teams_enabled? do |teams|
    teams.validates :team_max, presence: true
    teams.validates :team_descripion, length: { maximum: 500 }
  end
end