05 MarActiveRecord.find_by with join from irb
Friday, 05 March 2010 — 09:00If you like to run your code on irb before it goes to the project like me, you might find this problem when running “find_by” ActiveRecord’s queries joining multiple tables.
Let’s take this as an initial example of what i mean:
projects = Projects.find(:all, :select => "projects.id, projects.title, projects.version, managers.name", :joins => :manager, :conditions => "projects.technology='Rails'", :order => "managers.name asc")
What would you expect from this?. I would expect a resultset with the values under :selected clause, that is an array of projects, with title, version and manager name.
However, if you run this from irb, you will get something like this:
[#<Project id: 18004351, title: "ggomeze.com", version: "1.0">]
Where is the manager’s name?. But if you run next command, in the worst case, you will get:
>> projects[0].name => nil
Let me explain you what might be happening here: you probably has an instance method in project class with same name!!. Really?, ok, let’s check it::
>>Project.instance_methods.grep(/name/) ["name","name_xxx"]
There you go!. So applying rule of thumb “right and top”, rails is running that method instead of getting the attribute from the resultset. That’s obviously a flag that we are not doing things right. There are several techniques to refactor that code. Delegates might be one.
Just to demonstrate manager’s name is there, let’s make a small change in our initial code:
projects = Projects.find(:all, :select => "projects.id, projects.title, projects.version, managers.name as foo", :joins => :manager, :conditions => "projects.technology='Rails'", :order => "managers.name asc")
And now, back to irb:
[#<Project id: 18004351, title: "ggomeze.com", version: "1.0">] >> projects[0].foo => "Gerardo Gomez"
Don’t be lazy and refactor your models!
Ger