239 lines
7.1 KiB
JavaScript
239 lines
7.1 KiB
JavaScript
/** @module dstore/Memory **/
|
|
define("dstore/Memory", [
|
|
'dojo/_base/declare',
|
|
'dojo/_base/array',
|
|
'./Store',
|
|
'./Promised',
|
|
'./SimpleQuery',
|
|
'./QueryResults'
|
|
], function (declare, arrayUtil, Store, Promised, SimpleQuery, QueryResults) {
|
|
/**
|
|
* @class module:dstore/Memory
|
|
*/
|
|
return declare([Store, Promised, SimpleQuery ], {
|
|
constructor: function () {
|
|
// summary:
|
|
// Creates a memory object store.
|
|
// options: dstore/Memory
|
|
// This provides any configuration information that will be mixed into the store.
|
|
// This should generally include the data property to provide the starting set of data.
|
|
|
|
// Add a version property so subcollections can detect when they're using stale data
|
|
this.storage.version = 0;
|
|
},
|
|
|
|
postscript: function () {
|
|
this.inherited(arguments);
|
|
|
|
// Set the data in `postscript` so subclasses can override `data` in their constructors
|
|
// (e.g., a LocalStorage store that retrieves its data from localStorage)
|
|
this.setData(this.data || []);
|
|
},
|
|
|
|
// data: Array
|
|
// The array of all the objects in the memory store
|
|
data: null,
|
|
|
|
autoEmitEvents: false, // this is handled by the methods themselves
|
|
|
|
getSync: function (id) {
|
|
// summary:
|
|
// Retrieves an object by its identity
|
|
// id: Number
|
|
// The identity to use to lookup the object
|
|
// returns: Object
|
|
// The object in the store that matches the given id.
|
|
return this.storage.fullData[this.storage.index[id]];
|
|
},
|
|
putSync: function (object, options) {
|
|
// summary:
|
|
// Stores an object
|
|
// object: Object
|
|
// The object to store.
|
|
// options: dstore/Store.PutDirectives?
|
|
// Additional metadata for storing the data. Includes an 'id'
|
|
// property if a specific id is to be used.
|
|
// returns: Number
|
|
|
|
options = options || {};
|
|
|
|
var storage = this.storage,
|
|
index = storage.index,
|
|
data = storage.fullData;
|
|
|
|
var Model = this.Model;
|
|
if (Model && !(object instanceof Model)) {
|
|
// if it is not the correct type, restore a
|
|
// properly typed version of the object. Note that we do not allow
|
|
// mutation here
|
|
object = this._restore(object);
|
|
}
|
|
var id = this.getIdentity(object);
|
|
if (id == null) {
|
|
this._setIdentity(object, ('id' in options) ? options.id : Math.random());
|
|
id = this.getIdentity(object);
|
|
}
|
|
storage.version++;
|
|
|
|
var eventType = id in index ? 'update' : 'add',
|
|
event = { target: object },
|
|
previousIndex,
|
|
defaultDestination;
|
|
if (eventType === 'update') {
|
|
if (options.overwrite === false) {
|
|
throw new Error('Object already exists');
|
|
} else {
|
|
data.splice(previousIndex = index[id], 1);
|
|
defaultDestination = previousIndex;
|
|
}
|
|
} else {
|
|
defaultDestination = this.defaultNewToStart ? 0 : data.length;
|
|
}
|
|
|
|
var destination;
|
|
if ('beforeId' in options) {
|
|
var beforeId = options.beforeId;
|
|
|
|
if (beforeId === null) {
|
|
destination = data.length;
|
|
} else {
|
|
destination = index[beforeId];
|
|
|
|
// Account for the removed item
|
|
if (previousIndex < destination) {
|
|
--destination;
|
|
}
|
|
}
|
|
|
|
if (destination !== undefined) {
|
|
event.beforeId = beforeId;
|
|
} else {
|
|
console.error('options.beforeId was specified but no corresponding index was found');
|
|
destination = defaultDestination;
|
|
}
|
|
} else {
|
|
destination = defaultDestination;
|
|
}
|
|
data.splice(destination, 0, object);
|
|
|
|
// the fullData has been changed, so the index needs updated
|
|
var i = isFinite(previousIndex) ? Math.min(previousIndex, destination) : destination;
|
|
for (var l = data.length; i < l; ++i) {
|
|
index[this.getIdentity(data[i])] = i;
|
|
}
|
|
|
|
this.emit(eventType, event);
|
|
|
|
return object;
|
|
},
|
|
addSync: function (object, options) {
|
|
// summary:
|
|
// Creates an object, throws an error if the object already exists
|
|
// object: Object
|
|
// The object to store.
|
|
// options: dstore/Store.PutDirectives?
|
|
// Additional metadata for storing the data. Includes an 'id'
|
|
// property if a specific id is to be used.
|
|
// returns: Number
|
|
(options = options || {}).overwrite = false;
|
|
// call put with overwrite being false
|
|
return this.putSync(object, options);
|
|
},
|
|
removeSync: function (id) {
|
|
// summary:
|
|
// Deletes an object by its identity
|
|
// id: Number
|
|
// The identity to use to delete the object
|
|
// returns: Boolean
|
|
// Returns true if an object was removed, falsy (undefined) if no object matched the id
|
|
var storage = this.storage;
|
|
var index = storage.index;
|
|
var data = storage.fullData;
|
|
if (id in index) {
|
|
var removed = data.splice(index[id], 1)[0];
|
|
// now we have to reindex
|
|
this._reindex();
|
|
this._ignoreChangeEvents !==true && this.emit('delete', {id: id, target: removed});
|
|
return true;
|
|
}
|
|
},
|
|
setData: function (data) {
|
|
// summary:
|
|
// Sets the given data as the source for this store, and indexes it
|
|
// data: Object[]
|
|
// An array of objects to use as the source of data. Note that this
|
|
// array will not be copied, it is used directly and mutated as
|
|
// data changes.
|
|
|
|
if (this.parse) {
|
|
data = this.parse(data);
|
|
}
|
|
if (data.items) {
|
|
// just for convenience with the data format ItemFileReadStore expects
|
|
this.idProperty = data.identifier || this.idProperty;
|
|
data = data.items;
|
|
}
|
|
var storage = this.storage;
|
|
storage.fullData = this.data = data;
|
|
this._reindex();
|
|
},
|
|
|
|
_reindex: function () {
|
|
var storage = this.storage;
|
|
var index = storage.index = {};
|
|
var data = storage.fullData;
|
|
var Model = this.Model;
|
|
var ObjectPrototype = Object.prototype;
|
|
for (var i = 0, l = data.length; i < l; i++) {
|
|
var object = data[i];
|
|
if (Model && !(object instanceof Model)) {
|
|
var restoredObject = this._restore(object,
|
|
// only allow mutation if it is a plain object
|
|
// (which is generally the expected input),
|
|
// if "typed" objects are actually passed in, we will
|
|
// respect that, and leave the original alone
|
|
object.__proto__ === ObjectPrototype);
|
|
if (object !== restoredObject) {
|
|
// a new object was generated in the restoration process,
|
|
// so we have to update the item in the data array.
|
|
data[i] = object = restoredObject;
|
|
}
|
|
}
|
|
index[this.getIdentity(object)] = i;
|
|
}
|
|
storage.version++;
|
|
},
|
|
|
|
fetchSync: function () {
|
|
var data = this.data;
|
|
if (!data || data._version !== this.storage.version) {
|
|
// our data is absent or out-of-date, so we requery from the root
|
|
// start with the root data
|
|
data = this.storage.fullData;
|
|
var queryLog = this.queryLog;
|
|
// iterate through the query log, applying each querier
|
|
for (var i = 0, l = queryLog.length; i < l; i++) {
|
|
data = queryLog[i].querier(data);
|
|
}
|
|
// store it, with the storage version stamp
|
|
data._version = this.storage.version;
|
|
this.data = data;
|
|
}
|
|
return new QueryResults(data);
|
|
},
|
|
|
|
fetchRangeSync: function (kwArgs) {
|
|
var data = this.fetchSync(),
|
|
start = kwArgs.start,
|
|
end = kwArgs.end;
|
|
return new QueryResults(data.slice(start, end), {
|
|
totalLength: data.length
|
|
});
|
|
},
|
|
|
|
_includePropertyInSubCollection: function (name) {
|
|
return name !== 'data' && this.inherited(arguments);
|
|
}
|
|
});
|
|
});
|