pubsub.dnode.js | |
---|---|
Backbone-DNode (c) 2011 Beau Sorensen Backbone-DNode may be freely distributed under the MIT license. For all details and documentation: https://github.com/sorensen/backbone-dnode | (function() { |
Pubsub Middleware | |
This protocol is to be used as DNode middleware to provide built-in pub/sub support to all Backbone models and collections, as well as the client side counterpart to the server version of this same RPC protocol. The naming of methods in the past tense is to signal that the process has already happened on the server, this is a common naming pattern found throughout the app. | |
Save a reference to the global object. | var root = this;
|
The top-level namespace. All public classes and modules will be attached to this. | var pubsub;
|
Remote server socket connection reference | var server;
|
Storage container for subscribed models, allowing the returning method calls from the server know where and how to find the model in question | var Store = root.Store || (root.Store = {});
|
Require Underscore, if we're on the server, and it's not already present. | var _ = root._;
if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._;
|
Require Backbone, if we're on the server, and it's not already present. | var Backbone = root.Backbone;
if (!Backbone && (typeof require !== 'undefined')) Backbone = require('backbone');
_.mixin({ |
getUrlHelper function to get a URL from a Model or Collection as a property or as a function. | getUrl : function(object) {
if (!(object && object.url)) return null;
return _.isFunction(object.url) ? object.url() : object.url;
}
});
|
Add to the main namespace with the Pubsub middleware for DNode, accepts a socket client and connection | pubsub = function(client, con) { |
Set a reference to the remote connection | server = client;
_.extend(this, {
|
subscribedSomeone has subscribed to a channel Note: This method is not required to run the application, it may prove as a useful way to update clients, and it may prove to be an added security risk, when private channels are involved | subscribed : function(resp, options) {
if (!options.channel) return;
options.finished && options.finished(resp);
},
|
unsubscribedSomeone has unsubscribed from a channel, see the note above, as it applies to this method as well | unsubscribed : function(resp, options) {
if (!options.channel) return;
options.finished && options.finished(resp);
},
|
publishedData has been published by another client, this serves as the main entry point for server to client communication. Events are delegated based on the original method passed, and are sent to 'crud.dnode.js' for completion | published : function(resp, options) {
if (!options.channel) return;
switch (options.method) {
case 'create' : this.created(resp, options); break;
case 'read' : this.read(resp, options); break;
case 'update' : this.updated(resp, options); break;
case 'delete' : this.destroyed(resp, options); break;
};
}
});
};
|
Extend default Backbone functionality | _.extend(Backbone.Model.prototype, {
|
urlThis should probably be overriden with the underscore mixins from the helpers.js methods | url : function() {
var base = _.getUrl(this.collection) || this.urlRoot || '';
if (this.isNew()) return base;
return base + (base.charAt(base.length - 1) == ':' ? '' : ':') + encodeURIComponent(this.id);
},
|
publishPublish model data to the server for processing, this serves as the main entry point for client to server communications. If no method is provided, it defaults to an 'update', which is the least conflicting method when returned to the client for processing | publish : function(options, next) {
if (!server) return (options.error && options.error(503, model, options));
var model = this;
options || (options = {});
options.method || (options.method = 'update');
options.channel || (options.channel = (model.collection) ? _.getUrl(model.collection) : _.getUrl(model));
server.publish(model.toJSON(), options, function(resp, options){
if (!options.silent) model.trigger('publish', model, options);
next && next(resp, options);
});
return this;
}
});
|
Common extention object for both models and collections | var common = {
|
connectionSetting a reference to the DNode/socket connection to allow direct server communication without the need of a global object | connection : server,
|
subscribeSubscribe to the 'Server' for model changes, if 'override' is set to true in the options, this model will replace any other models in the local 'Store' which holds the reference for future updates. Uses Backbone 'url' for subscriptions, relabeled to 'channel' for clarity | subscribe : function(options, next) {
if (!server) return (options.error && options.error(503, model, options));
var model = this;
options || (options = {});
options.channel || (options.channel = (model.collection) ? _.getUrl(model.collection) : _.getUrl(model));
|
Add the model to a local object container so that other methods called from the 'Server' have access to it | if (!Store[options.channel] || options.override) {
Store[options.channel] = model;
server.subscribe(model.toJSON(), options, function(resp, options) {
if (!options.silent) model.trigger('subscribe', model, options);
next && next(resp, options);
});
} else {
if (!options.silent) model.trigger('subscribe', model, options);
next && next(model, options);
}
return this;
},
|
unsubscribeStop listening for published model data, removing the reference in the local subscription 'Store', will trigger an unsubscribe event unless 'silent' is passed in the options | unsubscribe : function(options, next) {
if (!server) return (options.error && options.error(503, model, options));
var model = this;
options || (options = {});
options.channel || (options.channel = (model.collection) ? _.getUrl(model.collection) : _.getUrl(model));
server.unsubscribe({}, options, function(resp, options) {
if (!options.silent) model.trigger('unsubscribe', model, options);
next && next(resp, options);
});
|
The object must be deleted, or a new subscription with the same channel name will not be correctly 'synced', unless a 'override' option is sent upon subscription | delete Store[options.channel];
return this;
}
};
|
Extend both model and collection with the pub/sub mechanics | _.extend(Backbone.Model.prototype, common);
_.extend(Backbone.Collection.prototype, common);
|
Exported for both CommonJS and the browser. | if (typeof exports !== 'undefined') {
module.exports = pubsub;
} else {
root.pubsub = pubsub;
}
})();
|