Real Time notifications using slanger and sidekiq

With the increasing expectation of web-applications, everyone wants real time updates or real time notifications to improve the web portals user experience. Understandably, my project required Real time notifications too. I successfully implemented and deployed on production. During my development I found some interesting gems, javascript libraries that I came across. So I thought it would be helpful if I share my experience. To make it more useful I have created a sample repository for demonstrating Real Time notifications for your further reference.

The AIM: Notify the user with a reminder on specific user defined date and time using Web notifications.

I choose slanger gem. (Recently updated by Jiren to make it compatible with rails4). To make it work, add pusher to rails app and for slanger instead of adding it in rails Gemfile, I created a sub directory called ‘slanger’ in my rails project and added slanger. Now we are done with installation. Now let’s see some configuration:

  #config/initializers/pusher.rb

  Pusher.app_id = app-id
  Pusher.key = app-key
  Pusher.secret = app-secret
  Pusher.port   = 4567
  Pusher.host   = 'localhost'
 

where app-id, app-key and secret could be any secure random number for authentication purpose.

Note: Add the IP address of the machine if your running app is in production mode or staging mode.

Now it’s time to add a sidekiq job to push notifications on a particular user defined time. Here is code snippet how to add sidekiq job:


# app/models/reminder.rb

class Reminder
  include Mongoid::Document
  field :text, type: String
  field :notify_at, type: Time

  after_create :add_sidekiq_job
  before_destroy :remove_sidekiq_job

  def add_sidekiq_job
    id = ReminderWorker.perform_at(scheduled_at.second, {id: self.id.to_s})
    self.update_attributes(job_id: id)
  end 

  def remove_sidekiq_job
    queue =  Sidekiq::ScheduledSet.new
    job = queue.find_job(self.job_id)
    job.delete if job
  end 

  def scheduled_at
    notify_at.to_time.to_i - Time.now.to_i
  end
end

The above code snippet adds a sidekiq job after reminder object has been created.

Now let’s trigger some notifications:

  # app/worker/reminder_worker.rb

 class ReminderWorker
  include Sidekiq::Worker

  def perform(args)
    reminder = Reminder.find(args['id'])
    Pusher.trigger("reminder", "reminder_12345", {notification_text: "Reminder: #{reminder.text}"})
  end
end

where trigger takes three arguments. First argument is ‘channel’ and second argument is ‘event’ and third one is data that you want to push to client.

Note: reminder_12345 is a unique way to identify to which user we have to push notification.

Once data has been published on channel the browser should respond and display data on front end. To do this every client should subscribe reminder channel using following javascript snippet:

  #app/assets/javascripts/remainder.js  

  var reminder = pusher.subscribe('reminder');

Prior to subscribe, pusher needs to authenticate. To do this, use following snippet:

  # app/assets/javascripts/remainder.js 

  window.pusher = new Pusher(PUSHER_API_KEY, {authEndpoint: '/pusher/authentication', authTransport: 'ajax',activityTimeout: 120000, disableStats: true});

Where PUSHER_API_KEY would unique key to identify your application. To know more about the remaining options please go through this.

Once you authenticated, client should respond once pusher triggers(nothing but event) a notification. To achieve this use following snippet:

  #app/assets/javascripts/remainder.js  

  reminder.bind('reminder_12345', function(data){
    $.notify(data['notification_text']);
   });

Once you watched above code snippet carefully you will get doubt about what is $.notify and why we need it?

notifyjs is a beautiful javascript library to display notifications on your front end. To know how to customize your notifications please go through the documentation.

So finally coalition of sidekiq, slanger and notifyjs has done job for me. It would be great if you share your thoughts on this coalition. Do play around with the demonstration on Github to see this in action.