Simpler Piggy Backing
Posted 29 May 2006
A while ago I posted an article about the performance improvements obtainable by piggy backing. Although the speedups where substantial, I received some negative comments about exposing too much SQL to the programmer.
Exposure to the underlying SQL is not such a big problem for me, that it would deter me from applying the technique. But I found that I had a lot of repetition in my code, which I didn't like.
After ignoring the problem for a while, I decided it was about time to deliver a better solution than manually coding selects and joins.
I wrote a small extension to ActiveRecord, which makes piggy backing extremely simple.
Instead of coding joins and selects manually, you can now declare the piggy backed attributes from a has_one or belongs_to association in your model classes like so:
class Article belongs_to :user piggy_back :user_name_and_birthday, :user, :name, :birthday end
This will generate all necessary select and join statements, which can then be added to a query rather elegantly:
article = Article.find :first, :piggy => :user_name_and_birthday, :conditions => "articles.title LIKE '%piggy%'"
would retrieve the first article which contains 'piggy' in its title attribute, along with the birthday and name attributes of the corresponding User object, storing them in the attributes hash of the AR instance.
But this is not all! After declaring the piggy backed attributes, you will also get reader methods which retrieve the attribute for you from the AR instance, and, should a type cast be required, convert the value to the correct type.
The piggy backed attributes are accessed like this:
article.user_name ==> a string article.user_birthday ==> a date
Additionally, should you forget to piggy back a query,
article = Article.find :first, :conditions => "articles.title LIKE '%piggy%'"
methods user_name and birthday will silently use the user association to retrieve the values for you. This make is possible to use user_name and user_birthday regardless of whether your original query was piggy backed or not.
I've ensured that all of this works with pagination. In fact, using the extension is slightly faster than coding the select and joins manually (1%-3%).
You can download the code here. I haven't turned it into a plugin yet, so you need to add a require 'piggy_back' to your environment.rb.
- I failed to mention that piggy backing also works with finds on associations, so
article.comments.find(:all, :piggy => :user_name)
works as expected.
- Upon popular demand it's now possible to use a more verbose syntax. You can write
class Article belongs_to :user piggy_back :user_name_and_birthday, :from => :user, :attributes => [:name, :birthday] end
for the example above.
- Piggy backing is now available as a plugin using the URL svn://railsexpress.de/plugins/piggy_back/trunk