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
@@ -79,6 +79,12 @@ Schema.setStatic(function unDry(value, custom_method, whenDone) {
79
79
  Schema.setDeprecatedProperty('modelName', 'model_name');
80
80
  Schema.setDeprecatedProperty('modelClass', 'model_class');
81
81
  Schema.setDeprecatedProperty('modelInstance', 'model_instance');
82
+ Schema.setDeprecatedProperty('hasTranslations', 'has_translations');
83
+ Schema.setDeprecatedProperty('hasAlternates', 'has_alternates');
84
+ Schema.setDeprecatedProperty('translatableFields', 'translatable_fields');
85
+ Schema.setDeprecatedProperty('indexFields', 'index_fields');
86
+ Schema.setDeprecatedProperty('enumValues', 'enum_values');
87
+ Schema.setDeprecatedProperty('hasBehaviours', 'has_behaviours');
82
88
 
83
89
  /**
84
90
  * Set a reference to itself
@@ -128,17 +134,34 @@ Schema.setProperty(function field_count() {
128
134
  return result;
129
135
  });
130
136
 
137
+ /**
138
+ * Does this schema have translatable fields?
139
+ *
140
+ * @author Jelle De Loecker <jelle@elevenways.be>
141
+ * @since 1.3.0
142
+ * @version 1.3.0
143
+ *
144
+ * @type {Boolean}
145
+ */
146
+ Schema.setProperty(function has_translatable_fields() {
147
+ return this.has_translations;
148
+ });
149
+
131
150
  /**
132
151
  * Clone for JSON-Dry (JSON.clone())
133
152
  *
134
153
  * @author Jelle De Loecker <jelle@elevenways.be>
135
154
  * @since 1.1.3
136
- * @version 1.1.3
155
+ * @version 1.3.1
137
156
  *
138
157
  * @return {Object}
139
158
  */
140
- Schema.setMethod(function dryClone() {
141
- return this.clone();
159
+ Schema.setMethod(function dryClone(wm, custom_method) {
160
+
161
+ let obj = JSON.toDryObject(this),
162
+ cloned = JSON.undry(obj);
163
+
164
+ return cloned;
142
165
  });
143
166
 
144
167
  /**
@@ -147,16 +170,12 @@ Schema.setMethod(function dryClone() {
147
170
  *
148
171
  * @author Jelle De Loecker <jelle@elevenways.be>
149
172
  * @since 1.1.3
150
- * @version 1.1.3
173
+ * @version 1.3.1
151
174
  *
152
175
  * @return {Object}
153
176
  */
154
177
  Schema.setMethod(function clone() {
155
-
156
- let obj = JSON.toDryObject(this),
157
- cloned = JSON.undry(obj);
158
-
159
- return cloned;
178
+ return this.dryClone();
160
179
  });
161
180
 
162
181
  /**
@@ -164,21 +183,31 @@ Schema.setMethod(function clone() {
164
183
  *
165
184
  * @author Jelle De Loecker <jelle@develry.be>
166
185
  * @since 1.1.0
167
- * @version 1.1.0
186
+ * @version 1.3.1
168
187
  *
169
188
  * @return {Object}
170
189
  */
171
190
  Schema.setMethod(function toDry() {
172
191
 
173
- var value = {
192
+ const value = {
174
193
  name : this.name,
175
194
  associations : this.associations,
176
195
  fields : [],
177
- rules : this.rules,
196
+ rules : new Deck(),
178
197
  };
179
198
 
180
- if (this.parent) {
181
- value.parent = this.parent;
199
+ let rule_dict = this.rules.getDict();
200
+
201
+ // Only add rules that are manually added to the schema,
202
+ // not ones defined by the field (else we would revive the rules double)
203
+ for (let key in rule_dict) {
204
+ let entry = rule_dict[key];
205
+
206
+ if (entry.options?.from_field) {
207
+ continue;
208
+ }
209
+
210
+ value.rules.set(key, entry);
182
211
  }
183
212
 
184
213
  if (this.model_name) {
@@ -191,24 +220,25 @@ Schema.setMethod(function toDry() {
191
220
 
192
221
  value.options = this.options;
193
222
 
223
+ let result = {value};
224
+
194
225
  // Get the sorted fields
195
- let options,
196
- fields = this.getSorted(false),
226
+ let fields = this.getSorted(false),
197
227
  field,
198
228
  i;
199
-
229
+
200
230
  for (i = 0; i < fields.length; i++) {
201
231
  field = fields[i];
202
232
 
203
233
  value.fields.push({
204
234
  name : field.name,
205
- options : field.options,
235
+ options : field.getOptionsForDrying(),
206
236
  class_name : field.constructor.name,
207
237
  namespace : field.constructor.namespace,
208
238
  });
209
239
  }
210
240
 
211
- return {value: value};
241
+ return result;
212
242
  });
213
243
 
214
244
  /**
@@ -216,7 +246,7 @@ Schema.setMethod(function toDry() {
216
246
  *
217
247
  * @author Jelle De Loecker <jelle@develry.be>
218
248
  * @since 1.1.0
219
- * @version 1.1.0
249
+ * @version 1.3.0
220
250
  */
221
251
  Schema.setMethod(function init() {
222
252
  this.associations = {};
@@ -225,19 +255,19 @@ Schema.setMethod(function init() {
225
255
  this.indexes = {};
226
256
 
227
257
  // All fields belonging to an index group
228
- this.indexFields = {};
258
+ this.index_fields = {};
229
259
 
230
260
  // All translatable fields
231
- this.translatableFields = {};
261
+ this.translatable_fields = {};
232
262
 
233
263
  // Amount of translatable fields
234
- this.hasTranslations = 0;
264
+ this.has_translations = 0;
235
265
 
236
266
  // Amount of alternate indexes
237
- this.hasAlternates = 0;
267
+ this.has_alternates = 0;
238
268
 
239
269
  // Enum values
240
- this.enumValues = {};
270
+ this.enum_values = {};
241
271
 
242
272
  // Attached behaviours
243
273
  this.behaviours = {};
@@ -249,7 +279,7 @@ Schema.setMethod(function init() {
249
279
  this.rules = new Deck();
250
280
 
251
281
  // Behaviour count
252
- this.hasBehaviours = 0;
282
+ this.has_behaviours = 0;
253
283
  });
254
284
 
255
285
  /**
@@ -448,9 +478,9 @@ Schema.setMethod(function getPrivateFields() {
448
478
  /**
449
479
  * Add a field to this schema
450
480
  *
451
- * @author Jelle De Loecker <jelle@develry.be>
481
+ * @author Jelle De Loecker <jelle@elevenways.be>
452
482
  * @since 0.2.0
453
- * @version 1.1.7
483
+ * @version 1.3.0
454
484
  *
455
485
  * @param {String} name
456
486
  * @param {String} type
@@ -510,8 +540,8 @@ Schema.setMethod(function addField(name, type, options) {
510
540
  field = new FieldClass(this, name, options);
511
541
 
512
542
  if (field.requires_translating) {
513
- this.hasTranslations++;
514
- this.translatableFields[name] = field;
543
+ this.has_translations++;
544
+ this.translatable_fields[name] = field;
515
545
  }
516
546
 
517
547
  this.set(name, field);
@@ -521,12 +551,12 @@ Schema.setMethod(function addField(name, type, options) {
521
551
  i;
522
552
 
523
553
  for (i = 0; i < rules.length; i++) {
524
- this.addRule(rules[i], [name]);
554
+ this.addRule(rules[i], {from_field: true, fields: [name]});
525
555
  }
526
556
  }
527
557
 
528
558
  if (options.required) {
529
- this.addRule('not_empty', [name]);
559
+ this.addRule('not_empty', {from_field: true, fields: [name]});
530
560
  }
531
561
 
532
562
  return field;
@@ -706,7 +736,7 @@ Schema.setMethod(function getFieldNames() {
706
736
  *
707
737
  * @author Jelle De Loecker <jelle@develry.be>
708
738
  * @since 0.2.0
709
- * @version 1.1.0
739
+ * @version 1.3.0
710
740
  *
711
741
  * @param {String|FieldType} _field
712
742
  * @param {Object} options
@@ -772,7 +802,7 @@ Schema.setMethod(function addIndex(_field, _options) {
772
802
  // it needs the 'alternate' property in order to be used
773
803
  // as an alternate method of updating without _id
774
804
  if (options.alternate) {
775
- this.hasAlternates++;
805
+ this.has_alternates++;
776
806
  }
777
807
 
778
808
  that.getDatasource().done(function gotDs(err, datasource) {
@@ -799,7 +829,7 @@ Schema.setMethod(function addIndex(_field, _options) {
799
829
 
800
830
  // Store the field order in the index groups
801
831
  that.indexes[options.name].fields[path] = options.order;
802
- that.indexFields[path] = options;
832
+ that.index_fields[path] = options;
803
833
 
804
834
  datasource.ensureIndex(that.model_class, that.indexes[options.name], function ensuredIndex(err, result) {
805
835
 
@@ -5,7 +5,7 @@ var data_listeners = alchemy.shared('data_binding_listeners');
5
5
  *
6
6
  * @author Jelle De Loecker <jelle@develry.be>
7
7
  * @since 0.2.0
8
- * @version 1.1.0
8
+ * @version 1.3.1
9
9
  *
10
10
  * @param {Conduit} conduit The initializing conduit
11
11
  */
@@ -36,10 +36,19 @@ var Session = Function.inherits('Alchemy.Base', function ClientSession(conduit)
36
36
  this.data_listener_ids = {};
37
37
 
38
38
  // Postponed requests
39
- this.postponed = new Classes.Develry.Cache();
39
+ this.postponements = new Classes.Develry.Cache();
40
+
41
+ // Postponed requests are invalidated after 3 hours
42
+ this.postponements.max_age = 3 * 60 * 60 * 1000;
43
+
44
+ // Time spent in the queue
45
+ this.queued_time = 0;
40
46
 
41
- // Postponed requests are invalidated after 1 hour
42
- this.postponed.max_age = 60 * 60 * 1000;
47
+ // Any postponements that have happened already
48
+ this.postponement_counter = 0;
49
+
50
+ // The amount of renders that have happened during this session
51
+ this.render_counter = 0;
43
52
 
44
53
  // Increment the alchemy session count
45
54
  alchemy.session_count++;
@@ -103,6 +112,56 @@ Session.setProperty(function idle_time() {
103
112
  return result;
104
113
  });
105
114
 
115
+ /**
116
+ * Is this an active session?
117
+ * When no activity happens for 5 minutes, the session becomes inactive.
118
+ *
119
+ * @author Jelle De Loecker <jelle@elevenways.be>
120
+ * @since 1.3.1
121
+ * @version 1.3.1
122
+ *
123
+ * @type {Boolean}
124
+ */
125
+ Session.setProperty(function is_active() {
126
+
127
+ if (this.idle_time < 5 * 60 * 1000) {
128
+ return true;
129
+ }
130
+
131
+ return false;
132
+ });
133
+
134
+ /**
135
+ * Add the amount of time this client has been in the queue
136
+ *
137
+ * @author Jelle De Loecker <jelle@elevenways.be>
138
+ * @since 1.3.1
139
+ * @version 1.3.1
140
+ *
141
+ * @return {Boolean}
142
+ */
143
+ Session.setMethod(function addFinishedQueueDuration(ms) {
144
+ this.queued_time += ms;
145
+ });
146
+
147
+ /**
148
+ * Has this client already been in the queue?
149
+ *
150
+ * @author Jelle De Loecker <jelle@elevenways.be>
151
+ * @since 1.3.1
152
+ * @version 1.3.1
153
+ *
154
+ * @return {Boolean}
155
+ */
156
+ Session.setMethod(function hasAlreadyQueued() {
157
+
158
+ if (this.queued_time > 0) {
159
+ return true;
160
+ }
161
+
162
+ return false;
163
+ });
164
+
106
165
  /**
107
166
  * Register a conduit
108
167
  *
@@ -134,21 +193,84 @@ Session.setMethod(function createMenuItem(conduit) {
134
193
  /**
135
194
  * Postpone the result
136
195
  *
137
- * @author Jelle De Loecker <jelle@develry.be>
196
+ * @author Jelle De Loecker <jelle@elevenways.be>
138
197
  * @since 0.2.0
139
- * @version 0.4.0
198
+ * @version 1.3.1
140
199
  *
141
200
  * @param {Conduit} conduit The conduit to postpone
201
+ * @param {Object} options
142
202
  *
143
- * @return {String} The postponed id
203
+ * @return {Alchemy.Conduit.Postponement} The postponement
204
+ */
205
+ Session.setMethod(function postpone(conduit, options) {
206
+
207
+ let id = Crypto.uid();
208
+
209
+ let postponement = new Classes.Alchemy.Conduit.Postponement(conduit, id, options);
210
+
211
+ this.postponements.set(id, postponement);
212
+
213
+ this.postponement_counter++;
214
+
215
+ return postponement;
216
+ });
217
+
218
+ /**
219
+ * Increment the render count
220
+ *
221
+ * @author Jelle De Loecker <jelle@elevenways.be>
222
+ * @since 1.3.1
223
+ * @version 1.3.1
224
+ *
225
+ * @return {Number} The amount of renders
144
226
  */
145
- Session.setMethod(function postpone(conduit) {
227
+ Session.setMethod(function incrementRenderCount() {
228
+ return ++this.render_counter;
229
+ });
146
230
 
147
- var key = Crypto.uid();
231
+ /**
232
+ * Get a postponement by its id
233
+ *
234
+ * @author Jelle De Loecker <jelle@elevenways.be>
235
+ * @since 1.3.1
236
+ * @version 1.3.1
237
+ *
238
+ * @param {String} id The postponement id
239
+ *
240
+ * @return {Alchemy.Conduit.Postponement} The postponement
241
+ */
242
+ Session.setMethod(function getPostponement(id) {
243
+ return this.postponements.get(id);
244
+ });
148
245
 
149
- this.postponed.set(key, conduit);
246
+ /**
247
+ * See if a postponement already exists for the given conduit
248
+ *
249
+ * @author Jelle De Loecker <jelle@elevenways.be>
250
+ * @since 1.3.1
251
+ * @version 1.3.1
252
+ *
253
+ * @param {Conduit} conduit
254
+ *
255
+ * @return {Alchemy.Conduit.Postponement} The postponement
256
+ */
257
+ Session.setMethod(function getExistingPostponement(conduit) {
258
+
259
+ if (!this.postponement_counter) {
260
+ return;
261
+ }
262
+
263
+ if (!this.postponements.length) {
264
+ return;
265
+ }
150
266
 
151
- return key;
267
+ let postponement;
268
+
269
+ for (postponement of this.postponements) {
270
+ if (postponement.original_path === conduit.path) {
271
+ return postponement;
272
+ }
273
+ }
152
274
  });
153
275
 
154
276
  /**
@@ -156,7 +278,7 @@ Session.setMethod(function postpone(conduit) {
156
278
  *
157
279
  * @author Jelle De Loecker <jelle@develry.be>
158
280
  * @since 0.2.0
159
- * @version 1.1.0
281
+ * @version 1.3.1
160
282
  *
161
283
  * @param {Boolean} expired True if removed because time ran out
162
284
  */
@@ -180,6 +302,10 @@ Session.setMethod(function removed(expired) {
180
302
  this.menu_item.remove();
181
303
  }
182
304
 
305
+ for (let postponement of this.postponements) {
306
+ postponement.expire();
307
+ }
308
+
183
309
  this.emit('removed', expired);
184
310
  });
185
311