real-time messaging

Due to the real-time nature of CloudHire.com, we needed a way to communicate with users in the browser in real-time to notify them when something important required their attention (like an interview invitation, change in interview platform, etc.)   There are a lot of underlying technologies to enable real-time browser push notifications, from long-lived HTTP requests (comet GET) to Flash to WebSockets. Ultimately we chose to use pusher.com for our basic real-time messaging.  This was a great way to quickly get up and running.

However, there were still a couple of issues I felt we needed to address:

First, what if the user happened to be switching pages on our site when a pusher message was sent? (it’s not a single page app).  There might be brief times when client-side pusher was not connected, and I didn’t want our users to miss important messages.

Second, what if the user had more than one browser window open? — how do we determine that the correct window receives and responds to the message appropriately?

I solved the first issue by creating a lower-level notification system for important real-time messages. We setup JavaScript code to poll for these notifications periodically using jQuery and AJAX. Then, we used pusher merely to inform the browser to request notifications immediately rather than waiting for the next periodic poll.   This allowed us to achieve real-time messaging with a graceful fallback all wrapped into the same AJAX request system.

For the second issue, we allowed each type of notification to include meta data in its JSON response describing what types of elements must be in the DOM in order for the notification to be handled and acknowledged by a given browser window.  For example, if a notification was only relevant to a given interview, we wanted that notification only to be shown in the interview window, not in any other browser windows that might be open on our site (although those windows might respond to other types of notifications).  Each notification type carried the knowledge of what conditions must exist within the browser window in order for the browser window to be eligible to react to the notification.

In the snippet below, the `:selector` values indicate that the browser must have a particular DOM element with specific data attributes in order to respond to this notification. In this case, the notification was used to switch a user to a different interview type (i.e. video to phone), so there is one set of instructions if the data type doesn’t already match, and another set of instructions if the data type does match.  The notification would not be deleted (acknowledged) until the user successfully moved to the new interview type (which would be indicated by the presence of specific data attributes in the DOM):

      render :json => { :conditionals => [{:selector => "#interview-notification-hook[data-interview-id=#{@presenter.interview.id}][data-interview-type-id=#{@presenter.type_id}]", :data => {:ajax_url => acknowledge_employers_notification_path(params[:id])}},
                                          {:selector => "#interview-notification-hook[data-interview-id=#{@presenter.interview.id}][data-interview-type-id!=#{@presenter.type_id}]", :data => {:html => render_to_string(:partial => 'show'), :refresh => "#live-interview-content"}}]}

To facilitate this on the front-end we built a JavaScript class that knew how to parse this JSON and act accordingly.