Friends of friends in Ruby on Rails - ruby-on-rails

I have a self-referrential association Friendship. A user has many friends through friendships. I want to get a list of friends of friends. What would be the most efficient way to do this? I'm using ActiveRecord. Thanks!

class User < ActiveRecord::Base
has_many :friendships
has_many :friends, through => :friendships, :class_name => "User"
def friends_of_friends
User.joins(:friendships).where(:user_id => friendships.pluck(:friend_id))
end
end
The friendship model would be something like
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User'
end

Related

Ruby on Rails how to distinguish multiple relationships between same models

So I have the following models
User, Course, Order, Line_item
User (seller) has_many :courses (as the instructor, uploading them)
On the other hand...
User (buyer): has_many :orders
Order: has many :line_items
line_item: belongs_to :course
So I want a list of all courses purchased by a buyer, can I at this point use #user.courses to do so? How can I distinguish between buyer.courses and seller.courses?
Any help is appreciated. Thanks!
You need to use self-referential association for this.
#cousre.rb
Class Course < ActiveRecord::Base
belongs_to :sellar, :class_name => 'User', :foreign_key => 'sellar_id'
end
#order.rb
Class Order < ActiveRecord::Base
belongs_to :buyer, :class_name => 'User', :foreign_key => 'buyer_id'
end

How do you model "Likes" in rails?

I have 3 models: User, Object, Likes
Currently, I have the model: a user has many Objects. How do I go about modeling:
1) A user can like many objects
2) an Object can have many likes (from different users)
So I want to be able to do something like this:
User.likes = list of objects liked by a user
Objects.liked_by = list of Users liked by object
The model below is definitely wrong...
class User < ActiveRecord::Base
has_many :objects
has_many :objects, :through => :likes
end
class Likes < ActiveRecord::Base
belongs_to :user
belongs_to :object
end
class Objects < ActiveRecord::Base
belongs_to :users
has_many :users, :through => :likes
end
To elaborate further on my comment to Brandon Tilley's answer, I would suggest the following:
class User < ActiveRecord::Base
# your original association
has_many :things
# the like associations
has_many :likes
has_many :liked_things, :through => :likes, :source => :thing
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :thing
end
class Thing < ActiveRecord::Base
# your original association
belongs_to :user
# the like associations
has_many :likes
has_many :liking_users, :through => :likes, :source => :user
end
You are close; to use a :through, relation, you first must set up the relationship you're going through:
class User < ActiveRecord::Base
has_many :likes
has_many :objects, :through => :likes
end
class Likes < ActiveRecord::Base
belongs_to :user
belongs_to :object
end
class Objects < ActiveRecord::Base
has_many :likes
has_many :users, :through => :likes
end
Note that Objects should has_many :likes, so that the foreign key is in the right place. (Also, you should probably use the singular form Like and Object for your models.)
Here is a simple method to achieve this. Basically, you can create as many relationships as needed as long as you specify the proper class name using the :class_name option. However, it is not always a good idea, so make sure only one is used during any given request, to avoid additional queries.
class User < ActiveRecord::Base
has_many :likes, :include => :obj
has_many :objs
has_many :liked, :through => :likes, :class_name => 'Obj'
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :obj
end
class Obj < ActiveRecord::Base
belongs_to :user
has_many :likes, :include => :user
has_many :users, :through => :likes
# having both belongs to and has many for users may be confusing
# so it's better to use a different name
has_many :liked_by, :through => :likes, :class_name => 'User'
end
u = User.find(1)
u.objs # all objects created by u
u.liked # all objects liked by u
u.likes # all likes
u.likes.collect(&:obj) # all objects liked by u
o = Obj.find(1)
o.user # creator
o.users # users who liked o
o.liked_by # users who liked o. same as o.users
o.likes # all likes for o
o.likes.collect(&:user)
Models & associations as per naming conventions of rails modeling
class User < ActiveRecord::Base
has_many :likes
has_many :objects, :through => :likes
end
class Like < ActiveRecord::Base
belongs_to :user
belongs_to :object
end
class Object < ActiveRecord::Base
belongs_to :user
has_many :likes
has_many :users, :through => :likes
end
Also, you can use of already built-in gems like acts-as-taggable-on to have same functionality without code :)

What would be a good way to model a group in Ruby on Rails

I've started learning Rails recently and have completed the sample app at http://ruby.railstutorial.org. I am trying to add user-created groups to the sample social network. I've gotten stuck at how exactly I should model the group. I know there should be a user_id of who owns the group and there should be strings of the name and description. Aside from that I have no idea what the rest of the group data model should look like. Thank you in advance.
If you want a user to be in multiple groups your relations should be something like this
class Group < ActiveRecord::Base
belongs_to :owner, :class_name => "User"
has_many :memberships
has_many :members, :through => :memberships
end
class Membership < ActiveRecord::Base
belongs_to :member, :class_name => "User"
belongs_to :group
end
class User < ActiveRecord::Base
has_many :memberships
has_many :groups, :through => :memberships
has_many :owned_groups, :class_name => "Group", :foreign_key => "owner_id"
end
What about something like this?
class Group < ActiveRecord::Base
has_many :users
end
class User < ActiveRecord::Base
belongs_to :group
end

User has_many :users, :through => :friends - how?

This is my code:
class Friend < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
end
class User < ActiveRecord::Base
#...
has_many :friends
has_many :users, :through => :friends
#...
end
When I now start adding users by...
user.users << user2
user.save
Only the user_id of friend is filled, friend_id is null.
Any help?
Yours,
Joern.
Try: Railscasts - Self-Referential Associations. Generally has very good tutorials on all topics listed.
You need to add the :source attribute to your has_many through association.
class User < ActiveRecord::Base
has_many :friends
has_many :users, :source => :friend, :through => :friends
end
Now the following calls will work.
u1.users << u2
u.friends.last
# will print #<Friend id: 1, user_id: 1, friend_id: 4>
Notes:
Rails auto saves the associations.You need to call save only if the user model is new.
You probably should rename the association to something more explicit. E.g: friend_users etc.
I think you need delete the belongs_to :user in your Friend model

Rails - Two-way "friendship" model (cont'd)

This is a continuation of this question:
Original Question (SO)
The answer to this question involved the following set of models:
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships #...
end
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => 'User', :foreign_key => 'friend_id'
end
<% for friendship in #user.friendships %>
<%= friendship.status %>
<%= friendship.friend.firstname %>
<% end %>
This works fine if say, I have a user and I want to get all the "friendships" for which his or her id is the :user_id FK on the Friendship model. BUT, when I run something like
#user.friendships.friends
I would like it to return all User records for which that User is either the :user or the :friend in the friendship - so, in other words, return all friendships in which that user is involved.
Hopefully the above makes sense. I'm still quite new to rails and hope there is a way to do this elegantly without making just a standard link table or providing custom SQL.
Thank you!
Tom
railscasts episode on this topic
You cannot just use #user.friendships here because it will only give you those friendships where #friendship.user_id == #user.id.
The only thing I can think of right now is just to do
Friendship.find_by_user_id(#user.id).concat Friendship.find_by_friend_id(#user.id)
my solution is a a scope:
# model
class Message < ActiveRecord::Base
belongs_to :sender, :class_name => "User"
belongs_to :recipient, :class_name => "User"
scope :of_user, lambda { |user_id| where("sender_id = ? or recipient_id = ?",
user_id, user_id) }
end
# in controller
#messages = Message.of_user(current_user)
From the railscast link:
# models/user.rb
has_many :friendships
has_many :friends, :through => :friendships
has_many :inverse_friendships, :class_name => "Friendship", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user

Resources