How To Build A Realtime Commenting System with Rails

This tutorial is a quick and easy example of Collaboration and Realtime Updates using PubNub to build an interactive realtime commenting system where a comment made in one user’s browser will appear in a second user’s the moment it’s submitted.

We’ll build a simple Rails application for a blog with multiple articles with individual comment streams. In a nutshell, we’ll be taking the basic comments system built in this tutorial, which only takes a couple minutes using Rails scaffolding. With that, I slightly adjusted the CSS to style it a bit as well.

Gemfile

Firstly, we add pubnub gem to Gemfile and then bundle install in terminal.

gem 'pubnub', '~> 4.0.21'

app/views/layouts/application.html.erb

We added PubNub’s CDN to base the template on which every other template is rendered.

We have also added Turbolinks. As per their GitHub page:

Turbolinks makes navigating your web application faster. Get the performance benefits of a single-page application without the added complexity of a client-side JavaScript framework. Use HTML to render your views on the server side and link to pages as usual. When you follow a link, Turbolinks automatically fetches the page, swaps in its, and merges its, all without incurring the cost of a full page load.

We need to use Turbolinks because we want to use the functionality of a client-side framework without actually bringing it in, otherwise, it’d become more complex.

Flash block is only to show the success/error messages happening inside the application.

<!DOCTYPE html>
<html>
<head>
 <title>Blog</title>
 <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track' => true %>
 <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
 <script src="https://cdn.pubnub.com/sdk/javascript/pubnub.4.8.0.min.js"></script>
 <%= csrf_meta_tags %>
</head>
<body>
  <div id="container">
     <%= link_to "Home", root_path %>
     <%= render 'layouts/user_widget' %>

     <% flash.each do |name, msg| %>
       <%= content_tag :div, msg, id: "flash_#{name}" %>
     <% end %>

     <%= yield %>
   </div>
</body>
</html>

config/initializers/pubnub.rb

Here, we are instantiating PubNub on the server side to be used for server side operations, which we will see next. The keys between the client side and server side should be the same, so ideally, something like environment variables should be used, but for now we are hard coding it.

require 'pubnub'

$pubnub = Pubnub.new(
   subscribe_key: :demo,
   publish_key: :demo
)

app/controllers/comments_controller.rb

This is where the magic is happening that when a new comment is created, whoever is watching that same article will get the new comment in realtime. There are a couple of ways this could be done, but I am showing a simpler approach.

As soon as the comment is saved in a database, a message is published to the channel of that article. For this example purpose, I have created naming conventions for channel name as “comments-[article_id]”.

def create
  @commentable = Article.find(params[:article_id])
  @comment = @commentable.comments.new(comment_params)
  if @comment.save
    $pubnub.publish(
    channel: "comments-" + "#{@commentable.id}",
    message: { comment: comment_params["content"] }
    )
    respond_to do |format|
     format.js { render :nothing => true }
     format.html
   end
  else
    render :new
  end
end

app/views/articles/show.html.erb

The first few parts of this file is just rendering HTML. Code in the script handles how to receive the message and how to render it. Firstly, we instantiated PubNub on client side with same keys as on server side. Then we subscribed to the particular channel to receive comments as per the naming conventions earlier mentioned. Then it listens for new messages and appends it to HTML.

<h1><%= @article.name %></h1>

<%= simple_format @article.content %>

<p>
    <%= link_to "Edit", edit_article_path(@article) %>
</p>

<h2>Comments</h2>

<%= render 'comments/comments' %>

<p>
    <%= render 'comments/form' %>
</p>

<script>
$(document).ready(function(){
   var $pubnub = new PubNub({
       publishKey : 'demo',
       subscribeKey : 'demo'
   })
  
   var article_id = "comments-" + window.location.pathname.split("/")[2];
   console.log(article_id);

   $pubnub.subscribe({
       channels: [article_id]
   });

   $pubnub.addListener({
       message: function(message) {
           console.log(message.message["comment"]);
           $("#comments").append("<div class=\"comment\"><p>" + message.message["comment"] + "</p></div>")
       }
   })

   $("#new_comment").bind("ajax:complete", function(event,xhr,status){
    $('#comment_content').val('');
   });

    $('.actions input').keypress(function (e) {
        if (e.which == 13) {
            $('form#new_comment').submit();
            return false;
        }
    });
});
</script>

And that’s it! We now have a blog where comments are streamed in realtime, no manual refreshing required.

Try PubNub Today

Connect up to 100 devices for Free