August 22, 2019; in Ruby on Rails

How Ruby on Rails ActiveRecord chaining works

If you’ve used Ruby on Rails you’ve probably chained where before.

In app/controllers/posts_controller.rb:

class PostsController < ApplicationController 
  def index
    @posts = Post.where(active: true).where(user_id: params[:user_id])
  end 
end 

But how does this work?

Understanding when the query gets sent

One thing that’s not readily apparent is that in our above example, @posts is not loaded?.

Only when @posts is iterated upon does it get load. When does it get iterated upon? Things like each or map trigger load.

Try it yourself:

User.all.limit(3).loaded? # nil 
User.all.limit(3).load.loaded? # true

If you hit to_s, you’ll see when things get queried:

User.all.limit(3).load.to_s # #<User::ActiveRecord_Relation:0x00007f8f892e8298>
User.all.limit(3).loaded.to_s # ""

A practical example of the query builder pattern

class User
  def self.all_args
    @all_args ||= []
  end

  def self.where(*args)
    all_args << args
    self
  end

  def self.results
    @results
  end

  def self.load
    puts "Executing sql #{all_args.join(", ")}"
    @results = [1, 2, 3]
  end

  def self.loaded?
    !!!@results.nil?
  end

  def self.each
    load
    yield @results
  end
end
users = User.where(friend: true)
puts users # 
users.each do |record|
  puts record # 1, 2, 3
end  

If you run that in irb and compare that to your commands in rails console, you’ll notice one thing: The rails console version executes your users from puts users. This is because of inspect.