kojac 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (101) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +7 -0
  3. data/Gemfile +17 -0
  4. data/Gemfile.lock +158 -0
  5. data/MIT-LICENSE +20 -0
  6. data/README.md +69 -0
  7. data/Rakefile +27 -0
  8. data/app/assets/javascripts/can_extensions.js +45 -0
  9. data/app/assets/javascripts/kojac.js +1230 -0
  10. data/app/assets/javascripts/kojac_canjs.js +191 -0
  11. data/app/assets/javascripts/kojac_ember.js +463 -0
  12. data/app/controllers/kojac_controller.rb +70 -0
  13. data/app/serializers/default_kojac_serializer.rb +10 -0
  14. data/diagram.odg +0 -0
  15. data/kojac.gemspec +36 -0
  16. data/lib/kojac/app_serialize.rb +29 -0
  17. data/lib/kojac/kojac_rails.rb +432 -0
  18. data/lib/kojac/ring_strong_parameters.rb +195 -0
  19. data/lib/kojac/version.rb +3 -0
  20. data/lib/kojac.rb +8 -0
  21. data/lib/tasks/kojac_tasks.rake +4 -0
  22. data/notes.txt +48 -0
  23. data/spec/.DS_Store +0 -0
  24. data/spec/can_cache_spec.js +87 -0
  25. data/spec/can_factory_spec.js +144 -0
  26. data/spec/can_model_spec.js +127 -0
  27. data/spec/demo/README.rdoc +261 -0
  28. data/spec/demo/Rakefile +7 -0
  29. data/spec/demo/app/assets/javascripts/application.js +15 -0
  30. data/spec/demo/app/assets/stylesheets/application.css +13 -0
  31. data/spec/demo/app/controllers/application_controller.rb +3 -0
  32. data/spec/demo/app/helpers/application_helper.rb +2 -0
  33. data/spec/demo/app/mailers/.gitkeep +0 -0
  34. data/spec/demo/app/models/.gitkeep +0 -0
  35. data/spec/demo/app/views/layouts/application.html.erb +14 -0
  36. data/spec/demo/config/application.rb +65 -0
  37. data/spec/demo/config/boot.rb +10 -0
  38. data/spec/demo/config/database.yml +25 -0
  39. data/spec/demo/config/environment.rb +5 -0
  40. data/spec/demo/config/environments/development.rb +37 -0
  41. data/spec/demo/config/environments/production.rb +67 -0
  42. data/spec/demo/config/environments/test.rb +37 -0
  43. data/spec/demo/config/initializers/backtrace_silencers.rb +7 -0
  44. data/spec/demo/config/initializers/inflections.rb +15 -0
  45. data/spec/demo/config/initializers/mime_types.rb +5 -0
  46. data/spec/demo/config/initializers/secret_token.rb +7 -0
  47. data/spec/demo/config/initializers/session_store.rb +8 -0
  48. data/spec/demo/config/initializers/wrap_parameters.rb +14 -0
  49. data/spec/demo/config/locales/en.yml +5 -0
  50. data/spec/demo/config/routes.rb +58 -0
  51. data/spec/demo/config.ru +4 -0
  52. data/spec/demo/lib/assets/.gitkeep +0 -0
  53. data/spec/demo/log/.gitkeep +0 -0
  54. data/spec/demo/public/404.html +26 -0
  55. data/spec/demo/public/422.html +26 -0
  56. data/spec/demo/public/500.html +25 -0
  57. data/spec/demo/public/favicon.ico +0 -0
  58. data/spec/demo/script/rails +6 -0
  59. data/spec/ember_factory_spec.js +157 -0
  60. data/spec/ember_model_spec.js +179 -0
  61. data/spec/external/.DS_Store +0 -0
  62. data/spec/external/ember/.DS_Store +0 -0
  63. data/spec/external/ember/ember-1.0.0-rc.6.js +30970 -0
  64. data/spec/external/ember/handlebars-1.0.0-rc.4.js +2239 -0
  65. data/spec/external/jasmine/MIT.LICENSE +20 -0
  66. data/spec/external/jasmine/jasmine-html.js +616 -0
  67. data/spec/external/jasmine/jasmine.css +81 -0
  68. data/spec/external/jasmine/jasmine.js +2529 -0
  69. data/spec/external/jasmine.async.js +51 -0
  70. data/spec/external/jquery/jquery-1.7.2.js +9404 -0
  71. data/spec/external/jquery/jquery-1.7.2.min.js +4 -0
  72. data/spec/external/jquery/jquery-1.9.1.js +9597 -0
  73. data/spec/external/json2.js +480 -0
  74. data/spec/external/steal/steal-121115.js +2747 -0
  75. data/spec/external/steal/steal-3.2.3.js +2098 -0
  76. data/spec/external/steal/steal-3.2.3.min.js +27 -0
  77. data/spec/external/steal/steal.js +2466 -0
  78. data/spec/external/steal/steal.min.js +32 -0
  79. data/spec/external/steal/stealconfig.js +19 -0
  80. data/spec/external/underscore.js +1223 -0
  81. data/spec/external/underscore_plus.js +261 -0
  82. data/spec/external.zip +0 -0
  83. data/spec/handler_stack_spec.js +143 -0
  84. data/spec/helpers/SpecHelper.js +10 -0
  85. data/spec/kojac_caching_spec.js +105 -0
  86. data/spec/kojac_mock_spec.js +230 -0
  87. data/spec/kojac_model_spec.js +126 -0
  88. data/spec/kojac_object_spec.js +171 -0
  89. data/spec/kojac_operations_spec.js +41 -0
  90. data/spec/mockjson/order_item.js +37 -0
  91. data/spec/mockjson/order_item__49.js +15 -0
  92. data/spec/mockjson/order_item__50.js +15 -0
  93. data/spec/mockjson/order_item__51.js +15 -0
  94. data/spec/mockjson/product.js +82 -0
  95. data/spec/mockjson/product__3.js +22 -0
  96. data/spec/model_ring_spec.rb +52 -0
  97. data/spec/operation_include_spec.js +77 -0
  98. data/spec/run.html +81 -0
  99. data/spec/spec.js +2 -0
  100. data/spec/support/jasmine.yml +86 -0
  101. metadata +380 -0
