mongoscope-client
Version:
185 lines (150 loc) • 4.4 kB
JavaScript
var Client = require('../client'),
assert = require('assert'),
types = {
ns: require('mongodb-ns'),
uri: require('mongodb-uri')
},
debug = require('debug')('mongoscope-client:backbone');
var clients = {};
var _mongodb = function(resource){
var uri = _result(resource, 'mongodb');
if(!uri) return null;
if(uri.indexOf('mongodb://') !== 0){
uri = 'mongodb://' + uri;
}
var info = types.uri.parse(uri),
ns = null;
resource.mongodb = info.hosts[0].host + ':' + info.hosts[0].port;
if(info.database && !resource.url){
ns = types.ns(info.database);
if(ns.collection){
resource.url = '/collections/' + ns.toString() + '/find';
}
else {
resource.url = '/databases/' + ns.database;
}
}
return resource;
};
var _result = function(resource, key){
return (typeof resource[key] === 'function') ? resource[key]() : resource[key];
};
function use(resource, config){
var clientId;
config = config || {};
var defaultClient = clients[Object.keys(clients)[0]];
assert(resource, 'Missing backbone resource');
if(resource.mongodb && !resource.url){
_mongodb(resource);
}
if(defaultClient && !config.scope){
config.scope = defaultClient.config.scope;
}
if(_result(resource, 'mongoscope')){
config.scope = _result(resource, 'mongoscope');
}
if(!config.seed || !config.scope){
return defaultClient;
}
clientId = config.scope + '/' + config.seed;
if(!clients[clientId]){
console.log('creating new client', clientId);
clients[clientId] = new Client(config);
}
return clients[clientId];
}
function sync(method, model, options){
var url = options.url,
// Backbone resources will set the current closures context to
// the resource instance.
resource = this,
client = options.client || use(resource),
ender = function(err, res){
if(err){
return options.error(err);
}
options.success(res);
};
if(method !== 'read'){
throw new Error('mongoscope is readonly, so create, update, ' +
'patch, and delete sync methods are not available.');
}
if(!options.url){
url = _result(resource, 'url');
}
if(!url){
throw new Error('A "url" property or function must be specified');
}
var params = {}, docs = [];
Object.keys(options).map(function(k){
if(['error', 'success', 'client', 'parse'].indexOf(k) === -1){
params[k] = options[k];
}
});
if(method === 'read'){
if(!options.all) return client.get(url, params, ender);
return client.get(url, params)
.on('error', ender)
.on('data', function(doc){docs.push(doc);})
.on('end', function(){
ender(null, docs);
});
}
if(method === 'destroy'){
var info = client.resolve(url);
if(!info) throw new Error('Could not resolve ' + url);
var handler = info[0],
args = info[1];
if(!handler.destroy) throw new Error('No destory hnadler found for' + handler);
args.push(ender);
return handler.destroy.apply(client, args);
}
}
module.exports.Model = {
sync: sync,
getScopeClient: function(config){
return use(this, config);
}
};
module.exports.Collection = {
sync: sync,
getScopeClient: function(config){
return use(this, config);
}
};
module.exports.ReadableStream = {
subscription: null,
subscribe: function(){
if(this.subscription !== null){
debug('already have a subscription', this.subscription);
return this;
}
var resource = this,
client = use(resource),
url = (typeof resource.url === 'function') ? resource.url() : resource.url;
this.subscription = client.get(url)
.on('error', function(err){
resource.trigger('error', err, resource, {client: client});
})
.on('data', function(data){
if (!resource.set(data)) return false;
resource.trigger('sync', resource, data, {client: client});
});
// If the client context changes, move our subscription.
this.subscription.client.on('change', function(){
this.unsubscribe();
this.subscribe();
}.bind(this));
return resource;
},
unsubscribe: function(){
if(this.subscription === null){
debug('already unsubscribed');
return this;
}
this.subscription.close();
this.subscription = null;
return this;
}
};
module.exports.clients = clients;