alchemymvc 1.2.8 → 1.3.1

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 (45) hide show
  1. package/lib/app/behaviour/sluggable_behaviour.js +4 -2
  2. package/lib/app/conduit/http_conduit.js +7 -2
  3. package/lib/app/conduit/loopback_conduit.js +2 -2
  4. package/lib/app/conduit/socket_conduit.js +20 -5
  5. package/lib/app/controller/alchemy_info_controller.js +4 -8
  6. package/lib/app/helper/backed_map.js +2 -2
  7. package/lib/app/helper/router_helper.js +98 -24
  8. package/lib/app/helper_controller/controller.js +45 -30
  9. package/lib/app/helper_datasource/00-nosql_datasource.js +44 -10
  10. package/lib/app/helper_field/enum_field.js +4 -4
  11. package/lib/app/helper_field/schema_field.js +50 -36
  12. package/lib/app/helper_model/document.js +81 -46
  13. package/lib/app/helper_model/field_set.js +11 -0
  14. package/lib/app/helper_model/model.js +107 -53
  15. package/lib/app/helper_validator/00_validator.js +38 -6
  16. package/lib/app/helper_validator/not_empty_validator.js +1 -3
  17. package/lib/app/routes.js +7 -1
  18. package/lib/bootstrap.js +1 -0
  19. package/lib/class/conduit.js +438 -290
  20. package/lib/class/controller.js +18 -15
  21. package/lib/class/datasource.js +19 -8
  22. package/lib/class/document.js +3 -3
  23. package/lib/class/field.js +34 -3
  24. package/lib/class/inode.js +27 -0
  25. package/lib/class/inode_file.js +204 -4
  26. package/lib/class/migration.js +2 -1
  27. package/lib/class/model.js +16 -5
  28. package/lib/class/path_definition.js +76 -120
  29. package/lib/class/path_param_definition.js +202 -0
  30. package/lib/class/postponement.js +573 -0
  31. package/lib/class/route.js +193 -33
  32. package/lib/class/router.js +22 -4
  33. package/lib/class/schema.js +47 -11
  34. package/lib/class/schema_client.js +65 -35
  35. package/lib/class/session.js +138 -12
  36. package/lib/class/sitemap.js +341 -0
  37. package/lib/core/base.js +13 -3
  38. package/lib/core/client_alchemy.js +78 -7
  39. package/lib/core/client_base.js +16 -10
  40. package/lib/core/middleware.js +56 -45
  41. package/lib/init/alchemy.js +124 -11
  42. package/lib/init/constants.js +11 -0
  43. package/lib/init/functions.js +163 -86
  44. package/lib/stages.js +18 -3
  45. package/package.json +6 -6
@@ -1,21 +1,16 @@
1
- var fallback_datasource,
2
- model_info,
1
+ let class_cache = new Map(),
2
+ fallback_datasource,
3
3
  TABLE = Symbol('table');
4
4
 
