zerosum dirt(nap)

evolution through a series of accidents

zerosum dirt(nap)

ActiveRecord Association Extensions

February 08, 2007 by nap · Comments

So this is probably old hat to a lot of you, but for those that don’t know, ActiveRecord Association Extensions are a seriously useful widget to have in your Batman Fantasy Camp utility belt.

You’ve no doubt noticed that when you define an association between models, you get a bunch of nifty methods on the resultant collection like push, delete, count, uniq, and so on. But what if you want some bit of functionality defined on an association that isn’t already baked in?

Why we can extend our associations to define our own methods, of course. Let’s see how…

Okay, so we have a relationship defined between two models: articles and opinions. An article has_many opinions. An opinion has a score on it, which is an integer between 1 and 10 representing how a user felt about the article.

Using Association Extensions we can define our own method on the association itself, called total_score:

class Article < ActiveRecord::Base
  has_many :opinions do
    def total_score
      sum(:score)
    end
  end
end

Now you can access this information by writing something like:

article.opinions.total_score

Damn, that’s intuitive. Maybe we want to add another method on the association to retrieve the average opinion of users who voted on this article today:

has_many :opinions do
  def average_opinion_today
    find(:all, :conditions => ["created_at >= current_date()"]).average
  end
end

article.opinions.average_opinion_today

And it gets better. If we find ourselves using the same extensions in more than one place, in true DRY spirit we can build a module and reuse it. When we use the :extend option in an association, we get all the methods in that module mixed in.

module MiscellaneousExtensions
  def average_opinion_today
    find(:all, :condition => ["created_at >= current_date()"]).average
  end

  def total_score
    sum(:score)
  end
end

class Article < ActiveRecord::Base
  has_many :opinions, :extend => MiscellaneousExtensions
end

I put this module in a subdirectory of lib called extensions. You’ll have to add that path to the config.load_paths in environment.rb in order for it to be recognized.

Using these sorts of extensions on my own project earlier today really helped me clean up some troublesome model code. I was just so amped up about it that I had to share.

For more information, see the ActiveRecord::Associations API Reference. Oh and a big shout out to Brian Hogan for pointing me in the right direction.

blog comments powered by Disqus