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 endarticle.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 enddef total_score sum(:score) end endclass 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.