More at rubyonrails.org:

Active Support Core Extensions

Active Support is the Ruby on Rails component responsible for providing Ruby language extensions and utilities.

It offers a richer bottom-line at the language level, targeted both at the development of Rails applications, and at the development of Ruby on Rails itself.

After reading this guide, you will know:

  • What Core Extensions are.
  • How to load all extensions.
  • How to cherry-pick just the extensions you want.
  • What extensions Active Support provides.

1. How to Load Core Extensions

1.1. Stand-Alone Active Support

In order to have the smallest default footprint possible, Active Support loads the minimum dependencies by default. It is broken in small pieces so that only the desired extensions can be loaded. It also has some convenience entry points to load related extensions in one shot, even everything.

Thus, after a simple require like:

require "active_support"

only the extensions required by the Active Support framework are loaded.

1.1.1. Cherry-picking a Definition

This example shows how to load Hash#with_indifferent_access. This extension enables the conversion of a Hash into an ActiveSupport::HashWithIndifferentAccess which permits access to the keys as either strings or symbols.

{ a: 1 }.with_indifferent_access["a"] # => 1

For every single method defined as a core extension this guide has a note that says where such a method is defined. In the case of with_indifferent_access the note reads:

That means that you can require it like this:

require "active_support"
require "active_support/core_ext/hash/indifferent_access"

Active Support has been carefully revised so that cherry-picking a file loads only strictly needed dependencies, if any.

1.1.2. Loading Grouped Core Extensions

The next level is to simply load all extensions to Hash. As a rule of thumb, extensions to SomeClass are available in one shot by loading active_support/core_ext/some_class.

Thus, to load all extensions to Hash (including with_indifferent_access):

require "active_support"
require "active_support/core_ext/hash"

1.1.3. Loading All Core Extensions

You may prefer just to load all core extensions, there is a file for that:

require "active_support"
require "active_support/core_ext"

1.1.4. Loading All Active Support

And finally, if you want to have all Active Support available just issue:

require "active_support/all"

That does not even put the entire Active Support in memory upfront indeed, some stuff is configured via autoload, so it is only loaded if used.

1.2. Active Support Within a Ruby on Rails Application

A Ruby on Rails application loads all Active Support unless config.active_support.bare is true. In that case, the application will only load what the framework itself cherry-picks for its own needs, and can still cherry-pick itself at any granularity level, as explained in the previous section.

2. Extensions to All Objects

2.1. blank? and present?

The following values are considered to be blank in a Rails application:

  • nil and false,

  • strings composed only of whitespace (see note below),

  • empty arrays and hashes, and

  • any other object that responds to empty? and is empty.

The predicate for strings uses the Unicode-aware character class [:space:], so for example U+2029 (paragraph separator) is considered to be whitespace.

Note that numbers are not mentioned. In particular, 0 and 0.0 are not blank.

For example, this method from ActionController::HttpAuthentication::Token::ControllerMethods uses blank? for checking whether a token is present:

def authenticate(controller, &login_procedure)
  token, options = token_and_options(controller.request)
  unless token.blank?
    login_procedure.call(token, options)
  end
end

The method present? is equivalent to !blank?. This example is taken from ActionDispatch::Http::Cache::Response:

def set_conditional_cache_control!
  unless self["Cache-Control"].present?
    # ...
  end
end

2.2. presence

The presence method returns its receiver if present?, and nil otherwise. It is useful for idioms like this:

host = config[:host].presence || "localhost"

2.3. duplicable?

As of Ruby 2.5, most objects can be duplicated via dup or clone:

"foo".dup           # => "foo"
"".dup              # => ""
Rational(1).dup     # => (1/1)
Complex(0).dup      # => (0+0i)
1.method(:+).dup    # => TypeError (allocator undefined for Method)

Active Support provides duplicable? to query an object about this:

"foo".duplicable?           # => true
"".duplicable?              # => true
Rational(1).duplicable?     # => true
Complex(1).duplicable?      # => true
1.method(:+).duplicable?    # => false

Any class can disallow duplication by removing dup and clone or raising exceptions from them. Thus only rescue can tell whether a given arbitrary object is duplicable. duplicable? depends on the hard-coded list above, but it is much faster than rescue. Use it only if you know the hard-coded list is enough in your use case.

2.4. deep_dup

The deep_dup method returns a deep copy of a given object. Normally, when you dup an object that contains other objects, Ruby does not dup them, so it creates a shallow copy of the object. If you have an array with a string, for example, it will look like this:

array     = ["string"]
duplicate = array.dup

duplicate.push "another-string"

# the object was duplicated, so the element was added only to the duplicate
array     # => ["string"]
duplicate # => ["string", "another-string"]

duplicate.first.gsub!("string", "foo")

# first element was not duplicated, it will be changed in both arrays
array     # => ["foo"]
duplicate # => ["foo, "another-string"]

As you can see, after duplicating the Array instance, we got another object, therefore we can modify it and the original object will stay unchanged. This is not true for array's elements, however. Since dup does not make a deep copy, the string inside the array is still the same object.

If you need a deep copy of an object, you should use deep_dup. Here is an example:

array     = ["string"]
duplicate = array.deep_dup

duplicate.first.gsub!("string", "foo")

array     # => ["string"]
duplicate # => ["foo"]

If the object is not duplicable, deep_dup will just return it:

number = 1
duplicate = number.deep_dup
number.object_id == duplicate.object_id   # => true

2.5. try

When you want to call a method on an object only if it is not nil, the simplest way to achieve it is with conditional statements, adding unnecessary clutter. The alternative is to use try. try is like Object#public_send except that it returns nil if sent to nil.

Here is an example:

# without try
unless @number.nil?
  @number.next
end

# with try
@number.try(:next)

Another example is this code from ActiveRecord::ConnectionAdapters::AbstractAdapter where @logger could be nil. You can see that the code uses try and avoids an unnecessary check.

def log_info(sql, name, ms)
  if @logger.try(:debug?)
    name = "%s (%.1fms)" % [name || "SQL", ms]
    @logger.debug(format_log_entry(name, sql.squeeze(" ")))
  end
end

try can also be called without arguments but a block, which will only be executed if the object is not nil:

@person.try { |p| "#{p.first_name} #{p.last_name}" }

Note that try will swallow no-method errors, returning nil instead. If you want to protect against typos, use try! instead:

@number.try(:nest)  # => nil
@number.try!(:nest) # NoMethodError: undefined method `nest' for 1:Integer

2.6. class_eval(*args, &block)

You can evaluate code in the context of any object's singleton class using class_eval:

class Proc
  def bind(object)
    block, time = self, Time.current
    object.class_eval do
      method_name