You're viewing a post from the archive. Don't forget to checkout our latest post Adding awesome mini views to your web apps

Not to blow our own trumpet, but there is currently a lot of buzz around our realtime web service called Pusher. However, since we originally made this app for our own use, I thought I'd show how it fits into a real app. I recently set out to integrate Pusher with TrueStory, our backlog management app. Not being part of the Pusher team, I was pleasantly surprised at not only how simple it was, but also how quickly I could add advanced real time behaviour to an existing app.

Don't have an awesome browser? Download the video.

What is Pusher?

You aren't going to get very far with this if you don't know what Pusher is. A brief summary would be that it is a super simple service which uses Web sockets to broadcast custom 'events' to your client side code.

What is TrueStory?

TrueStory is our project backlog management tool, we use it for maintaining a list of stories for each of our projects and planning sprints based on these stories. Typically the development team, clients and senior stakeholders will be using TrueStory to add and re-arrange stories on a project backlog, making sure each person is viewing the most up to date data is vital to how useful TrueStory is.

The Incremental Real Time Web

TrueStory existed before we released Pusher, it wasn't built or architected with Pusher in mind. In fact, it worked perfectly well and was a useful application without the real time web. The reason we wanted to add Pusher was because TrueStory is the kind of app that people generally leave open in the background for a long period of time, this led to the problem of stale data and conflicting updates.

With the addition of Websockets, other people's updates to a project backlog are pushed to everyone else viewing it. In this way Pusher is an incremental improvement, TrueStory didn't rely on these real time updates, they just improved the user experience in much the same way both CSS and JavaScript should enhance the user experience of a plain html page.

Existing Architecture

The bulk of the application code in TrueStory is on the client side, and relies on libraries such as jQuery, Sammy and js-model. This combination of technology was a huge help in integrating Pusher into TrueStory. JavaScript events are already used throughout the application, for example we have the following event when someone submits a form for creating a story:

/public/javascripts/routes.js

sammy.post('#/stories', function() {
  var attributes = this.params.toHash()["story"]
  var story = new Story(attributes)

  if (story.valid()) {
    story.save(function() {
      Backlog.plan()
    })
  }

  return false
})

Even if you are not familiar with either Sammy or js-model the above example should be fairly self explanatory: a new story is created with the attributes from the posted form. This story is saved if it is valid and we add a callback to the save function that will redraw the backlog, including the newly added story, after the save is complete.

The key part here is Backlog.plan(). This method will re-plan and re-draw the project backlog using all stories. This is used throughout the application to reflect a change of state in the client side application.

Adding Pusher

On the client

What is happening when we add Pusher is not that much different from the example above. This time the event is a particular message on a WebSocket connection, rather than a Sammy route. So the above example, adding a new story to the backlog looks like this when the trigger is a Pusher event:

/public/javascripts/websockets.js

socket.bind('stories-create', function (attributes) {
  var story = new Story(attributes)
  Story.add(story)
  Backlog.plan()
})

The two should look fairly similar because we expect the same behaviour when a story is added by this client or any other client. The only difference is that we are not saving this story, as this would trigger an Ajax call to the server where this story already exists.

On the server

On the server side this create story event needs to be sent to the Pusher REST API. Using the Pusher gem this was very simple.

/app/controllers/stories_controller.rb

def create
  @story = @project.stories.new(params[:story])

  if @story.save
    Pusher[@project.channel].trigger_async('stories-create', @story.to_h, request.headers['x-socket-id'])

    respond_to do |format|
      format.json { render :json => @story.to_json(:only => :id) }
      format.html { redirect_to @project }
    end
  end
end

The interface for the gem requires a channel name, an event name, some data to be sent, and an optional socket id. The above should be self explanatory: we implemented a to_h instance method on Story that passes the right attributes to Pusher. We use the channel attribute on the project to give us the key for the channel we should push to. These events are only pushed to clients who are viewing that specific project. The socket id is a little less obvious, but is given to the client when they connect to Pusher, and sent as a header when making Ajax requests. This means that the client that initiated a change doesn't get a duplicate of the event.

We are also using the trigger_async method to prevent the call to Pusher blocking the rest of the application. We are not specifying callbacks for success or failure here, but it is possible to add them if these events are important to your application. To use trigger_async your app must run inside of eventmachine, for example the Thin web server, and you must install the em-http-request gem. Otherwise you can just use trigger and handle any errors from the Pusher gem.

Hopefully you can see from the above examples that adding real time capability to your app can be very simple. Using Pusher allows you to leverage your existing application architecture when adding real time web functionality, rather than necessitating a whole new architecture. By using Pusher this way it is easily possible to make your application highly collaborative in less than a day. TrueStory itself uses Pusher for eight, mostly CRUD, events in total, and none of them were any more complicated to implement than the example above.

This video shows you TrueStory and the real time updates across different browsers, as you can see for very little effort you greatly enhance the user experience of your application.