Extend has_many with the :extend option
Wednesday, December 9th, 2009The method has_many has several options that help you refine an association. The :extend option is one that I found myself using recently, and thought it would be nice to share with the internet.
Say you have a Friendship class that belongs to a User class, and that your User class has_many :friends, :through => :friendships. A good old fashioned @user.friends would return all the user records for @user’s friends.
Let’s also say your friendships table contains the fields user_id:integer, friend_id:integer, and state:string, where state can either be ‘active’, ‘requested’, or ‘pending’.
You might be tempted to do something like this in your User class:
has_many :friends do
def active
...some query...
end
...and so on for each state
end
I suppose, this would be fine if you only needed one method. You need something for each state, though, and this direction leads to unsightliness quite fast. Enter :extend.
Using this method, you can define methods in a module and call that module using extend. So the association looks like this:
has_many :friends, :through => :friendships, :extend => FriendshipState
FriendshipState is the module where your methods are defined:
module FriendshipState
Order = "users.last_name asc, users.first_name desc"
def pending
find(:all, :conditions => ['friendships.state = ?', 'pending'], : order => Order)
end
def requested
find(:all, :conditions => ['friendships.state = ?','requested'], : order => Order)
end
def active
find(:all, :conditions => ['friendships.state = ?','active'], : order => Order)
end
end
NOTE: there should be no space between ‘:’ and ‘order’. I did that to prevent god forsaken emoticons
The join is handled for you, so @user.friends.active returns all user records for @user’s active friendships; and so on with pending and requested.
SELECT `users`.* FROM `users` INNER JOIN `friendships` ON `users`.id = `friendships`.friend_id WHERE ((`friendships`.user_id = 1) AND (friendships.state = 'active')) ORDER BY users.last_name asc, users.first_name desc
Nice, huh?
Quick disclaimer, I’m no command line guru, but I am a huge fan of anything that helps me do my job faster, so I decided to load up some aliases in .bashrc this morning. A perfectly good Sunday morning activity.




I'm Eric Hurst, and I make the Internet from a comfy chair in Kansas City. Send an email to "eric at erichurst dot com" (spam is bad) to hire me.
