How to Lazily Find All Records in Rails 3 with Arel

To my surprise, Post.all in Rails 3 performs the query immediately. It does not do lazy loading, as Arel’s where method does.

def index
  @posts = Post.all     # not lazy; returns Array
end

It turns out this is by design. See Force loading – all, first & last.

So: what do you use if your view has fragment caching and you want your controller’s index method to delay the query until you’re sure you’ll need to use it?

At first I did this:

def index
  @posts = Post.where('1=1')  # returns ActiveRecord::Relation
end

But the documented way is to use the scoped method:

def index
  @posts = Post.scoped        # returns ActiveRecord::Relation
end

Another alternative I recommend, which lets you do lazy execution queries in any version of Rails: move the queries to helper methods.

Well, That Didn’t Last Long. Bye, SQLite.

I recently started a new project in Rails 3.0 and decided to try SQLite as the development database. In the past I’ve always used MySQL for my Rails projects. I’m hosting the app on Heroku so it’s PostgreSQL in production, making this my first time using SQLite and my first time using different databases in development and production.

At first I was having fun with SQLite. It’s lightweight, doesn’t use much memory, runs fast on a local dev machine. A single database is stored in a single file. No server to configure, no nothing. Nice.

But I gave up on it after about five days because of an annoying limitation that bit me twice: if you add a NOT NULL column (:null => false in your migration) to an existing table, SQLite requires you to specify a default value—even if the table is empty.

It makes sense to require a default value on a NOT NULL column if there are already rows in the table. All relational databases require that. But if you don’t want to specify a default and the table is empty SQLite shouldn’t force you to.

I found discussions where people suggested a workaround of “Can’t you just drop the table and create a new one? As the table is empty, you won’t be losing any data.” Sure, but it’s far more error-prone to recreate the table because it’s not an incremental change. You have to carefully reconstruct the table and all indexes from all past migrations, which became unwieldy for me even in a 5-day-old project. It really is more error-prone: I tried it and screwed up a couple of times. That was it for me, so I decided it’s time to switch back to MySQL for local development.

The other experiment—using different databases in local development and production—is still going fine. We’ll see what snags I hit that make me change my mind about that too. I still wouldn’t do that on a big project but this is a little ol’ thang.