kojac 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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,191 @@
1
+ /*--------------------------------------------------------------------------
2
+ *
3
+ * Key Oriented JSON Application Cache (KOJAC)
4
+ * (c) 2011-12 Buzzware Solutions
5
+ * https://github.com/buzzware/KOJAC
6
+ *
7
+ * KOJAC is freely distributable under the terms of an MIT-style license.
8
+ *
9
+ *--------------------------------------------------------------------------*/
10
+ (function() {
11
+
12
+ // minimal plugin example
13
+ //(function(can, window, undefined){
14
+ // var oldSetup = can.Observe.setup;
15
+ // can.extend(can.Observe, {
16
+ // setup : function(superClass){
17
+ // oldSetup.apply(this, arguments);
18
+ // }
19
+ // });
20
+ //
21
+ //// can.extend(can.Observe.prototype, {
22
+ //// });
23
+ //
24
+ //})(this.can, this );
25
+
26
+ MODEL_FIELD_CONVERTERS = { // these are only used when you call .serialize()
27
+ 'Int' : function( aValue ){
28
+ return Kojac.interpretValueAsType(aValue,Int);
29
+ },
30
+ 'Number' : function( aValue ){
31
+ return Kojac.interpretValueAsType(aValue,Number);
32
+ },
33
+ 'String' : function( aValue ){
34
+ return Kojac.interpretValueAsType(aValue,String);
35
+ },
36
+ 'Boolean' : function( aValue ){
37
+ return Kojac.interpretValueAsType(aValue,Boolean);
38
+ },
39
+ 'Date' : function( aValue ){
40
+ return Kojac.interpretValueAsType(aValue,Date);
41
+ },
42
+ 'Array' : function( aValue ){
43
+ return Kojac.interpretValueAsType(aValue,Array);
44
+ },
45
+ 'Object' : function( aValue ){
46
+ return Kojac.interpretValueAsType(aValue,Object);
47
+ }
48
+ };
49
+
50
+ Kojac.CanModel = can.Observe({
51
+ fullName: 'Kojac.CanModel',
52
+ extend: function(aFullName, aClass, aInstanceProps) {
53
+ if ( typeof aFullName != 'string' ) {
54
+ aInstanceProps = aClass;
55
+ aClass = aFullName;
56
+ aFullName = null;
57
+ }
58
+ if ( ! aInstanceProps ) {
59
+ aInstanceProps = aClass;
60
+ aClass = null;
61
+ }
62
+ aInstanceProps = aInstanceProps || {};
63
+ aClass = aClass || {};
64
+
65
+ if (aClass && (('attributes' in aClass) || ('defaults' in aClass) || ('serialize' in aClass) || ('convert' in aClass)))
66
+ throw new Error('attributes, defaults, serialize and convert not supported');
67
+
68
+ var attributes = {};
69
+ var defaults = {};
70
+ for (var p in aInstanceProps) {
71
+ var v = aInstanceProps[p];
72
+ var i = Kojac.FieldTypes.indexOf(v);
73
+ if (i >= 0) { // field type
74
+ v = null;
75
+ defaults[p] = v;
76
+ attributes[p] = Kojac.FieldTypeStrings[i];
77
+ delete aInstanceProps[p];
78
+ } else if (_.isFunction(v)) { // a method
79
+ // don't touch this
80
+ } else { // default value
81
+ i = Kojac.FieldTypes.indexOf(Kojac.getPropertyValueType(v));
82
+ if (i >= 0) {
83
+ attributes[p] = Kojac.FieldTypeStrings[i];
84
+ defaults[p] = v;
85
+ }
86
+ delete aInstanceProps[p];
87
+ }
88
+ }
89
+ _.extend(aClass,{
90
+ attributes: attributes,
91
+ defaults: defaults,
92
+ serialize : MODEL_FIELD_CONVERTERS, // these are only used when you call .serialize()
93
+ convert: MODEL_FIELD_CONVERTERS // these are only used when you set properties with attr
94
+ });
95
+ if (aFullName)
96
+ return can.Observe.extend.apply(this, [aFullName,aClass,aInstanceProps]);
97
+ else
98
+ return can.Observe.extend.apply(this, [aClass,aInstanceProps]);
99
+ }
100
+ },{
101
+ });
102
+
103
+ Kojac.CanObjectFactory = Kojac.ObjectFactory.extend({
104
+
105
+ defaultClass: can.Observe,
106
+
107
+ copyProperties: function(aDest,aSource) {
108
+ return CanUtils.copyProperties(aDest,aSource);
109
+ }
110
+
111
+ });
112
+
113
+
114
+ Kojac.CanCache = can.Observe({
115
+ // convert: {
116
+ // default: function(aValue) { return aValue; }
117
+ // }
118
+ },{
119
+
120
+ // __convert: function(prop, value){
121
+ // console.log('__convert');
122
+ // return value;
123
+ // },
124
+
125
+ get: function(k) {
126
+ return this.attr(k);
127
+ },
128
+ set: function(k,v) {
129
+ return this.attr(k,v);
130
+ },
131
+
132
+ cacheResults: function(aRequest) {
133
+ console.log('BEGIN cacheResults')
134
+ var results = {};
135
+ var deletes = [];
136
+ for (var i=0;i<aRequest.ops.length;i++) {
137
+ var op = aRequest.ops[i];
138
+ if (op.error)
139
+ break;
140
+ if (op.options.cacheResults===false)
141
+ continue;
142
+ for (p in op.results) {
143
+ var v = op.results[p];
144
+ results[p] = v;
145
+ if (v===undefined)
146
+ deletes.push(p);
147
+ else
148
+ deletes = _.without(deletes, [p]);
149
+ }
150
+ }
151
+ for (p in results) {
152
+ try {
153
+ this.attr(p,results[p]);
154
+ } catch (e) {
155
+ console.log('Error when caching '+p+' : '+ e.message);
156
+ }
157
+ }
158
+ // if (deletes.length) {
159
+ // for (var i=0;i<deletes.length;i++) {
160
+ // console.log('cacheResults remove');
161
+ // this.removeAttr(deletes[i]);
162
+ // }
163
+ // }
164
+ console.log('END cacheResults');
165
+ },
166
+
167
+ collectIds: function(aPrefix, aIds) {
168
+ return Kojac.collectIds(aPrefix,aIds,this);
169
+ },
170
+
171
+ collectionValues: function(aCollectionKey) {
172
+ return Kojac.collectionValues(aCollectionKey,this);
173
+ }
174
+
175
+ });
176
+
177
+ Kojac.collectIds = function(aPrefix,aIds,aCache) {
178
+ if (!aIds)
179
+ return [];
180
+ var result = [];
181
+ for (var i=0;i<aIds.length;i++)
182
+ result.push(aCache.get(aPrefix+'__'+aIds[i]));
183
+ return result;
184
+ };
185
+
186
+ Kojac.collectionValues = function(aCollectionKey,aCache) {
187
+ var ids = aCache.get(aCollectionKey);
188
+ return Kojac.collectIds(aCollectionKey,ids,aCache);
189
+ };
190
+
191
+ })();
@@ -0,0 +1,463 @@
1
+ /*--------------------------------------------------------------------------
2
+ *
3
+ * Key Oriented JSON Application Cache (KOJAC)
4
+ * (c) 2011-12 Buzzware Solutions
5
+ * https://github.com/buzzware/KOJAC
6
+ *
7
+ * KOJAC is freely distributable under the terms of an MIT-style license.
8
+ *
9
+ *--------------------------------------------------------------------------*/
10
+
11
+ // recommended http://www.cerebris.com/blog/2012/03/06/understanding-ember-object/
12
+ //var Person = Ember.Object.extend({
13
+ // chromosomes: null,
14
+ // init: function() {
15
+ // this._super();
16
+ // this.chromosomes = ["x"]; // everyone gets at least one X chromosome
17
+ // }
18
+ //});
19
+
20
+ // declaring property : {chromosomes: null}
21
+ // for values, generate function that calls this._super(); then loops thru setting values
22
+
23
+ // example :
24
+ //
25
+ //Product = Kojac.EmberModel.extend({
26
+ // name: String,
27
+ // purchases: Int,
28
+ // weight: Number,
29
+ // isMember: Boolean,
30
+ // init: function() {
31
+ // this._super();
32
+ // this.weight = 0.0;
33
+ // }
34
+ //});
35
+ //
36
+ //
37
+ // 1. Create extendObject with properties: null
38
+ // 2.
39
+
40
+
41
+ Kojac.EmberObjectFactory = Kojac.Object.extend({
42
+
43
+ matchers: null,
44
+
45
+ register: function(aPairs) {
46
+ if (!aPairs)
47
+ return;
48
+ if (this.matchers===null)
49
+ this.matchers = [];
50
+ for (var i = 0; i < aPairs.length; i++)
51
+ this.matchers.push(aPairs[i]);
52
+ },
53
+
54
+ emberClassFromKey: function(aKey) {
55
+ var pair;
56
+ var re;
57
+ var newClass;
58
+ for (var i = 0; i < this.matchers.length; i++) {
59
+ pair = this.matchers[i];
60
+ re = pair[0];
61
+ if (!re.test(aKey))
62
+ continue;
63
+ newClass = pair[1];
64
+ break;
65
+ }
66
+ if (newClass===undefined)
67
+ newClass = Ember.Object;
68
+ return newClass;
69
+ },
70
+
71
+ emberObjectFactoryArray: function(aArray,aKey) {
72
+ var newClass = this.emberClassFromKey(aKey);
73
+ var result = [];
74
+ for (var i=0; i<aArray.length; i++) {
75
+ var newv = new newClass();
76
+ newv.setProperties(aArray[i]);
77
+ result.push(newv);
78
+ }
79
+ return result;
80
+ },
81
+
82
+ emberObjectFactory: function(aObject,aKey) {
83
+ var newClass = this.emberClassFromKey(aKey);
84
+ var newv = new newClass();
85
+ newv.setProperties(aObject);
86
+ return newv;
87
+ },
88
+
89
+ transformResultsToValueObjects: function(aRequest) {
90
+ for (var i=0;i<aRequest.ops.length;i++) {
91
+ var op = aRequest.ops[i];
92
+ if (op.error)
93
+ break;
94
+ for (var k in op.results) {
95
+ var v = op.results[k];
96
+ if (!jQuery.isPlainObject(v))
97
+ continue;
98
+ op.results[k] = this.emberObjectFactory(v,k);
99
+ }
100
+ }
101
+ }
102
+
103
+ });
104
+
105
+
106
+
107
+ //ExampleRemoteProvider = Ember.Object.extend({
108
+ //Kojac.RemoteProvider = Kojac.Object.extend({
109
+ //
110
+ // useMockFileValues: false,
111
+ // mockFilePath: '',
112
+ // mockReadOperationHandler: null,
113
+ // mockWriteOperationHandler: function(aOp) {
114
+ // log.debug(JSON.stringify(EmberUtils.copyProperties({},aOp,null,['request'])));
115
+ // },
116
+ //
117
+ // handleOperationFromFiles: function(aRequest) {
118
+ // var op = aRequest.handlers.parameter;
119
+ //
120
+ // if (op.verb==='READ' || op.verb==='EXECUTE') {
121
+ // if (this.mockReadOperationHandler) {
122
+ // this.mockReadOperationHandler(op);
123
+ // } else {
124
+ // aRequest.handlers.waitForCallNext = true;
125
+ // var fp = mockFilePath+op.key+'.js';
126
+ // var data = null;
127
+ // jQuery.ajax({url: fp, dataType: 'json', cache: false, data: data}).done(
128
+ // function( aData ) {
129
+ // for (p in aData)
130
+ // op[p] = aData[p];
131
+ // aRequest.handlers.callNext();
132
+ // }
133
+ // ).fail(
134
+ // function(jqXHR, textStatus) {
135
+ // aRequest.handlers.handleError(textStatus);
136
+ // }
137
+ // );
138
+ // }
139
+ // } else {
140
+ // if (this.mockWriteOperationHandler)
141
+ // this.mockWriteOperationHandler(op);
142
+ // }
143
+ // },
144
+ //
145
+ // handleAjaxRequest: function(aRequest) {
146
+ // var op = aRequest.parameter;
147
+ // aRequest.handlers.waitForCallNext = true;
148
+ // jQuery.ajax({},function(){
149
+ // aRequest.handlers.callNext();
150
+ // })
151
+ // },
152
+ //
153
+ // addRequestHandlers: function(aRequest) {
154
+ // if (this.useMockFileValues) {
155
+ // for (var i=0;i<aRequest.ops.length;i++)
156
+ // aRequest.handlers.add(this.handleOperationFromFiles,aRequest.ops[i],this);
157
+ // } else {
158
+ // aRequest.handlers.add(this.handleAjaxRequest,null,this);
159
+ // }
160
+ // }
161
+ //
162
+ //});
163
+
164
+
165
+ Kojac.EmberModel = Ember.Object.extend({});
166
+
167
+ Kojac.EmberModel.reopenClass({
168
+
169
+ //_extend: Ember.Object.extend,
170
+
171
+ extend: function() {
172
+ var defs = arguments[0];
173
+ var extender = {};
174
+ var definitions = this.getDefinitions();
175
+ var defaults = this.getDefaults();
176
+ var _type;
177
+ var _value;
178
+ //var _init;
179
+ if (defs) {
180
+ for (p in defs) {
181
+ var pValue = defs[p];
182
+ // if (p=='init') {
183
+ // _init = pValue;
184
+ // } else {
185
+ if (Kojac.FieldTypes.indexOf(pValue)>=0) { // pValue is field type
186
+ definitions[p] = pValue;
187
+ defaults[p] = null;
188
+ extender[p] = null;
189
+ } else {
190
+ var ft=Kojac.getPropertyValueType(pValue);
191
+ if (ft && (Kojac.SimpleTypes.indexOf(ft)>=0)) { // pValue is simple field value
192
+ definitions[p] = ft;
193
+ defaults[p] = pValue;
194
+ extender[p] = pValue;
195
+ } else { // pValue is something else
196
+ //definitions[p] = _type;
197
+ //defaults[p] = _value;
198
+ extender[p] = pValue;
199
+ }
200
+ }
201
+ // }
202
+ }
203
+ // extender.init = function() { // we need to call the super init, then initialise default values, then call the given init
204
+ // this._super();
205
+ // //var defaults = this.constructor.getDefaults();
206
+ // //for (dp in defaults)
207
+ // // this[dp] = defaults[dp];
208
+ // if (_init) {
209
+ // var _super = this._super;
210
+ // this._super = function() {}; // temporarily disable _super to make init function work like normal ember init
211
+ // _init.call(this);
212
+ // if (_super)
213
+ // this._super = _super; // restore _super
214
+ // }
215
+ // };
216
+ }
217
+ var result = this._super(extender);
218
+ result.setDefinitions(definitions);
219
+ result.setDefaults(defaults);
220
+ return result;
221
+ },
222
+
223
+ setDefinitions: function(aDefinitions) {
224
+ this._definitions = (aDefinitions || {});
225
+ },
226
+
227
+ getDefinitions: function() {
228
+ return this._definitions || {};
229
+ },
230
+
231
+ setDefaults: function(aDefaults) {
232
+ this._defaults = (aDefaults || {});
233
+ },
234
+
235
+ getDefaults: function() {
236
+ return this._defaults || {};
237
+ },
238
+
239
+ __createWithMixins: Kojac.EmberModel.createWithMixins,
240
+ createWithMixins: function() {
241
+ var inputs = arguments;
242
+ if (inputs.length) {
243
+ inputs[0] = Kojac.readTypedProperties({},inputs[0],this.getDefinitions());
244
+ }
245
+ return this.__createWithMixins.apply(this,inputs);
246
+ },
247
+
248
+ __create: Kojac.EmberModel.create,
249
+ create: function() {
250
+ var inputs = arguments;
251
+ if (inputs.length) {
252
+ inputs[0] = Kojac.readTypedProperties({},inputs[0],this.getDefinitions());
253
+ }
254
+ return this.__create.apply(this,inputs);
255
+ }
256
+
257
+ });
258
+
259
+ Kojac.EmberModel.reopen({
260
+
261
+ ___set: Ember.set,
262
+ set: function(k,v) {
263
+ var def = this.constructor.getDefinitions();
264
+ var t = (def && def[k]);
265
+ if (t)
266
+ v = Kojac.interpretValueAsType(v,t);
267
+ return this.___set(this,k,v);
268
+ },
269
+
270
+ ___setProperties: Ember.setProperties,
271
+ setProperties: function(values) {
272
+ values = Kojac.readTypedProperties({},values,this.constructor.getDefinitions());
273
+ return this.___setProperties(this,values);
274
+ },
275
+
276
+ toObject: function(aOptions) {
277
+ var result = {};
278
+ var defs = this.constructor.getDefinitions();
279
+ var includes = aOptions && aOptions.include
280
+ for (var p in defs)
281
+ result[p] = this.get(p);
282
+ if (includes) {
283
+ includes = this.getProperties(includes);
284
+ var v;
285
+ for (var p in includes) {
286
+ v = includes[p];
287
+ if (v && (typeof(v)=="object") && ("toObject" in v))
288
+ includes[p] = v.toObject();
289
+ }
290
+ _.extend(result,includes);
291
+ }
292
+ return result;
293
+ }
294
+
295
+ });
296
+
297
+
298
+ Kojac.EmberCache = Ember.Object.extend({
299
+
300
+ generateKey: function(aPrefix) {
301
+ var key;
302
+ do {
303
+ key = aPrefix+'__'+(-_.random(1000000,2000000)).toString();
304
+ } while (key in this);
305
+ return key;
306
+ },
307
+
308
+ generateId: function(aPrefix) {
309
+ var key;
310
+ var id;
311
+ do {
312
+ id = -_.random(1000000,2000000)
313
+ key = aPrefix+'__'+id.toString();
314
+ } while (key in this);
315
+ return id;
316
+ },
317
+
318
+ store: function(aKeysValues) {
319
+ this.beginPropertyChanges();
320
+ for (p in aKeysValues) {
321
+ if (aKeysValues[p]===undefined) {
322
+ this.set(p,undefined);
323
+ delete this[p];
324
+ } else {
325
+ this.set(p,aKeysValues[p]);
326
+ }
327
+ }
328
+ this.endPropertyChanges();
329
+ },
330
+
331
+ cacheResults: function(aRequest) {
332
+ this.beginPropertyChanges();
333
+ for (var i=0;i<aRequest.ops.length;i++) {
334
+ var op = aRequest.ops[i];
335
+ if (op.error)
336
+ break;
337
+ if (op.options.cacheResults===false)
338
+ continue;
339
+ this.store(op.results);
340
+ }
341
+ this.endPropertyChanges();
342
+ },
343
+
344
+ collectIds: function(aPrefix, aIds) {
345
+ return Kojac.collectIds(aPrefix,aIds,this);
346
+ }
347
+
348
+ });
349
+
350
+ Kojac.collectIds = function(aPrefix,aIds,aCache,aFilterFn) {
351
+ if (!aIds)
352
+ return [];
353
+ var result = [];
354
+ var item;
355
+ for (var i=0;i<aIds.length;i++) {
356
+ item = aCache.get(aPrefix+'__'+aIds[i]);
357
+ if (!aFilterFn || aFilterFn(item))
358
+ result.push(item);
359
+ }
360
+ return result;
361
+ };
362
+
363
+ Ember.computed.collectIds = function(aCollectionProperty,aPrefix,aModelCachePath,aFilterFn){
364
+ if (!aPrefix)
365
+ aPrefix = _.last(aCollectionProperty.split('.'));
366
+
367
+ return Ember.computed(aCollectionProperty, function(){
368
+ var cache;
369
+ if (aModelCachePath)
370
+ cache = Ember.Handlebars.get(this,aModelCachePath); //(aModelCachePath && Ember.get(aModelCachePath));
371
+ else
372
+ cache = this;
373
+ var ids = Ember.Handlebars.get(this,aCollectionProperty);
374
+ if (!ids)
375
+ return [];
376
+ var result = [];
377
+ var item;
378
+ for (var i=0;i<ids.length;i++) {
379
+ item = cache.get(aPrefix+'__'+ids[i]);
380
+ if (!aFilterFn || aFilterFn(item))
381
+ result.push(item);
382
+ }
383
+ return result;
384
+ }).property(aModelCachePath,aCollectionProperty);
385
+ };
386
+
387
+ Ember.computed.has_many = function(aResource,aForeignKey,aLocalPropertyPath,aModelCachePath,aFilterFn){
388
+
389
+ return Ember.computed(function(){
390
+ var cache;
391
+ if (aModelCachePath)
392
+ cache = Ember.Handlebars.get(this,aModelCachePath); //(aModelCachePath && Ember.get(aModelCachePath));
393
+ else
394
+ cache = this;
395
+ var localValue = Ember.Handlebars.get(this,aLocalPropertyPath);
396
+
397
+ if (!_.endsWith(aResource,'__'))
398
+ aResource = aResource+'__';
399
+
400
+ var results = [];
401
+ // get all keys that begin with aPrefix
402
+ var keys = _.keys(cache);
403
+ for (var i=0;i<keys.length;i++) {
404
+ var k = keys[i];
405
+ if (!_.beginsWith(k,aResource))
406
+ continue;
407
+ var v = cache.get(k);
408
+ if (!v || (v.get(aForeignKey) != localValue))
409
+ continue;
410
+ if (aFilterFn && !aFilterFn(k,v))
411
+ continue;
412
+ results.push(v);
413
+ }
414
+ return results;
415
+ }).property(aModelCachePath,aLocalPropertyPath);
416
+ };
417
+
418
+ Ember.computed.modelById = function(aIdProperty,aPrefix,aModelCachePath) {
419
+ if (!aModelCachePath)
420
+ aModelCachePath = 'App.cache';
421
+
422
+ return Ember.computed(aIdProperty, function(){
423
+ var id = Ember.Handlebars.get(this,aIdProperty);
424
+ if (!id)
425
+ return null;
426
+ var cache = Ember.Handlebars.get(this,aModelCachePath);
427
+ var key = keyJoin(aPrefix,id);
428
+ if (!key || !cache)
429
+ return null;
430
+ return Ember.Handlebars.get(cache,key);
431
+ }).property(aModelCachePath,aIdProperty);
432
+ };
433
+
434
+ Ember.computed.modelByIdVersioned = function(aIdProperty,aVerProperty,aPrefix,aModelCachePath) {
435
+ if (!aModelCachePath)
436
+ aModelCachePath = 'App.cache';
437
+
438
+ return Ember.computed(aIdProperty, function(){
439
+ var id = Ember.Handlebars.get(this,aIdProperty);
440
+ var cache = Ember.Handlebars.get(this,aModelCachePath);
441
+ if (!id || !cache)
442
+ return null;
443
+ var key;
444
+ var ver = Ember.Handlebars.get(this,aVerProperty);
445
+ if (ver) {
446
+ id = [id,ver].join('_');
447
+ key = keyJoin(aPrefix,id);
448
+ return Ember.Handlebars.get(cache,key);
449
+ } else {
450
+ var versions = _.pickWithPrefix(cache,keyJoin(aPrefix,id)+'_');
451
+ var v,vi;
452
+ key = _.max(_.keys(versions), function(k){
453
+ vi = k.lastIndexOf('_');
454
+ return Number(k.substr(vi+1));
455
+ });
456
+ return key ? versions[key] : null;
457
+ }
458
+ }).property(aModelCachePath,aIdProperty,aVerProperty);
459
+ };
460
+
461
+
462
+
463
+
@@ -0,0 +1,70 @@
1
+ class KojacController < ApplicationController
2
+
3
+ respond_to :json
4
+ protect_from_forgery :only => []
5
+
6
+ protected
7
+
8
+ def controller_for_key(aKey)
9
+ resource = aKey.split('__').first
10
+ controller_name = resource.camelize+'Controller'
11
+ if controller = controller_name.constantize
12
+ result = controller.new
13
+ result.current_user = self.current_user
14
+ result
15
+ else
16
+ nil
17
+ end
18
+ end
19
+
20
+ def process_ops(aInput)
21
+ result = { }
22
+ if ops = aInput[:ops]
23
+ result[:ops] = []
24
+ ops.each do |op|
25
+ method = "#{op[:verb].to_s.downcase}_op".to_sym
26
+ ctrlr = self.controller_for_key(op[:key])
27
+ if ctrlr.respond_to? method
28
+ ctrlr.params = {op: op}
29
+ output = ctrlr.send method
30
+ result[:ops] << output
31
+ else
32
+ raise "Unsupported verb #{op[:verb]}"
33
+ end
34
+ end
35
+ end
36
+ result
37
+ end
38
+
39
+ public
40
+
41
+ def receive
42
+ input = nil
43
+ output = nil
44
+ status = :ok
45
+
46
+ begin
47
+ input = params[:kojac]
48
+ output = process_ops(input)
49
+ status = :ok
50
+ rescue => e
51
+ raise e unless Rails.env.production?
52
+ Rails.logger.debug e.message
53
+ Rails.logger.debug e.backtrace.join("\n")
54
+ output = {
55
+ results: nil,
56
+ errors: [{
57
+ message: e.message,
58
+ backtrace: e.backtrace
59
+ }]
60
+ }
61
+ status = :unprocessable_entity
62
+ end
63
+ #output = ActiveModel::Serializer.new(output,current_user).to_json
64
+ #sz = output.active_model_serializer.new(output)
65
+ #jsons = sz.to_json(:scope => current_user, :root => false)
66
+ jsons = app_serialize(output,current_user)
67
+ render json: jsons, status: status
68
+ end
69
+
70
+ end