kojac 0.9.1 → 0.11.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 (95) hide show
  1. checksums.yaml +6 -14
  2. data/.gitignore +4 -0
  3. data/Gemfile +1 -1
  4. data/app/assets/javascripts/kojac.js +393 -125
  5. data/app/assets/javascripts/kojac_canjs.js +34 -34
  6. data/app/assets/javascripts/kojac_ember.js +110 -152
  7. data/app/controllers/{kojac_controller.rb → kojac_base_controller.rb} +18 -11
  8. data/app/policies/kojac_base_policy.rb +114 -0
  9. data/app/serializers/kojac_base_serializer.rb +35 -0
  10. data/kojac.gemspec +12 -10
  11. data/lib/kojac/app_serialize.rb +31 -29
  12. data/lib/kojac/concentric.rb +152 -0
  13. data/lib/kojac/kojac_policy.rb +70 -0
  14. data/lib/kojac/kojac_rails.rb +200 -49
  15. data/lib/kojac/version.rb +1 -1
  16. data/spec/can_cache_spec.js +19 -19
  17. data/spec/demo/.gitignore +16 -0
  18. data/spec/demo/.ruby-gemset +1 -0
  19. data/spec/demo/.ruby-version +1 -0
  20. data/spec/demo/Gemfile +59 -0
  21. data/spec/demo/Gemfile.lock +153 -0
  22. data/spec/demo/README.rdoc +15 -248
  23. data/spec/demo/Rakefile +25 -1
  24. data/spec/demo/app/{mailers/.gitkeep → assets/images/.keep} +0 -0
  25. data/spec/demo/app/assets/javascripts/application.js +3 -3
  26. data/spec/demo/app/controllers/application_controller.rb +6 -1
  27. data/spec/demo/app/{models/.gitkeep → controllers/concerns/.keep} +0 -0
  28. data/spec/demo/app/controllers/users_controller.rb +5 -0
  29. data/spec/demo/{lib/assets/.gitkeep → app/mailers/.keep} +0 -0
  30. data/spec/demo/{log/.gitkeep → app/models/.keep} +0 -0
  31. data/spec/demo/app/models/concerns/.keep +0 -0
  32. data/spec/demo/app/models/user.rb +36 -0
  33. data/spec/demo/app/policies/user_policy.rb +42 -0
  34. data/spec/demo/bin/bundle +3 -0
  35. data/spec/demo/bin/rails +4 -0
  36. data/spec/demo/bin/rake +4 -0
  37. data/spec/demo/config.ru +1 -1
  38. data/spec/demo/config/application.rb +14 -46
  39. data/spec/demo/config/application.yml +4 -0
  40. data/spec/demo/config/boot.rb +3 -9
  41. data/spec/demo/config/database.yml +6 -6
  42. data/spec/demo/config/environment.rb +4 -2
  43. data/spec/demo/config/environments/development.rb +11 -19
  44. data/spec/demo/config/environments/production.rb +40 -27
  45. data/spec/demo/config/environments/test.rb +13 -14
  46. data/spec/demo/config/initializers/concentric_config.rb +9 -0
  47. data/spec/demo/config/initializers/filter_parameter_logging.rb +4 -0
  48. data/spec/demo/config/initializers/inflections.rb +6 -5
  49. data/spec/demo/config/initializers/initialize_kojac.rb +16 -0
  50. data/spec/demo/config/initializers/secret_token.rb +7 -2
  51. data/spec/demo/config/initializers/session_store.rb +0 -5
  52. data/spec/demo/config/initializers/wrap_parameters.rb +6 -6
  53. data/spec/demo/config/locales/en.yml +20 -2
  54. data/spec/demo/config/routes.rb +24 -24
  55. data/spec/demo/db/migrate/20131212034312_add_user.rb +14 -0
  56. data/spec/demo/db/migrate/20140107085351_add_owner_id.rb +5 -0
  57. data/spec/demo/db/schema.rb +28 -0
  58. data/spec/demo/db/seeds.rb +7 -0
  59. data/spec/demo/lib/assets/.keep +0 -0
  60. data/spec/demo/lib/tasks/.keep +0 -0
  61. data/spec/demo/log/.keep +0 -0
  62. data/spec/demo/public/404.html +43 -11
  63. data/spec/demo/public/422.html +43 -11
  64. data/spec/demo/public/500.html +43 -11
  65. data/spec/demo/public/robots.txt +5 -0
  66. data/spec/demo/spec/controllers/allowed_fields_spec.rb +171 -0
  67. data/spec/demo/spec/factories/users.rb +9 -0
  68. data/spec/demo/spec/features/concentric_spec.rb +63 -0
  69. data/spec/demo/spec/features/serialization_spec.rb +86 -0
  70. data/spec/demo/spec/spec_helper.rb +133 -0
  71. data/spec/demo/spec/spec_utils.rb +42 -0
  72. data/spec/demo/vendor/assets/javascripts/.keep +0 -0
  73. data/spec/demo/vendor/assets/stylesheets/.keep +0 -0
  74. data/spec/ember_factory_spec.js +1 -1
  75. data/spec/ember_model_spec.js +13 -3
  76. data/spec/ember_tojsono_spec.js +105 -0
  77. data/spec/error_handling_spec.js +90 -0
  78. data/spec/external/underscore_plus.js +318 -9
  79. data/spec/kojac_caching_spec.js +3 -1
  80. data/spec/kojac_ember_cache_spec.js +9 -0
  81. data/spec/kojac_mock_spec.js +4 -4
  82. data/spec/kojac_operations_spec.js +4 -4
  83. data/spec/local_provider_spec.js +184 -0
  84. data/spec/model_ring_spec.rb +2 -2
  85. data/spec/operation_include_spec.js +2 -2
  86. data/spec/run.html +34 -24
  87. data/spec/type_conversion_spec.js +38 -0
  88. data/vendor/assets/javascripts/jstorage.js +950 -0
  89. metadata +115 -129
  90. data/Gemfile.lock +0 -157
  91. data/app/serializers/default_kojac_serializer.rb +0 -10
  92. data/lib/kojac/ring_strong_parameters.rb +0 -195
  93. data/spec/.DS_Store +0 -0
  94. data/spec/demo/script/rails +0 -6
  95. data/spec/external/.DS_Store +0 -0
