This site is dedicated to further knowledge about creating Ruby on Rails applications professionaly. We discuss Ruby on Rails features from a performance angle, discuss Ruby on Rails performance analysis methods, provide information on Ruby on Rails scaling and benchmark Ruby on Rails performance for each release. We discuss best practices for selecting Ruby on Rails session containers, fragment and page caching and optimizing database queries.

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.

Enjoy!

Update:

  • 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

Posted in plugins | Tags performance

Comments

blog comments powered by Disqus