Back to blog

Magic behind Ruby code you see every day

Ruby

January 28, 2025

Paweł Dąbrowski

Founder / Software Engineer

As Ruby developers, we live in a world full of readable DSL expressions, self-explaining code, and functions that we can almost read like English sentences. Have you ever wondered how someone built methods we often see in libraries and the Ruby on Rails framework?

I decided to investigate the source code for an explanation and to learn how to create such constructions myself. This article collects popular Ruby methods and provides a simple explanation of how to recreate them from scratch.

Checking for presence

I bet methods #blank? and #present? are some of the most popular Ruby on Rails methods in a Rails application. They are great because they are very flexible and available on all objects.

Some developers are surprised that those methods are not part of the Ruby standard library, and I must admit that I often miss them when writing pure Ruby code. Let's examine the #blank? method to understand its magic.

The place where it all happens

If you look into the Rails source, you will notice the Active Support module. This module is responsible for overwriting standard Ruby classes like String, Array, etc. These classes are called core extensions and are grouped in a special directory. There is no one method blank? that works for all types of objects; each class is updated independently.

It happens in the activesupport/lib/active_support/core_ext/object/blank.rb definition file that overwrites many different classes. For string, nil is not the only representation we consider as a blank string. We also have a string that is filled only with spaces.

class String
  BLANK_RE = /\A[[:space:]]*\z/
  ENCODED_BLANKS = Concurrent::Map.new do |h, enc|
    h[enc] = Regexp.new(BLANK_RE.source.encode(enc), BLANK_RE.options | Regexp::FIXEDENCODING)
  end

  def blank?
    empty? ||
      begin
        BLANK_RE.match?(self)
      rescue Encoding::CompatibilityError
        ENCODED_BLANKS[self.encoding].match?(self)
      end
  end
end

The standard #empty? method easily handles blank strings but cannot detect empty spaces. It's used first because regular expressions are much more expensive, and it does not make sense to execute them if we deal with empty strings.

For some classes, we don't have to implement #blank? method, we can make it an alias for #empty? as they are working as expected. For some objects like Time or Numeric this method will always return false as those objects are always considered present.

When exploring #blank? and #present? it's also worth mentioning the #presence method, which is useful when we want to the assignment, but in case of blank value, we want to assign some other value without the if expression:

name = current_user.full_name.presence || 'Unknown'

The source code is straightforward:

def presence
  self if present?
end

The core extension section in the ActiveSupport module is a valuable source of information when you want to understand how Ruby on Rails works under the hood. In addition to the #blank?, #present?, and #presence methods, it contains more magic that is present in multiple Rails mechanisms.

High-Scalable Active Record

Master the art of database optimization in Rails applications. Learn proven techniques to improve your Ruby on Rails application performance.

The config-way of setting variables

I'm sure you have already got used to the way how we can manipulate configuration data for Rubygems, and it has become a standard to use the following format:

UsefulGem.configure do |config|
  config.api_key = 'secret'
end

It's a typical representation of the DSL in Ruby. We are not doing anything advanced; we only assign values to variables later used in the gem source to alter the behavior of the library.

In the above example, we yield the instance of the configuration class and can set the value for attributes just like we would for any other class. Here is the simple implementation:

module UsefulGem
  class << self
    attr_accessor :configuration

    def configure
      self.configuration ||= Configuration.new
      yield(configuration)
    end
  end

  class Configuration
    attr_accessor :api_key
  end
end

Once we set the configuration in the initializer, access to the configuration values is as simple as calling UsefulGem.configuration.api_key anywhere in the application.

Duck typing

Duck typing is one of the features that makes Ruby such a flexible programming language. One of the examples when you see it in action is when you use some libraries to perform HTTP requests:

response = SomeGem.request('https://example.com')

response.status # => 200
response.body # => 'Hello world'

response.class # => SomeGem::Response

puts response # => 'status: 200, body: Hello world'

How is that even possible? The value assigned to the response variable is not a String, which we can see, yet it behaves like a String. It's because it implements the to_s definition, which tells Ruby how this object should behave when we treat it like a String:

module SomeGem
  class Response
    attr_reader :status, :body

    def initialize(status:, body:)
      @status = status
      @body = body
    end

    def to_s
      "status: #{@status}, body: #{@body}"
    end
  end