@@ -122,47 +122,47 @@
122
122
  // return value;
123
123
  // },
124
124
 
125
- get: function(k) {
125
+ retrieve: function(k) {
126
126
  return this.attr(k);
127
127
  },
128
- set: function(k,v) {
128
+ store: function(k,v) {
129
129
  return this.attr(k,v);
130
130
  },
131
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]);
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]);
162
149
  // }
163
150
  // }
164
- console.log('END cacheResults');
165
- },
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
166
 
167
167
  collectIds: function(aPrefix, aIds) {
168
168
  return Kojac.collectIds(aPrefix,aIds,this);
@@ -72,147 +72,89 @@ Kojac.EmberObjectFactory = Kojac.Object.extend({
72
72
  var newClass = this.emberClassFromKey(aKey);
73
73
  var result = [];
74
74
  for (var i=0; i<aArray.length; i++) {
75
- var newv = new newClass();
76
- newv.setProperties(aArray[i]);
75
+ var newv = newClass.create(aArray[i]);
77
76
  result.push(newv);
78
77
  }
79
78
  return result;
80
79
  },
81
80
 
82
- emberObjectFactory: function(aObject,aKey) {
81
+ manufacture: function(aObject,aKey) {
83
82
  var newClass = this.emberClassFromKey(aKey);
84
- var newv = new newClass();
85
- newv.setProperties(aObject);
83
+ var newv = newClass.create(aObject);
86
84
  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
85
  }
102
86
 
103
87
  });
104
88
 
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
89
  Kojac.EmberModel = Ember.Object.extend({});
166
90
 
167
- Kojac.EmberModel.reopenClass({
91
+ Kojac.EmberModel.TypedField = function() {
92
+ this.get = function(obj,keyName) { // Here code was copied from Ember get to avoid endless recursion. This is more efficient but brittle for future Ember versions
93
+ var meta = Ember.meta(obj),
94
+ desc = (meta && meta.descs[keyName]),
95
+ ret;
96
+ if (Ember.ENV.MANDATORY_SETTER && meta && (meta.watching[keyName] > 0)) {
97
+ ret = meta.values[keyName];
98
+ } else {
99
+ ret = obj[keyName];
100
+ }
101
+ if ((ret === undefined) &&
102
+ ('object' === typeof obj) && !(keyName in obj) && ('function' === typeof obj.unknownProperty)) {
103
+ return obj.unknownProperty(keyName);
104
+ }
105
+ return ret;
106
+ }
107
+ this.set = function(obj,keyName,aValue) { // here we call the standard method after removing the desc to prevent endless recursion, then put it back.
108
+ var result;
109
+ var def = obj.constructor.getDefinitions();
110
+ var t = (def && def[keyName]);
111
+ if (t)
112
+ aValue = Kojac.interpretValueAsType(aValue,t);
113
+ var meta = Ember.meta(obj),
114
+ desc = (meta && meta.descs[keyName]);
115
+ meta.descs[keyName] = undefined;
116
+ try {
117
+ result = Ember.set(obj,keyName,aValue);
118
+ }
119
+ finally {
120
+ meta.descs[keyName] = desc;
121
+ }
122
+ return result;
123
+ }
124
+ };
125
+ Kojac.EmberModel.TypedField.prototype = new Ember.Descriptor();
168
126
 
169
- //_extend: Ember.Object.extend,
127
+ Kojac.EmberModel.reopenClass({
170
128
 
171
129
  extend: function() {
172
130
  var defs = arguments[0];
173
131
  var extender = {};
174
132
  var definitions = this.getDefinitions();
175
133
  var defaults = this.getDefaults();
134
+
176
135
  var _type;
177
136
  var _value;
178
137
  //var _init;
179
138
  if (defs) {
180
139
  for (p in defs) {
181
140
  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
- }
141
+ if (Kojac.FieldTypes.indexOf(pValue)>=0) { // pValue is field type
142
+ definitions[p] = pValue;
143
+ defaults[p] = null;
144
+ extender[p] = null;
145
+ } else {
146
+ var ft=Kojac.getPropertyValueType(pValue);
147
+ if (ft && (Kojac.SimpleTypes.indexOf(ft)>=0)) { // pValue is simple field value
148
+ definitions[p] = ft;
149
+ defaults[p] = pValue;
150
+ extender[p] = pValue;
151
+ } else { // pValue is something else
152
+ //definitions[p] = _type;
153
+ //defaults[p] = _value;
154
+ extender[p] = pValue;
200
155
  }
201
- // }
156
+ }
202
157
  }
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
158
  }
217
159
  var result = this._super(extender);
218
160
  result.setDefinitions(definitions);
@@ -236,13 +178,26 @@ Kojac.EmberModel.reopenClass({
236
178
  return this._defaults || {};
237
179
  },
238
180
 
181
+ __addDescs: function(aNewInstance){
182
+ var meta = Ember.meta(aNewInstance);
183
+ var defs = this.getDefinitions();
184
+ if (defs) {
185
+ for (p in defs) {
186
+ meta.descs[p] = new Kojac.EmberModel.TypedField(aNewInstance);
187
+ }
188
+ }
189
+ },
190
+
191
+
239
192
  __createWithMixins: Kojac.EmberModel.createWithMixins,
240
193
  createWithMixins: function() {
241
194
  var inputs = arguments;
242
195
  if (inputs.length) {
243
196
  inputs[0] = Kojac.readTypedProperties({},inputs[0],this.getDefinitions());
244
197
  }
245
- return this.__createWithMixins.apply(this,inputs);
198
+ var result = this.__createWithMixins.apply(this,inputs);
199
+ this.__addDescs(result);
200
+ return result;
246
201
  },
247
202
 
248
203
  __create: Kojac.EmberModel.create,
@@ -251,7 +206,9 @@ Kojac.EmberModel.reopenClass({
251
206
  if (inputs.length) {
252
207
  inputs[0] = Kojac.readTypedProperties({},inputs[0],this.getDefinitions());
253
208
  }
254
- return this.__create.apply(this,inputs);
209
+ var result = this.__create.apply(this,inputs);
210
+ this.__addDescs(result);
211
+ return result;
255
212
  }
256
213
 
257
214
  });
@@ -273,28 +230,40 @@ Kojac.EmberModel.reopen({
273
230
  return this.___setProperties(this,values);
274
231
  },
275
232
 
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;
233
+ // copy the property from source to dest
234
+ // this could be a static fn
235
+ toJsonoCopyFn: function(aDest,aSource,aProperty,aOptions) {
236
+ aDest[aProperty] = Kojac.Utils.toJsono(Ember.get(aSource,aProperty),aOptions);
237
+ },
238
+
239
+ // return array of names, or an object and all keys will be used
240
+ // this could be a static fn
241
+ toPropListFn: function(aSource,aOptions) {
242
+ if ("getDefinitions" in aSource.constructor)
243
+ return aSource.constructor.getDefinitions(); // return an object to use all keys from
244
+ else
245
+ return aSource; // this is a simple object, so use all keys
246
+ },
247
+
248
+ toJsono: function(aOptions) {
249
+ return Kojac.Utils.toJsono(this,aOptions,this.toPropListFn,this.toJsonoCopyFn)
250
+ // for (var p in defs)
251
+ // result[p] = this.get(p);
252
+ // if (includes) {
253
+ // includes = this.getProperties(includes);
254
+ // var v;
255
+ // for (var p in includes) {
256
+ // v = includes[p];
257
+ // if (v && (typeof(v)=="object") && ("toJsono" in v))
258
+ // includes[p] = v.toJsono();
259
+ // }
260
+ // _.extend(result,includes);
261
+ // }
262
+ // return result;
293
263
  }
294
264
 
295
265
  });
296
266
 
297
-
298
267
  Kojac.EmberCache = Ember.Object.extend({
299
268
 
300
269
  generateKey: function(aPrefix) {
@@ -315,28 +284,17 @@ Kojac.EmberCache = Ember.Object.extend({
315
284
  return id;
316
285
  },
317
286
 
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();
287
+ retrieve: function(k) {
288
+ return this.get(k);
329
289
  },
330
290
 
331
- cacheResults: function(aRequest) {
291
+ store: function(k,v) {
332
292
  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);
293
+ if (v===undefined) {
294
+ this.set(k,v);
295
+ delete this[k];
296
+ } else {
297
+ this.set(k,v);
340
298
  }
341
299
  this.endPropertyChanges();
342
300
  },
@@ -1,16 +1,20 @@
1
- class KojacController < ApplicationController
1
+ class KojacBaseController < ApplicationController
2
2
 
3
3
  respond_to :json
4
4
  protect_from_forgery :only => []
5
5
 
6
6
  protected
7
7
 
8
+ def kojac_current_user
9
+ current_user
10
+ end
11
+
8
12
  def controller_for_key(aKey)
9
13
  resource = aKey.split('__').first
10
14
  controller_name = resource.camelize+'Controller'
11
15
  if controller = controller_name.constantize
12
16
  result = controller.new
13
- result.current_user = self.current_user
17
+ result.current_user = self.kojac_current_user
14
18
  result
15
19
  else
16
20
  nil
@@ -46,25 +50,28 @@ class KojacController < ApplicationController
46
50
  begin
47
51
  input = params[:kojac]
48
52
  output = process_ops(input)
49
- status = :ok
50
53
  rescue => e
51
54
  raise e unless Rails.env.production?
52
55
  Rails.logger.debug e.message
53
56
  Rails.logger.debug e.backtrace.join("\n")
57
+ handle_exception(e) if respond_to? :handle_exception
54
58
  output = {
55
- results: nil,
56
- errors: [{
57
- message: e.message,
58
- backtrace: e.backtrace
59
- }]
59
+ error: {
60
+ format: 'KojacError',
61
+ kind: 'Exception',
62
+ errors: [{
63
+ message: e.message,
64
+ backtrace: e.backtrace
65
+ }]
66
+ }
60
67
  }
61
- status = :unprocessable_entity
62
68
  end
69
+ status = output[:error] ? :unprocessable_entity : :ok
63
70
  #output = ActiveModel::Serializer.new(output,current_user).to_json
64
71
  #sz = output.active_model_serializer.new(output)
65
72
  #jsons = sz.to_json(:scope => current_user, :root => false)
66
- jsons = app_serialize(output,current_user)
67
- render json: jsons, status: status
73
+ jsono = KojacUtils.to_jsono(output,scope: kojac_current_user)
74
+ render json: jsono, status: status
68
75
  end
69
76
 
70
77
  end