Tuesday, March 25, 2008

ActiveRecord #find syntax and you

I've recently been adding eager-loading of ActiveRecord associations into a project at work, and I've run into a few problems because of how the original #find queries were wrtten. I did a little research, and found out how you can help out AR a bit and save yourself (and me) some hassle.

The easiest way to query AR is to simply use an id, like this:

User.find(10)

This uses this sql, nicely referenced by the table name:

SELECT * FROM users WHERE (users.`id` = 10)

The downside to this is that a plain #find call will throw an exception if no record exists, and that’s not good (unless caught, the client will see a 500 at that point).

We’ve been getting around this by using find calls like this:

User.find( :all, :conditions => ['id = ?', 10] )

Which generates this sql:

SELECT * FROM users WHERE (id = 10)

This way is good because it will return nil if no record is found, instead of throwing an exception. The subtle downside is that the string in the conditions parameter is copied directly into the sql string, because AR doesn’t know how to do anything with it.

If instead you use a hash for the conditions parameter, like this:

User.find( :all, :conditions => { :id => 10 } )

AR knows that you’re referencing a column from that model’s table, and gives you the safe sql with no bad exception aftertaste:

SELECT * FROM users WHERE (users.`id` = 10)

As an added bonus, because AR is allowed to do it’s magic, if you decide to pass it an array instead of a single integer, it can do the right thing without you having to change your #find code:

User.find( :all, :conditions => { :id => [10,12] } )

Which automatically comes out as:

SELECT * FROM users WHERE (users.`id` IN (10,12))

Summary:

Don’t do this:
User.find(10)
or this: User.find( :all, :conditions => ['id = ?', 10] ).

Do this:
User.find( :all, :conditions => { :id => 10 } )

Monday, March 17, 2008

Rescue Me!

With all the talk recently about elegant nil handling in Ruby (like andand and if_not_nil and #try), I was surprised that no one mentioned using rescue.

Surely every ruby programmer knows rescue when used with its friend begin, but did you know rescue has a statement modifier form?

For example, the following throws an exception:
>> [ "Bacon Lettuce Tomato", nil ].collect {|s| s.upcase.reverse[0,6].reverse }
NoMethodError: You have a nil object when you didn't expect it!
The error occurred while evaluating nil.upcase
from (irb):15
from (irb):15:in `collect'
from (irb):15

Adding rescue nil after that ugly bit of method chaining gets me this:
>> [ "Bacon Lettuce Tomato", nil ].collect {|s| s.upcase.reverse[0,6].reverse rescue nil }
=> ["TOMATO", nil]

... and rescue "" gets me this, useful if I want to return all the same types:
>> [ "Bacon Lettuce Tomato", nil ].collect {|s| s.upcase.reverse[0,6].reverse rescue "" }
=> ["TOMATO", ""]

I haven't used this other than playing around in a console, so I don't really know if this has nasty side effects or not. Maybe there's a reason all these Ruby gurus haven't mentioned it...