backbone-pubsub.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 | |
Publish Subscribe middleware | |
Save a reference to the global object. | var root = this; |
Create the top level namespaced objects | var Pubsub,
clients = {},
channels = {},
subscriptions = {}; |
Redis client references | var redis,
pub,
sub; |
Require Underscore, if we're on the server, and it's not already present. | var _ = root._;
if (!_ && (typeof require !== 'undefined')) _ = require('underscore')._; |
Add to the main namespace with the Pubsub middleware for DNode, accepts a socket client and connection | Pubsub = function(client, con) {
var self = this;
|
Client connected | con.on('ready', function() {
clients[con.id] = client;
});
|
Client disconnected | con.on('end', function() {
if (clients[con.id]) delete clients[con.id];
|
Remove all instances of current client the channels, by traversing through the clients 'channel' property, instead of going through every channel | if (subscriptions[con.id] && subscriptions[con.id].channels) _.each(subscriptions[con.id].channels, function(chan) {
if (channels[chan] && channels[chan].clients[con.id]) {
delete channels[chan].clients[con.id];
}
delete subscriptions[con.id];
});
if (typeof redis !== 'undefined') { |
Unsubscribe from all redis channels | sub.unsubscribe();
}
});
_.extend(this, { |
connectionsReturn all current connections contained in this thread, | connections : function(next) {
next(Object.keys(clients));
},
|
subscribeChannel subscription, add the client to the internal subscription object, creating a container for the channel if one does not exist, then subscribe to the Redis client | subscribe : function(model, options, next) {
if (!options.channel) {
options.error && options.error(400, model, options);
next && next(model, options);
return;
} |
Extract the lookup keys | var id = con.id,
chan = options.channel;
|
Create the channel container and add the current client to it if needed, stored in objects for easier lookups, and extra information | if (!channels[chan]) {
channels[chan] = {
name : chan,
clients : {}
};
}
channels[chan].clients[id] = client; |
Create the subscription object for the client if needed, used for easier lookups based on clients | if (!subscriptions[id]) {
subscriptions[id] = {
client : client,
channels : {}
};
} |
Add the channel to the clients subscriptions, used for easy lookups on disconnections | if (!subscriptions[id].channels[chan]) {
subscriptions[id].channels[chan] = {};
} |
Redis subscription | if (typeof redis !== 'undefined') {
sub.subscribe(chan);
}
next && next(model, options);
},
|
unsubscribeUnsubscribe from model changes via channel | unsubscribe : function(model, options, next) {
if (!model || !options.channel || !channels[options.channel]) {
options.error && options.error(400, model, options);
next && next(model, options);
return;
}
delete channels[options.channel].clients[con.id];
delete subscriptions[con.id].channels[options.channel]
|
Redis unsubscribe | if (typeof redis !== 'undefined') {
sub.unsubscribe(options.channel);
}
next && next(model, options);
},
|
publishPublish to redis if a connection has been supplied, otherwise send through to clients on this thread | publish : function(model, options, next) {
if (!model || !options.channel || !channels[options.channel]) {
options.error && options.error(400, model, options);
next && next(model, options);
return;
}
if (typeof redis === 'undefined') {
self.pushed(model, options, next);
} else { |
Send to redis | pub.publish(options.channel, JSON.stringify({
model : model,
options : options
}));
}
next && next(model, options);
},
|
pushedPush a message to application clients based on channels, used as the delivery method for redis published events, but can be used by itself on a single thread basis | pushed : function(model, options, next) {
if (!model || !options.channel || !channels[options.channel]) {
options.error && options.error(400, model, options);
next && next(model, options);
return;
} |
Publish based by channel | _.each(channels[options.channel].clients, function(someone) {
someone.published(model, options)
});
next && next(model, options);
}
});
|
##Redis publish subscribe event handling |
if (typeof redis !== 'undefined') { |
Redis published message, push new data to each client connected with the givin channel | sub.on('message', function(channel, message) {
message = JSON.parse(message);
var model = message.model,
options = message.options;
if (options.channel != channel) {
return;
}
self.pushed(model, options);
});
|
Redis subscribe message, alert each client that someone has joined the channel ( optional ) | sub.on('subscribe', function(channel, count) {
_.each(channels[channel].clients, function(someone) {
someone.subscribed({}, {
channel : channel
});
});
});
|
Redis unsubscribe message, alert each client that someone has left the channel ( optional ) | sub.on('unsubscribe', function(channel, count) {
_.each(channels[channel].clients, function(someone) {
someone.unsubscribed({}, {
channel : channel
});
});
});
};
}; |
configCreate and configure the redis clients used for pubsub events, if no config is set, the pubsub mechanics will default to a single threaded mode, with no redis support | Pubsub.config = function(redis, params, next) { |
Set the redis defaults if no parameters are provided | params.port || (params.port = 6379);
params.host || (params.host = '127.0.0.1');
params.options || (params.options = {
parser : 'javascript',
return_buffer : false
});
|
Setup and configure the redis clients | redis = redis;
pub = redis.createClient(params.port, params.host, params.options);
sub = redis.createClient(params.port, params.host, params.options);
|
Authenticate if provided a password | if (params.password) {
params.authcb || (params.authcb = function(){});
pub.auth(params.password, params.authcb);
sub.auth(params.password, params.authcb);
}
next && next();
}; |
The top-level namespace. All public classes and modules will be attached to this. Exported for both CommonJS and the browser. | if (typeof exports !== 'undefined') {
module.exports = Pubsub;
} else {
root.Pubsub = Pubsub;
}
|