app/assets/javascripts/kojac.js in kojac-0.11.0 vs app/assets/javascripts/kojac.js in kojac-0.12.0

- old
+ new

@@ -1,9 +1,9 @@ /*-------------------------------------------------------------------------- * * Key Oriented JSON Application Cache (KOJAC) - * (c) 2011-12 Buzzware Solutions + * (c) 2011-14 Buzzware Solutions * https://github.com/buzzware/KOJAC * * KOJAC is freely distributable under the terms of an MIT-style license. * *--------------------------------------------------------------------------*/ @@ -326,11 +326,14 @@ break; case Int: return aValue; break; case String: - return Number(aValue); + if (aValue.trim()=='') + return null; + var n = Number(aValue); + return isFinite(n) ? n : null; break; } break; case Int: @@ -342,17 +345,17 @@ break; case Boolean: return aValue ? 1 : 0; break; case Number: - if (isNaN(aValue)) - return null; - else - return Math.round(aValue); + return isFinite(aValue) ? Math.round(aValue) : null; break; case String: - return Math.round(Number(aValue)); + if (aValue.trim()=='') + return null; + var n = Number(aValue); + return isFinite(n) ? Math.round(n) : null; break; } break; case Date: @@ -450,11 +453,11 @@ return result; ia = parts[1]; parts = ia.split('.'); if (parts.length>=1) { // id id = parts[0]; - var id_as_i = Number(id); + var id_as_i = Number(id); // !!! watch for Number('') => 0 if (_.isFinite(id_as_i)) id = id_as_i; result.push(id); } if (parts.length>=2) { // association @@ -739,12 +742,12 @@ if (!aResponseOp) { this.error = "no result"; } else if (aResponseOp.error) { this.error = aResponseOp.error; } else { - var request_key = this.result_key || this.key; - var response_key = aResponseOp.result_key || this.key; + var request_key = this.key; + var response_key = aResponseOp.result_key || aResponseOp.key || this.key; var final_result_key = this.result_key || response_key; // result_key should not be specified unless trying to override var results = _.isObjectStrict(aResponseOp.results) ? aResponseOp.results : _.createObject(response_key,aResponseOp.results); // fix up server mistake var result; if (aResponseOp.verb==='DESTROY') result = undefined; @@ -766,10 +769,11 @@ * @class Kojac.Request * @extends Kojac.Object */ Kojac.Request = Kojac.Object.extend({ kojac: null, + chaining: false, options: {}, ops: [], handlers: null, op: null, //result: undefined, @@ -809,13 +813,16 @@ op.key = k; else op.key = keyResource(k); if ((i===0) && result_key) op.result_key = result_key; - op.value = v; + op.value = Kojac.Utils.toJsono(v,op.options); } - return this; + if (this.chaining) + return this; + else + return this.request(); }, // !!! if aKeys is String, split on ',' into an array // known options will be moved from aOptions to op.options; remaining keys will be put into params read: function(aKeys,aOptions) { @@ -833,11 +840,14 @@ if (i===0) op.result_key = result_key || k; else op.result_key = k; }); - return this; + if (this.chaining) + return this; + else + return this.request(); }, cacheRead: function(aKeys,aOptions) { aOptions = _.extend({},aOptions,{preferCache: true}); return this.read(aKeys,aOptions); @@ -860,13 +870,16 @@ if (first) { op.result_key = result_key || k; first = false; } else op.result_key = k; - op.value = v; + op.value = Kojac.Utils.toJsono(v,op.options); }; - return this; + if (this.chaining) + return this; + else + return this.request(); }, destroy: function(aKeys,aOptions) { var keys = Kojac.Utils.interpretKeys(aKeys); var result_key = (aOptions && _.removeKey(aOptions,'result_key')); @@ -882,11 +895,14 @@ if (i===0) op.result_key = result_key || k; else op.result_key = k; }); - return this; + if (this.chaining) + return this; + else + return this.request(); }, execute: function(aKey,aValue,aOptions) { var op = this.newOperation(); op.verb = 'EXECUTE'; @@ -894,33 +910,83 @@ var params = (aOptions && _.removeKey(aOptions,'params')); // extract specific params op.result_key = (aOptions && _.removeKey(aOptions,'result_key')) || aKey; op.options = _.extend({cacheResults: false, manufacture: false},aOptions || {}); op.params = (params && _.clone(params)); op.key = aKey; - op.value = aValue; - return this; + op.value = Kojac.Utils.toJsono(aValue,op.options); + if (this.chaining) + return this; + else + return this.request(); }, - request: function() { - return this.kojac.performRequest(this); + request: function(aDone) { + var result = this.kojac.performRequest(this); + if (aDone) + result = result.done(aDone); + if (this.kojac.errorHandler) + result = result.fail(this.kojac.errorHandler); + return result; } }); /** * The Kojac core object * @class Kojac.Core * @extends Kojac.Object + * + * Normal API V2 Usage + * + * App = {}; + * App.cache = {}; + * App.kojac = new Kojac.Core({ + * remoteProvider: ..., + * cache: App.cache + * }); + * + * App.kojac.read('products').done(...); + * App.kojac.chain().create('products').execute('refresh').request(function(aKR){ // using optional done handler + * // handle done here + * }).fail(function(aKR){ + * // handle fail here + * }); + * + * Old API V1 usage + * + * App = {}; + * App.cache = {}; + * App.kojac = new Kojac.Core({ + * remoteProvider: ..., + * cache: App.cache, + * apiVersion: 1 + * }); + * + * App.kojac.readRequest('products').done(...); + * App.kojac.create('products').execute('refresh').request().done(function(aKR){ + * // handle done here + * }).fail(function(aKR){ + * // handle fail here + * }); + * */ Kojac.Core = Kojac.Object.extend({ remoteProvider: null, objectFactory: null, cache: null, + errorHandler: null, dependentKeys: {}, + apiVersion: 2, // set this to 1 for old read() and readRequest() - newRequest: function() { - return new Kojac.Request({kojac: this}); + newRequest: function(aOptions) { + if (!aOptions) + aOptions = {}; + aOptions = _.extend(aOptions,{kojac: this}); + if (!(this.chaining in aOptions)) { + aOptions.chaining = this.apiVersion < 2 + } + return new Kojac.Request(aOptions); }, // var v; // for (var i=0;i<aRequest.ops.length;i++) { // var op = aRequest.ops[i]; @@ -958,25 +1024,33 @@ if (op.error) break; for (var key in op.results) { var value = op.results[key]; - if ((op.options.atomise!==false) && _.isObjectStrict(value)) { + if ((op.options.atomise!==false) && _.isObjectStrict(value)) { // we are atomising and this is an object var existing = this.cache.retrieve(key); - if (_.isObjectStrict(existing)) { + if (_.isObjectStrict(existing) && (op.options.cacheResults!==false)) { // object is already in cache, and we are caching, so update it if (existing.beginPropertyChanges) { existing.beginPropertyChanges(); updatedObjects.push(existing); } if (existing.setProperties) existing.setProperties(value); else _.copyProperties(existing,value); value = existing; - } else { - if ((op.options.manufacture!==false) && (this.objectFactory)) - value = this.objectFactory.manufacture(value,key); + } else { // otherwise manufacture + if ((op.options.manufacture!==false) && (this.objectFactory)) { + // if primary key & reassigned by result_key then manufacture with original key + var mkey = key; // use the key from results by default + if (key === op.result_key) { // this is the result key, so may have been renamed + var has_dot = op.key.indexOf('.') >= 0; // prefer original key unless it contains a dot + if (!has_dot) + mkey = op.key; + } + value = this.objectFactory.manufacture(value,mkey); + } } } op.results[key] = value; if (op.options.cacheResults!==false) this.cache.store(key,value); @@ -1061,61 +1135,79 @@ }, // BEGIN User Functions // These functions enable the user to build and trigger requests to the server/remote provider - // eg. kojac.read('cur_super').read('cur_super_products').request() - // or kojac.readRequest('cur_super','cur_super_products') + chain: function() { + return this.newRequest({chaining: true}); + }, + create: function(aKeyValues,aOptions) { var req = this.newRequest(); return req.create(aKeyValues,aOptions); }, - createRequest: function(aKeyValues,aOptions) { - return this.create(aKeyValues,aOptions).request(); - }, read: function(aKeys,aOptions) { var req = this.newRequest(); return req.read(aKeys,aOptions); }, - readRequest: function(aKeys,aOptions) { - return this.read(aKeys,aOptions).request(); - }, - cacheReadRequest: function(aKeys,aOptions) { - aOptions = _.extend({},aOptions,{preferCache: true}); - return this.read(aKeys,aOptions).request(); - }, + cacheRead: function(aKeys,aOptions) { aOptions = _.extend({},aOptions,{preferCache: true}); return this.read(aKeys,aOptions); }, update: function(aKeyValues,aOptions) { var req = this.newRequest(); return req.update(aKeyValues,aOptions); }, - updateRequest: function(aKeyValues,aOptions) { - return this.update(aKeyValues,aOptions).request(); - }, destroy: function(aKeys,aOptions) { var req = this.newRequest(); return req.destroy(aKeys,aOptions); }, - destroyRequest: function(aKeys,aOptions) { - return this.destroy(aKeys,aOptions).request(); - }, execute: function(aKey,aValue,aOptions) { var req = this.newRequest(); return req.execute(aKey,aValue,aOptions); }, + // END Convenience Functions + + // BEGIN DEPRECATED API V1 FUNCTIONS + createRequest: function(aKeyValues,aOptions) { + if (this.apiVersion > 1) + throw "*Request methods are deprecated, and only supported when apiVersion is 1"; + return this.create(aKeyValues,aOptions).request(); + }, + readRequest: function(aKeys,aOptions) { + if (this.apiVersion > 1) + throw "*Request methods are deprecated, and only supported when apiVersion is 1"; + return this.read(aKeys,aOptions).request(); + }, + cacheReadRequest: function(aKeys,aOptions) { + if (this.apiVersion > 1) + throw "*Request methods are deprecated, and only supported when apiVersion is 1"; + aOptions = _.extend({},aOptions,{preferCache: true}); + return this.read(aKeys,aOptions).request(); + }, + updateRequest: function(aKeyValues,aOptions) { + if (this.apiVersion > 1) + throw "*Request methods are deprecated, and only supported when apiVersion is 1"; + return this.update(aKeyValues,aOptions).request(); + }, + destroyRequest: function(aKeys,aOptions) { + if (this.apiVersion > 1) + throw "*Request methods are deprecated, and only supported when apiVersion is 1"; + return this.destroy(aKeys,aOptions).request(); + }, executeRequest: function(aKey,aValue,aOptions) { + if (this.apiVersion > 1) + throw "*Request methods are deprecated, and only supported when apiVersion is 1"; return this.execute(aKey,aValue,aOptions).request(); } - // END Convenience Functions + // END DEPRECATED API V1 FUNCTIONS }); Kojac.Cache = Kojac.Object.extend({ store: function(k,v) { if (v===undefined) { @@ -1436,10 +1528,11 @@ * @class Kojac.ObjectFactory * @extends Kojac.Object */ Kojac.ObjectFactory = Kojac.Object.extend({ + namespace: null, matchers: null, defaultClass: Object, register: function(aPairs) { if (!aPairs) @@ -1460,39 +1553,36 @@ if (!re.test(aKey)) continue; newClass = pair[1]; break; } - if (newClass===undefined) + if (!newClass) { + var ns = this.namespace || Window; + var r = keyResource(aKey); + if (r && (r[0]==r[0].toUpperCase()) && _.isFunction(ns[r])) + newClass = ns[r]; + } + if (!newClass) newClass = this.defaultClass; return newClass; }, createInstance: function(aClass,aProperties) { aProperties = aProperties || {}; return new aClass(aProperties); }, - copyProperties: function(aDest,aSource) { - return _.extend(aDest,aSource); - }, - manufacture: function(aObject,aKey) { var newClass = this.classFromKey(aKey); - var result; - var me = this; + var result = []; if (_.isArray(aObject)) { - result = []; for (var i=0; i<aObject.length; i++) { - var newv = me.createInstance(newClass,aObject[i]); + var newv = this.createInstance(newClass,aObject[i]); result.push(newv); } } else { - var newClass = this.classFromKey(aKey); - result = me.createInstance(newClass,aObject); + result = this.createInstance(newClass,aObject); } - console.log('END manufacture'); return result; } - });