end

How about the domain objects behaving like an Array? Imagine we have a garage, and we want to park cars easily:

garage = Garage.new
garage.cars << Car.new('Mercedes')

garage.cars.class # => CarsCollection

The CarsCollection behaves like an array:

class CarsCollection
  def initialize
    @cars = []
  end

  def <<(car)
    @cars << car
  end

  def to_a
    @cars
  end
end

class Garage
  attr_reader :cars

  def initialize
    @cars = CarsCollection.new
  end
end

There are probably endless possibilities to build your own classes and then make them behave like standard Ruby classes like String or Array. Thanks to such an approach, developers can interact with objects using the old, well-known method.

Dynamic methods

The methods that are not defined in a standard way are a blessing when it comes to flexibility and a curse when it comes to debugging them; good luck with searching for their definition. Ruby on Rails is full of them, and they are among the most remarkable features of the framework.

Let's have a look at the method that allows us to find an article using it's title:

Article.find_by_title('Ruby on Rails')

There is no find_by_title method defined by the source code. However, the magic behind this method is easily explainable. Let's explain it by creating a dynamic method that allows us to compare values with syntax sugar:

article = Article.new
article.same_as_title?('Ruby on Rails')

This can be achieved by altering the method_missing method:

module SameAsMethod
  private

  def method_missing(method_name, *args, &block)
    attribute_name = method_name.to_s.match(/same_as_(.*)\?/)&.captures&.first

    if !attribute_name.nil? && instance_variable_defined?("@#{attribute_name}")
      instance_variable_get("@#{attribute_name}") == args.first
    else
      super
    end
  end

  def respond_to_missing?(method_name, include_private = false)
    method_name.to_s.match?(/same_as_(.*)\?/) || super
  end
end

We can now include the module in the Article class to make a test:

class Article
  include SameAsMethod

  attr_reader :title, :category, :author

  def initialize(title:, category:, author:)
    @title = title
    @category = category
    @author = author
  end
end

Let's check if it's working as expected:

article = Article.new(title: 'Ruby on Rails', category: 'Programming', author: 'John')

article.same_as_author?('Tim') # => false
article.same_as_title?('Ruby on Rails') # => true

When you try to execute a method that does not exist, the method_missing is triggered. We can then ensure that we support the dynamic method name and create it in "fly". As you may notice, I also defined the respond_to_missing? method which allows us to ensure that the object responds to the method:

article = Article.new(...)
article.respond_to?(:same_as_author?) # => true

When you redefine method_missing, always define respond_to_missing? with corresponding logic. Without it, you won't be able to call article.method(:same_as_author?) as well.

High-Scalable Active Record

Master the art of database optimization in Rails applications. Learn proven techniques to improve your Ruby on Rails application performance.

Duration objects

One of the most incredible things about Rails is that you can call 3.hours, 1.month or 15.minutes and get a time object or perform time operations with the mentioned code. It's readable, and you don't have to memorize some special classes. Yes, it's related to duck typing because integers in Ruby don't have methods like month, hours, or minutes.

If you inspect similar instructions, you will notice they are instances of ActiveSupport::Duration class. This class is responsible for handling the magic. And the magic itself is added to the standard types quite simply:

class Numeric
  def seconds
    ActiveSupport::Duration.seconds(self)
  end
end

So, if you want to create three instances of Car class with the 3.cars fancy syntax, you can implement it quickly:

class Car; end

class Integer
  def cars
    Array.new(self) { Car.new }
  end
end

And then quickly create as many instances as you want:

3.cars
# => [#<Car:0x000000010824ff50>, #<Car:0x000000010824fed8>, #<Car:0x000000010824feb0>]

As simple as that. You might be wondering the same, but when you call 3.seconds you get the ActiveSupport::Duration instance, but in the console, you will see only 3 seconds:

3.seconds
# => 3 seconds

I was wondering how is that possible until I saw the core definition for this part of the Rails codebase, and it is possible thanks to defining the inspect method:

class City
  def initialize(name)
    @name = name
  end

  def inspect
    "#{@name} city"
  end
end

It's now possible:

City.new('Paris')
# => Paris city

Join the newsletter. Pure knowledge straight to your inbox.

You can opt out anytime.