5
5
  if (Blast.isBrowser) {
6
6
  Blast.once('hawkejs_init', function gotScene(hawkejs, variables, settings, view) {
7
7
 
8
- var ModelClass,
9
- model_name,
10
- config,
11
- key;
12
-
13
- model_info = hawkejs.scene.exposed.model_info;
8
+ let config;
14
9
 
15
- for (model_name in model_info) {
16
- config = model_info[model_name];
17
- ModelClass = Model.getClass(model_name);
18
- config.schema.setModel(model_name);
10
+ for (config of hawkejs.scene.exposed.model_info) {
11
+ // First get or create the client-side Model class,
12
+ // then set some extra configuration coming from the server-side
13
+ Model.getClass(config.name).setModelConfig(config);
19
14
  }
20
15
  });
21
16
  }
@@ -90,6 +85,30 @@ Model.setStatic(function hasServerAction(action) {
90
85
  return !!config;
91
86
  });
92
87
 
88
+ /**
89
+ * Set the Model configuration
90
+ *
91
+ * @author Jelle De Loecker <jelle@elevenways.be>
92
+ * @since 1.3.1
93
+ * @version 1.3.1
94
+ *
95
+ * @param {Object} config
96
+ */
97
+ Model.setStatic(function setModelConfig(config) {
98
+
99
+ let model_name = this.model_name;
100
+
101
+ config.schema.setModel(model_name);
102
+
103
+ if (!this.prototype.hasOwnProperty('primary_key')) {
104
+ this.setProperty('primary_key', config.primary_key);
105
+ }
106
+
107
+ if (!this.prototype.hasOwnProperty('display_field')) {
108
+ this.setProperty('display_field', config.display_field);
109
+ }
110
+ });
111
+
93
112
  /**
94
113
  * Table name to use in the database.
95
114
  * False if no table should be used.
@@ -142,6 +161,19 @@ Model.setProperty(function datasource() {
142
161
  return fallback_datasource;
143
162
  });
144
163
 
164
+ /**
165
+ * Does this model have translatable fields?
166
+ *
167
+ * @author Jelle De Loecker <jelle@elevenways.be>
168
+ * @since 1.3.0
169
+ * @version 1.3.0
170
+ *
171
+ * @type {Boolean}
172
+ */
173
+ Model.setProperty(function has_translatable_fields() {
174
+ return this.schema.has_translatable_fields;
175
+ });
176
+
145
177
  /**
146
178
  * The conduit
147
179
  *
@@ -188,50 +220,43 @@ Model.prepareStaticProperty('Document', function getDocumentClass() {
188
220
  *
189
221
  * @author Jelle De Loecker <jelle@develry.be>
190
222
  * @since 1.0.0
191
- * @version 1.2.7
223
+ * @version 1.3.1
192
224
  *
193
225
  * @param {String} model_name
194
226
  * @param {Boolean} allow_create
227
+ * @param {String} parent
195
228
  */
196
- Model.setStatic(function getClass(model_name, allow_create ) {
197
-
198
- var model_constructor,
199
- parent_path,
200
- class_name,
201
- class_path,
202
- ModelClass,
203
- config,
204
- key;
229
+ Model.setStatic(function getClass(model_name, allow_create, parent) {
230
+
231
+ if (class_cache.has(model_name)) {
232
+ return class_cache.get(model_name);
233
+ }
205
234
 
206
235
  // Construct the name of the class
207
- class_name = model_name;
236
+ let class_name = model_name;
208
237
 
209
238
  // Construct the path to this class
210
- class_path = 'Alchemy.Client.Model.' + class_name;
239
+ let class_path = 'Alchemy.Client.Model.' + class_name;
211
240
 
212
241
  // Get the class
213
- ModelClass = Object.path(Blast.Classes, class_path);
242
+ let ModelClass = Object.path(Blast.Classes, class_path);
214
243
 
215
244
  if (allow_create == null) {
216
245
  allow_create = true;
217
246
  }
218
247
 
219
248
  if (ModelClass == null && allow_create) {
220
- model_constructor = Function.create(class_name, function ModelConstructor(record, options) {
249
+ let config;
250
+
251
+ let model_constructor = Function.create(class_name, function ModelConstructor(record, options) {
221
252
  ModelConstructor.wrapper.super.call(this, record, options);
222
253
  });
223
254
 
224
255
  // @TODO: inherit from parents
225
- parent_path = 'Alchemy.Client.Model';
226
-
227
- if (Blast.isBrowser && window._hawkejs_static_expose) {
228
- let exposed = window._hawkejs_static_expose;
229
-
230
- config = exposed.model_info;
256
+ let parent_path = 'Alchemy.Client.Model';
231
257
 
232
- if (config) {
233
- config = config[model_name];
234
- }
258
+ if (Blast.isBrowser) {
259
+ // No longer needed
235
260
  } else if (Blast.isNode) {
236
261
  config = alchemy.getModel(model_name, false);
237
262
 
@@ -245,8 +270,12 @@ Model.setStatic(function getClass(model_name, allow_create ) {
245
270
  }
246
271
 
247
272
  if (config && config.parent) {
248
- getClass(config.parent);
249
- parent_path += '.' + config.parent;
273
+ parent = config.parent;
274
+ }
275
+
276
+ if (parent) {
277
+ getClass(parent);
278
+ parent_path += '.' + parent;
250
279
  }
251
280
 
252
281
  ModelClass = Function.inherits(parent_path, model_constructor);
@@ -268,6 +297,8 @@ Model.setStatic(function getClass(model_name, allow_create ) {
268
297
  }
269
298
  }
270
299
 
300
+ class_cache.set(model_name, ModelClass);
301
+
271
302
  return ModelClass;
272
303
  });
273
304
 
@@ -349,7 +380,7 @@ Model.setProperty(function name() {
349
380
  *
350
381
  * @author Jelle De Loecker <jelle@develry.be>
351
382
  * @since 1.1.0
352
- * @version 1.1.0
383
+ * @version 1.3.0
353
384
  *
354
385
  * @param {Object} options
355
386
  */
@@ -371,7 +402,7 @@ Model.setMethod(function init(options) {
371
402
  }
372
403
 
373
404
  // Initialize behaviours
374
- if (this.schema && this.schema.hasBehaviours && this.initBehaviours) {
405
+ if (this.schema && this.schema.has_behaviours && this.initBehaviours) {
375
406
  this.initBehaviours();
376
407
  }
377
408
  });
@@ -568,7 +599,7 @@ Model.setMethod(function getAliasModel(alias) {
568
599
  *
569
600
  * @author Jelle De Loecker <jelle@develry.be>
570
601
  * @since 0.0.1
571
- * @version 1.2.5
602
+ * @version 1.3.0
572
603
  *
573
604
  * @param {String} type The type of find (first, all)
574
605
  * @param {Criteria} criteria The criteria object
@@ -626,10 +657,17 @@ Model.setMethod(function find(type, criteria, callback) {
626
657
  return pledge;
627
658
  }
628
659
 
660
+ if (this.conduit) {
661
+ criteria.conduit = this.conduit;
662
+ }
663
+
664
+ if (!criteria.options.locale && this.conduit) {
665
+ criteria.setOption('locale', this.conduit.prefix);
666
+ }
667
+
629
668
  let that = this,
630
669
  available,
631
670
  records,
632
- query,
633
671
  Type;
634
672
 
635
673
  // The criteria instance has to know about the model
@@ -1310,8 +1348,7 @@ Model.setMethod(function save(data, _options, _callback) {
1310
1348
  return iter.hasNext();
1311
1349
  }, function saveData(next) {
1312
1350
 
1313
- var document = iter.next().value,
1314
- temp;
1351
+ var document = iter.next().value;
1315
1352
 
1316
1353
  // Skip invalid items
1317
1354
  if (!document) {
@@ -1360,9 +1397,9 @@ Model.setMethod(function save(data, _options, _callback) {
1360
1397
  * Create a new document.
1361
1398
  * If data is given, the document is populated
1362
1399
  *
1363
- * @author Jelle De Loecker <jelle@develry.be>
1400
+ * @author Jelle De Loecker <jelle@elevenways.be>
1364
1401
  * @since 0.2.0
1365
- * @version 1.1.0
1402
+ * @version 1.3.0
1366
1403
  *
1367
1404
  * @param {Object} data Optional data
1368
1405
  * @param {Object} options
@@ -1371,13 +1408,15 @@ Model.setMethod(function save(data, _options, _callback) {
1371
1408
  */
1372
1409
  Model.setMethod(function createDocument(data, options) {
1373
1410
 
1374
- var doc;
1411
+ let original_record;
1375
1412
 
1376
1413
  if (!options) {
1377
1414
  options = {};
1378
1415
  }
1379
1416
 
1380
1417
  if (data && this.constructor.Document.isDocument(data)) {
1418
+ original_record = data.$attributes.original_record;
1419
+
1381
1420
  data = {
1382
1421
  [data.$model_alias] : data.$main,
1383
1422
  };
@@ -1385,7 +1424,11 @@ Model.setMethod(function createDocument(data, options) {
1385
1424
 
1386
1425
  options.model = this;
1387
1426
 
1388
- doc = new this.constructor.Document(data, options);
1427
+ let doc = new this.constructor.Document(data, options);
1428
+
1429
+ if (original_record) {
1430
+ doc.$attributes.original_record = original_record;
1431
+ }
1389
1432
 
1390
1433
  return doc;
1391
1434
  });
@@ -1477,11 +1520,14 @@ Model.setProperty(function model_info() {
1477
1520
  return {};
1478
1521
  }
1479
1522
 
1480
- if (!hawkejs.scene.exposed.model_info[name]) {
1481
- hawkejs.scene.exposed.model_info[name] = {};
1482
- }
1523
+ let config;
1483
1524
 
1484
- data = hawkejs.scene.exposed.model_info[name];
1525
+ for (config of hawkejs.scene.exposed.model_info) {
1526
+ if (config.name === name) {
1527
+ data = config;
1528
+ break;
1529
+ }
1530
+ }
1485
1531
  } else {
1486
1532
 
1487
1533
  let MainClass = Blast.Classes.Alchemy.Model[name];
@@ -1699,7 +1745,7 @@ Model.setMethod(function compose(data, options) {
1699
1745
  *
1700
1746
  * @author Jelle De Loecker <jelle@develry.be>
1701
1747
  * @since 0.2.0
1702
- * @version 1.1.1
1748
+ * @version 1.3.1
1703
1749
  *
1704
1750
  * @param {Object} data The record data to check
1705
1751
  * @param {Object} options
@@ -1725,6 +1771,10 @@ Model.setMethod(function createRecord(data, options, callback) {
1725
1771
  return callback(err);
1726
1772
  }
1727
1773
 
1774
+ if (!that.datasource) {
1775
+ return callback(new Error('Model "' + that.model_name + '" has no datasource'));
1776
+ }
1777
+
1728
1778
  that.datasource.create(that, data, options, function afterCreate(err, result) {
1729
1779
 
1730
1780
  if (err != null) {
@@ -1743,7 +1793,7 @@ Model.setMethod(function createRecord(data, options, callback) {
1743
1793
  *
1744
1794
  * @author Jelle De Loecker <jelle@develry.be>
1745
1795
  * @since 0.2.0
1746
- * @version 1.1.1
1796
+ * @version 1.3.1
1747
1797
  *
1748
1798
  * @param {Object} data The record data to check
1749
1799
  * @param {Object} options
@@ -1770,6 +1820,10 @@ Model.setMethod(function updateRecord(data, options, callback) {
1770
1820
  return callback(err);
1771
1821
  }
1772
1822
 
1823
+ if (!that.datasource) {
1824
+ return callback(new Error('Model "' + that.model_name + '" has no datasource'));
1825
+ }
1826
+
1773
1827
  that.datasource.update(that, data, options, function afterUpdate(err, result) {
1774
1828
 
1775
1829
  if (err != null) {
@@ -60,13 +60,13 @@ Validator.setMethod(function toDry() {
60
60
  *
61
61
  * @author Jelle De Loecker <jelle@elevenways.be>
62
62
  * @since 1.1.0
63
- * @version 1.1.0
63
+ * @version 1.3.1
64
64
  *
65
65
  * @param {FieldValue} fv The FieldValue instance
66
66
  *
67
67
  * @return {String}
68
68
  */
69
- Validator.setMethod(function getInvalidFieldMessage(fv) {
69
+ Validator.setTypedMethod([Types.Alchemy.FieldValue], function getInvalidFieldMessage(fv) {
70
70
 
71
71
  let field = fv.field,
72
72
  value = fv.value;
@@ -105,20 +105,52 @@ Validator.setMethod(function createFieldViolation(fv) {
105
105
  return result;
106
106
  });
107
107
 
108
+ /**
109
+ * Validate a value
110
+ *
111
+ * @author Jelle De Loecker <jelle@elevenways.be>
112
+ * @since 1.3.1
113
+ * @version 1.3.1
114
+ *
115
+ * @param {Field} field
116
+ * @param {*} value
117
+ *
118
+ * @return {Promise<Violation|undefined>}
119
+ */
120
+ Validator.setTypedMethod([Types.Alchemy.Field, Types.Any], function validateFieldValue(field, value) {
121
+ return this.validateFieldValue(field, field.path, value);
122
+ });
123
+
124
+ /**
125
+ * Validate a value
126
+ *
127
+ * @author Jelle De Loecker <jelle@elevenways.be>
128
+ * @since 1.3.1
129
+ * @version 1.3.1
130
+ *
131
+ * @param {Field} field
132
+ * @param {String} path
133
+ * @param {*} value
134
+ *
135
+ * @return {Promise<Violation|undefined>}
136
+ */
137
+ Validator.setTypedMethod([Types.Alchemy.Field, Types.String, Types.Any], function validateFieldValue(field, path, value) {
138
+ let fv = new Classes.Alchemy.FieldValue(field, path, value);
139
+ return this.validateFieldValue(fv);
140
+ });
141
+
108
142
  /**
109
143
  * Validate a value
110
144
  *
111
145
  * @author Jelle De Loecker <jelle@elevenways.be>
112
146
  * @since 1.1.0
113
- * @version 1.1.4
147
+ * @version 1.3.1
114
148
  *
115
149
  * @param {FieldValue[]} field_values The FieldValue instance(s)
116
150
  *
117
151
  * @return {Promise<Violation|undefined>}
118
152
  */
119
- Validator.setMethod(async function validateFieldValue(field_values) {
120
-
121
- field_values = Array.cast(field_values);
153
+ Validator.setTypedMethod([Types.Alchemy.FieldValue.array()], async function validateFieldValue(field_values) {
122
154
 
123
155
  let violation,
124
156
  passes,
@@ -10,9 +10,7 @@
10
10
  *
11
11
  * @param {Object} options
12
12
  */
13
- var NotEmpty = Function.inherits('Alchemy.Validator', function NotEmpty(options) {
14
- NotEmpty.super.call(this, options);
15
- });
13
+ const NotEmpty = Function.inherits('Alchemy.Validator', 'NotEmpty');
16
14
 
17
15
  /**
18
16
  * The message to use inside errors
package/lib/app/routes.js CHANGED
@@ -1,3 +1,9 @@
1
1
  Router.add(['get', 'post'], 'APIResource', '/api/{action}', 'Api#{action}');
2
2
  Router.get('AlchemyInfo', '/alchemy-info', 'AlchemyInfo#info');
3
- Router.get('Postponed', '/alchemy/postponed/{id}', 'AlchemyInfo#postponed');
3
+
4
+ Router.POSTPONED_ROUTE = Router.add({
5
+ name : 'AlchemyInfo#postponed',
6
+ paths : '/alchemy/postponed/{id}',
7
+ methods : ['get'],
8
+ can_be_postponed : false,
9
+ });
package/lib/bootstrap.js CHANGED
@@ -160,6 +160,7 @@ hawkejs_options.server = false;
160
160
  * @version 1.1.0
161
161
  */
162
162
  alchemy.hawkejs.load(libpath.resolve(PATH_CORE, 'class', 'path_definition.js'), hawkejs_options);
163
+ alchemy.hawkejs.load(libpath.resolve(PATH_CORE, 'class', 'path_param_definition.js'), hawkejs_options);
163
164
 
164
165
  /**
165
166
  * Require the element class on the client side too