alchemymvc 1.4.0-alpha.1 → 1.4.0-alpha.3

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.
@@ -1,3 +1,5 @@
1
+ @use "alchemy";
2
+
1
3
  html {
2
4
  font-family: sans-serif;
3
5
  text-size-adjust: 100%;
@@ -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
+ }
32
+
33
+ if (typeof model == 'string') {
34
+ model = Model.get(model);
35
+ }
36
+
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
+ }
29
49
 
30
- if (Classes.Alchemy.Model[class_name]) {
31
- return Classes.Alchemy.Model[class_name];
50
+ if (revision_model) {
51
+ return revision_model;
32
52
  }
53
+
54
+ let namespace = model.constructor.namespace,
55
+ class_name = model.name + 'DataRevision';
33
56
 
34
- let model_class = Function.inherits('Alchemy.Model', Function.create(class_name, function DataRevision(options) {
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
 
@@ -48,6 +71,12 @@ Revision.setProperty(function revision_model_class() {
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', 'Number', {
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
@@ -177,14 +221,12 @@ Revision.setMethod(function beforeSave(record, options, creating) {
177
221
  next = this.wait('series');
178
222
 
179
223
  // Find the original record
180
- Model.get(that.model.name).findById(main._id, async function gotRecord(err, result) {
181
-
182
- var ori;
224
+ Model.get(this.model.model_name).findById(record.$pk, async function gotRecord(err, result) {
183
225
 
184
226
  if (result) {
185
227
 
186
228
  // Get the original data
187
- ori = await that.model.convertRecordToDatasourceFormat(result);
229
+ let ori = await that.model.convertRecordToDatasourceFormat(result);
188
230
 
189
231
  // Store the original data in a weakmap for later
190
232
  revision_before.set(options, ori);
@@ -206,17 +248,11 @@ Revision.setMethod(function beforeSave(record, options, creating) {
206
248
  *
207
249
  * @author Jelle De Loecker <jelle@elevenways.be>
208
250
  * @since 0.0.1
209
- * @version 1.1.0
251
+ * @version 1.4.0
210
252
  */
211
253
  Revision.setMethod(function afterSave(record, options, created) {
212
254
 
213
- var that = this,
214
- earlier_data,
215
- right,
216
- main,
217
- that,
218
- left,
219
- next;
255
+ let earlier_data;
220
256
 
221
257
  if (created) {
222
258
  earlier_data = {};
@@ -229,11 +265,12 @@ Revision.setMethod(function afterSave(record, options, created) {
229
265
  return;
230
266
  }
231
267
 
232
- next = this.wait();
233
- main = record[that.model.name] || record;
268
+ let doc = this.model.createDocument(record);
269
+ let next = this.wait();
270
+ const that = this;
234
271
 
235
272
  // Find the complete saved item
236
- Model.get(that.model.name).findById(main._id, async function gotRecord(err, result) {
273
+ Model.get(this.model.model_name).findByPk(doc.$pk, async function gotRecord(err, result) {
237
274
 
238
275
  if (result) {
239
276
 
@@ -243,8 +280,8 @@ Revision.setMethod(function afterSave(record, options, created) {
243
280
  if (new_data) {
244
281
 
245
282
  // Convert the objects so they can be diffed properly
246
- left = JSON.toDryObject(earlier_data);
247
- right = JSON.toDryObject(new_data);
283
+ let left = JSON.toDryObject(earlier_data),
284
+ right = JSON.toDryObject(new_data);
248
285
 
249
286
  // Diff them
250
287
  let delta = that.compare(left, right);
@@ -267,7 +304,7 @@ Revision.setMethod(function afterSave(record, options, created) {
267
304
 
268
305
  // Add the delta information
269
306
  revision_data = {
270
- [that.revision_model.name] : revision_data
307
+ [that.revision_model.model_name] : revision_data
271
308
  };
272
309
 
273
310
  // Save the data
@@ -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) {
@@ -659,7 +659,7 @@ Mongo.setMethod(function _remove(model, query, options, callback) {
659
659
  *
660
660
  * @author Jelle De Loecker <jelle@elevenways.be>
661
661
  * @since 0.2.0
662
- * @version 1.3.17
662
+ * @version 1.4.0
663
663
  */
664
664
  Mongo.setMethod(function _ensureIndex(model, index, callback) {
665
665
 
@@ -696,11 +696,20 @@ Mongo.setMethod(function _ensureIndex(model, index, callback) {
696
696
 
697
697
  // Check for IndexOptionsConflict
698
698
  if (err.code === 85) {
699
+ let index_to_drop;
700
+
701
+ if (err.message.includes('already exists with a different name:')) {
702
+ index_to_drop = err.message.after('different name:').trim();
703
+ }
704
+
705
+ if (!index_to_drop) {
706
+ index_to_drop = options.name;
707
+ }
699
708
 
700
709
  try {
701
710
 
702
711
  // Index already exists, drop it
703
- await collection.dropIndex(options.name);
712
+ await collection.dropIndex(index_to_drop);
704
713
 
705
714
  // Try again
706
715
  await collection.createIndex(index_specs, options);
@@ -40,7 +40,7 @@ EnumMap.setMethod(function get(name) {
40
40
  *
41
41
  * @author Jelle De Loecker <jelle@elevenways.be>
42
42
  * @since 1.2.1
43
- * @version 1.2.1
43
+ * @version 1.4.0
44
44
  *
45
45
  * @param {string} name
46
46
  * @param {*} value
@@ -65,18 +65,37 @@ EnumMap.setMethod(function set(name, value) {
65
65
  name : value,
66
66
  title : value,
67
67
  };
68
- } else if (typeof value == 'function') {
69
- result = {
70
- name : value.name,
71
- title : value.title,
72
- };
73
68
  } else {
74
- result = {
75
- name : value.name,
76
- title : value.title || value.name,
77
- };
69
+ if (typeof value == 'function') {
70
+ result = {
71
+ name : value.name,
72
+ title : value.title,
73
+ };
74
+ } else {
75
+ result = {
76
+ name : value.name,
77
+ title : value.title || value.name,
78
+ };
79
+
80
+ if (value.icon) {
81
+ result.icon = value.icon;
82
+ }
83
+
84
+ if (value.color) {
85
+ result.color = value.color;
86
+ }
87
+
88
+ if (value.text_color) {
89
+ result.text_color = value.text_color;
90
+ }
91
+ }
92
+
93
+ if (value.enum_info) {
94
+ Object.assign(result, value.enum_info);
95
+ }
78
96
  }
79
97
 
98
+ result.number = this.local.size + 1;
80
99
  result.value = value;
81
100
  result.is_enumified = true;
82
101
 
@@ -112,7 +112,7 @@ Router.setMethod(function isLocalUrl(url) {
112
112
  *
113
113
  * @author Jelle De Loecker <jelle@elevenways.be>
114
114
  * @since 1.1.0
115
- * @version 1.3.1
115
+ * @version 1.4.0
116
116
  *
117
117
  * @param {Element} element The element to apply to
118
118
  * @param {string} name The route name
@@ -225,6 +225,8 @@ Router.setMethod(function applyDirective(element, name, options) {
225
225
  disable_ajax = true;
226
226
  }
227
227
 
228
+ let method_attribute = false;
229
+
228
230
  if (element.role == 'link') {
229
231
  attribute_name = 'href';
230
232
  } else {
@@ -232,6 +234,7 @@ Router.setMethod(function applyDirective(element, name, options) {
232
234
  switch (element.nodeName) {
233
235
  case 'FORM':
234
236
  attribute_name = 'action';
237
+ method_attribute = 'method';
235
238
  break;
236
239
 
237
240
  case 'AREA':
@@ -260,6 +263,10 @@ Router.setMethod(function applyDirective(element, name, options) {
260
263
  }
261
264
  }
262
265
 
266
+ if (method_attribute && config.methods?.[0]) {
267
+ element.setAttribute(method_attribute, config.methods[0]);
268
+ }
269
+
263
270
  if (disable_ajax) {
264
271
  element.setAttribute('data-he-link', 'false');
265
272
  }
@@ -384,19 +391,13 @@ Router.setMethod(function routeConfig(name, socket_route) {
384
391
  */
385
392
  Router.setMethod(function routeUrl(name, parameters, options) {
386
393
 
387
- var base_url,
388
- locales,
389
- config,
390
- url,
391
- key,
392
- i;
393
-
394
394
  if (!options) {
395
395
  options = {};
396
396
  }
397
397
 
398
- url = '';
399
- config = options.config || this.routeConfig(name);
398
+ let locales,
399
+ config = options.config || this.routeConfig(name),
400
+ url = '';
400
401
 
401
402
  if (options.locale || options.prefix) {
402
403
  locales = [options.prefix || options.locale];
@@ -417,11 +418,15 @@ Router.setMethod(function routeUrl(name, parameters, options) {
417
418
  // (in case it's a special parameters object, like a Document)
418
419
  parameters = Object.create(parameters);
419
420
  }
421
+ } else {
422
+ parameters = {};
420
423
  }
421
424
 
422
425
  let chosen_prefix;
423
426
 
424
427
  if (config != null) {
428
+ let i;
429
+
425
430
  for (i = 0; i < locales.length; i++) {
426
431
  if (config.paths && config.paths[locales[i]]) {
427
432
  chosen_prefix = locales[i];
@@ -435,7 +440,7 @@ Router.setMethod(function routeUrl(name, parameters, options) {
435
440
  }
436
441
 
437
442
  if (!url && config.paths) {
438
- key = Object.keys(config.paths).first();
443
+ let key = Object.keys(config.paths).first();
439
444
 
440
445
  if (key) {
441
446
  url = '/' + key + config.paths[key];
@@ -520,6 +525,9 @@ Router.setMethod(function routeUrl(name, parameters, options) {
520
525
  }
521
526
 
522
527
  if (options.full || options.absolute) {
528
+
529
+ let base_url;
530
+
523
531
  if (this.view) {
524
532
  base_url = this.view.internal('url');
525
533
  }
@@ -83,4 +83,17 @@ Enum.setMethod(function getClientConfigOptions(wm) {
83
83
  let options = JSON.clone(this.options, 'toHawkejs', wm);
84
84
 
85
85
  return options;
86
+ });
87
+
88
+ /**
89
+ * Get the configuration entry for the given value
90
+ *
91
+ * @author Jelle De Loecker <jelle@elevenways.be>
92
+ * @since 1.4.0
93
+ * @version 1.4.0
94
+ *
95
+ * @return {Object}
96
+ */
97
+ Enum.setMethod(function getValueConfiguration(value) {
98
+ return this.getValues()?.get?.(value);
86
99
  });
@@ -1002,7 +1002,7 @@ Document.setMethod(function recomputeFieldIfNecessary(name, force = false) {
1002
1002
  *
1003
1003
  * @author Jelle De Loecker <jelle@elevenways.be>
1004
1004
  * @since 1.3.21
1005
- * @version 1.3.21
1005
+ * @version 1.4.0
1006
1006
  *
1007
1007
  * @param {string|Alchemy.Field} name
1008
1008
  * @param {*} value
@@ -1018,9 +1018,7 @@ Document.setMethod(function _setComputedFieldValue(name, value) {
1018
1018
  }
1019
1019
 
1020
1020
  if (Pledge.isThenable(value)) {
1021
- value.then(value => {
1022
- this.$main[name] = value;
1023
- });
1021
+ value = value.then(value => this.$main[name] = value);
1024
1022
  } else {
1025
1023
  this.$main[name] = value;
1026
1024
  }
@@ -1034,7 +1032,7 @@ Document.setMethod(function _setComputedFieldValue(name, value) {
1034
1032
  *
1035
1033
  * @author Jelle De Loecker <jelle@elevenways.be>
1036
1034
  * @since 1.3.21
1037
- * @version 1.3.21
1035
+ * @version 1.4.0
1038
1036
  *
1039
1037
  * @return {Pledge|undefined}
1040
1038
  */
@@ -1089,8 +1087,11 @@ Document.setMethod(function recomputeValues() {
1089
1087
  }
1090
1088
 
1091
1089
  let result = compute_method.call(this, this, field);
1090
+ result = this._setComputedFieldValue(key, result);
1092
1091
 
1093
- this._setComputedFieldValue(key, result);
1092
+ if (Pledge.isThenable(result)) {
1093
+ promises.push(result);
1094
+ }
1094
1095
  }
1095
1096
 
1096
1097
  if (promises.length) {
@@ -15,7 +15,7 @@ const SystemTask = Function.inherits('Alchemy.Model.System', 'Task');
15
15
  *
16
16
  * @author Jelle De Loecker <jelle@elevenways.be>
17
17
  * @since 1.3.17
18
- * @version 1.3.17
18
+ * @version 1.4.0
19
19
  */
20
20
  SystemTask.constitute(function addTaskFields() {
21
21
 
@@ -48,9 +48,18 @@ SystemTask.constitute(function addTaskFields() {
48
48
 
49
49
  this.addField('schedule_type', 'Enum', {
50
50
  values: {
51
- user : 'User',
52
- system_forced : 'System (forced)',
53
- system_fallback : 'System (fallback)',
51
+ user : {
52
+ title : 'User',
53
+ icon : 'user',
54
+ },
55
+ system_forced : {
56
+ title: 'System (forced)',
57
+ icon : 'shield-keyhole',
58
+ },
59
+ system_fallback : {
60
+ title: 'System (fallback)',
61
+ icon : 'shield-halved',
62
+ }
54
63
  },
55
64
  default: 'user',
56
65
  });
package/lib/bootstrap.js CHANGED
@@ -7,6 +7,11 @@ const libpath = require('path');
7
7
  */
8
8
  const Protoblast = require('protoblast')(true);
9
9
 
10
+ /**
11
+ * Require Hawkejs next
12
+ */
13
+ require('hawkejs');
14
+
10
15
  /**
11
16
  * Define shared global constants and require methods
12
17
  */
@@ -7,6 +7,22 @@
7
7
  */
8
8
  const Element = Function.inherits('Hawkejs.Element', 'Alchemy.Element', 'Element');
9
9
 
10
+ /**
11
+ * Let Alchemy handle the stylesheets of custom elements
12
+ *
13
+ * @author Jelle De Loecker <jelle@elevenways.be>
14
+ * @since 1.4.0
15
+ * @version 1.4.0
16
+ */
17
+ Classes.Hawkejs.Element.Element.setStylesheetHandler(function handleStylesheet(path) {
18
+
19
+ if (!path || Blast.isBrowser) {
20
+ return;
21
+ }
22
+
23
+ alchemy.registerRequiredStylesheet(path);
24
+ });
25
+
10
26
  /**
11
27
  * The default element prefix (when element contains no hyphen) is "al"
12
28
  *
@@ -59,11 +59,6 @@ Model.postInherit(function setModelName() {
59
59
  model_name = ns + '_' + model_name;
60
60
  }
61
61
 
62
- if (model_name[0] == '_') {
63
- console.log(ns, model_name)
64
- throw new Error('KAK')
65
- }
66
-
67
62
  // The simple name of the model
68
63
  this.model_name = model_name;
69
64
  this.setProperty('model_name', model_name);
@@ -1186,10 +1181,6 @@ Model.setMethod(function auditRecord(document, options, callback) {
1186
1181
  */
1187
1182
  Model.setMethod(function convertRecordToDatasourceFormat(record, options, callback) {
1188
1183
 
1189
- var that = this,
1190
- pledge,
1191
- data;
1192
-
1193
1184
  if (typeof options == 'function') {
1194
1185
  callback = options;
1195
1186
  options = {};
@@ -1199,12 +1190,12 @@ Model.setMethod(function convertRecordToDatasourceFormat(record, options, callba
1199
1190
  options = {};
1200
1191
  }
1201
1192
 
1202
- data = record[this.name] || record;
1193
+ let data = record.$main || record[this.model_name] || record;
1203
1194
 
1204
1195
  // Normalize the data
1205
1196
  data = this.compose(data, options);
1206
1197
 
1207
- pledge = this.datasource.toDatasource(this, data);
1198
+ let pledge = this.datasource.toDatasource(this, data);
1208
1199
 
1209
1200
  pledge.handleCallback(callback);
1210
1201