alchemymvc 1.2.2 → 1.2.5

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.
@@ -44,15 +44,24 @@ const FieldConfig = Fn.inherits('Alchemy.Base', 'Alchemy.Criteria', function Fie
44
44
  *
45
45
  * @author Jelle De Loecker <jelle@elevenways.be>
46
46
  * @since 1.1.3
47
- * @version 1.1.3
47
+ * @version 1.2.3
48
48
  *
49
49
  * @param {Object} obj
50
50
  *
51
51
  * @return {Alchemy.Form.FieldConfig}
52
52
  */
53
53
  FieldConfig.setStatic(function unDry(obj) {
54
- let set = new FieldConfig(obj.path, obj.options);
55
- return set;
54
+ let result = new FieldConfig(obj.path, obj.options);
55
+
56
+ if (obj.association) {
57
+ result.association = obj.association;
58
+ }
59
+
60
+ if (obj.model) {
61
+ result.model = obj.model;
62
+ }
63
+
64
+ return result;
56
65
  });
57
66
 
58
67
  /**
@@ -122,7 +131,7 @@ FieldConfig.setMethod(function getContextModel() {
122
131
  *
123
132
  * @author Jelle De Loecker <jelle@elevenways.be>
124
133
  * @since 1.2.2
125
- * @version 1.2.2
134
+ * @version 1.2.5
126
135
  */
127
136
  FieldConfig.setMethod(function getModel() {
128
137
 
@@ -133,13 +142,15 @@ FieldConfig.setMethod(function getModel() {
133
142
  }
134
143
 
135
144
  if (this.association) {
136
- let config = model.getAssociation(this.association);
145
+ try {
146
+ let config = model.getAssociation(this.association);
137
147
 
138
- if (config) {
139
- model = alchemy.getModel(config.modelName);
140
- } else {
141
- model = null;
142
- }
148
+ if (config) {
149
+ model = alchemy.getModel(config.modelName);
150
+ } else {
151
+ model = null;
152
+ }
153
+ } catch (ignored_error) {}
143
154
  }
144
155
 
145
156
  return model;
@@ -150,17 +161,28 @@ FieldConfig.setMethod(function getModel() {
150
161
  *
151
162
  * @author Jelle De Loecker <jelle@elevenways.be>
152
163
  * @since 1.2.2
153
- * @version 1.2.2
164
+ * @version 1.2.5
165
+ *
166
+ * @return {Field}
154
167
  */
155
168
  FieldConfig.setMethod(function getFieldDefinition() {
156
169
 
157
- let model = this.getModel();
170
+ let model = this.getModel(),
171
+ result;
158
172
 
159
- if (!model) {
160
- return;
173
+ if (model) {
174
+ result = model.getField(this.local_path);
175
+ }
176
+
177
+ if (!result && this.options?.type) {
178
+ let constructor = Classes.Alchemy.Field.Field.getMember(this.options.type);
179
+
180
+ if (constructor) {
181
+ result = new constructor(null, this.name);
182
+ }
161
183
  }
162
184
 
163
- return model.getField(this.local_path);
185
+ return result;
164
186
  });
165
187
 
166
188
  /**
@@ -194,6 +216,7 @@ FieldConfig.setMethod(function toJSON() {
194
216
  local_path : this.local_path,
195
217
  association : this.association,
196
218
  options : this.options,
219
+ model : this.model,
197
220
  };
198
221
  });
199
222
 
@@ -65,7 +65,7 @@ FieldSet.setStatic(function fromArray(fields) {
65
65
  *
66
66
  * @author Jelle De Loecker <jelle@elevenways.be>
67
67
  * @since 1.1.3
68
- * @version 1.1.3
68
+ * @version 1.2.5
69
69
  *
70
70
  * @type {Deck}
71
71
  */
@@ -88,6 +88,14 @@ FieldSet.enforceProperty(function fields(new_value, old_value) {
88
88
  };
89
89
  }
90
90
 
91
+ if (!entry.options) {
92
+ entry.options = {};
93
+ }
94
+
95
+ if (entry.type) {
96
+ entry.options.type = entry.type;
97
+ }
98
+
91
99
  field = new Classes.Alchemy.Criteria.FieldConfig(entry.name, entry.options);
92
100
  deck.set(entry.name, field);
93
101
  }
@@ -205,4 +213,17 @@ FieldSet.setMethod(function addField(name, options) {
205
213
  this.fields.set(name, config);
206
214
 
207
215
  return config;
216
+ });
217
+
218
+ /**
219
+ * Get a fieldconfig by its field name
220
+ *
221
+ * @author Jelle De Loecker <jelle@elevenways.be>
222
+ * @since 1.2.5
223
+ * @version 1.2.5
224
+ *
225
+ * @return {Alchemy.Form.FieldConfig}
226
+ */
227
+ FieldSet.setMethod(function get(name) {
228
+ return this.fields.get(name);
208
229
  });
@@ -32,7 +32,7 @@ if (Blast.isBrowser) {
32
32
  *
33
33
  * @param {Object} options
34
34
  */
35
- var Model = Function.inherits('Alchemy.Client.Base', 'Alchemy.Client.Model', function Model(options) {
35
+ const Model = Function.inherits('Alchemy.Client.Base', 'Alchemy.Client.Model', function Model(options) {
36
36
  this.init(options);
37
37
  });
38
38
 
@@ -49,6 +49,22 @@ Model.constitute(function setModelName() {
49
49
  this.table = this.model_name.tableize();
50
50
  });
51
51
 
52
+ /**
53
+ * Map these events to methods
54
+ *
55
+ * @author Jelle De Loecker <jelle@elevenways.be>
56
+ * @since 1.2.5
57
+ * @version 1.2.5
58
+ */
59
+ Model.mapEventToMethod({
60
+ saved : 'afterSave',
61
+ finding : 'beforeFind',
62
+ queried : 'afterQuery',
63
+ associated : 'afterAssociated',
64
+ found : 'afterData',
65
+ foundDocuments : 'afterFind',
66
+ });
67
+
52
68
  /**
53
69
  * Is the given action enabled on the server?
54
70
  *
@@ -485,7 +501,7 @@ Model.setMethod(function getAliasModel(alias) {
485
501
  *
486
502
  * @author Jelle De Loecker <jelle@develry.be>
487
503
  * @since 0.0.1
488
- * @version 1.1.3
504
+ * @version 1.2.5
489
505
  *
490
506
  * @param {String} type The type of find (first, all)
491
507
  * @param {Criteria} criteria The criteria object
@@ -530,14 +546,10 @@ Model.setMethod(function find(type, criteria, callback) {
530
546
  error = new TypeError('Find type should be a string');
531
547
  }
532
548
 
533
- if (!Blast.Classes.Alchemy.Criteria.Criteria.isCriteria(criteria)) {
534
- try {
535
- let options = criteria;
536
- criteria = new Blast.Classes.Alchemy.Criteria();
537
- criteria.applyOldOptions(options);
538
- } catch (err) {
539
- error = err;
540
- }
549
+ try {
550
+ criteria = Blast.Classes.Alchemy.Criteria.Criteria.cast(criteria);
551
+ } catch (err) {
552
+ error = err;
541
553
  }
542
554
 
543
555
  if (error != null) {
@@ -1103,13 +1115,20 @@ Model.setMethod(function addAssociatedDataToRecord(criteria, item, callback) {
1103
1115
 
1104
1116
  aliases[alias] = function aliasRecordTask(nextAlias) {
1105
1117
 
1106
- var key;
1118
+ let pledge,
1119
+ key;
1107
1120
 
1108
1121
  if (options.create_references !== false) {
1109
1122
  key = alchemy.checksum(assoc_crit);
1110
1123
 
1111
- if (key != null && options.assoc_cache[key]) {
1112
- return nextAlias(null, options.assoc_cache[key]);
1124
+ if (key != null) {
1125
+ if (options.assoc_cache[key]) {
1126
+ Pledge.done(options.assoc_cache[key], nextAlias);
1127
+ return;
1128
+ } else {
1129
+ pledge = new Pledge();
1130
+ options.assoc_cache[key] = pledge;
1131
+ }
1113
1132
  }
1114
1133
  }
1115
1134
 
@@ -1125,6 +1144,11 @@ Model.setMethod(function addAssociatedDataToRecord(criteria, item, callback) {
1125
1144
  }
1126
1145
 
1127
1146
  if (err != null) {
1147
+
1148
+ if (pledge) {
1149
+ pledge.reject(err);
1150
+ }
1151
+
1128
1152
  return nextAlias(err);
1129
1153
  }
1130
1154
 
@@ -1140,6 +1164,10 @@ Model.setMethod(function addAssociatedDataToRecord(criteria, item, callback) {
1140
1164
 
1141
1165
  if (key) {
1142
1166
  options.assoc_cache[key] = result;
1167
+
1168
+ if (pledge) {
1169
+ pledge.resolve(result);
1170
+ }
1143
1171
  }
1144
1172
 
1145
1173
  nextAlias(null, result);
@@ -5,6 +5,7 @@ var fileCache = alchemy.shared('files.fileCache'),
5
5
  libua = alchemy.use('useragent'),
6
6
  zlib = alchemy.use('zlib'),
7
7
  BODY = Symbol('body'),
8
+ TESTED_ROUTES = Symbol('tested_routes'),
8
9
  magic,
9
10
  fs = alchemy.use('fs'),
10
11
  prefixes = alchemy.shared('Routing.prefixes');
@@ -265,6 +266,62 @@ Conduit.setMethod(function setRequestBody(body) {
265
266
  Object.assign(this.body, body);
266
267
  });
267
268
 
269
+ /**
270
+ * Has the given route been tested yet?
271
+ *
272
+ * @author Jelle De Loecker <jelle@elevenways.be>
273
+ * @since 1.2.5
274
+ * @version 1.2.5
275
+ *
276
+ * @param {Route}
277
+ */
278
+ Conduit.setMethod(function hasRouteBeenTested(route) {
279
+
280
+ if (!route || !this[TESTED_ROUTES]) {
281
+ return false;
282
+ }
283
+
284
+ return this[TESTED_ROUTES].has(route);
285
+ });
286
+
287
+ /**
288
+ * Mark this route as having been tested
289
+ *
290
+ * @author Jelle De Loecker <jelle@elevenways.be>
291
+ * @since 1.2.5
292
+ * @version 1.2.5
293
+ *
294
+ * @param {Route}
295
+ */
296
+ Conduit.setMethod(function markRouteAsTested(route) {
297
+
298
+ if (!this[TESTED_ROUTES]) {
299
+ this[TESTED_ROUTES] = new Set();
300
+ }
301
+
302
+ this[TESTED_ROUTES].add(route);
303
+ });
304
+
305
+ /**
306
+ * Rewrite a certain URL parameter
307
+ * (Causing some kind of redirect)
308
+ *
309
+ * @author Jelle De Loecker <jelle@elevenways.be>
310
+ * @since 1.2.5
311
+ * @version 1.2.5
312
+ *
313
+ * @param {String} route_param
314
+ * @param {*} new_value
315
+ */
316
+ Conduit.setMethod(function rewriteRequestRouteParam(route_param, new_value) {
317
+
318
+ if (!this.rewritten_request_route_param) {
319
+ this.rewritten_request_route_param = {};
320
+ }
321
+
322
+ this.rewritten_request_route_param[route_param] = new_value;
323
+ });
324
+
268
325
  /**
269
326
  * Set the request files
270
327
  *
@@ -426,7 +483,7 @@ Conduit.setMethod(function time() {
426
483
  *
427
484
  * @author Jelle De Loecker <jelle@develry.be>
428
485
  * @since 0.2.0
429
- * @version 1.1.5
486
+ * @version 1.2.5
430
487
  *
431
488
  * @param {IncomingMessage} req
432
489
  * @param {ServerResponse} res
@@ -457,6 +514,12 @@ Conduit.setMethod(async function parseRequest() {
457
514
  this.encrypted = this.request.connection.encrypted;
458
515
  }
459
516
 
517
+ if (this.rewritten_request_route_param) {
518
+ let params = Object.assign({}, this.route_string_parameters, this.rewritten_request_route_param);
519
+ let new_url = this.route.generateUrl(params, this);
520
+ this.overrideResponseUrl(new_url);
521
+ }
522
+
460
523
  // If the url has already been parsed, return early
461
524
  if (this.url) {
462
525
  return;
@@ -1170,7 +1233,7 @@ Conduit.setMethod(function callHandler() {
1170
1233
  *
1171
1234
  * @author Jelle De Loecker <jelle@develry.be>
1172
1235
  * @since 1.1.0
1173
- * @version 1.1.0
1236
+ * @version 1.2.5
1174
1237
  *
1175
1238
  * @param {String|Object} options Options or url
1176
1239
  */
@@ -1214,11 +1277,27 @@ Conduit.setMethod(function postpone(options) {
1214
1277
  // Nullify the response
1215
1278
  this.response = null;
1216
1279
 
1217
- let old_url = String(this.url);
1218
-
1219
1280
  // Set the original url
1220
- this.setHeader('x-history-url', old_url);
1221
- this.expose('redirected_to', old_url);
1281
+ this.overrideResponseUrl(this.url)
1282
+ });
1283
+
1284
+ /**
1285
+ * Set the response url
1286
+ *
1287
+ * @author Jelle De Loecker <jelle@develry.be>
1288
+ * @since 1.2.5
1289
+ * @version 1.2.5
1290
+ *
1291
+ * @param {String|RURL} url
1292
+ */
1293
+ Conduit.setMethod(function overrideResponseUrl(url) {
1294
+
1295
+ if (typeof url != 'string') {
1296
+ url = String(url);
1297
+ }
1298
+
1299
+ this.setHeader('x-history-url', url);
1300
+ this.expose('redirected_to', url);
1222
1301
  });
1223
1302
 
1224
1303
  /**
@@ -1226,7 +1305,7 @@ Conduit.setMethod(function postpone(options) {
1226
1305
  *
1227
1306
  * @author Jelle De Loecker <jelle@develry.be>
1228
1307
  * @since 0.2.0
1229
- * @version 1.1.0
1308
+ * @version 1.2.5
1230
1309
  *
1231
1310
  * @param {Number} status 3xx redirection codes. 302 (temporary redirect) by default
1232
1311
  * @param {String|Object} options Options or url
@@ -1283,8 +1362,7 @@ Conduit.setMethod(function redirect(status, options) {
1283
1362
  }
1284
1363
 
1285
1364
  // Register the new url as the one to use for the history
1286
- this.setHeader('x-history-url', url);
1287
- this.expose('redirected_to', url);
1365
+ this.overrideResponseUrl(url);
1288
1366
 
1289
1367
  this.original_path = url;
1290
1368
 
@@ -386,7 +386,7 @@ Document.setMethod(function toDry() {
386
386
  *
387
387
  * @author Jelle De Loecker <jelle@develry.be>
388
388
  * @since 0.2.0
389
- * @version 1.1.0
389
+ * @version 1.2.4
390
390
  *
391
391
  * @param {WeakMap} wm
392
392
  *
@@ -412,7 +412,10 @@ Document.setMethod(function toHawkejs(wm) {
412
412
  // Sometimes we get an EMPTY $record value,
413
413
  // this is probably because it's already in the process of being clones
414
414
  if (!Object.isEmpty(record)) {
415
- result.setDataRecord(record);
415
+ // Get clean options
416
+ let options = JSON.clone(this.getCleanOptions(), 'toHawkejs', wm);
417
+
418
+ result.setDataRecord(record, options);
416
419
  } else {
417
420
  record.$record = record;
418
421
  }
@@ -425,6 +428,24 @@ Document.setMethod(function toHawkejs(wm) {
425
428
  return result;
426
429
  });
427
430
 
431
+ /**
432
+ * Keep private fields when sending to browser
433
+ *
434
+ * @author Jelle De Loecker <jelle@elevenways.be>
435
+ * @since 1.2.4
436
+ * @version 1.2.4
437
+ */
438
+ Document.setMethod(function keepPrivateFields(value) {
439
+
440
+ if (arguments.length == 0) {
441
+ value = true;
442
+ } else {
443
+ value = !!value;
444
+ }
445
+
446
+ this.$options.keep_private_fields = value;
447
+ });
448
+
428
449
  /**
429
450
  * Return the basic record for JSON
430
451
  *
@@ -536,27 +557,24 @@ Document.setMethod(function remove(callback) {
536
557
  /**
537
558
  * Add associated data to this record
538
559
  *
539
- * @author Jelle De Loecker <jelle@develry.be>
540
- * @since 0.2.0
541
- * @version 1.1.0
560
+ * @author Jelle De Loecker <jelle@elevenways.be>
561
+ * @since 1.2.3
562
+ * @version 1.2.3
542
563
  *
543
- * @param {Criteria} criteria
544
- * @param {Function} callback
564
+ * @param {Criteria|String} criteria
545
565
  *
546
- * @return {Pledge}
566
+ * @return {Criteria}
547
567
  */
548
- Document.setMethod(['populate', 'addAssociatedData'], function addAssociatedData(criteria, callback) {
568
+ Document.setMethod(function preparePopulationCriteria(criteria) {
549
569
 
550
- var that = this,
570
+ let type = typeof criteria,
551
571
  model = this.$model,
552
- type = typeof criteria,
553
572
  selects;
554
573
 
555
574
  if (type == 'string') {
556
575
  selects = [criteria];
557
576
  criteria = {};
558
577
  } else if (type == 'function') {
559
- callback = criteria;
560
578
  criteria = {};
561
579
  } else if (Array.isArray(criteria)) {
562
580
  selects = criteria;
@@ -583,7 +601,31 @@ Document.setMethod(['populate', 'addAssociatedData'], function addAssociatedData
583
601
  }
584
602
  }
585
603
 
586
- return model.addAssociatedDataToRecord(criteria, this.$record, callback);
604
+ return criteria;
605
+ });
606
+
607
+ /**
608
+ * Add associated data to this record
609
+ *
610
+ * @author Jelle De Loecker <jelle@develry.be>
611
+ * @since 0.2.0
612
+ * @version 1.2.3
613
+ *
614
+ * @param {Criteria} criteria
615
+ * @param {Function} callback
616
+ *
617
+ * @return {Pledge}
618
+ */
619
+ Document.setMethod(['populate', 'addAssociatedData'], function addAssociatedData(criteria, callback) {
620
+
621
+ if (typeof criteria == 'function') {
622
+ callback = criteria;
623
+ criteria = null;
624
+ }
625
+
626
+ criteria = this.preparePopulationCriteria(criteria);
627
+
628
+ return this.$model.addAssociatedDataToRecord(criteria, this.$record, callback);
587
629
  });
588
630
 
589
631
  /**
@@ -63,13 +63,23 @@ DocumentList.setMethod(function clone() {
63
63
  *
64
64
  * @author Jelle De Loecker <jelle@develry.be>
65
65
  * @since 1.1.0
66
- * @version 1.1.0
66
+ * @version 1.2.3
67
67
  *
68
68
  * @param {Criteria} criteria
69
69
  *
70
70
  * @return {Pledge}
71
71
  */
72
72
  DocumentList.setMethod(['populate', 'addAssociatedData'], function addAssociatedData(criteria) {
73
+
74
+ if (!this.records?.length) {
75
+ return;
76
+ }
77
+
78
+ let doc = this.records[0];
79
+
80
+ // Use the first doc to prepare the population criteria
81
+ criteria = doc.preparePopulationCriteria(criteria);
82
+
73
83
  return Function.forEach.parallel(this.records, function eachDoc(doc, key, next) {
74
84
  doc.populate(criteria).done(next);
75
85
  });
@@ -105,4 +115,26 @@ DocumentList.setMethod(function findNextBatch(callback) {
105
115
  }
106
116
 
107
117
  this.options.model.find('all', options, callback);
118
+ });
119
+
120
+ /**
121
+ * Keep private fields when sending to browser
122
+ *
123
+ * @author Jelle De Loecker <jelle@elevenways.be>
124
+ * @since 1.2.4
125
+ * @version 1.2.4
126
+ */
127
+ DocumentList.setMethod(function keepPrivateFields(value) {
128
+
129
+ if (arguments.length == 0) {
130
+ value = true;
131
+ } else {
132
+ value = !!value;
133
+ }
134
+
135
+ let record;
136
+
137
+ for (record of this) {
138
+ record.keepPrivateFields(value);
139
+ }
108
140
  });
@@ -5,9 +5,7 @@
5
5
  * @since 1.0.0
6
6
  * @version 1.0.0
7
7
  */
8
- var Element = Function.inherits('Hawkejs.Element', 'Alchemy.Element', function Element() {
9
- return Element.super.call(this);
10
- });
8
+ const Element = Function.inherits('Hawkejs.Element', 'Alchemy.Element', 'Element');
11
9
 
12
10
  /**
13
11
  * The default element prefix (when element contains no hyphen) is "al"
@@ -23,21 +21,12 @@ Function.getNamespace('Alchemy.Element').setStatic('default_element_prefix', 'al
23
21
  *
24
22
  * @author Jelle De Loecker <jelle@elevenways.be>
25
23
  * @since 1.1.3
26
- * @version 1.2.1
24
+ * @version 1.2.5
27
25
  *
28
26
  * @return {RURL}
29
27
  */
30
28
  Element.setMethod(function getCurrentUrl() {
31
- if (Blast.isBrowser) {
32
-
33
- if (hawkejs.scene.opening_url && hawkejs.scene.opening_url.url) {
34
- return Blast.Classes.RURL.parse(hawkejs.scene.opening_url.url);
35
- }
36
-
37
- return Blast.Classes.RURL.parse(window.location);
38
- } else if (this.hawkejs_renderer && this.hawkejs_renderer.variables && this.hawkejs_renderer.variables.__url) {
39
- return this.hawkejs_renderer.variables.__url.clone();
40
- }
29
+ return this.hawkejs_renderer?.helpers?.Router?.current_url;
41
30
  });
42
31
 
43
32
  /**
@@ -111,4 +100,20 @@ Element.setMethod(function getModel(model_name, options) {
111
100
  this._model_instances[model_name] = instance;
112
101
 
113
102
  return instance;
103
+ });
104
+
105
+ /**
106
+ * Default translation implementation
107
+ *
108
+ * @author Jelle De Loecker <jelle@elevenways.be>
109
+ * @since 1.2.4
110
+ * @version 1.2.4
111
+ *
112
+ * @param {String} key
113
+ * @param {Object} parameters
114
+ *
115
+ * @return {String}
116
+ */
117
+ Element.setMethod(function __(key, parameters) {
118
+ return key;
114
119
  });
@@ -105,12 +105,17 @@ Field.setStatic(function createPathEvaluator(path) {
105
105
  *
106
106
  * @author Jelle De Loecker <jelle@develry.be>
107
107
  * @since 1.0.0
108
- * @version 1.0.0
108
+ * @version 1.2.4
109
109
  *
110
110
  * @type {Boolean}
111
111
  */
112
112
  Field.setProperty(function is_private() {
113
- return !!this.options.is_private;
113
+
114
+ if (this.options.is_private || this.options.private) {
115
+ return true;
116
+ }
117
+
118
+ return false;
114
119
  });
115
120
 
116
121
  /**
@@ -1044,7 +1049,7 @@ Field.setMethod(function getRules() {
1044
1049
  *
1045
1050
  * @author Jelle De Loecker <jelle@develry.be>
1046
1051
  * @since 1.1.4
1047
- * @version 1.1.4
1052
+ * @version 1.2.5
1048
1053
  */
1049
1054
  Field.setMethod(function translateRecord(prefixes, record, allow_empty) {
1050
1055
 
@@ -1053,4 +1058,10 @@ Field.setMethod(function translateRecord(prefixes, record, allow_empty) {
1053
1058
  // Use the final result, if we found something or not
1054
1059
  record[this.name] = found.result;
1055
1060
  record['_prefix_' + this.name] = found.prefix;
1061
+
1062
+ if (!record.$translated_fields) {
1063
+ record.$translated_fields = {};
1064
+ }
1065
+
1066
+ record.$translated_fields[this.name] = found.prefix;
1056
1067
  });