Rails 3 lazy-loading and the console
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.