Extend has_many with the :extend option

The 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?

Tags: , , ,

Leave your two cents...







(I would never publish this)