Facebook Like Chat application using Ruby On Rails .....!!
Kanhaiya Kumar
BI (SME/COE Lead) |Lead Analyst | Power BI |Tableau| SQL | Snowflake | Gen AI | GCP | Azure| Python| Hive-QL | TYPESCRIPT | D3.JS|Altreyx| MS-SQL Server| Xurmo | RUBY| ROR| HTML| R |
In this tutorial, I will guide you on how to implement a Gmail/Facebook like real-time chat application in a Ruby on Rails application.
Introduction :
As a framework, Rails is not very good at handling asynchronous events and therefore establishing a socket connection to a Rails application is almost impossible. There are many solutions that are designed to handle this kind of problem. Frameworks such as Node.js with Socket.IO, or, if you want to stick with Ruby, Cramp, async_sinatra, or Goliath framework are all great solutions but it would be super awesome to continue using Rails for our application's logic and also have the benefits of some kind of asynchronous event handling with publishing and subscribing when we need that. To achieve this, we will use Private Pub Gem which is built on top of Faye and makes it dead simple to publish and subscribe to real-time events in a Rails app.
GETTING STARTED :
We’re going to add the Instant Messaging feature to an existing Rails application. Below is a screenshot from a simple rails app (Friend Circle) that uses Devise to authenticate users. It's current functionality is very basic. On the home page, we are just looping the list of all users except the currently logged in user.
We want to enable the users of our app to chat with each other. Let's start by modeling up the logic of our chat application. Our logic is very simple. A user will have many conversations and a conversation will have many messages. Here is the relationship diagram
USER MODEL :
Let's get started by creating the USER model. Install Devise to your Application.
Follow up this -:
CONVERSATION MODEL :
Let's get started by creating the conversation model. A conversation will hold the sender_id and the recipient_id both of which are instances of a user. The sender_id will hold the id of the user starting the conversation and the recipient_id will hold the id of the other user. On your terminal
$ rails g model Conversation sender_id:integer recipient_id:integer?
It's a good idea to add an index to both sender_id and recipient_id as we will use this fields while searching.
After migrating the database, lets update our user model. Since we don't have the user_id column in our conversation table, we must explicitly tell rails which foreign key to use.
Next, lets edit our conversation model. A conversation will belong to both a sender and a recipient all of which are instances of a user.
class Conversation < ApplicationRecord
belongs_to :sender, :foreign_key => :sender_id, class_name: 'User'
belongs_to :recipient, :foreign_key => :recipient_id, class_name: 'User'
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, :scope => :recipient_id
scope :involving, -> (user) do
where("conversations.sender_id =? OR conversations.recipient_id =?",user.id,user.id)
end
scope :between, -> (sender_id,recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id = ?) OR (conversations.sender_id = ? AND conversations.recipient_id = ?)",sender_id,recipient_id,recipient_id,sender_id)
end
end
As you can see, our conversation will have many messages. We are then validating uniqueness of the sender_id and passing a scope of the recipeint_id. What this does is that it ensures that the sender_id and the recipient_id are always unique so that we only have unique conversations in our application. For instance, a conversation with (sender_id: 1, recipient_id: 2) and another with (sender_id: 2, and recipient_id: 1) should never occur since the conversation is essentially between the same users.
We have also added two scopes involving and between. The first scope will help us retrieve all conversations of the currently logged-in user while the last scope will help us check if a conversation exists between any given two users before we create the conversation.
MESSAGE MODEL :
Now that we have our conversation model, lets proceed and create our message model. Run the following command on your terminal and run rake db:migrate.
$ rails g model Message body:text conversation:references user:references
Our messages table will have a body, conversation_id, and user_id columns. The conversation_id column will keep track of which conversation a message belongs to and the user_id column will keep track of the user who sent the message during chat. Adding some validations
class Message < ApplicationRecord
belongs_to :conversation
belongs_to :user
validates_presence_of :body, :conversation_id, :user_id
end
LOGIC :
Now that our models are in place, I want to explain the general flow of how the inline chat application will work. On our home page, we will add a send message button along each user's name. The button will hold two data attributes i.e. the id of the current user and another id of the reciever id. When a user clicks the button, we will send an asynchronous request to our rails app with the current user's id and the recipient id. If a conversation exists, we return the conversation id immediately, otherwise we create the conversation and return the id of the newly created conversation.
We will then pick, the conversation_id returned by the server using jQuery. Using this conversation_id, we will then request the respective show page of that conversation which will have a list of all its associated messages. For instance, if the server returns conversation_id 3, we will request the html page at conversations/3. We will then append this conversation data in our home page in a popup div.
ADDING THE MESSAGE BUTTON :
For each user we are displaying on our home page, we associate a send message button with him or her. The data-sid attribute will hold our current users' id while data-rip attribute will hold the recipient's id. We will send this values through an ajax request to our server which should create a conversation if necessary.Create index.html.erb file inside users view folder.
To keep this tutorial, short I have created most of the jQuery logic in a single file which I will link here. I have tried to make it as modular as possible and also provide comments on each method so it shouldn't be hard to comprehend what's cooking. Lets create a file chat.js inside our javascripts folder and paste the contents of chat.js. This file has all the functions we will need when creating chatboxes in our home page and also communicating with our rails app.
Let's not forget to add this file to our javascript manifest file.If using rails 4 and turbolinks, require this file before requiring turbolinks.
//= require chat
In my users.js we'll listen for various events on our home page document and call respective methods inside our chat.js file
CREATING CONTROLLERS :
We now have our javascript files logic ready all is left is piecing up our conversations and messages controllers to handle requests from our javascript file. Let's start by our conversations controller
$ rails g controller conversations
Our conversations controller will have only two actions, the create and the show actions.
You will notice, the layout false directive. This is because we don't want the show view inheriting from application.html layout. We will be appending this view on our home page.
In the create action, we start off by searching if a conversation exists between the sender_id and recipient_id. Recall our between scope we defined earlier in our conversation model. If a conversation is found, we return it and assign it to the @conversation instance variable. If no conversation was found between the two users, we create a new conversation. We then eventually return a json response with the id of the conversation.
Our show action is also pretty standard, after getting the conversation with the specific id, (note this id is sent by our javascript code in chat.js), we also find who is receiving the message. We will use the receiver's name as the chatbox title in our chat.
INSTALLING PRIVATE_PUB :
It would be nice if we installed private_pub at this point as our next set of task, we will be utilizing functionality offered by the gem. Add private_pub gem in your Gemfile and run bundle. Installing it will also install Faye and its dependencies. We will also include the thin gem as we will be using it to serve Faye.
What stand's out here is the suscribe_to method. Here we subscribe to a channel ( the conversation's path ). We will use this same path to publish update notifications to this channel from our controller. We do this by calling subscribe_to and passing in the name of a channel which takes a format of a path. In our case we pass the current conversation's path.
The suscribe_to function is provided by the private_pub javascript we required inside our application.js manifest file. We also notice that our form will always be submitting via ajax due to the remote option.
Our last controller is the messages controller.
$ rails g controller messages
Here we publish to the same path we suscribed to in our view. We also asign a number to variables from our rails app to variables in our javascript file. You are probably wondering what the following line does?
var reciever_id = $('meta[name=user-id]').attr("content");
We need a way to identity who is the recipient when publishing the notifications. A simple way around this is to create a meta tag in our application.html layout file that store's the id of the currently logged in user. In the head section of our layout file add
<meta content='<%= user_signed_in? ? current_user.id : "" %>' name='user-id'/>
You will also note that we are rendering a message partial. We have not created that yet. In our messages folder
Note, we have used to helper methods self_or_other and message_interlocutor. They both take a message as an argument. Lets go ahead and define them. In our messages_helper file
Since we created our conversation's controller and messages controller, we have not yet defined their respective routes. Let's add them right away
Notice we don't have yet any stylings for our chat box. Create a file chat.css in our stylesheets folder and paste the contents of chat.css. Let's also add the fon't awesome css in our application.html layout. This will provide us with some nice icon stylings for the minimize and close buttons.
TESTING OUR CHAT APPLICATION :
Here comes the most interesting part of the tutorial. Our application should now be capable of handling near-realtime instant messages between any two users. In one browser window, I'm logged in as "kanhaiya" and in another i'm logged in as "Tarini". As "sunny", clicking on kanhaiya's send message button, creates a new conversation between us and pops up a beautiful chat window. On my other browser as "kanhaiya" clicking on Joseph's send message button pops up a similar window and we can now chat instantly!. Check it out
You can get the source code at My Github Link. Enjoy your chat. Feel Happy Happy Happy & Keep always Smiling with Technology.
General Manager at Ngan Long
7 年s
Frontend Engineer
7 年this looks like Joseph Ndungu idea https://josephndungu.com/tutorials/gmail-like-chat-application-in-ruby-on-rails Give him credit
Cofounder | Product | Emerging Markets | Payments
7 年This is plagiarism to the highest level .. Give credit ...
Rubyist and Passionate Web programmer
7 年I'm the original author of this tutorial. I'd really appreciate if you gave credit where its due. Thanks!!