diff options
Diffstat (limited to 'src/DataStore.js')
| -rw-r--r-- | src/DataStore.js | 332 |
1 files changed, 332 insertions, 0 deletions
diff --git a/src/DataStore.js b/src/DataStore.js new file mode 100644 index 00000000..97ab92ff --- /dev/null +++ b/src/DataStore.js @@ -0,0 +1,332 @@ +// Copyright (C) 2009 BRAT Tech LLC + +nglr.DataStore = function(post, users, anchor) { + this.post = post; + this.users = users; + this._cache = {$collections:[]}; + this.anchor = anchor; + this.bulkRequest = []; +}; + +nglr.DataStore.prototype.cache = function(document) { + if (document.constructor != nglr.Model) { + throw "Parameter must be an instance of Entity! " + nglr.toJson(document); + } + var key = document.$entity + '/' + document.$id; + var cachedDocument = this._cache[key]; + if (cachedDocument) { + nglr.Model.copyDirectFields(document, cachedDocument); + } else { + this._cache[key] = document; + cachedDocument = document; + } + return cachedDocument; +}; + +nglr.DataStore.prototype.load = function(instance, id, callback, failure) { + if (id && id !== '*') { + var self = this; + this._jsonRequest(["GET", instance.$entity + "/" + id], function(response) { + instance.$loadFrom(response); + instance.$migrate(); + var clone = instance.$$entity(instance); + self.cache(clone); + (callback||nglr.noop)(instance); + }, failure); + } + return instance; +}; + +nglr.DataStore.prototype.loadMany = function(entity, ids, callback) { + var self=this; + var list = []; + var callbackCount = 0; + jQuery.each(ids, function(i, id){ + list.push(self.load(entity(), id, function(){ + callbackCount++; + if (callbackCount == ids.length) { + (callback||nglr.noop)(list); + } + })); + }); + return list; +} + +nglr.DataStore.prototype.loadOrCreate = function(instance, id, callback) { + var self=this; + return this.load(instance, id, callback, function(response){ + if (response.$status_code == 404) { + instance.$id = id; + (callback||nglr.noop)(instance); + } else { + throw response; + } + }); +}; + +nglr.DataStore.prototype.loadAll = function(entity, callback) { + var self = this; + var list = []; + list.$$accept = function(doc){ + return doc.$entity == entity.title; + }; + this._cache.$collections.push(list); + this._jsonRequest(["GET", entity.title], function(response) { + var rows = response; + for ( var i = 0; i < rows.length; i++) { + var document = entity(); + document.$loadFrom(rows[i]); + list.push(self.cache(document)); + } + (callback||nglr.noop)(list); + }); + return list; +}; + +nglr.DataStore.prototype.save = function(document, callback) { + var self = this; + var data = {}; + document.$saveTo(data); + this._jsonRequest(["POST", "", data], function(response) { + document.$loadFrom(response); + var cachedDoc = self.cache(document); + _.each(self._cache.$collections, function(collection){ + if (collection.$$accept(document)) { + angular.Array.includeIf(collection, cachedDoc, true); + } + }); + if (document.$$anchor) { + self.anchor[document.$$anchor] = document.$id; + } + if (callback) + callback(document); + }); +}; + +nglr.DataStore.prototype.remove = function(document, callback) { + var self = this; + var data = {}; + document.$saveTo(data); + this._jsonRequest(["DELETE", "", data], function(response) { + delete self._cache[document.$entity + '/' + document.$id]; + _.each(self._cache.$collections, function(collection){ + for ( var i = 0; i < collection.length; i++) { + var item = collection[i]; + if (item.$id == document.$id) { + collection.splice(i, 1); + } + } + }); + (callback||nglr.noop)(response); + }); +}; + +nglr.DataStore.prototype._jsonRequest = function(request, callback, failure) { + request.$$callback = callback; + request.$$failure = failure||function(response){ + throw response; + }; + this.bulkRequest.push(request); +}; + +nglr.DataStore.prototype.flush = function() { + if (this.bulkRequest.length === 0) return; + var self = this; + var bulkRequest = this.bulkRequest; + this.bulkRequest = []; + console.log('REQUEST:', bulkRequest); + function callback(code, bulkResponse){ + console.log('RESPONSE[' + code + ']: ', bulkResponse); + if(bulkResponse.$status_code == 401) { + self.users.login(function(){ + self.post(bulkRequest, callback); + }); + } else if(bulkResponse.$status_code) { + nglr.alert(nglr.toJson(bulkResponse)); + } else { + for ( var i = 0; i < bulkResponse.length; i++) { + var response = bulkResponse[i]; + var request = bulkRequest[i]; + var code = response.$status_code; + if(code) { + if(code == 403) { + self.users.notAuthorized(); + } else { + request.$$failure(response); + } + } else { + request.$$callback(response); + } + } + } + } + this.post(bulkRequest, callback); +}; + +nglr.DataStore.prototype.saveScope = function(scope, callback) { + var saveCounter = 1; + function onSaveDone() { + saveCounter--; + if (saveCounter === 0 && callback) + callback(); + } + for(var key in scope) { + var item = scope[key]; + if (item && item.$save == nglr.Model.prototype.$save) { + saveCounter++; + item.$save(onSaveDone); + } + } + onSaveDone(); +}; + +nglr.DataStore.prototype.query = function(type, query, arg, callback){ + var self = this; + var queryList = []; + queryList.$$accept = function(doc){ + return false; + }; + this._cache.$collections.push(queryList); + var request = type.title + '/' + query + '=' + arg; + this._jsonRequest(["GET", request], function(response){ + var list = response; + for(var i = 0; i < list.length; i++) { + var document = new type().$loadFrom(list[i]); + queryList.push(self.cache(document)); + } + if (callback) + callback(queryList); + }); + return queryList; +}; + +nglr.DataStore.prototype.entities = function(callback) { + var entities = []; + var self = this; + this._jsonRequest(["GET", "$entities"], function(response) { + for (var entityName in response) { + entities.push(self.entity(entityName)); + } + entities.sort(function(a,b){return a.title > b.title ? 1 : -1;}); + if (callback) callback(entities); + }); + return entities; +}; + +nglr.DataStore.prototype.documentCountsByUser = function(){ + var counts = {}; + var self = this; + self.post([["GET", "$users"]], function(code, response){ + jQuery.each(response[0], function(key, value){ + counts[key] = value; + }); + }); + return counts; +}; + +nglr.DataStore.prototype.userDocumentIdsByEntity = function(user){ + var ids = {}; + var self = this; + self.post([["GET", "$users/" + user]], function(code, response){ + jQuery.each(response[0], function(key, value){ + ids[key] = value; + }); + }); + return ids; +}; + +nglr.DataStore.NullEntity = function(){}; +nglr.DataStore.NullEntity.all = function(){return [];}; +nglr.DataStore.NullEntity.query = function(){return [];}; +nglr.DataStore.NullEntity.load = function(){return {};}; +nglr.DataStore.NullEntity.title = undefined; + +nglr.DataStore.prototype.entity = function(name, defaults){ + if (!name) { + return nglr.DataStore.NullEntity; + } + var self = this; + var entity = function(initialState){ + return new nglr.Model(entity, initialState); + }; + // entity.name does not work as name seems to be reserved for functions + entity.title = name; + entity.$$factory = true; + entity.datastore = this; + entity.defaults = defaults || {}; + entity.load = function(id, callback){ + return self.load(entity(), id, callback); + }; + entity.loadMany = function(ids, callback){ + return self.loadMany(entity, ids, callback); + }; + entity.loadOrCreate = function(id, callback){ + return self.loadOrCreate(entity(), id, callback); + }; + entity.all = function(callback){ + return self.loadAll(entity, callback); + }; + entity.query = function(query, queryArgs, callback){ + return self.query(entity, query, queryArgs, callback); + }; + entity.properties = function(callback) { + self._jsonRequest(["GET", name + "/$properties"], callback); + }; + return entity; +}; + +nglr.DataStore.prototype.join = function(join){ + var fn = function(){ + throw "Joined entities can not be instantiated into a document."; + }; + function base(name){return name ? name.substring(0, name.indexOf('.')) : undefined;} + function next(name){return name.substring(name.indexOf('.') + 1);} + var joinOrder = _(join).chain(). + map(function($, name){ + return name;}). + sortBy(function(name){ + var path = []; + do { + if (_(path).include(name)) throw "Infinite loop in join: " + path.join(" -> "); + path.push(name); + if (!join[name]) throw _("Named entity '<%=name%>' is undefined.").template({name:name}); + name = base(join[name].on); + } while(name); + return path.length; + }). + value(); + if (_(joinOrder).select(function($){return join[$].on;}).length != joinOrder.length - 1) + throw "Exactly one entity needs to be primary."; + fn.query = function(exp, value) { + var joinedResult = []; + var baseName = base(exp); + if (baseName != joinOrder[0]) throw _("Named entity '<%=name%>' is not a primary entity.").template({name:baseName}); + var Entity = join[baseName].join; + var joinIndex = 1; + Entity.query(next(exp), value, function(result){ + var nextJoinName = joinOrder[joinIndex++]; + var nextJoin = join[nextJoinName]; + var nextJoinOn = nextJoin.on; + var joinIds = {}; + _(result).each(function(doc){ + var row = {}; + joinedResult.push(row); + row[baseName] = doc; + var id = nglr.Scope.getter(row, nextJoinOn); + joinIds[id] = id; + }); + nextJoin.join.loadMany(_.toArray(joinIds), function(result){ + var byId = {}; + _(result).each(function(doc){ + byId[doc.$id] = doc; + }); + _(joinedResult).each(function(row){ + var id = nglr.Scope.getter(row, nextJoinOn); + row[nextJoinName] = byId[id]; + }); + }); + }); + return joinedResult; + }; + return fn; +}; |