@@ -0,0 +1,261 @@
1
+ // underscore_plus.js
2
+ // (c) 2012 Gary McGhee, Buzzware Solutions
3
+ // https://github.com/buzzware/underscore_plus
4
+ // Underscore may be freely distributed under the MIT license.
5
+
6
+ (function() {
7
+
8
+ //BEGIN copied from underscore
9
+ var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;
10
+ // Create quick reference variables for speed access to core prototypes.
11
+ var slice = ArrayProto.slice;
12
+ //END copied from underscore
13
+
14
+ _.stringify = function(aObject) {
15
+ if (_.isString(aObject))
16
+ return aObject;
17
+ if (aObject===null || aObject===undefined)
18
+ return '';
19
+ if (aObject.toString)
20
+ return aObject.toString();
21
+ return '';
22
+ };
23
+
24
+ //var MAX_DUMP_DEPTH = 10;
25
+ //
26
+ // function dumpObj(obj, name, indent, depth) {
27
+ // if (depth > MAX_DUMP_DEPTH) {
28
+ // return indent + name + ": <Maximum Depth Reached>\n";
29
+ // }
30
+ // if (typeof obj == "object") {
31
+ // var child = null;
32
+ // var output = indent + name + "\n";
33
+ // indent += "\t";
34
+ // for (var item in obj)
35
+ // {
36
+ // try {
37
+ // child = obj[item];
38
+ // } catch (e) {
39
+ // child = "<Unable to Evaluate>";
40
+ // }
41
+ // if (typeof child == "object") {
42
+ // output += dumpObj(child, item, indent, depth + 1);
43
+ // } else {
44
+ // output += indent + item + ": " + child + "\n";
45
+ // }
46
+ // }
47
+ // return output;
48
+ // } else {
49
+ // return obj;
50
+ // }
51
+ // }
52
+
53
+ _.concat = function(aArray,aAnotherArray) {
54
+ var result = [];
55
+ result.push.apply(result, aArray);
56
+ result.push.apply(result, aAnotherArray);
57
+ return result;
58
+ };
59
+
60
+ _.nameValueString = function(aObject) {
61
+ return _.map(_.keys(aObject),function(k) { return k+'="'+_.stringify(aObject[k])+'"'}).join(' ');
62
+ };
63
+
64
+ _.classEnsure = function(aOrig,aNew) {
65
+ if (!aOrig)
66
+ return aNew;
67
+ if (!aNew)
68
+ return aOrig;
69
+ return _.union(aOrig.split(' '),aNew.split(' '));
70
+ };
71
+
72
+ _.getPath = function(aObject,aPath,aDefault) {
73
+ if ((typeof aObject)=='string') { // allow aObject to be left out and assume this
74
+ if (arguments.length==1) {
75
+ aPath = aObject;
76
+ aObject = window;
77
+ } else if (arguments.length==2) {
78
+ aDefault = aPath;
79
+ aPath = aObject;
80
+ aObject = window;
81
+ }
82
+ }
83
+ var nodes = aPath.split('.');
84
+ var curr = aObject;
85
+ for (var i=0;i<nodes.length;i++) {
86
+ var name = nodes[i];
87
+ if ((curr===undefined)||(curr===null))
88
+ return aDefault;
89
+ if ((name in curr) || curr[name]) {
90
+ var val = curr[name];
91
+ if ((typeof val)=='function')
92
+ curr = val.call(curr);
93
+ else
94
+ curr = val;
95
+ } else {
96
+ return aDefault; // name doesn't exist
97
+ }
98
+ }
99
+ return curr;
100
+ }
101
+
102
+ _.moveKeys = function(aDest,aSource,aKeys) {
103
+ if (!aSource)
104
+ return aDest;
105
+ if (!aKeys)
106
+ aKeys = _.keys(aSource);
107
+ for (var i=0;i<aKeys.length;i++) {
108
+ var k = aKeys[i];
109
+ if (!(k in aSource))
110
+ continue;
111
+ aDest[k] = aSource[k];
112
+ delete aSource[k];
113
+ }
114
+ return aDest;
115
+ };
116
+
117
+ _.removeKey = function(aObject,aKey) {
118
+ var result = aObject[aKey];
119
+ delete aObject[aKey];
120
+ return result;
121
+ };
122
+
123
+ // // find all dynamic objects in an array that have matching values for the contents of aAndCriteria
124
+ // public static function ObjectArrayFind(aArray:Array, aAndCriteria:Object):Array {
125
+ // return aArray.filter(
126
+ // function(item:*, index:int, array:Array):Boolean {
127
+ // // for all items in aAndCriteria, aItem has matching values
128
+ // for (var i:String in aAndCriteria) {
129
+ // if (aAndCriteria[i] != item[i])
130
+ // return false;
131
+ // }
132
+ // return true;
133
+ // }
134
+ // );
135
+ // }
136
+ //
137
+ // // like ObjectArrayFind except only returns one object. Could be optimised to quit searching when one is found
138
+ // // adds listener and removes on first occurrence
139
+ // public static function ObjectArrayFindOne(aArray:Array, aAndCriteria:Object):Object {
140
+ // var results:Array = ObjectArrayFind(aArray, aAndCriteria);
141
+ // return results[0];
142
+ // }
143
+ //
144
+ // public static function ObjectArrayLookup(aArray:Array, aAndCriteria:Object, aProperty:String):Object {
145
+ // var results:Array = ObjectArrayFind(aArray, aAndCriteria);
146
+ // var item:Object = results[0];
147
+ // if (!item)
148
+ // return null;
149
+ // return item[aProperty];
150
+ // }
151
+
152
+ _.hasMatchingProperties = function(aObject,aCriteria) {
153
+ for (var p in aCriteria) {
154
+ if (!(p in aObject) || (aObject[p]!==aCriteria[p]))
155
+ return false;
156
+ }
157
+ return true;
158
+ };
159
+
160
+ // find one item matching object properties
161
+ _.findByCriteria = function(aArray,aCriteria) {
162
+ return _.find(aArray,function(obj){
163
+ return _.hasMatchingProperties(obj,aCriteria);
164
+ });
165
+ };
166
+
167
+ // find one item matching object properties
168
+ _.filterByCriteria = function(aArray,aCriteria) {
169
+ return _.filter(aArray,function(obj){
170
+ return _.hasMatchingProperties(obj,aCriteria);
171
+ });
172
+ };
173
+
174
+ _.isObjectStrict = function(aSomething) {
175
+ return Object.prototype.toString.call(aSomething)==='[object Object]';
176
+ }
177
+
178
+ // create an object using key,value arguments eg. createObject('a',2) returns {a: 2}
179
+ _.createObject = function() {
180
+ var result = {};
181
+ result[arguments[0]] = arguments[1];
182
+ return result;
183
+ }
184
+
185
+ _.endsWith = function(aString, aSuffix) {
186
+ var i = aString.lastIndexOf(aSuffix);
187
+ return (i>=0 && i===aString.length-aSuffix.length);
188
+ }
189
+
190
+ _.beginsWith = function(aString, aPrefix) {
191
+ var i = aString.indexOf(aPrefix);
192
+ return (i==0);
193
+ }
194
+
195
+ _.chop = function(aString, aSuffix) {
196
+ var i = aString.lastIndexOf(aSuffix);
197
+ return (i===aString.length-aSuffix.length) ? aString.substring(0,i) : aString;
198
+ }
199
+
200
+ _.bite = function(aString, aPrefix) {
201
+ var i = aString.indexOf(aPrefix);
202
+ return (i===0) ? aString.substring(aPrefix.length) : aString;
203
+ }
204
+
205
+ _.typeOf = function(aSomething) {
206
+ if (aSomething===undefined)
207
+ return 'undefined';
208
+ if (aSomething===null)
209
+ return 'null';
210
+
211
+ var result = Object.prototype.toString.call(aSomething);
212
+ result = _.bite(result,'[object ');
213
+ result = _.chop(result,']');
214
+ return result.toLowerCase();
215
+ }
216
+
217
+ /**
218
+ * Clone properties that are objects or arrays, otherwise if aSource and aDestination are different, properties will be copied
219
+ * @param aDestination
220
+ * @param aSource (optional, defaults to aSource)
221
+ * @return aDestination
222
+ */
223
+ _.cloneComplexValues = function (aDestination, aSource) {
224
+ if (!aSource)
225
+ aSource = aDestination;
226
+ for (var p in aSource) {
227
+ var t = _.typeOf(aSource[p]);
228
+ if (t==='array' || t==='object')
229
+ aDestination[p] = _.clone(aSource[p]);
230
+ else if (aDestination!==aSource)
231
+ aDestination[p] = aSource[p];
232
+ }
233
+ return aDestination;
234
+ }
235
+
236
+ // http://justtalkaboutweb.com/2008/01/06/javascript-object-extension/
237
+
238
+ _.originalClone = _.originalClone || _.clone;
239
+ // Create a copy (shallow or deep) of an object from https://github.com/cederberg/underscore/commits/feature-clone
240
+ _.clone = function(obj, deep) {
241
+ if (!deep)
242
+ return _.originalClone(obj);
243
+ if (!_.isObject(obj) || _.isFunction(obj)) return obj;
244
+ if (_.isDate(obj)) return new Date(obj.getTime());
245
+ if (_.isRegExp(obj)) return new RegExp(obj.source, obj.toString().replace(/.*\//, ""));
246
+ var isArr = (_.isArray(obj) || _.isArguments(obj));
247
+ if (deep) {
248
+ var func = function (memo, value, key) {
249
+ if (isArr)
250
+ memo.push(_.clone(value, true));
251
+ else
252
+ memo[key] = _.clone(value, true);
253
+ return memo;
254
+ };
255
+ return _.reduce(obj, func, isArr ? [] : {});
256
+ } else {
257
+ return isArr ? slice.call(obj) : _.extend({}, obj);
258
+ }
259
+ };
260
+
261
+ }).call(this);
data/spec/external.zip ADDED
Binary file
@@ -0,0 +1,143 @@
1
+ describe("HandlerStack", function() {
2
+
3
+ it("simple add", function() {
4
+ var hs = new HandlerStack();
5
+ var context = {text: 'A'};
6
+
7
+ hs.add(function(aContext) {
8
+ context.text += 'B';
9
+ });
10
+ hs.add(function(aContext) {
11
+ context.text += 'C';
12
+ });
13
+ runs(function() {
14
+ hs.run(context);
15
+ });
16
+ waitsFor(function() { return context.isResolved(); }, "request done", 3000);
17
+ runs(function() {
18
+ expect(context.text).toBe('ABC');
19
+ });
20
+ });
21
+
22
+ it("add, push, then and done", function() {
23
+ var hs = new HandlerStack();
24
+ var context = {text: 'A'};
25
+
26
+ hs.add(function(aContext) {
27
+ context.text += 'B';
28
+ });
29
+ hs.add(function(aContext) {
30
+ context.text += 'C';
31
+ });
32
+ hs.push(function(aContext) {
33
+ context.text += 'X';
34
+ });
35
+ runs(function() {
36
+ hs.run(context);
37
+ context.then(function(aContext){
38
+ context.text += 'W';
39
+ });
40
+ context.done(function(aContext){
41
+ context.text += 'Z';
42
+ });
43
+ });
44
+ waitsFor(function() { return context.isResolved(); }, "request done", 3000);
45
+ runs(function() {
46
+ expect(context.text).toBe('AXBCWZ');
47
+ });
48
+ });
49
+
50
+ it("error handling - stop processing handlers and set context.error and context.isRejected and call fail handler", function() {
51
+ var hs = new HandlerStack();
52
+ var context = {text: 'A'};
53
+
54
+ hs.add(function(aContext) {
55
+ context.text += 'B';
56
+ });
57
+ hs.add(function(aContext) {
58
+ throw new Error();
59
+ });
60
+ hs.add(function(aContext) {
61
+ context.text += 'X';
62
+ });
63
+ hs.run(context);
64
+ context.then(function(aContext){
65
+ context.text += 'W';
66
+ });
67
+ context.done(function(aContext){
68
+ context.text += 'Z';
69
+ });
70
+ context.fail(function(aContext){
71
+ context.text += 'F';
72
+ });
73
+ waitsFor(function() {
74
+ return context.isRejected();
75
+ }, "rejected", 3000);
76
+ runs(function() {
77
+ expect(context.text).toBe('ABF');
78
+ expect(context.error).toBeDefined();
79
+ expect(typeof(context.error)).toBe('object');
80
+ });
81
+ });
82
+
83
+ it("reset and recall", function() {
84
+ var hs = new HandlerStack();
85
+ var context = {text: 'A'};
86
+
87
+ hs.add(function(aContext) {
88
+ context.text += 'B';
89
+ });
90
+ runs(function() {
91
+ hs.run(context);
92
+ context.then(function(aContext){
93
+ context.text += 'W';
94
+ });
95
+ context.done(function(aContext){
96
+ context.text += 'Z';
97
+ });
98
+ });
99
+ waitsFor(function() { return context.isResolved(); }, "resolved", 3000);
100
+ runs(function() {
101
+ expect(context.text).toBe('ABWZ');
102
+ expect(context.error).toBeUndefined();
103
+ hs.reset();
104
+ });
105
+
106
+ runs(function() {
107
+ context = {text: 'G'};
108
+ hs.run(context);
109
+ context.then(function(aContext){
110
+ context.text += 'W';
111
+ });
112
+ context.done(function(aContext){
113
+ context.text += 'Z';
114
+ });
115
+ });
116
+ waitsFor(function() { return context.isResolved(); }, "resolved", 3000);
117
+ runs(function() {
118
+ expect(context.text).toBe('GBWZ');
119
+ expect(context.error).toBeUndefined();
120
+ });
121
+
122
+ });
123
+
124
+ it("handler on an object", function() {
125
+ var hs = new HandlerStack();
126
+ var context = {text: 'A'};
127
+ var TestClass = function() {
128
+ this.text = 'B';
129
+ this.go = function(aContext) {
130
+ aContext.text += this.text;
131
+ };
132
+ };
133
+ var testClass = new TestClass();
134
+ hs.add(testClass.go,null,testClass); // note: using ability to specify this as third parameter
135
+ runs(function() {
136
+ hs.run(context);
137
+ });
138
+ waitsFor(function() { return context.isResolved(); }, "resolved", 3000);
139
+ runs(function() {
140
+ expect(context.text).toBe('AB');
141
+ });
142
+ });
143
+ });
@@ -0,0 +1,10 @@
1
+ //beforeEach(function() {
2
+ // this.addMatchers({
3
+ // toBePlaying: function(expectedSong) {
4
+ // var player = this.actual;
5
+ // return player.currentlyPlayingSong === expectedSong &&
6
+ // player.isPlaying;
7
+ // }
8
+ // });
9
+ //});
10
+
@@ -0,0 +1,105 @@
1
+ describe("Kojac Caching", function() {
2
+
3
+ beforeEach(function () {
4
+ this.cache = new Kojac.Object();
5
+ this.kojac = new Kojac.Core({
6
+ cache: this.cache,
7
+ remoteProvider: new Kojac.RemoteProvider({useMockFileValues: true,mockFilePath: 'mockjson/'})
8
+ });
9
+ });
10
+
11
+ it("should return extra keys from a cacheRead that were returned from the server",function(){
12
+ var products_records = { // this is the normal data to return when requesting 'products'
13
+ products: [1,2,3],
14
+ products__1: {
15
+ id: 1,
16
+ name: "Product One"
17
+ },
18
+ products__2: {
19
+ id: 2,
20
+ name: "Product Two"
21
+ },
22
+ products__3: {
23
+ id: 3,
24
+ name: "Product Three"
25
+ }
26
+ };
27
+ var products_server_response = _.clone(products_records); // the contents of this var will be returned by the mock server
28
+ this.kojac.remoteProvider.mockReadOperationHandler = function(aOp) { // this function acts as the server
29
+ switch (aOp.key) {
30
+ case 'products':
31
+ aOp.result_key = 'products';
32
+ aOp.results = _.extend(aOp.results,products_server_response);
33
+ break;
34
+ }
35
+ };
36
+ var readReq = this.kojac.readRequest('products');
37
+ waitsFor(function() { return readReq.isResolved(); }, "request done", 3000);
38
+ runs(function() {
39
+ expect(readReq.results).toEqual(products_records); // should equal normal response
40
+ products_server_response = {}; // kill server response - non-cache reads should return empty now
41
+ readReq = this.kojac.readRequest('products',{cacheResults: false}); // read from server to check this, and don't store result in cache
42
+ });
43
+ waitsFor(function() { return readReq.isResolved(); }, "request done", 3000);
44
+ runs(function() {
45
+ expect(readReq.results).toEqual({}); // indeed, server now returns empty, but lets try cache
46
+ readReq = this.kojac.cacheReadRequest('products'); // read from cache
47
+ });
48
+ waitsFor(function() { return readReq.isResolved(); }, "request done", 3000);
49
+ runs(function() {
50
+ expect(readReq.results).toEqual(products_records); // should return original records - must be coming from cache not server
51
+ });
52
+ });
53
+
54
+ it("should return extra keys from a cacheRead that were returned from the server, using result_key",function(){
55
+ var products_records = { // this is the normal data to return when requesting 'products'
56
+ products: [1,2,3],
57
+ products__1: {
58
+ id: 1,
59
+ name: "Product One"
60
+ },
61
+ products__2: {
62
+ id: 2,
63
+ name: "Product Two"
64
+ },
65
+ products__3: {
66
+ id: 3,
67
+ name: "Product Three"
68
+ }
69
+ };
70
+ var products_server_response = _.clone(products_records); // the contents of this var will be returned by the mock server
71
+ this.kojac.remoteProvider.mockReadOperationHandler = function(aOp) { // this function acts as the server
72
+ switch (aOp.key) {
73
+ case 'products':
74
+ if (aOp.result_key!==aOp.key)
75
+ products_server_response[aOp.result_key] = _.removeKey(products_server_response,aOp.key);
76
+ aOp.results = _.extend(aOp.results,products_server_response);
77
+ break;
78
+ }
79
+ };
80
+ expect(_.keys(this.kojac.cache)).toEqual([]);
81
+ var readReq = this.kojac.readRequest('products',{result_key: 'products_latest'});
82
+ waitsFor(function() { return readReq.isResolved(); }, "request done", 3000);
83
+ runs(function() {
84
+ var expected = _.clone(products_records);
85
+ expected.products_latest = _.removeKey(expected,'products');
86
+ expect(readReq.results).toEqual(expected); // should equal normal response
87
+ expect(_.keys(this.kojac.cache).sort()).toEqual(['products__1','products__2','products__3','products_latest']);
88
+ products_server_response = {}; // kill server response - non-cache reads should return empty now
89
+ readReq = this.kojac.readRequest('products',{cacheResults: false, result_key: 'products_latest'}); // read from server to check this, and don't store result in cache
90
+ });
91
+ waitsFor(function() { return readReq.isResolved(); }, "request done", 3000);
92
+ runs(function() {
93
+ expect(readReq.results).toEqual({}); // indeed, server now returns empty, but lets try cache
94
+ readReq = this.kojac.cacheReadRequest('products',{result_key: 'products_latest'}); // read from cache
95
+ });
96
+ waitsFor(function() { return readReq.isResolved(); }, "request done", 3000);
97
+ runs(function() {
98
+ var expected = _.clone(products_records);
99
+ expected.products_latest = _.removeKey(expected,'products');
100
+ expect(readReq.results).toEqual(expected); // should return original records - must be coming from cache not server
101
+ });
102
+ });
103
+
104
+ });
105
+