alchemymvc 1.4.0-alpha.1 → 1.4.0-alpha.11

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 (76) hide show
  1. package/lib/app/assets/stylesheets/{alchemy-info.less → alchemy-info.scss} +2 -0
  2. package/lib/app/behaviour/publishable_behaviour.js +1 -3
  3. package/lib/app/behaviour/revision_behaviour.js +118 -66
  4. package/lib/app/behaviour/sluggable_behaviour.js +8 -8
  5. package/lib/app/conduit/loopback_conduit.js +23 -8
  6. package/lib/app/datasource/mongo_datasource.js +360 -288
  7. package/lib/app/element/al_time.js +126 -0
  8. package/lib/app/helper/alchemy_helper.js +5 -3
  9. package/lib/app/helper/enum_values.js +44 -16
  10. package/lib/app/helper/router_helper.js +72 -25
  11. package/lib/app/helper/syncable.js +3 -0
  12. package/lib/app/helper_controller/controller.js +15 -4
  13. package/lib/app/helper_datasource/00-nosql_datasource.js +43 -4
  14. package/lib/app/helper_datasource/05-fallback_datasource.js +65 -83
  15. package/lib/app/helper_datasource/10-datasource_operational_context.js +153 -0
  16. package/lib/app/helper_datasource/idb_datasource.js +74 -57
  17. package/lib/app/helper_datasource/indexed_db.js +41 -33
  18. package/lib/app/helper_datasource/read_operational_context.js +179 -0
  19. package/lib/app/helper_datasource/remote_datasource.js +51 -24
  20. package/lib/app/helper_datasource/remove_operational_context.js +10 -0
  21. package/lib/app/helper_datasource/save_operational_context.js +194 -0
  22. package/lib/app/helper_datasource/schema_operational_context.js +109 -0
  23. package/lib/app/helper_field/00-objectid_field.js +1 -3
  24. package/lib/app/helper_field/11-date_field.js +13 -16
  25. package/lib/app/helper_field/15-local_temporal_field.js +13 -16
  26. package/lib/app/helper_field/20-decimal_field.js +12 -15
  27. package/lib/app/helper_field/30-meta_field.js +27 -0
  28. package/lib/app/helper_field/40-foreign_key_field.js +39 -0
  29. package/lib/app/helper_field/association_alias_field.js +12 -0
  30. package/lib/app/helper_field/belongsto_field.js +2 -2
  31. package/lib/app/helper_field/big_int_field.js +13 -16
  32. package/lib/app/helper_field/datetime_field.js +6 -9
  33. package/lib/app/helper_field/enum_field.js +13 -0
  34. package/lib/app/helper_field/habtm_field.js +1 -5
  35. package/lib/app/helper_field/hasoneparent_field.js +2 -6
  36. package/lib/app/helper_field/mixed_field.js +13 -14
  37. package/lib/app/helper_field/object_field.js +6 -7
  38. package/lib/app/helper_field/password_field.js +12 -9
  39. package/lib/app/helper_field/schema_field.js +322 -197
  40. package/lib/app/helper_field/settings_field.js +6 -6
  41. package/lib/app/helper_model/00-base_criteria.js +67 -6
  42. package/lib/app/helper_model/05-criteria_expressions.js +23 -4
  43. package/lib/app/helper_model/10-model_criteria.js +5 -3
  44. package/lib/app/helper_model/document.js +29 -27
  45. package/lib/app/helper_model/model.js +186 -25
  46. package/lib/app/model/system_setting_model.js +10 -2
  47. package/lib/app/model/system_task_model.js +13 -4
  48. package/lib/bootstrap.js +5 -0
  49. package/lib/class/behaviour.js +1 -25
  50. package/lib/class/conduit.js +15 -4
  51. package/lib/class/datasource.js +119 -272
  52. package/lib/class/element.js +16 -0
  53. package/lib/class/field.js +97 -96
  54. package/lib/class/inode_file.js +1 -1
  55. package/lib/class/model.js +55 -63
  56. package/lib/class/operational_context.js +146 -0
  57. package/lib/class/route.js +6 -7
  58. package/lib/class/router.js +2 -2
  59. package/lib/class/schema.js +3 -21
  60. package/lib/class/schema_client.js +83 -22
  61. package/lib/class/task_service.js +3 -3
  62. package/lib/core/alchemy.js +143 -50
  63. package/lib/core/alchemy_functions.js +68 -3
  64. package/lib/core/alchemy_load_functions.js +37 -5
  65. package/lib/core/base.js +4 -4
  66. package/lib/core/client_alchemy.js +19 -5
  67. package/lib/core/middleware.js +451 -64
  68. package/lib/core/setting.js +114 -4
  69. package/lib/core/stage.js +6 -0
  70. package/lib/scripts/create_settings.js +24 -0
  71. package/lib/scripts/create_shared_constants.js +36 -11
  72. package/lib/scripts/preload_modules.js +0 -1
  73. package/lib/scripts/setup_devwatch.js +0 -0
  74. package/lib/stages/00-load_core.js +14 -1
  75. package/lib/stages/50-routes.js +2 -0
  76. package/package.json +21 -20
