Share Gaia assets and adapters, keep private profiles and development documents local, and update both public READMEs.
386 lines
8.5 KiB
JavaScript
Executable file
386 lines
8.5 KiB
JavaScript
Executable file
define(function(require, exports, module) {
|
|
'use strict';
|
|
|
|
var Responder = require('common/responder');
|
|
var core = require('core');
|
|
var denodeifyAll = require('common/promise').denodeifyAll;
|
|
var nextTick = require('common/next_tick');
|
|
|
|
/**
|
|
* Creates an abstract store instance.
|
|
* Every store must contain a reference
|
|
* to the database.
|
|
*/
|
|
function Abstract() {
|
|
this._cached = Object.create(null);
|
|
Responder.call(this);
|
|
|
|
denodeifyAll(this, [
|
|
'persist',
|
|
'all',
|
|
'_allCached',
|
|
'removeByIndex',
|
|
'get',
|
|
'remove',
|
|
'count'
|
|
]);
|
|
}
|
|
module.exports = Abstract;
|
|
|
|
Abstract.prototype = {
|
|
__proto__: Responder.prototype,
|
|
|
|
_store: null,
|
|
|
|
/**
|
|
* Stores that will need to be removed
|
|
* when a record is removed from this store.
|
|
*
|
|
* @type {Array}
|
|
*/
|
|
_dependentStores: null,
|
|
|
|
_createModel: function(object, id) {
|
|
if (typeof(id) !== 'undefined') {
|
|
object._id = id;
|
|
}
|
|
|
|
return object;
|
|
},
|
|
|
|
_addToCache: function(object) {
|
|
this._cached[object._id] = object;
|
|
},
|
|
|
|
_clearCache: function() {
|
|
this._cached = Object.create(null);
|
|
},
|
|
|
|
_removeFromCache: function(id) {
|
|
if (id in this._cached) {
|
|
delete this._cached[id];
|
|
}
|
|
},
|
|
|
|
_transactionCallback: function(trans, callback) {
|
|
if (callback) {
|
|
trans.addEventListener('error', function(e) {
|
|
callback(e);
|
|
});
|
|
|
|
trans.addEventListener('complete', function() {
|
|
callback(null);
|
|
});
|
|
}
|
|
},
|
|
|
|
/**
|
|
* Adds an account to the database.
|
|
*
|
|
* @param {Object} object reference to account object to store.
|
|
* @param {IDBTransaction} trans transaction.
|
|
* @param {Function} callback node style callback.
|
|
*/
|
|
persist: function(object, trans, callback) {
|
|
if (typeof(trans) === 'function') {
|
|
callback = trans;
|
|
trans = undefined;
|
|
}
|
|
|
|
if (!trans) {
|
|
trans = core.db.transaction(
|
|
this._dependentStores || this._store,
|
|
'readwrite'
|
|
);
|
|
}
|
|
|
|
var self = this;
|
|
var store = trans.objectStore(this._store);
|
|
var data = this._objectData(object);
|
|
var id;
|
|
var model;
|
|
|
|
var putReq;
|
|
var reqType = this._detectPersistType(object);
|
|
|
|
trans.addEventListener('error', function(event) {
|
|
if (callback) {
|
|
callback(event);
|
|
}
|
|
});
|
|
|
|
// determine type of event
|
|
if (reqType === 'update') {
|
|
putReq = store.put(data);
|
|
} else {
|
|
this._assignId(data);
|
|
putReq = store.add(data);
|
|
this._addDependents(object, trans);
|
|
}
|
|
|
|
// when we have the id we can add the model to the cache.
|
|
if (data._id) {
|
|
id = data._id;
|
|
model = self._createModel(object, id);
|
|
self._addToCache(model);
|
|
}
|
|
|
|
trans.addEventListener('complete', function(data) {
|
|
if (!model) {
|
|
id = putReq.result;
|
|
model = self._createModel(object, id);
|
|
self._addToCache(model);
|
|
}
|
|
|
|
if (callback) {
|
|
callback(null, id, model);
|
|
}
|
|
|
|
self.emit(reqType, id, model);
|
|
self.emit('persist', id, model);
|
|
});
|
|
},
|
|
|
|
_allCached: function(callback) {
|
|
var list = this._cached;
|
|
nextTick(function() {
|
|
callback(null, list);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Loads all records in the database
|
|
* for this store.
|
|
*
|
|
* Using this function will fill
|
|
* the cache with all records in the store.
|
|
* As such this should only be used once
|
|
* during the app life-cycle.
|
|
*/
|
|
all: function(callback) {
|
|
if (this._allCallbacks) {
|
|
this._allCallbacks.push(callback);
|
|
return;
|
|
}
|
|
|
|
// list of pending callbacks
|
|
this._allCallbacks = [callback];
|
|
|
|
var self = this;
|
|
var trans = core.db.transaction(this._store);
|
|
var store = trans.objectStore(this._store);
|
|
|
|
function process(data) {
|
|
return self._addToCache(self._createModel(data));
|
|
}
|
|
|
|
function fireQueue(err, value) {
|
|
var callback;
|
|
while ((callback = self._allCallbacks.shift())) {
|
|
callback(err, value);
|
|
}
|
|
}
|
|
|
|
store.mozGetAll().onsuccess = function(event) {
|
|
event.target.result.forEach(process);
|
|
};
|
|
|
|
trans.onerror = function(event) {
|
|
fireQueue(event.target.error.name);
|
|
};
|
|
|
|
trans.oncomplete = function() {
|
|
fireQueue(null, self._cached);
|
|
self.all = self._allCached;
|
|
};
|
|
},
|
|
|
|
_addDependents: function() {},
|
|
_removeDependents: function(trans) {},
|
|
|
|
_detectPersistType: function(object) {
|
|
return ('_id' in object) ? 'update' : 'add';
|
|
},
|
|
|
|
_parseId: function(id) {
|
|
return id;
|
|
},
|
|
|
|
_assignId: function(obj) {
|
|
},
|
|
|
|
/**
|
|
* Removes all records a index value and removes
|
|
* them from the cache. 'remove' events are *not* emitted
|
|
* when removing in this manner for performance reasons.
|
|
*
|
|
* TODO: the test for this method still lives in the event store tests
|
|
* where this code began should refactor those tests to be general
|
|
* and live in the abstract tests.
|
|
*
|
|
* @param {String} indexName name of store index.
|
|
* @param {Numeric} indexValue value in index.
|
|
* @param {IDBTransation} [trans] optional transaction to reuse.
|
|
* @param {Function} [callback] optional callback to use.
|
|
* When called without a transaction chances
|
|
* are you should pass a callback.
|
|
*/
|
|
removeByIndex: function(indexName, indexValue, trans, callback) {
|
|
var self = this;
|
|
if (typeof(trans) === 'function') {
|
|
callback = trans;
|
|
trans = undefined;
|
|
}
|
|
|
|
if (!trans) {
|
|
trans = core.db.transaction(
|
|
this._dependentStores || this._store,
|
|
'readwrite'
|
|
);
|
|
}
|
|
if (callback) {
|
|
|
|
trans.addEventListener('complete', function() {
|
|
callback(null);
|
|
});
|
|
|
|
trans.addEventListener('error', function(event) {
|
|
callback(event);
|
|
});
|
|
}
|
|
|
|
|
|
var index = trans.objectStore(this._store).index(indexName);
|
|
var req = index.openCursor(
|
|
IDBKeyRange.only(indexValue)
|
|
);
|
|
|
|
req.onsuccess = function(event) {
|
|
var cursor = event.target.result;
|
|
if (cursor) {
|
|
// remove deps first intentionally to mimic, removes normal behaviour
|
|
self._removeDependents(cursor.primaryKey, trans);
|
|
self._removeFromCache(cursor.primaryKey);
|
|
cursor.delete();
|
|
cursor.continue();
|
|
}
|
|
};
|
|
|
|
return req;
|
|
},
|
|
|
|
/**
|
|
* Finds a single record.
|
|
*
|
|
* Does not go through any cache or emit any events.
|
|
*
|
|
* @param {String} id id of record.
|
|
* @param {IDBTransaction} [trans] optional transaction.
|
|
* @param {Function} callback node style [err, record].
|
|
*/
|
|
get: function(id, trans, callback) {
|
|
var self = this;
|
|
|
|
if (typeof(trans) === 'function') {
|
|
callback = trans;
|
|
trans = null;
|
|
}
|
|
|
|
if (!trans) {
|
|
trans = core.db.transaction(this._store);
|
|
}
|
|
|
|
var store = trans.objectStore(this._store);
|
|
var req = store.get(this._parseId(id));
|
|
|
|
req.onsuccess = function() {
|
|
var model;
|
|
|
|
if (req.result) {
|
|
model = self._createModel(req.result);
|
|
}
|
|
|
|
callback(null, model);
|
|
};
|
|
|
|
req.onerror = function(event) {
|
|
callback(event);
|
|
};
|
|
},
|
|
|
|
/**
|
|
* Removes a object from the store.
|
|
*
|
|
* @param {String} id record reference.
|
|
* @param {IDBTransaction} trans transaction.
|
|
* @param {Function} callback node style callback.
|
|
*/
|
|
remove: function(id, trans, callback) {
|
|
if (typeof(trans) === 'function') {
|
|
callback = trans;
|
|
trans = undefined;
|
|
}
|
|
|
|
if (!trans) {
|
|
trans = core.db.transaction(
|
|
this._dependentStores || this._store,
|
|
'readwrite'
|
|
);
|
|
}
|
|
|
|
var self = this;
|
|
var store = trans.objectStore(this._store);
|
|
id = this._parseId(id);
|
|
|
|
store.delete(id);
|
|
|
|
this._removeDependents(id, trans);
|
|
self.emit('preRemove', id);
|
|
|
|
trans.addEventListener('error', function(event) {
|
|
if (callback) {
|
|
callback(event);
|
|
}
|
|
});
|
|
|
|
trans.addEventListener('complete', function() {
|
|
if (callback) {
|
|
callback(null, id);
|
|
}
|
|
|
|
self.emit('remove', id);
|
|
|
|
// intentionally after the callbacks...
|
|
self._removeFromCache(id);
|
|
});
|
|
},
|
|
|
|
/**
|
|
* Find number of records in store.
|
|
*
|
|
* @param {Function} callback node style err/count.
|
|
*/
|
|
count: function(callback) {
|
|
var trans = core.db.transaction(this._store);
|
|
var store = trans.objectStore(this._store);
|
|
|
|
var req = store.count();
|
|
|
|
req.onsuccess = function() {
|
|
callback(null, req.result);
|
|
};
|
|
|
|
req.onerror = function(e) {
|
|
callback(e);
|
|
};
|
|
},
|
|
|
|
_objectData: function(data) {
|
|
if ('toJSON' in data) {
|
|
return data.toJSON();
|
|
}
|
|
return data;
|
|
}
|
|
};
|
|
|
|
});
|