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)

> 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

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 }