@@ -1,3 +1,5 @@
1
+ @use "alchemy";
2
+
1
3
  html {
2
4
  font-family: sans-serif;
3
5
  text-size-adjust: 100%;
@@ -8,9 +8,7 @@
8
8
  * @since 0.1.0
9
9
  * @version 0.3.0
10
10
  */
11
- var Publish = Function.inherits('Alchemy.Behaviour', function PublishableBehaviour(model, options) {
12
- PublishableBehaviour.super.call(this, model, options);
13
- });
11
+ var Publish = Function.inherits('Alchemy.Behaviour', 'PublishableBehaviour');
14
12
 
15
13
  /**
16
14
  * Listen to attachments to schema's
@@ -12,26 +12,49 @@ var jsondiffpatch = alchemy.use('jsondiffpatch'),
12
12
  * @since 0.0.1
13
13
  * @version 0.2.0
14
14
  */
15
- var Revision = Function.inherits('Alchemy.Behaviour', function RevisionBehaviour(model, options) {
16
- Behaviour.call(this, model, options);
17
- });
15
+ const Revision = Function.inherits('Alchemy.Behaviour', 'RevisionBehaviour');
18
16
 
19
17
  /**
20
- * Get the Revision Model class for the attached model
18
+ * Get the Revision model class for the given main model
21
19
  *
22
20
  * @author Jelle De Loecker <jelle@elevenways.be>
23
- * @since 1.0.3
24
- * @version 1.1.0
21
+ * @since 1.4.0
22
+ * @version 1.4.0
23
+ *
24
+ * @param {Schema} schema
25
+ * @param {Object} options
25
26
  */
26
- Revision.setProperty(function revision_model_class() {
27
+ Revision.setStatic(function getRevisionModel(model) {
27
28
 
28
- var class_name = this.model.name + 'DataRevision';
29
+ if (typeof model == 'function') {
30
+ model = model.model_name;
31
+ }
29
32
 
30
- if (Classes.Alchemy.Model[class_name]) {
31
- return Classes.Alchemy.Model[class_name];
33
+ if (typeof model == 'string') {
34
+ model = Model.get(model);
32
35
  }
33
36
 
34
- let model_class = Function.inherits('Alchemy.Model', Function.create(class_name, function DataRevision(options) {
37
+ if (!model) {
38
+ throw new Error('Unable to add Revision behaviour to undefined model');
39
+ }
40
+
41
+ let revision_model_name = model.model_name + 'DataRevision',
42
+ revision_model;
43
+
44
+ try {
45
+ revision_model = Model.get(revision_model_name, false);
46
+ } catch (err) {
47
+ // Ignore
48
+ }
49
+
50
+ if (revision_model) {
51
+ return revision_model;
52
+ }
53
+
54
+ let namespace = model.constructor.namespace,
55
+ class_name = model.name + 'DataRevision';
56
+
57
+ let model_class = Function.inherits('Alchemy.Model', namespace, Function.create(class_name, function DataRevision(options) {
35
58
  Model.call(this, options);
36
59
  }));
37
60
 
@@ -42,12 +65,18 @@ Revision.setProperty(function revision_model_class() {
42
65
  this.schema.remove('updated');
43
66
 
44
67
  this.addField('record_id', 'ObjectId');
45
- this.addField('revision', 'Number');
68
+ this.addField('revision', 'Integer');
46
69
  this.addField('delta', 'Object');
47
70
 
48
71
  if (Classes.Alchemy.Model.User) {
49
72
  this.belongsTo('User');
50
73
  }
74
+
75
+ // Add an index on the record_id
76
+ this.addIndex('record_id', {
77
+ unique : false,
78
+ sparse : false,
79
+ });
51
80
  });
52
81
 
53
82
  // Force the constitutors to load now
@@ -56,6 +85,39 @@ Revision.setProperty(function revision_model_class() {
56
85
  return model_class;
57
86
  });
58
87
 
88
+ /**
89
+ * Listen to attachments to schema's
90
+ *
91
+ * @author Jelle De Loecker <jelle@elevenways.be>
92
+ * @since 1.0.3
93
+ * @version 1.4.0
94
+ *
95
+ * @param {Schema} schema
96
+ * @param {Object} options
97
+ */
98
+ Revision.setStatic(function attached(schema, new_options) {
99
+
100
+ const context = schema.model_class;
101
+
102
+ // Add the revision number to the main model
103
+ context.addField('__r', 'Integer', {
104
+ title: 'Revision',
105
+ });
106
+
107
+ Revision.getRevisionModel(schema.model_class);
108
+ });
109
+
110
+ /**
111
+ * Get the Revision Model class for the attached model
112
+ *
113
+ * @author Jelle De Loecker <jelle@elevenways.be>
114
+ * @since 1.0.3
115
+ * @version 1.4.0
116
+ */
117
+ Revision.setProperty(function revision_model_class() {
118
+ return Revision.getRevisionModel(this.model);
119
+ });
120
+
59
121
  /**
60
122
  * Get the revision model for the attached model
61
123
  *
@@ -122,24 +184,6 @@ Revision.setProperty(function diff_patcher() {
122
184
  return diff_patch_instance;
123
185
  });
124
186
 
125
- /**
126
- * Listen to attachments to schema's
127
- *
128
- * @author Jelle De Loecker <jelle@elevenways.be>
129
- * @since 1.0.3
130
- * @version 1.0.3
131
- *
132
- * @param {Schema} schema
133
- * @param {Object} options
134
- */
135
- Revision.setStatic(function attached(schema, new_options) {
136
-
137
- var context = schema.model_class;
138
-
139
- // Add the revision
140
- context.addField('__r', 'Number', {title: 'Revision'});
141
- });
142
-
143
187
  /**
144
188
  * Compare 2 objects
145
189
  *
@@ -157,14 +201,14 @@ Revision.setMethod(function compare(left, right) {
157
201
  *
158
202
  * @author Jelle De Loecker <jelle@elevenways.be>
159
203
  * @since 0.0.1
160
- * @version 1.0.5
204
+ * @version 1.4.0
161
205
  */
162
206
  Revision.setMethod(function beforeSave(record, options, creating) {
163
207
 
164
- let main = record[this.model.name];
208
+ let main = record.$main;
165
209
 
166
210
  if (!main) {
167
- throw new Error('Unable to find main "' + this.model.name + '" data');
211
+ throw new Error('Unable to find main "' + this.model.model_name + '" data');
168
212
  }
169
213
 
170
214
  // No revision to save when creating a record
@@ -173,32 +217,42 @@ Revision.setMethod(function beforeSave(record, options, creating) {
173
217
  return;
174
218
  }
175
219
 
176
- let that = this,
177
- next = this.wait('series');
178
-
179
- // Find the original record
180
- Model.get(that.model.name).findById(main._id, async function gotRecord(err, result) {
220
+ let that = this;
181
221
 
182
- var ori;
222
+ let pledge = new Swift();
183
223
 
184
- if (result) {
224
+ // Find the original record
225
+ Model.get(this.model.model_name).findById(record.$pk, function gotRecord(err, result) {
185
226
 
186
- // Get the original data
187
- ori = await that.model.convertRecordToDatasourceFormat(result);
227
+ if (err || !result) {
228
+ return pledge.resolve();
229
+ }
188
230
 
189
- // Store the original data in a weakmap for later
190
- revision_before.set(options, ori);
231
+ let conversion;
191
232
 
192
- // Increase the revision count by 1
193
- if (ori.__r) {
194
- main.__r = ori.__r+1;
195
- } else {
196
- main.__r = 1;
197
- }
233
+ try {
234
+ conversion = that.model.convertRecordToDatasourceFormat(result);
235
+ } catch (err) {
236
+ return pledge.reject(err);
198
237
  }
199
238
 
200
- next();
239
+ pledge.resolve(Swift.waterfall(
240
+ conversion,
241
+ ori => {
242
+ // Store the original data in a weakmap for later
243
+ revision_before.set(options, ori);
244
+
245
+ // Increase the revision count by 1
246
+ if (ori.__r) {
247
+ main.__r = ori.__r+1;
248
+ } else {
249
+ main.__r = 1;
250
+ }
251
+ }
252
+ ));
201
253
  });
254
+
255
+ return pledge;
202
256
  });
203
257
 
204
258
  /**
@@ -206,17 +260,11 @@ Revision.setMethod(function beforeSave(record, options, creating) {
206
260
  *
207
261
  * @author Jelle De Loecker <jelle@elevenways.be>
208
262
  * @since 0.0.1
209
- * @version 1.1.0
263
+ * @version 1.4.0
210
264
  */
211
265
  Revision.setMethod(function afterSave(record, options, created) {
212
266
 
213
- var that = this,
214
- earlier_data,
215
- right,
216
- main,
217
- that,
218
- left,
219
- next;
267
+ let earlier_data;
220
268
 
221
269
  if (created) {
222
270
  earlier_data = {};
@@ -229,11 +277,13 @@ Revision.setMethod(function afterSave(record, options, created) {
229
277
  return;
230
278
  }
231
279
 
232
- next = this.wait();
233
- main = record[that.model.name] || record;
280
+ let doc = this.model.createDocument(record);
281
+ const that = this;
282
+
283
+ let pledge = new Swift();
234
284
 
235
285
  // Find the complete saved item
236
- Model.get(that.model.name).findById(main._id, async function gotRecord(err, result) {
286
+ Model.get(this.model.model_name).findByPk(doc.$pk, async function gotRecord(err, result) {
237
287
 
238
288
  if (result) {
239
289
 
@@ -243,8 +293,8 @@ Revision.setMethod(function afterSave(record, options, created) {
243
293
  if (new_data) {
244
294
 
245
295
  // Convert the objects so they can be diffed properly
246
- left = JSON.toDryObject(earlier_data);
247
- right = JSON.toDryObject(new_data);
296
+ let left = JSON.toDryObject(earlier_data),
297
+ right = JSON.toDryObject(new_data);
248
298
 
249
299
  // Diff them
250
300
  let delta = that.compare(left, right);
@@ -267,7 +317,7 @@ Revision.setMethod(function afterSave(record, options, created) {
267
317
 
268
318
  // Add the delta information
269
319
  revision_data = {
270
- [that.revision_model.name] : revision_data
320
+ [that.revision_model.model_name] : revision_data
271
321
  };
272
322
 
273
323
  // Save the data
@@ -275,6 +325,8 @@ Revision.setMethod(function afterSave(record, options, created) {
275
325
  }
276
326
  }
277
327
 
278
- next();
328
+ pledge.resolve();
279
329
  });
330
+
331
+ return pledge;
280
332
  });
@@ -153,8 +153,7 @@ Sluggable.setMethod(async function beforeSave(data, options, creating) {
153
153
  has_new_value,
154
154
  old_record,
155
155
  new_value,
156
- old_value,
157
- next;
156
+ old_value;
158
157
 
159
158
  // Get the actual record data
160
159
  if (data[that.model.name]) {
@@ -170,9 +169,6 @@ Sluggable.setMethod(async function beforeSave(data, options, creating) {
170
169
  has_new_value = true;
171
170
  }
172
171
 
173
- // Let other event callbacks wait for this one
174
- next = this.wait('series');
175
-
176
172
  if (!creating) {
177
173
  old_record = await this.model.findById(data._id);
178
174
 
@@ -186,7 +182,7 @@ Sluggable.setMethod(async function beforeSave(data, options, creating) {
186
182
  // and we're not explicitly setting a new slug value,
187
183
  // then do nothing
188
184
  if (!creating && old_value && !has_new_value) {
189
- return next();
185
+ return;
190
186
  }
191
187
 
192
188
  let new_data = {};
@@ -198,19 +194,23 @@ Sluggable.setMethod(async function beforeSave(data, options, creating) {
198
194
  Object.assign(new_data, data);
199
195
  }
200
196
 
197
+ let pledge = new Swift();
198
+
201
199
  // Try creating a new slug
202
200
  that.createSlug(new_data, new_value, function createdSlug(err, result) {
203
201
 
204
202
  if (err) {
205
- return next(err);
203
+ return pledge.reject(err);
206
204
  }
207
205
 
208
206
  if (!Object.isEmpty(result)) {
209
207
  data[that.target_field.name] = result;
210
208
  }
211
209
 
212
- next();
210
+ pledge.resolve();
213
211
  });
212
+
213
+ return pledge;
214
214
  });
215
215
 
216
216
  /**
@@ -89,7 +89,7 @@ LoopConduit.setMethod(function copyParentProperties(conduit) {
89
89
  *
90
90
  * @author Jelle De Loecker <jelle@elevenways.be>
91
91
  * @since 1.1.3
92
- * @version 1.3.0
92
+ * @version 1.4.0
93
93
  *
94
94
  * @param {Object} options
95
95
  * @param {Function} callback
@@ -108,17 +108,20 @@ LoopConduit.setMethod(function setOptions(options, callback) {
108
108
  options = JSON.clone(options);
109
109
 
110
110
  for (key in options) {
111
- let set_method = false;
111
+
112
+ // Keep track if a request method has been set
113
+ let got_method = false;
112
114
 
113
115
  info = Classes.Develry.Request.getMethodInfo(key);
114
116
 
115
117
  if (info && options[key]) {
116
118
 
117
- if (info.method == 'get' && set_method) {
118
- // Ignore
119
+ if (info.method == 'get' && got_method) {
120
+ // If a request method has already been set,
121
+ // do not let `get` options overwrite it!
119
122
  } else {
120
123
  this.method = key;
121
- set_method = true;
124
+ got_method = true;
122
125
  }
123
126
 
124
127
  if (info.has_body && typeof options[key] == 'object') {
@@ -131,6 +134,8 @@ LoopConduit.setMethod(function setOptions(options, callback) {
131
134
  this.method = options.method;
132
135
  }
133
136
 
137
+ let route_params = options.params;
138
+
134
139
  if (options.name) {
135
140
  // @TODO: what about path sections?
136
141
  route = this.getRouteByName(options.name);
@@ -147,7 +152,7 @@ LoopConduit.setMethod(function setOptions(options, callback) {
147
152
 
148
153
  // @WARNING: It's best to just generate the URL
149
154
  // and let it parse all the information that way
150
- options.href = this.routeUrl(options.name, options.params, {extra_get_parameters: false});
155
+ options.href = this.routeUrl(options.name, route_params, {extra_get_parameters: false});
151
156
  }
152
157
 
153
158
  if (!this.method) {
@@ -158,6 +163,10 @@ LoopConduit.setMethod(function setOptions(options, callback) {
158
163
 
159
164
  this.original_url = RURL.parse(options.href);
160
165
 
166
+ if (options.get && typeof options.get == 'object') {
167
+ this.original_url.addQuery(options.get);
168
+ }
169
+
161
170
  this.url = this.original_url;
162
171
  this.original_path = this.url.path;
163
172
  this.original_pathname = this.url.pathname;
@@ -169,14 +178,20 @@ LoopConduit.setMethod(function setOptions(options, callback) {
169
178
  this.parseSection();
170
179
 
171
180
  promise = this.parseRoute();
181
+ } else if (options.get && typeof options.get == 'object') {
182
+ if (!this.params) {
183
+ this.params = {};
184
+ }
185
+
186
+ Object.assign(this.params, options.get);
172
187
  }
173
188
 
174
189
  if (options.body) {
175
190
  this.body = options.body;
176
191
  }
177
192
 
178
- if (options.params) {
179
- this.setRouteParameters(options.params);
193
+ if (route_params) {
194
+ this.setRouteParameters(route_params);
180
195
  }
181
196
 
182
197
  if (options.arguments) {