Rails 3 lazy-loading and the console

2010-08-20

While debugging an Rails 3 application in development, I started to wonder if the lazy-loading feature of ActiveRecord 3 was really working.

In a model, I was building a query depending on some parameters. Something like this (obviously simplified) :

class Thing < ActiveRecord::Base
  def initialize(what, options = {})
    collection = what.scoped
    collection = collection.limit(options[:limit] if options[:limit]
    []
  end
end

A great thing about Rails is the console. You start you application in a terminal and you can use it (except for controllers and view).

So I did a simple :

t = Thing.new(AnotherModel)

Then in my development.log file I saw a beautiful

SELECT * FROM things

The thing is until you really ask for the data, the database should not be queried and the object is a Relation, not a collection of your model.
In the documentation, I was reading that “asking for the data” is calling a method like “first”, “last”, “all” or any iterator. I didn’t use that kind of methods, it shouldn’t query the database.

I turned everything upside down, tried to look in ActiveRecord’s code, then ActiveRelation’s, then built a dummy application to see if my setup configuration was wrong, then read 2 times every article I could find about ActiveRecord Query Interface 3.0, then look in the Rails Lighthouse, then ask on several IRC channels… nothing !
I started to question the reality of this “lazy-loading” feature.

Then I asked on Twitter and my friend Sébastien Gruhier (of the Maptimize fame) gave me the solution. It’s as simple as print, really !
The damn print IRB (the console) is using to show me the data I (in fact) asked for.

Here is the solution for the console :

t = Thing.new(AnotherModel); nil
nil

t.class
ActiveRecord::Relation < Object

I was able to verify that everything was working in the Controller/View part by clearing the whole view and still loading the page : no SELECT in the logs.

I admit that the few hours I spent on this scared me a little. Such a big bug on the Rails side could not be possible, but I used all the tricks I knew to find the issue. As often, an (great) outsider found the solution in a couple of minutes. I should have asked earlier. Even after a decade in this, I still make the mistake.