views:

86

answers:

3

This code is taken from a previous question, but my question directly relates to it, so I've copied it here:

class User < ActiveRecord::Base
  has_many :group_memberships
  has_many :groups, :through => :group_memberships
end

class GroupMembership < ActiveRecord::Base
  belongs_to :user
  belongs_to :role
  belongs_to :group
end

class Role < ActiveRecord::Base
  has_many :group_memberships
end

class Group < ActiveRecord::Base
  has_many :group_memberships
  has_many :users, :through > :group_memberships
end

New Question Below

How you take the above code one step further and add and work with friends?

class User < ActiveRecord::Base
  has_many :group_memberships
  has_many :groups, :through => :group_memberships
  has_many :friends # what comes here?
  has_many :actions
end

In the code above I also added actions. Let's say that the system kept track of each user's actions on the site. How would you code this so that each action was unique and sorted with the most recent at the top for all the user's friends?

<% for action in @user.friends.actions %>
 <%= action.whatever %>
<% end %>

The code above may not be valid, you could do something like what I have below, but then the actions wouldn't be sorted.

<% for friend in @user.friends %>
 <% for action in friend.actions %>
  <%= action.whatever %>
 <% end %>
<% end %>

UPDATE

I guess the real issue here is how to define friends? Do I create a new model or join table that links users to other users? Ideally, I'd like to define friends through the common group memberships of other users, but I'm not sure how to go about defining that.

has_many :friends, :through => :group_memberships, :source => :user

But that doesn't work. Any ideas or best practice suggestions?

A: 

It's been a while since I've worked with Rails (think Rails 1.x), but I think you can do something like the following:

Action.find(:all, :conditions => ['user_id in (?)', @user.friends.map(&:id)], :order => 'actions.date DESC')

and then in your view:

<% @actions.each do |action| %>
  <%= action.whatever -%>
<% end %>
Marcel Guzman
A: 

To add friends you can use has_and_belongs_to_many or has_many :through. Here is example how to do it. You should self join users table.

To list recent actions, get them from db using :order => :created_at (or :updated_at). Than you can filter out actions that not belongs to users.

@actions = Action.all(:order => :created_at).select {|a| a.user.friends.include?(@user)}

Probably you can also write sql query to do this.

klew
A: 

You could make a "friend" object that uses the "user" table and go from there or you could define a friend as a partial entity (friend name and user_id reference) and pull the user object into the friend object.

I like this last model better because it allows you to represent "deleted" friends more easily. You would still have a copy of the friend name (even if it is a duplicate of the user name) and the ID to identify if the user is gone or not. Keeps the "friend" concept a little further away from the "user" concept and any security implications. I will argue it's appropriate de-normalization to duplicate the "name" field as it adds flexibility and "shadow" information about a user that used to exist.

My take on it, though. You would still have to eventually self-join back to the user table, but perhaps not as much. If you REALLY hate to de-normalize anything you can have a Person object that Friend and User both refer to for information. Personally, I think that is a nice theoretical construction, but not worth the complexity it would introduce into the bulk of your code (80% of it being talking about the user and 20% talking about the friend). I would make the user easy to talk about and the friend a little hard... not both. :)

Hope this helps. I enjoyed mentally toying with this one. :)

sam