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;
}
-
});