I like feedback, I like to know that when I click a button that I will be presented with some — sometimes small — visual indication that my action succeeded. However with TestPilot, we have a lot of things going on in the background, we use multiple background workers running in 3 different DCs, some have access to the database and others use a separate messaging layer to get messages back to the frontend.
The end result is usually a record being updated or created, but the only way to present this change to the user is reloading the page. This is fine for CRUD actions which respond instantly, but background jobs can take seconds or minutes to respond, or respond multiple times which is the case of importing your Github repositories.
You could ask the user to reload the page after a few seconds, but seriously I will not use your application beyond that point if you do this. I have my hand cautiously resting on ⌘ + W for such an event.
Make it real-time
Enter WebSockets, or more precisely Pusher — cross-browser WebSockets as a service.
I won’t go into details about what or how WebSockets work, there are a lot of great articles around which can go into more detail than I could ever cover here. But in a nutshell, Pusher allows you to “push” messages to the browser, instead of polling for changes. A message can contain any JSON serialisable data structure like a Hash (Hashes work well).
Putting it together
There are two components to making this work, firstly you need to be able to trigger an event, for the sake of this article we won’t talk about what is triggering the event we’ll just do it on the command line.
If you haven’t done so, add ‘pusher’ to your Gemfile
gem ‘pusher’
And configure it with your Pusher credentials:
Pusher.app_id = ‘YOUR APP ID’
Pusher.key = ‘YOUR API KEY’
Pusher.secret = ‘YOUR SECRETS’
I have this in an initializer: config/initializers/pusher.rb
You also need to grab the Pusher JS library and add it to your layout.
Triggering events
Triggering events is dead simple, try this in rails console
Pusher[‘SOME_CHANNEL_NAME’].trigger(‘some:event’, {body: “Hello World”, id: 1})
This will send an event containing the payload object {body: “Hello World”, id: 1} – Simple.
Responding to events
To simplify the handling of Pusher events and channel subscriptions in the frontend I’ve written a quick Backbone.view extension which you can extend to automatically subscribe to events.
while i < l @event events[i][0], this[events[i][1]] i++ In the head of your layout you will need to set the pusherToken variable or bad things will happen like this whole thing not working. : javascript var pusherToken = “YOUR_PUSHER_KEY”; This view extension is a lot simpler than it looks, all it does is handle the event bindings to Pusher and set up the channel. The channel attribute takes a string, if the string name matches a method name it will call that method for the result, or else use the string name. The view All you need to do is inherit from Backbone.real-time view, and everything else is a standard Backbone.View. class TestPilot.Views.DashboardView extends Backbone.real-time view el: ‘div.panel’ events: ‘message:create’ : ‘addMessage’ channel: ‘findChannel’ addMessage: (payload) ->
console.log(payload)Now if we send an event to the dashboard channel it should log the output to the console.
I usually set up my render method to be callable in such a way that I can render the state change simply by calling render from my event handling method.
Try it out
If you haven’t signed up yet, do so now and you can see your repositories import in realtime — don’t worry we don’t actually copy any source code, just the name, and details.
Everyone uses Backbone in different ways from progressive enhancement to building entire applications, and as such your requirements might differ. I personally like the idea of building the entire frontend using JS and communicating with Ajax and WebSockets, but it requires a different way of thinking and can drastically increase the learning curve when new developers join the project, nonetheless, you can expect to see a bit of that here in the future.