How to Create the Simplest Friendship Model on Rails
Engineering is about dividing problems into smaller partials. And then solving them with the best approach. As an engineer, I enjoy problem-solving.
In this article, you will learn how to create friendships on Rails. In the most basic form, this is far away from perfection. But totally working. You can find the GitHub repository link at the bottom of the page to dive deeper into the code. In the repository, you will also find very simple session management.
After reading this article you will have an understanding of friendships on Rails.
When you want to create a rails app with user friendship interaction, you have to decide what your final product will be. There are many different ways to form a friendship model. In this project we are choosing this model:
1-) Users are allowed to send a friendship invitation to other users.
2-) Users can accept or reject the invitation that is coming from another user.
2a. Accepting ->Will update a record column from false to true
2b. Rejecting -> Will delete the record.
3-) Unfriending will delete the record
We will have a User model, and an Invitation model.
Let’s focus only on friendship details. The invitation model will have only three columns(by the way, keep in your mind that the Rails is always giving you 3 more columns by default: id as primary key, and 2 timestamps).
Those columns are: user_id, friend_id, and confirmed. user_id and friend_id are both referencing back to the users table(User model).
When I am sending an invitation to my friend: user_id will be my id and friend_id will be my… well, you got that! And confirmed will be false by default.
This means an invitation is starting to wait to be accepted or rejected at the moment it got created. When the invitation is rejected, the row is gonna be deleted. When it is accepted the row is gonna be updated, confirmed: from false to true.
Any record with true value in the confirmed column means that those two ids are friends now.
Any record with a false value in the confirmed column means that user_id is waiting for friend_id to accept/reject his/her invitation.
Let’s run these three different commands on command line:
$ rails new friends4ever
$ rails g scaffold User name
$ rails g model Invitation user:references friend_id:integer confirmed:boolean
After this command we need to add
, default: :false
to our migration file. Right after this line:
Let’s go to our model files, first: app>models>user.rb
Hold on there, I am explaining one by one…
Our user has many invitations. This means A user object will respond to the “invitations” call.
User.first.invitations => should make sense
Our user has many pending invitations. Yes, try to stick with plain English, this will help a lot! This means the same thing, A user object should respond to the “pending_invitations” call. And we have options here. First option is a scope, this tells ruby to look for records only has “false” value on pending column. The other option is the “class_name”. We are adding this because rails can’t find the related class from the “pending_invitations”. So I am telling rails “Hey, I want this call on my object and go look for this in that class”. Rails going to the Invitation class and fetch all the records with specific user_id and confirmed values.
User.first.pending_invitations => should make sense
So what am I going to do with this call? On every users’ page, I want to list the people who sent an invitation to that user. Very useful!
We have instance methods. And of course, we have a “friends” method. This will return all the friends of a user in an array. I am not proud of what I do inside this method, but it is working 😃.
User.first.friends => should make sense (All the friends of the first user)
In the “friend_with?” method, we are returning a boolean value.
User.first.friend_with?(User.last) => should make sense (Is the first user have a confirmed friendship with the last user?)
This is useful when you want to do things with the logic of friendship. For example, show the posts if they are friends, don’t show otherwise.
With the “send_invitation” method we are sending invitations.
User.first.send_invitation(User.last) => should make sense (First user sending an invitation to the last user.)
With these very simple 5 interaction tool, we are done in the User model. Let’s go to the second one, the Invitation model.
Nothing fancy, in 4 steps we will go through this.
Every invitation belongs to a user. Plain English. Very tasty. Haha! Long live DHH! 😅 Long live Matz! 😅
Invitation.first.user => should make sense(this will return a record, a user who sent the invitation to the other one)
After this point, as you realize, we have three class methods. Because I want them to be usable regardless of Invitation objects.
Starting with the “reacted?” method we are questioning if two of the given ids have any records in our table.
Invitation.reacted?(id1, id2) => should make sense
In some situations, you need to know this. For example, you want to show a button for an invitation and you have to know if the two ids have any record in the invitations table. If they have a record then you don’t want to show the button because now you know something! Those two users are in either of the following phases:
1-) Some of them sent an invitation to the other one and waiting for a decision.
2-) They are already friends.
In both cases, we can’t create another record. So we are hiding our button with the information that is coming from this awesome method “.reacted?”.
Another class method is the confirmed_record?(..). We are using this method in the User model. This method is basically checking a confirmed record for two given ids.
Invitation.confirmed_record?(id1, id2) => should make sense
The last method is find_invitation(). We are using this method to find the invitation record of two given ids. Useful when deleting a confirmed relationship.
Invitation.find_invitation(id1, id2) => should make sense
Now we can run the migrations with:
And enter the rails console with:
You can create new users, send invitations, accept them with update methods, reject them with delete methods.
Furthermore, you can see the friends of a specific user, see the pending invitations, see the invitations that user sent, and more.