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,11 +1,11 @@
1
1
  /**
2
2
  * Route Class
3
3
  *
4
- * @author Jelle De Loecker <jelle@develry.be>
4
+ * @author Jelle De Loecker <jelle@elevenways.be>
5
5
  * @since 0.2.0
6
- * @version 1.2.5
6
+ * @version 1.3.1
7
7
  */
8
- var Route = Function.inherits('Alchemy.Base', function Route(router, paths, options) {
8
+ const Route = Function.inherits('Alchemy.Base', function Route(router, paths, options) {
9
9
 
10
10
  // Store the parent router
11
11
  this.router = router;
@@ -45,10 +45,19 @@ var Route = Function.inherits('Alchemy.Base', function Route(router, paths, opti
45
45
  this.permission = null;
46
46
  this.has_permission_assignments = false;
47
47
 
48
+ // The sitemap configuration
49
+ this.sitemap = null;
50
+
51
+ // Can this route's path be used in the browser's address location?
52
+ this.visible_location = true;
53
+
48
54
  // If no fnc is given, these will be called
49
55
  this.controller = null;
50
56
  this.action = null;
51
57
 
58
+ // All routes can be postponed by default
59
+ this.can_be_postponed = true;
60
+
52
61
  this.setPaths(paths);
53
62
  });
54
63
 
@@ -61,6 +70,65 @@ var Route = Function.inherits('Alchemy.Base', function Route(router, paths, opti
61
70
  */
62
71
  Route.setDeprecatedProperty('isMiddleware', 'is_middleware');
63
72
 
73
+ /**
74
+ * Does this route require extra data for translating to another language?
75
+ *
76
+ * @author Jelle De Loecker <jelle@elevenways.be>
77
+ * @since 1.3.0
78
+ * @version 1.3.0
79
+ *
80
+ * @return {Boolean}
81
+ */
82
+ Route.setProperty(function requires_data_for_translation() {
83
+
84
+ if (!this.is_prefix_aware || !this.has_path_assignments) {
85
+ return false;
86
+ }
87
+
88
+ return true;
89
+ });
90
+
91
+ /**
92
+ * Does this route have any path assignments?
93
+ *
94
+ * @author Jelle De Loecker <jelle@elevenways.be>
95
+ * @since 1.3.0
96
+ * @version 1.3.0
97
+ *
98
+ * @return {Boolean}
99
+ */
100
+ Route.setProperty(function has_path_assignments() {
101
+ return !!this.keys?.length;
102
+ });
103
+
104
+ /**
105
+ * Does this route use any type class checks?
106
+ * (Type CLASS checks use the type checker of a specific class)
107
+ *
108
+ * @author Jelle De Loecker <jelle@elevenways.be>
109
+ * @since 1.3.0
110
+ * @version 1.3.0
111
+ *
112
+ * @return {Boolean}
113
+ */
114
+ Route.setProperty(function has_type_class_checks() {
115
+
116
+ let result = false,
117
+ path,
118
+ key;
119
+
120
+ for (key in this.paths) {
121
+ path = this.paths[key];
122
+
123
+ if (path.uses_type_class_checks) {
124
+ result = true;
125
+ break;
126
+ }
127
+ }
128
+
129
+ return result;
130
+ });
131
+
64
132
  /**
65
133
  * Set the breadcrumb for this route
66
134
  *
@@ -117,12 +185,25 @@ Route.setMethod(function setPermission(permission) {
117
185
  }
118
186
  });
119
187
 
188
+ /**
189
+ * Set if this route can be postponed
190
+ *
191
+ * @author Jelle De Loecker <jelle@elevenways.be>
192
+ * @since 1.3.1
193
+ * @version 1.3.1
194
+ *
195
+ * @param {Boolean} postponable
196
+ */
197
+ Route.setMethod(function setCanBePostponed(postponable) {
198
+ this.can_be_postponed = postponable;
199
+ });
200
+
120
201
  /**
121
202
  * Compile paths for this route
122
203
  *
123
204
  * @author Jelle De Loecker <jelle@develry.be>
124
205
  * @since 0.2.0
125
- * @version 1.1.0
206
+ * @version 1.3.0
126
207
  *
127
208
  * @param {String|Object} paths
128
209
  */
@@ -150,7 +231,10 @@ Route.setMethod(function setPaths(paths) {
150
231
 
151
232
  for (prefix in paths) {
152
233
  path = paths[prefix];
153
- definition = new Classes.Alchemy.PathDefinition(path, {end: !this.is_middleware});
234
+ definition = new Classes.Alchemy.PathDefinition(path, {
235
+ prefix,
236
+ end: !this.is_middleware,
237
+ });
154
238
  this.paths[prefix] = definition;
155
239
 
156
240
  if (definition.keys.length) {
@@ -513,29 +597,21 @@ Route.setMethod(function generateUrl(parameters, conduit) {
513
597
  *
514
598
  * @author Jelle De Loecker <jelle@develry.be>
515
599
  * @since 0.2.0
516
- * @version 1.0.5
600
+ * @version 1.3.0
517
601
  *
518
602
  * @param {Conduit} conduit
519
603
  */
520
- Route.setMethod(function callController(conduit) {
604
+ Route.setMethod(function callController(...args) {
521
605
 
522
- var instance,
523
- args,
524
- i;
606
+ const conduit = args[0];
525
607
 
526
608
  if (this.controller) {
527
- instance = Controller.get(this.controller, conduit);
609
+ let instance = Controller.get(this.controller, conduit);
528
610
 
529
611
  if (!instance) {
530
612
  return conduit.error(new Error('Could not find controller "' + this.controller + '"'));
531
613
  }
532
614
 
533
- args = new Array(arguments.length);
534
-
535
- for (i = 0; i < args.length; i++) {
536
- args[i] = arguments[i];
537
- }
538
-
539
615
  return instance.doAction(this.action, args);
540
616
  }
541
617
 
@@ -546,41 +622,125 @@ Route.setMethod(function callController(conduit) {
546
622
  * Call a controller action as handler, but get info from the url first.
547
623
  * This gets called by `callHandler`.
548
624
  *
549
- * @author Jelle De Loecker <jelle@develry.be>
625
+ * @author Jelle De Loecker <jelle@elevenways.be>
550
626
  * @since 0.2.0
551
- * @version 0.2.0
627
+ * @version 1.3.0
552
628
  *
553
629
  * @param {Conduit} conduit
554
630
  */
555
- Route.setMethod(function callControllerAssignments(conduit) {
631
+ Route.setMethod(function callControllerAssignments(...args) {
556
632
 
557
- var controller,
558
- instance,
559
- action,
560
- args,
561
- i;
633
+ const conduit = args[0];
562
634
 
563
635
  if (this.controller) {
564
636
 
565
637
  // Get the controller name from the route parameters
566
- controller = this.controller.assign(conduit.params);
638
+ let controller = this.controller.assign(conduit.params);
567
639
 
568
640
  // Try getting a controller instance
569
- instance = Controller.get(controller, conduit);
641
+ let instance = Controller.get(controller, conduit);
570
642
 
571
643
  if (!instance) {
572
644
  throw new Error('Could not find controller "' + controller + '"');
573
645
  }
574
646
 
575
- args = new Array(arguments.length);
647
+ let action = this.action.assign(conduit.params);
648
+ return instance.doAction(action, args);
649
+ }
650
+
651
+ throw new Error('No valid controller was set');
652
+ });
653
+
654
+ /**
655
+ * Set the values needed for translating this route
656
+ *
657
+ * @author Jelle De Loecker <jelle@elevenways.be>
658
+ * @since 1.3.0
659
+ * @version 1.3.0
660
+ *
661
+ * @param {Controller} controller
662
+ * @param {Conduit} conduit
663
+ */
664
+ Route.setMethod(function getRouteTranslations(controller, conduit) {
665
+
666
+ if (!this.has_type_class_checks) {
667
+ return;
668
+ }
669
+
670
+ const current_prefix = conduit.prefix,
671
+ paths = [];
672
+
673
+ let path;
674
+
675
+ for (let key in this.paths) {
676
+
677
+ if (!key || key == current_prefix) {
678
+ continue;
679
+ }
680
+
681
+ path = this.paths[key];
576
682
 
577
- for (i = 0; i < args.length; i++) {
578
- args[i] = arguments[i];
683
+ if (!path?.param_definitions?.length) {
684
+ continue;
579
685
  }
580
686
 
581
- action = this.action.assign(conduit.params);
582
- return instance.doAction(action, args);
687
+ paths.push(path);
583
688
  }
584
689
 
585
- throw new Error('No valid controller was set');
690
+ if (!paths.length) {
691
+ return;
692
+ }
693
+
694
+ return this._getRouteTranslations(controller, conduit, paths);
695
+ });
696
+
697
+ /**
698
+ * Do the actual path value translations
699
+ *
700
+ * @author Jelle De Loecker <jelle@elevenways.be>
701
+ * @since 1.3.0
702
+ * @version 1.3.0
703
+ *
704
+ * @param {Controller} controller
705
+ * @param {Conduit} conduit
706
+ */
707
+ Route.setMethod(async function _getRouteTranslations(controller, conduit, paths) {
708
+
709
+ let param_def,
710
+ result = {},
711
+ path;
712
+
713
+ // Also add the current prefix
714
+ result[conduit.prefix] = alchemy.routeUrl(this.name, conduit.route_string_parameters, {prefix: conduit.prefix});
715
+
716
+ for (path of paths) {
717
+
718
+ // Create a shallow clone of the original string parameters
719
+ let params = Object.assign({}, conduit.route_string_parameters);
720
+
721
+ for (param_def of path.param_definitions) {
722
+ let current_value = conduit.param(param_def.name),
723
+ new_value;
724
+
725
+ if (current_value && current_value.getTranslatedValueOfField) {
726
+ new_value = await current_value.getTranslatedValueOfField(param_def.name, path.prefix);
727
+ }
728
+
729
+ if (!new_value) {
730
+ params = false;
731
+ break;
732
+ }
733
+
734
+ params[param_def.name] = new_value;
735
+ }
736
+
737
+ if (!params) {
738
+ result[path.prefix] = false;
739
+ continue;
740
+ }
741
+
742
+ result[path.prefix] = alchemy.routeUrl(this.name, params, {prefix: path.prefix});
743
+ }
744
+
745
+ return result;
586
746
  });
@@ -701,7 +701,7 @@ RouterClass.setMethod(function use(_paths, _fnc, _options) {
701
701
  *
702
702
  * @author Jelle De Loecker <jelle@develry.be>
703
703
  * @since 1.1.0
704
- * @version 1.1.0
704
+ * @version 1.3.0
705
705
  *
706
706
  * @param {String} module_name The name of the dependency
707
707
  * @param {Object} options The options or single string
@@ -740,6 +740,10 @@ RouterClass.setMethod(function serveDependencyFile(module_name, options) {
740
740
  }
741
741
 
742
742
  this.use(options.alias, function serveFile(req, res) {
743
+
744
+ // Cache this file so earlier middleware can handle this from now on
745
+ alchemy.getCache('files.assets').set(req.url, {path: options.path});
746
+
743
747
  req.conduit.serveFile(options.path);
744
748
  });
745
749
  });
@@ -761,9 +765,9 @@ RouterClass.setMethod(function setOption(name, value) {
761
765
  /**
762
766
  * Add a route
763
767
  *
764
- * @author Jelle De Loecker <jelle@develry.be>
768
+ * @author Jelle De Loecker <jelle@elevenways.be>
765
769
  * @since 0.2.0
766
- * @version 1.2.5
770
+ * @version 1.3.1
767
771
  *
768
772
  * @param {Object} args
769
773
  * @param {String} args.name Optional route name
@@ -813,11 +817,21 @@ RouterClass.setMethod(function add(args) {
813
817
  route.name = args.name;
814
818
  route.methods = args.methods;
815
819
  route.setBreadcrumb(args.breadcrumb);
820
+ route.sitemap = args.sitemap;
821
+ route.title = args.title;
822
+
823
+ if (args.visible_location != null) {
824
+ route.visible_location = args.visible_location;
825
+ }
816
826
 
817
827
  if (args.permission) {
818
828
  route.setPermission(args.permission);
819
829
  }
820
830
 
831
+ if (args.can_be_postponed != null) {
832
+ route.setCanBePostponed(args.can_be_postponed);
833
+ }
834
+
821
835
  if (args.breadcrumb) {
822
836
  breadcrumb_options = {
823
837
  route : route
@@ -846,6 +860,8 @@ RouterClass.setMethod(function add(args) {
846
860
  allroutes[this.name + '::' + args.name] = '' + this.name + ' - ' + args.name;
847
861
  }
848
862
 
863
+ alchemy.checkExposedRouteData();
864
+
849
865
  return route;
850
866
  });
851
867
 
@@ -1147,7 +1163,7 @@ RouterClass.setMethod(function getOptions(result) {
1147
1163
  *
1148
1164
  * @author Jelle De Loecker <jelle@elevenways.be>
1149
1165
  * @since 0.2.0
1150
- * @version 1.2.7
1166
+ * @version 1.3.0
1151
1167
  *
1152
1168
  * @param {Object} result Optional object to store sectioned results in
1153
1169
  *
@@ -1194,6 +1210,8 @@ RouterClass.setMethod(function getRoutes(result) {
1194
1210
  methods : route.methods,
1195
1211
  permission : route.permission || undefined,
1196
1212
  has_permission_assignments : route.has_permission_assignments || undefined,
1213
+ title : route.title,
1214
+ visible_location : route.visible_location,
1197
1215
  };
1198
1216
  }
1199
1217
 
@@ -74,7 +74,7 @@ Schema.setStatic(function unDry(value) {
74
74
  *
75
75
  * @author Jelle De Loecker <jelle@develry.be>
76
76
  * @since 0.2.0
77
- * @version 0.2.3
77
+ * @version 1.3.0
78
78
  *
79
79
  * @param {String} behaviour_name
80
80
  * @param {Object} options
@@ -88,7 +88,7 @@ Schema.setMethod(function addBehaviour(behaviour_name, options) {
88
88
  throw new Error('Could not find Behaviour "' + behaviour_name + '"');
89
89
  }
90
90
 
91
- this.hasBehaviours++;
91
+ this.has_behaviours++;
92
92
 
93
93
  if (!options) {
94
94
  options = {};
@@ -122,7 +122,7 @@ Schema.setMethod(function addBehaviour(behaviour_name, options) {
122
122
  *
123
123
  * @author Jelle De Loecker <jelle@develry.be>
124
124
  * @since 0.2.0
125
- * @version 0.3.0
125
+ * @version 1.3.0
126
126
  *
127
127
  * @param {Object} data
128
128
  * @param {String} hasType Get indexes with this type only
@@ -138,8 +138,8 @@ Schema.setMethod(function getRecordIndexes(data, hasType) {
138
138
  result = {};
139
139
 
140
140
  for (fieldName in data) {
141
- if (this.indexFields[fieldName] != null) {
142
- indexName = this.indexFields[fieldName].name;
141
+ if (this.index_fields[fieldName] != null) {
142
+ indexName = this.index_fields[fieldName].name;
143
143
 
144
144
  // Skip non alternates
145
145
  if (hasType && !this.indexes[indexName].options[hasType]) {
@@ -412,18 +412,18 @@ Schema.setMethod(function addAssociation(type, alias, modelName, options) {
412
412
  *
413
413
  * @author Jelle De Loecker <jelle@develry.be>
414
414
  * @since 0.2.0
415
- * @version 0.2.0
415
+ * @version 1.3.0
416
416
  *
417
417
  * @param {String} name
418
418
  * @param {Object} values
419
419
  */
420
420
  Schema.setMethod(function addEnumValues(name, values) {
421
421
 
422
- if (this.enumValues[name] == null) {
423
- this.enumValues[name] = {};
422
+ if (this.enum_values[name] == null) {
423
+ this.enum_values[name] = {};
424
424
  }
425
425
 
426
- Object.assign(this.enumValues[name], values);
426
+ Object.assign(this.enum_values[name], values);
427
427
  });
428
428
 
429
429
  /**
@@ -432,13 +432,13 @@ Schema.setMethod(function addEnumValues(name, values) {
432
432
  *
433
433
  * @author Jelle De Loecker <jelle@develry.be>
434
434
  * @since 0.2.0
435
- * @version 0.2.0
435
+ * @version 1.3.0
436
436
  *
437
437
  * @param {String} name
438
438
  * @param {Object} values
439
439
  */
440
440
  Schema.setMethod(function setEnumValues(name, values) {
441
- this.enumValues[name] = values;
441
+ this.enum_values[name] = values;
442
442
  });
443
443
 
444
444
  /**
@@ -514,4 +514,40 @@ Schema.setMethod(function hasMany(alias, modelName, options) {
514
514
  */
515
515
  Schema.setMethod(function hasOneChild(alias, modelName, options) {
516
516
  this.addAssociation('HasOneChild', alias, modelName, options);
517
+ });
518
+
519
+ /**
520
+ * Clone
521
+ *
522
+ * @author Jelle De Loecker <jelle@elevenways.be>
523
+ * @since 1.3.1
524
+ * @version 1.3.1
525
+ *
526
+ * @return {Object}
527
+ */
528
+ Schema.setMethod(function dryClone(wm, custom_method) {
529
+
530
+ if (!custom_method) {
531
+ custom_method = 'toShallowClone';
532
+ }
533
+
534
+ let cloned = JSON.clone(this.toDry(), custom_method, wm);
535
+
536
+ cloned = this.constructor.unDry(cloned.value);
537
+
538
+ return cloned;
539
+ });
540
+
541
+ /**
542
+ * Clone using JSON-Dry
543
+ * (Needed anyway because Deck also has a clone method)
544
+ *
545
+ * @author Jelle De Loecker <jelle@elevenways.be>
546
+ * @since 1.3.1
547
+ * @version 1.3.1
548
+ *
549
+ * @return {Object}
550
+ */
551
+ Schema.setMethod(function clone() {
552
+ return this.dryClone(new WeakMap(), 'toShallowClone');
517
553
  });