request-quote
Ruby on Rails

Making Real-Time Chat Rooms Use WebSockets on Rails

By Dima Y March 09th, 2016

A lot of complex web applications need a chat for users: starting from social networks and finishing with job seeking portals. It is fast and effecient to chat in real time. Moreover, there is no need to reload pages. 

Real-time chat means immediate message exchange. Let's see how to create chats on Rails using WebSocket protocol which is actually not supported in this framework. Nevertheless, it is possilbe to include this useful technology in our project. 

What are WebSockets?

WebSockets is a protocol providing full-duplex communication channels over a single TCP connection. With this API, you can send messages to the server and receive event-driven responses without having to poll the server for a reply.

Unfortunately, Rails doesn’t allow us to use WebSockets. That is why we need Faye.

What is Faye?

Faye is a publish-subscribe messaging system based on the Bayeux protocol. It can process asynchronous requests (publishing/subscribing). It allows different users to log in and notify of the events that happened.

How it works?

All Faye clients need central server for message exchange. The server records addresses and channels which clients are subscribed to. To notify users about new message in real time mode, we make our app listen to Faye server and our Rails app send messages via Faye server.

We can subscribe certain users to certain channels and publish new messages to the necessary channel. Faye server allows to keep the socket connection open to send messages from the server to the client. Let’s see how we can subscribe users to the channels and publish necessary messages for each user.

Mailboxer

Mailboxer is a Rails gem to send messages inside a web application. We just need to add acts_as_messageable to all the models which exchange messages.

Let’s add three new gems to Gemfile and install them.

gem 'faye-rails', '~> 2.0`
gem 'thin'
gem ‘maiboxer’

$ bundle install

 

We should start the application on Thin web server as Faye doesn’t work with WEBrick.

rails server thin

 

Now add it to application.rb:

config/application.rb

 

config.middleware.delete Rack::Lock
config.middleware.use FayeRails::Middleware, mount: '/faye', :timeout => 25

 

We use FayeRails as an intermediate program layer which is installed in /faye.

Now add these files to application.js.

application.js
//= require faye
//= require comments

 

Faye.js file is provided by faye-rails gem. It contains all necessary JS code so Faye could work properly.

Let’s create messages.js file.

(function($) {
  $(function() {
         var $form = $('.nxmail-box__form-box form'),
        	$input = $('#message_body'),
        	$nxmailBoxContent = $('.nxmail-box__content'),

var client = new Faye.Client(location.origin +'/faye');
client.subscribe('/conversations/id, function(data){
        var $message = renderMessage(data.message);
       $('#nxmail-container').animate({scrollTop:$('#nxmail-content').height()}, 300);
      }
    });

$form.submit(function(){
	$.ajax({
method: 'POST',
url: Routes.reply_to_conversation_path(conversation_id),
        data: {
          message: {
            body: $input.val()
          }
        }
});
$input.val('');
return false;
});

function renderMessage(data) {
var template = [
'<div class="message_text">',
                  '<p>' + data.body + '</p>',
	‘<div>’
].join('');
return $(template);
}
  });
})(jQuery);

 

We create a new Faye client and attach it to the window object. Subscribe this client to /messages/id channel. As soon as all data are received paste a new message to the end of the list.

The last thing to do is to create controller messages_controller.rb.

class MessagesController < JobSeekers::JobSeekerController
        def show
        	@conversation = Mailboxer::Conversation.find(params[:id])
end 

	def reply
	   @conversation = Mailboxer::Conversation.find(params[:id])
       	   current_user.reply_to_conversation(@conversation, params[:message][:body])
   @receipt = @conversation.receipts.last
               @message = @receipt.message

	   data = {message: {body: @message.body}}
   channel = '/conversations/' + @conversation.id.to_s
        	   broadcast(channel, data)
end

def broadcast(channel, data)
        base_url = request ? request.base_url : "http://localhost:3000"
        client = Faye::Client.new("#{base_url}/faye")
        client.publish(channel, data)
end
end

 

Broadcast method gets the name of the channel and a hash with the message, creates a new Faye user and publishes the message to the channel. 

466

 

Conclusion

We have created simple online chat with the help of Rails and WebSockets. WebSockets technology fits much better for data exchange in real time. There are just needed some addtional steps to make it work on Rails. 

 

The code can be found here

Dima Y

Dima Y

Ruby on Rails Developer at iKantam

Browse Recent Posts