How we implemented in-browser alerts with Pusher, Rails and Pnotify

Today we're announcing the launch of a fun feature: In-browser notifications. Once enabled on the Notifications tab of the Project Settings page, you'll get a popup notification in the corner of the browser window when one of the events occurs that you've selected to be alerted about (like an error occurring, or a comment being added). As an added bonus, you can also choose to have a popup window appear on your desktop. The combination of Pusher and pNotify made this easy to implement -- here's how I did it.

First, start off by creating a Pusher account. Ok, sure, you could use something like Faye to build this, but hey, Pusher is awesome, so why not? :) With a Pusher account in hand, let's take a look at the client-side implementation.

After including the Pusher javascript and setting up a new Pusher instance with our Pusher key, I subscribe each user to his own presence channel, and then look for events on that channel:

presenceChannel = pusher.subscribe("presence-user-#{user_id}")
presenceChannel.bind "alert", (data) ->
  notice = new PNotify
    title: data.title
    text: data.text
    type: data.type || 'alert'
    icon: null
    desktop:
      desktop: true
      icon: "https://www.honeybadger.io/images/icon.png"
      tag: "#{data.title}:#{data.url}"

  if data.url
    notice.get().click ->
      if notice.state is 'open'
        window.open(data.url)

The desktop hash specified when creating the notice enables the desktop module support in pNotify, which uses the Notifications API to do the desktop notifications. If there's a URL associated with the alert, the notice is set up to load that URL when the popup is clicked, unless the click is a result of the popup being closed with the close button (in which case the notice.state is 'closing'). This click behavior works on both the in-browser popup and the desktop notification, opening the URL in a new tab. Now on to the server side...

We have a whole lot of notifications flying around here at Honeybadger, and we really don't want to be sending payloads to Pusher if you don't have a browser window open to our site. So when an event happens, we first check to see if you are on the site before sending the notification to Pusher. Our notification engine knows if you're using the site thanks to Pusher's webhooks. Whenever you join or leave your personal presence channel, Pusher's webhook hits our endpoint to let us know, and we keep a tally of who's around in a redis set:

Array(params[:events]).each do |e|
  next unless e[:name] == 'member_added' || e[:name] == 'member_removed'
  Redis.current.send(e[:name] == 'member_added' ? :sadd : :srem, "active_users", e[:user_id])
end

And finally, we use Pusher to send a message to you:

Pusher.trigger("presence-user-#{user_id}", "alert",
               text: event.message,
               url: event.url,
               title: event.title)

And with that, you can get a popup on your desktop as soon as an error happens in your Rails app, and that's pretty cool. :)

author photo

Benjamin Curtis

Ben has been developing web apps and building startups since '99, and fell in love with Ruby and Rails in 2005. Before co-founding Honeybadger, he launched a couple of his own startups: Catch the Best, to help companies manage the hiring process, and RailsKits, to help Rails developers get a jump start on their projects. Ben's role at Honeybadger ranges from bare-metal to front-end... he keeps the server lights blinking happily, builds a lot of the back-end Rails code, and dips his toes into the front-end code from time to time. When he's not working, Ben likes to hang out with his wife and kids, ride his road bike, and of course hack on open source projects. :)

“We’ve looked at a lot of error management systems. Honeybadger is head and shoulders above the rest and somehow gets better with every new release.”
Michael Smith
Try Error Monitoring Free for 15 Days