aboutsummaryrefslogtreecommitdiffstats
path: root/src/DataStore.js
diff options
context:
space:
mode:
authorAdam Abrons2010-01-05 16:36:58 -0800
committerAdam Abrons2010-01-05 16:36:58 -0800
commitc9c176a53b1632ca2b1c6ed27382ab72ac21d45d (patch)
treeb5f719a095c03ee9f8b2721ffdaf1e5ff8c11b41 /src/DataStore.js
downloadangular.js-c9c176a53b1632ca2b1c6ed27382ab72ac21d45d.tar.bz2
angular.js
Diffstat (limited to 'src/DataStore.js')
-rw-r--r--src/DataStore.js332
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;
+};