It has been quite a while since I had an excuse to play with Faye—the awesome little pub-sub library for the browser, node.js and Ruby. That seems like a good excuse to get it working with Backbone.js.
First up is installation, which is a breeze:
➜ calendar git:(faye) npm install faye faye@0.6.6 ./node_modules/faye └── redis@0.6.7Adding support to my existing express.js server is similarly trivial. Just
require()
the npm package and attach the server:var express = require('express-unstable'), http = require('http'), faye = require('faye'); var app = module.exports = express.createServer(); // Lots of configuration here var bayeux = new faye.NodeAdapter({mount: '/faye', timeout: 45}); bayeux.attach(app); app.listen(3000);I need to add faye the list of scripts sourced by my HTML:
... <script src="/faye.js"></script> ...And then I am ready to try some simple pub-sub messaging:
Damn that's easy. I subscribe to the
/foo
channel to log any message sent there. Then I publish a message to the same channel and, it gets logged. If I publish a message to another channel, nothing happens since there are no subscriptions. Network round-trip pub-sub is verified to work. Easy-peasy. I am not yet certain how (or if) I want to use Faye in my Backbone calendar app. For now, I think it would be cool if public appointments showed up on the
/calendars/public
Faye channel. To verify something like that is working, in the web page, I add a subscription to the /calendars/public
Faye channel. The subscription simply logs the channel name and the message published to the channel: $(function() {
var client = new Faye.Client('/faye')
client.subscribe('/calendars/public', function(message) {
console.log('[/calendars/public]');
console.log(message);
});
});
Now all I need to do is get the model to send appointments to that channel. I will worry about the distinction between pubic and private events another day. For now, it is a party and everybody knows everybody. I could attach a callback to the
add
model event, but I think it more appropriate to do this in the model's sync()
method. When an updated model needs to be synchronized, it now needs to be synced with the Faye channel in addition to the backend store. Happily, overriding sync()
is pretty easy in Backbone: var Appointment = Backbone.Model.extend({
urlRoot : '/appointments',
initialize: function(attributes) {
// ...
this.faye = new Faye.Client('/faye');
},
// ...
sync: function(method, model, options) {
if (method == "create") {
this.faye.publish("/calendars/public", model);
}
Backbone.sync.call(this, method, this, options);
}
});
So, when syncing a "create", I publish the model to the target /calendars/public
channel. To ensure that the data is still sent to the backend, I call()
the Backbone.sync()
function with the same arguments that it would have received had I not overridden it.To try it out, I just need to fire up my server, add an event through the UI and check Chrome's Javascript console:
Yay! That really worked. The two
console.log()
messages from the Faye subscription came through. Better still, the POST to the backend goes through immediately afterwards via XHR.Faye is just as easy to use as I remember it. Coupled with Backbone, the two are super easy to get working with a myriad of possible uses. I think tomorrow I will kick the tires one of those uses.
Day #142
Even better, override the global backbone sync to do all your persistence via faye.
ReplyDeleteAlso, from the server, ship any model that has changed and turn it into a changes feed (I'll do a chapter on this in the book, it's what makes shortmail fast).
It did feel wrong having the frontend code being responsible for two different types of persistence (normal REST and Faye). Overriding the model for Faye and then calling the "super class" to do normal persistence made it feel a little less weird, but still...
ReplyDeleteIt's good to know how to do it this way, but yah, I think persistence via Faye sounds like a good plan for tonight's chain. A changes feed sounds awesome :)