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
@@ -69,7 +69,7 @@ SchemaField.setProperty(function root_model() {
69
69
  *
70
70
  * @author Jelle De Loecker <jelle@elevenways.be>
71
71
  * @since 1.1.4
72
- * @version 1.1.4
72
+ * @version 1.3.0
73
73
  *
74
74
  * @type {Boolean}
75
75
  */
@@ -80,7 +80,7 @@ SchemaField.setProperty(function requires_translating() {
80
80
  }
81
81
 
82
82
  if (this.field_schema) {
83
- return !!this.field_schema.hasTranslations;
83
+ return !!this.field_schema.has_translations;
84
84
  }
85
85
 
86
86
  return false;
@@ -93,7 +93,7 @@ SchemaField.setProperty(function requires_translating() {
93
93
  *
94
94
  * @author Jelle De Loecker <jelle@develry.be>
95
95
  * @since 0.2.0
96
- * @version 1.2.1
96
+ * @version 1.3.1
97
97
  *
98
98
  * @param {Object} record
99
99
  * @param {String} some_path Some path to a field in the wanted schema
@@ -102,29 +102,23 @@ SchemaField.setProperty(function requires_translating() {
102
102
  */
103
103
  SchemaField.setMethod(function getSubschema(record, some_path) {
104
104
 
105
- var schema = this.options.schema,
106
- external_field_name,
107
- property_name,
108
- record_value,
109
- pieces,
110
- field,
111
- name;
105
+ let schema = this.options.schema;
112
106
 
113
107
  // If schema is a string,
114
108
  // it needs to be extracted from another field's value
115
109
  if (typeof schema == 'string') {
116
110
 
117
111
  // When there are 2 pieces, the second piece is the property name
118
- pieces = schema.split('.');
112
+ let pieces = schema.split('.');
119
113
 
120
114
  // The first piece is the external field name
121
- external_field_name = pieces[0];
115
+ let external_field_name = pieces[0];
122
116
 
123
117
  // The second piece is the property name
124
- property_name = pieces[1] || 'schema';
118
+ let property_name = pieces[1] || 'schema';
125
119
 
126
120
  // Get that other field by its name
127
- field = this.schema.getField(external_field_name);
121
+ let field = this.schema.getField(external_field_name);
128
122
 
129
123
  if (!field) {
130
124
  console.error('Failed to get subschema', external_field_name, 'of', this.schema, some_path);
@@ -139,7 +133,7 @@ SchemaField.setMethod(function getSubschema(record, some_path) {
139
133
  }
140
134
 
141
135
  // Now get the actual external value from the record
142
- record_value = field.getRecordValue(record);
136
+ let record_value = field.getRecordValue(record);
143
137
 
144
138
  // I'm not sure if this will help
145
139
  if (record_value == null) {
@@ -155,6 +149,8 @@ SchemaField.setMethod(function getSubschema(record, some_path) {
155
149
  schema = enum_value.schema;
156
150
  } else if (enum_value.value) {
157
151
  schema = enum_value.value[property_name] || enum_value.value.schema;
152
+ } else {
153
+ console.log('Could not find', schema, 'in', record, 'enum values:', enum_value, 'of field', field)
158
154
  }
159
155
  }
160
156
 
@@ -166,7 +162,7 @@ SchemaField.setMethod(function getSubschema(record, some_path) {
166
162
  *
167
163
  * @author Jelle De Loecker <jelle@develry.be>
168
164
  * @since 0.2.0
169
- * @version 0.4.0
165
+ * @version 1.3.1
170
166
  *
171
167
  * @param {Object} value Value of field, an object in this case
172
168
  * @param {Object} data The data object containing `value`
@@ -174,18 +170,21 @@ SchemaField.setMethod(function getSubschema(record, some_path) {
174
170
  *
175
171
  * @return {Object}
176
172
  */
177
- SchemaField.setMethod(function _toDatasource(value, data, datasource, callback) {
173
+ SchemaField.setMethod(function _toDatasource(value, holder, datasource, callback) {
178
174
 
179
175
  var that = this,
180
176
  sub_schema,
181
177
  record,
182
178
  model,
183
179
  temp;
184
-
185
- record = {};
186
-
187
- // Recreate a record
188
- record[this.schema.name] = data;
180
+
181
+ if (this.schema.name) {
182
+ record = {
183
+ [this.schema.name] : holder,
184
+ };
185
+ } else {
186
+ record = holder;
187
+ }
189
188
 
190
189
  sub_schema = this.getSubschema(record);
191
190
 
@@ -217,7 +216,7 @@ SchemaField.setMethod(function _toDatasource(value, data, datasource, callback)
217
216
  }
218
217
 
219
218
  // Add the newly found data
220
- temp = Object.assign({}, data);
219
+ temp = Object.assign({}, holder);
221
220
  record = {};
222
221
  record[that.schema.name] = temp;
223
222
 
@@ -240,7 +239,7 @@ SchemaField.setMethod(function _toDatasource(value, data, datasource, callback)
240
239
  });
241
240
 
242
241
  /**
243
- * Get some more subschema data
242
+ * Turn datasource data into app data
244
243
  *
245
244
  * @author Jelle De Loecker <jelle@develry.be>
246
245
  * @since 0.2.0
@@ -323,11 +322,19 @@ SchemaField.setMethod(function _toApp(query, options, value, callback) {
323
322
  return;
324
323
  }
325
324
 
326
- // Create new dummy record
327
- let record = {};
325
+ let record;
328
326
 
329
- // Recreate the record
330
- record[this.schema.name] = value;
327
+ if (options.parent_value) {
328
+ record = {
329
+ [this.schema.name] : options.parent_value
330
+ };
331
+ } else {
332
+ record = {
333
+ [this.schema.name] : {
334
+ [this.name] : value,
335
+ }
336
+ };
337
+ }
331
338
 
332
339
  let sub_schema = this.getSubschema(record);
333
340
 
@@ -335,13 +342,20 @@ SchemaField.setMethod(function _toApp(query, options, value, callback) {
335
342
  if (sub_schema) {
336
343
  let tasks = {};
337
344
 
338
- Object.each(value, function eachField(field_value, field_name) {
345
+ Object.each(value, (field_value, field_name) => {
339
346
 
340
- var field = sub_schema.get(field_name);
347
+ let field = sub_schema.get(field_name);
341
348
 
342
349
  if (field != null) {
343
- tasks[field_name] = function doToDatasource(next) {
344
- field.toApp({}, {_root_data: options._root_data}, field_value, next);
350
+
351
+ let sub_options = {
352
+ _root_data: options._root_data,
353
+ parent_field_schema_name: this.name,
354
+ parent_value : value,
355
+ };
356
+
357
+ tasks[field_name] = (next) => {
358
+ field.toApp({}, sub_options, field_value, next);
345
359
  };
346
360
  }
347
361
  });
@@ -364,7 +378,7 @@ SchemaField.setMethod(function _toApp(query, options, value, callback) {
364
378
  *
365
379
  * @author Jelle De Loecker <jelle@elevenways.be>
366
380
  * @since 1.1.4
367
- * @version 1.2.6
381
+ * @version 1.3.0
368
382
  */
369
383
  SchemaField.setMethod(function translateRecord(prefixes, record, allow_empty) {
370
384
 
@@ -385,13 +399,13 @@ SchemaField.setMethod(function translateRecord(prefixes, record, allow_empty) {
385
399
  for (schema_record of subject) {
386
400
  count++;
387
401
 
388
- if (this.field_schema.hasTranslations && schema_record) {
402
+ if (this.field_schema.has_translations && schema_record) {
389
403
 
390
404
  let field_name,
391
405
  field;
392
406
 
393
- for (field_name in this.field_schema.translatableFields) {
394
- field = this.field_schema.translatableFields[field_name];
407
+ for (field_name in this.field_schema.translatable_fields) {
408
+ field = this.field_schema.translatable_fields[field_name];
395
409
  field.translateRecord(prefixes, schema_record, allow_empty);
396
410
 
397
411
  if (schema_record.$translated_fields) {
@@ -1,4 +1,4 @@
1
- var model_info,
1
+ let class_cache = new Map(),
2
2
  cache_stores_in_progress,
3
3
  did_check = Symbol('did_check');
4
4
 
@@ -7,16 +7,11 @@ if (Blast.isBrowser) {
7
7
 
8
8
  Blast.once('hawkejs_init', function gotScene(hawkejs, variables, settings, renderer) {
9
9
 
10
- var model_name,
11
- DocClass,
12
- config,
13
- key;
14
-
15
- model_info = hawkejs.scene.exposed.model_info;
16
-
17
- for (model_name in model_info) {
18
- config = model_info[model_name];
19
- DocClass = Document.getDocumentClass(model_name);
10
+ let DocClass,
11
+ config;
12
+
13
+ for (config of hawkejs.scene.exposed.model_info) {
14
+ DocClass = Document.getDocumentClass(config.name);
20
15
 
21
16
  for (key in config.schema.dict) {
22
17
  DocClass.setFieldGetter(key);
@@ -218,25 +213,18 @@ Document.setStatic(function getClassForUndry(class_name) {
218
213
  *
219
214
  * @author Jelle De Loecker <jelle@develry.be>
220
215
  * @since 1.0.0
221
- * @version 1.1.0
216
+ * @version 1.3.1
222
217
  *
223
218
  * @param {Object|String} model
224
219
  */
225
220
  Document.setStatic(function getDocumentClass(model) {
226
221
 
227
- var doc_constructor,
228
- document_name,
229
- parent_path,
230
- model_name,
231
- DocClass,
232
- doc_path,
233
- config,
234
- key;
235
-
236
222
  if (!model) {
237
223
  throw new Error('Can not get Hawkejs.Document class for non-existing model');
238
224
  }
239
225
 
226
+ let model_name;
227
+
240
228
  if (typeof model == 'function') {
241
229
  model_name = model.name;
242
230
  } else if (typeof model == 'string') {
@@ -246,26 +234,32 @@ Document.setStatic(function getDocumentClass(model) {
246
234
  }
247
235
 
248
236
  // Construct the name of the document class
249
- document_name = model_name;
237
+ let document_name = model_name;
238
+
239
+ if (class_cache.has(document_name)) {
240
+ return class_cache.get(document_name);
241
+ }
250
242
 
251
243
  // Construct the path to this class
252
- doc_path = 'Alchemy.Client.Document.' + document_name;
244
+ let doc_path = 'Alchemy.Client.Document.' + document_name;
253
245
 
254
246
  // Get the class
255
- DocClass = Object.path(Blast.Classes, doc_path);
247
+ let DocClass = Object.path(Blast.Classes, doc_path);
256
248
 
257
249
  if (DocClass == null) {
258
- doc_constructor = Function.create(document_name, function DocumentConstructor(record, options) {
250
+ let doc_constructor = Function.create(document_name, function DocumentConstructor(record, options) {
259
251
  DocumentConstructor.wrapper.super.call(this, record, options);
260
252
  });
261
253
 
262
- if (Blast.isBrowser && window._hawkejs_static_expose) {
263
- let exposed = window._hawkejs_static_expose;
254
+ let parent,
255
+ config;
256
+
257
+ if (Blast.isBrowser) {
264
258
 
265
- config = exposed.model_info;
259
+ let model = Blast.Classes.Alchemy.Client.Model.Model.getClass(model_name);
266
260
 
267
- if (config) {
268
- config = config[model_name];
261
+ if (model && model.super) {
262
+ parent = model.super.name;
269
263
  }
270
264
  } else if (Blast.isNode) {
271
265
  config = alchemy.getModel(model_name, false);
@@ -277,13 +271,17 @@ Document.setStatic(function getDocumentClass(model) {
277
271
  }
278
272
  }
279
273
 
280
- parent_path = 'Alchemy.Client.Document';
274
+ let parent_path = 'Alchemy.Client.Document';
281
275
 
282
276
  if (config && config.parent) {
277
+ parent = config.parent;
278
+ }
279
+
280
+ if (parent && parent != 'Model') {
283
281
  // Make sure the parent class exists
284
- getDocumentClass(config.parent);
282
+ getDocumentClass(parent);
285
283
 
286
- parent_path += '.' + config.parent;
284
+ parent_path += '.' + parent;
287
285
  }
288
286
 
289
287
  DocClass = Function.inherits(parent_path, doc_constructor);
@@ -297,6 +295,8 @@ Document.setStatic(function getDocumentClass(model) {
297
295
  DocClass.Model = Blast.Classes.Hawkejs.Model.getClass(model_name);
298
296
  }
299
297
 
298
+ class_cache.set(document_name, DocClass);
299
+
300
300
  return DocClass;
301
301
  });
302
302
 
@@ -641,7 +641,7 @@ Document.setMethod(function toDry() {
641
641
  *
642
642
  * @author Jelle De Loecker <jelle@elevenways.be>
643
643
  * @since 1.0.4
644
- * @version 1.2.5
644
+ * @version 1.3.0
645
645
  *
646
646
  * @param {Object} record
647
647
  * @param {Object} options
@@ -721,7 +721,7 @@ Document.setMethod(function setDataRecord(record, options) {
721
721
  }
722
722
 
723
723
  // If this has object fields we need to clone the document already
724
- if (Blast.isBrowser && this.hasObjectFields() && !this.$is_cloned) {
724
+ if (this.hasObjectFields() && !this.$is_cloned) {
725
725
  this.storeCurrentDataAsOriginalRecord();
726
726
  }
727
727
 
@@ -731,6 +731,30 @@ Document.setMethod(function setDataRecord(record, options) {
731
731
  }
732
732
  });
733
733
 
734
+ /**
735
+ * Get the translated value of a certain field.
736
+ * If this document has already been translated, a new instance will be queried
737
+ *
738
+ * @author Jelle De Loecker <jelle@elevenways.be>
739
+ * @since 1.3.0
740
+ * @version 1.3.0
741
+ *
742
+ * @param {String} prefix
743
+ */
744
+ Document.setMethod(async function getTranslatedValueOfField(field_name, prefix) {
745
+
746
+ const model = this.$model;
747
+
748
+ let crit = model.find();
749
+ crit.setOption('locale', prefix);
750
+ crit.select(field_name);
751
+ crit.where(model.primary_key, this.$pk);
752
+
753
+ let doc = await model.find('first', crit);
754
+
755
+ return doc?.[field_name];
756
+ });
757
+
734
758
  /**
735
759
  * Refresh the values
736
760
  *
@@ -793,7 +817,7 @@ Document.setMethod(function getCleanOptions() {
793
817
  *
794
818
  * @author Jelle De Loecker <jelle@develry.be>
795
819
  * @since 0.2.0
796
- * @version 1.0.7
820
+ * @version 1.3.0
797
821
  *
798
822
  * @param {Object} data
799
823
  * @param {Function} callback
@@ -890,11 +914,7 @@ Document.setMethod(function save(data, options, callback) {
890
914
  }
891
915
  }
892
916
 
893
- if (use_data) {
894
- sub_pledge = this.$model.save(data, options, updateDoc);
895
- } else {
896
- sub_pledge = this.$model.save(main, options, updateDoc);
897
- }
917
+ sub_pledge = this.$model.save(this, options, updateDoc);
898
918
 
899
919
  return pledge;
900
920
  });
@@ -1048,12 +1068,16 @@ Document.setMethod(function informDatasource(options, callback) {
1048
1068
  /**
1049
1069
  * Store the current data as the original record
1050
1070
  *
1051
- * @author Jelle De Loecker <jelle@develry.be>
1071
+ * @author Jelle De Loecker <jelle@elevenways.be>
1052
1072
  * @since 1.0.4
1053
- * @version 1.0.4
1073
+ * @version 1.3.1
1054
1074
  */
1055
1075
  Document.setMethod(function storeCurrentDataAsOriginalRecord() {
1056
- this.$attributes.original_record = JSON.clone(this.$main);
1076
+ try {
1077
+ this.$attributes.original_record = JSON.clone(this.$main);
1078
+ } catch (err) {
1079
+ alchemy.distinctProblem('store_current_data_error', err.message, {error: err});
1080
+ }
1057
1081
  });
1058
1082
 
1059
1083
  /**
@@ -1146,7 +1170,7 @@ Document.setMethod(function hasFieldValue(name) {
1146
1170
  *
1147
1171
  * @author Jelle De Loecker <jelle@develry.be>
1148
1172
  * @since 1.0.4
1149
- * @version 1.1.0
1173
+ * @version 1.3.0
1150
1174
  *
1151
1175
  * @param {String} name The optional field name
1152
1176
  *
@@ -1169,7 +1193,18 @@ Document.setMethod(function hasChanged(name) {
1169
1193
 
1170
1194
  // If we only want to check a single field
1171
1195
  if (name) {
1172
- result = !Object.alike(this.$attributes.original_record[name], this[name]);
1196
+ let current_value,
1197
+ old_value;
1198
+
1199
+ if (name.includes('.')) {
1200
+ current_value = Object.path(this, name);
1201
+ old_value = Object.path(this.$attributes.original_record, name);
1202
+ } else {
1203
+ current_value = this[name];
1204
+ old_value = this.$attributes.original_record[name];
1205
+ }
1206
+
1207
+ result = !Object.alike(old_value, current_value);
1173
1208
  } else {
1174
1209
 
1175
1210
  let key;
@@ -215,6 +215,17 @@ FieldSet.setMethod(function addField(name, options) {
215
215
  return config;
216
216
  });
217
217
 
218
+ /**
219
+ * Remove all current fields
220
+ *
221
+ * @author Jelle De Loecker <jelle@elevenways.be>
222
+ * @since 1.3.0
223
+ * @version 1.3.0
224
+ */
225
+ FieldSet.setMethod(function clear() {
226
+ this.fields = new Deck();
227
+ });
228
+
218
229
  /**
219
230
  * Get a fieldconfig by its field name
220
231
  *