Smart objects collection.

This commit is contained in:
Julien Fontanet 2015-02-10 14:42:56 +01:00
parent da99f3bc2a
commit f1d359b3e7
3 changed files with 173 additions and 7 deletions

View File

@ -0,0 +1,141 @@
'use strict';
//====================================================================
var forEach = require('lodash.foreach');
var indexOf = require('lodash.indexof');
//====================================================================
function defaultKey(item) {
return item.id || item._id || item;
}
//====================================================================
function Collection(opts) {
if (!opts) {
opts = {};
}
this._key = opts.key || defaultKey;
this._indexes = Object.create(null);
if (opts.indexes) {
forEach(opts.indexes, function (field) {
this[field] = Object.create(null);
}, this._indexes);
}
this._data = Object.create(null);
}
function createIndex(_, field) {
/* jshint validthis: true */
this[field] = Object.create(null);
}
Collection.prototype.clear = function () {
this._data = Object.create(null);
forEach(this._indexes, createIndex, this._indexes);
};
Collection.prototype.get = function (key) {
return this._data[key];
};
Collection.prototype.where = function (field, value) {
var index = this._indexes[field];
if (!index) {
throw new Error('no such index');
}
return index[value];
};
function unsetItemFromIndex(index, field) {
/* jshint validthis: true */
var prop = this[field];
if (!prop) {
return;
}
var items = index[prop];
var i = indexOf(items, this);
if (i === -1) {
return;
}
// The index contains only this one item for this prop.
if (items.length === 1) {
delete index[prop];
return;
}
// Remove this item.
items.splice(i, 1);
}
// Internal unset method.
function unset(item, key) {
/* jshint validthis: true */
delete this._data[key];
forEach(this._indexes, unsetItemFromIndex, item);
}
function setItemToIndex(index, field) {
/* jshint validthis: true */
var prop = this[field];
if (!prop) {
return;
}
var items = index[prop];
if (items) {
// Update the items list.
items.push(this);
} else {
// Create the items list.
index[prop] = [this];
}
}
Collection.prototype.set = function (item) {
var key = this._key(item);
if (!key) {
// Ignore empty keys.
return;
}
var previous = this._data[key];
if (previous) {
unset.call(this, previous, key);
}
this._data[key] = item;
forEach(this._indexes, setItemToIndex, item);
};
Collection.prototype.unset = function (item) {
unset.call(this, item, this._key(item));
};
Collection.prototype.setMultiple = function (items) {
forEach(items, this.set, this);
};
Collection.prototype.unsetMultiple = function (items) {
forEach(items, this.unset, this);
};
//====================================================================
function createCollection(opts) {
return new Collection(opts);
}
module.exports = createCollection;

View File

@ -11,6 +11,8 @@ var makeError = require('make-error');
var MethodNotFound = require('json-rpc/errors').MethodNotFound;
var WebSocket = require('ws');
var createCollection = require('./collection');
//====================================================================
function makeDeferred() {
@ -178,7 +180,16 @@ exports.Api = Api;
//====================================================================
var objectsOptions = {
indexes: [
'ref',
'type',
'UUID',
],
key: function (item) {
return item.UUID || item.ref;
},
};
// High level interface to Xo.
//
@ -189,7 +200,7 @@ function Xo(opts) {
this._api = new Api(opts.url);
this._auth = opts.auth;
this._backOff = fibonacci(1e3);
this._objects = Object.create(null);
this.objects = createCollection(objectsOptions);
this.user = null;
// Promise representing the connection status.
@ -200,17 +211,19 @@ function Xo(opts) {
email: self._auth.email,
password: self._auth.password,
}).then(function (user) {
this.user = user;
self.user = user;
return self._api.call('xo.getAllObjects');
}).then(function (objects) {
self._objects = objects;
self.objects.setMultiple(objects);
});
return self._connection;
};
self._api.on('disconnected', function () {
self._connection = null;
self._objects = Object.create(null);
self.objects.clear();
});
self._api.on('notification', function (notification) {
@ -218,14 +231,23 @@ function Xo(opts) {
return;
}
var method = (
notification.params.type === 'exit' ?
'unset' :
'set'
) + 'Multiple';
self.objects[method](notification.params.items);
});
}
assign(Xo.prototype, {
connect: function () {
var self = this;
if (this._connection) {
return this._connection;
}
var self = this;
return this._api.connect().then(this._onConnection).catch(function () {
return Bluebird.delay(self._backOff.next().value).then(function () {
return self.connect();

View File

@ -22,12 +22,15 @@
"test": "mocha index.spec.js"
},
"files": [
"index.js"
"index.js",
"collection.js"
],
"dependencies": {
"bluebird": "^2.9.6",
"json-rpc": "git://github.com/julien-f/js-json-rpc",
"lodash.assign": "^3.0.0",
"lodash.foreach": "^3.0.1",
"lodash.indexof": "^3.0.0",
"make-error": "^0.3.0",
"ws": "^0.7.1"
},