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.
- package/lib/app/behaviour/sluggable_behaviour.js +4 -2
- package/lib/app/conduit/http_conduit.js +7 -2
- package/lib/app/conduit/loopback_conduit.js +2 -2
- package/lib/app/conduit/socket_conduit.js +20 -5
- package/lib/app/controller/alchemy_info_controller.js +4 -8
- package/lib/app/helper/backed_map.js +2 -2
- package/lib/app/helper/router_helper.js +98 -24
- package/lib/app/helper_controller/controller.js +45 -30
- package/lib/app/helper_datasource/00-nosql_datasource.js +44 -10
- package/lib/app/helper_field/enum_field.js +4 -4
- package/lib/app/helper_field/schema_field.js +50 -36
- package/lib/app/helper_model/document.js +81 -46
- package/lib/app/helper_model/field_set.js +11 -0
- package/lib/app/helper_model/model.js +107 -53
- package/lib/app/helper_validator/00_validator.js +38 -6
- package/lib/app/helper_validator/not_empty_validator.js +1 -3
- package/lib/app/routes.js +7 -1
- package/lib/bootstrap.js +1 -0
- package/lib/class/conduit.js +438 -290
- package/lib/class/controller.js +18 -15
- package/lib/class/datasource.js +19 -8
- package/lib/class/document.js +3 -3
- package/lib/class/field.js +34 -3
- package/lib/class/inode.js +27 -0
- package/lib/class/inode_file.js +204 -4
- package/lib/class/migration.js +2 -1
- package/lib/class/model.js +16 -5
- package/lib/class/path_definition.js +76 -120
- package/lib/class/path_param_definition.js +202 -0
- package/lib/class/postponement.js +573 -0
- package/lib/class/route.js +193 -33
- package/lib/class/router.js +22 -4
- package/lib/class/schema.js +47 -11
- package/lib/class/schema_client.js +65 -35
- package/lib/class/session.js +138 -12
- package/lib/class/sitemap.js +341 -0
- package/lib/core/base.js +13 -3
- package/lib/core/client_alchemy.js +78 -7
- package/lib/core/client_base.js +16 -10
- package/lib/core/middleware.js +56 -45
- package/lib/init/alchemy.js +124 -11
- package/lib/init/constants.js +11 -0
- package/lib/init/functions.js +163 -86
- package/lib/stages.js +18 -3
- package/package.json +6 -6
|
@@ -1,21 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
let class_cache = new Map(),
|
|
2
|
+
fallback_datasource,
|
|
3
3
|
TABLE = Symbol('table');
|
|
4
4
|
|
|
5
5
|
if (Blast.isBrowser) {
|
|
6
6
|
Blast.once('hawkejs_init', function gotScene(hawkejs, variables, settings, view) {
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
model_name,
|
|
10
|
-
config,
|
|
11
|
-
key;
|
|
12
|
-
|
|
13
|
-
model_info = hawkejs.scene.exposed.model_info;
|
|
8
|
+
let config;
|
|
14
9
|
|
|
15
|
-
for (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
config.
|
|
10
|
+
for (config of hawkejs.scene.exposed.model_info) {
|
|
11
|
+
// First get or create the client-side Model class,
|
|
12
|
+
// then set some extra configuration coming from the server-side
|
|
13
|
+
Model.getClass(config.name).setModelConfig(config);
|
|
19
14
|
}
|
|
20
15
|
});
|
|
21
16
|
}
|
|
@@ -90,6 +85,30 @@ Model.setStatic(function hasServerAction(action) {
|
|
|
90
85
|
return !!config;
|
|
91
86
|
});
|
|
92
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Set the Model configuration
|
|
90
|
+
*
|
|
91
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
92
|
+
* @since 1.3.1
|
|
93
|
+
* @version 1.3.1
|
|
94
|
+
*
|
|
95
|
+
* @param {Object} config
|
|
96
|
+
*/
|
|
97
|
+
Model.setStatic(function setModelConfig(config) {
|
|
98
|
+
|
|
99
|
+
let model_name = this.model_name;
|
|
100
|
+
|
|
101
|
+
config.schema.setModel(model_name);
|
|
102
|
+
|
|
103
|
+
if (!this.prototype.hasOwnProperty('primary_key')) {
|
|
104
|
+
this.setProperty('primary_key', config.primary_key);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (!this.prototype.hasOwnProperty('display_field')) {
|
|
108
|
+
this.setProperty('display_field', config.display_field);
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
|
|
93
112
|
/**
|
|
94
113
|
* Table name to use in the database.
|
|
95
114
|
* False if no table should be used.
|
|
@@ -142,6 +161,19 @@ Model.setProperty(function datasource() {
|
|
|
142
161
|
return fallback_datasource;
|
|
143
162
|
});
|
|
144
163
|
|
|
164
|
+
/**
|
|
165
|
+
* Does this model have translatable fields?
|
|
166
|
+
*
|
|
167
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
168
|
+
* @since 1.3.0
|
|
169
|
+
* @version 1.3.0
|
|
170
|
+
*
|
|
171
|
+
* @type {Boolean}
|
|
172
|
+
*/
|
|
173
|
+
Model.setProperty(function has_translatable_fields() {
|
|
174
|
+
return this.schema.has_translatable_fields;
|
|
175
|
+
});
|
|
176
|
+
|
|
145
177
|
/**
|
|
146
178
|
* The conduit
|
|
147
179
|
*
|
|
@@ -188,50 +220,43 @@ Model.prepareStaticProperty('Document', function getDocumentClass() {
|
|
|
188
220
|
*
|
|
189
221
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
190
222
|
* @since 1.0.0
|
|
191
|
-
* @version 1.
|
|
223
|
+
* @version 1.3.1
|
|
192
224
|
*
|
|
193
225
|
* @param {String} model_name
|
|
194
226
|
* @param {Boolean} allow_create
|
|
227
|
+
* @param {String} parent
|
|
195
228
|
*/
|
|
196
|
-
Model.setStatic(function getClass(model_name, allow_create ) {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
class_path,
|
|
202
|
-
ModelClass,
|
|
203
|
-
config,
|
|
204
|
-
key;
|
|
229
|
+
Model.setStatic(function getClass(model_name, allow_create, parent) {
|
|
230
|
+
|
|
231
|
+
if (class_cache.has(model_name)) {
|
|
232
|
+
return class_cache.get(model_name);
|
|
233
|
+
}
|
|
205
234
|
|
|
206
235
|
// Construct the name of the class
|
|
207
|
-
class_name = model_name;
|
|
236
|
+
let class_name = model_name;
|
|
208
237
|
|
|
209
238
|
// Construct the path to this class
|
|
210
|
-
class_path = 'Alchemy.Client.Model.' + class_name;
|
|
239
|
+
let class_path = 'Alchemy.Client.Model.' + class_name;
|
|
211
240
|
|
|
212
241
|
// Get the class
|
|
213
|
-
ModelClass = Object.path(Blast.Classes, class_path);
|
|
242
|
+
let ModelClass = Object.path(Blast.Classes, class_path);
|
|
214
243
|
|
|
215
244
|
if (allow_create == null) {
|
|
216
245
|
allow_create = true;
|
|
217
246
|
}
|
|
218
247
|
|
|
219
248
|
if (ModelClass == null && allow_create) {
|
|
220
|
-
|
|
249
|
+
let config;
|
|
250
|
+
|
|
251
|
+
let model_constructor = Function.create(class_name, function ModelConstructor(record, options) {
|
|
221
252
|
ModelConstructor.wrapper.super.call(this, record, options);
|
|
222
253
|
});
|
|
223
254
|
|
|
224
255
|
// @TODO: inherit from parents
|
|
225
|
-
parent_path = 'Alchemy.Client.Model';
|
|
226
|
-
|
|
227
|
-
if (Blast.isBrowser && window._hawkejs_static_expose) {
|
|
228
|
-
let exposed = window._hawkejs_static_expose;
|
|
229
|
-
|
|
230
|
-
config = exposed.model_info;
|
|
256
|
+
let parent_path = 'Alchemy.Client.Model';
|
|
231
257
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
}
|
|
258
|
+
if (Blast.isBrowser) {
|
|
259
|
+
// No longer needed
|
|
235
260
|
} else if (Blast.isNode) {
|
|
236
261
|
config = alchemy.getModel(model_name, false);
|
|
237
262
|
|
|
@@ -245,8 +270,12 @@ Model.setStatic(function getClass(model_name, allow_create ) {
|
|
|
245
270
|
}
|
|
246
271
|
|
|
247
272
|
if (config && config.parent) {
|
|
248
|
-
|
|
249
|
-
|
|
273
|
+
parent = config.parent;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
if (parent) {
|
|
277
|
+
getClass(parent);
|
|
278
|
+
parent_path += '.' + parent;
|
|
250
279
|
}
|
|
251
280
|
|
|
252
281
|
ModelClass = Function.inherits(parent_path, model_constructor);
|
|
@@ -268,6 +297,8 @@ Model.setStatic(function getClass(model_name, allow_create ) {
|
|
|
268
297
|
}
|
|
269
298
|
}
|
|
270
299
|
|
|
300
|
+
class_cache.set(model_name, ModelClass);
|
|
301
|
+
|
|
271
302
|
return ModelClass;
|
|
272
303
|
});
|
|
273
304
|
|
|
@@ -349,7 +380,7 @@ Model.setProperty(function name() {
|
|
|
349
380
|
*
|
|
350
381
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
351
382
|
* @since 1.1.0
|
|
352
|
-
* @version 1.
|
|
383
|
+
* @version 1.3.0
|
|
353
384
|
*
|
|
354
385
|
* @param {Object} options
|
|
355
386
|
*/
|
|
@@ -371,7 +402,7 @@ Model.setMethod(function init(options) {
|
|
|
371
402
|
}
|
|
372
403
|
|
|
373
404
|
// Initialize behaviours
|
|
374
|
-
if (this.schema && this.schema.
|
|
405
|
+
if (this.schema && this.schema.has_behaviours && this.initBehaviours) {
|
|
375
406
|
this.initBehaviours();
|
|
376
407
|
}
|
|
377
408
|
});
|
|
@@ -568,7 +599,7 @@ Model.setMethod(function getAliasModel(alias) {
|
|
|
568
599
|
*
|
|
569
600
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
570
601
|
* @since 0.0.1
|
|
571
|
-
* @version 1.
|
|
602
|
+
* @version 1.3.0
|
|
572
603
|
*
|
|
573
604
|
* @param {String} type The type of find (first, all)
|
|
574
605
|
* @param {Criteria} criteria The criteria object
|
|
@@ -626,10 +657,17 @@ Model.setMethod(function find(type, criteria, callback) {
|
|
|
626
657
|
return pledge;
|
|
627
658
|
}
|
|
628
659
|
|
|
660
|
+
if (this.conduit) {
|
|
661
|
+
criteria.conduit = this.conduit;
|
|
662
|
+
}
|
|
663
|
+
|
|
664
|
+
if (!criteria.options.locale && this.conduit) {
|
|
665
|
+
criteria.setOption('locale', this.conduit.prefix);
|
|
666
|
+
}
|
|
667
|
+
|
|
629
668
|
let that = this,
|
|
630
669
|
available,
|
|
631
670
|
records,
|
|
632
|
-
query,
|
|
633
671
|
Type;
|
|
634
672
|
|
|
635
673
|
// The criteria instance has to know about the model
|
|
@@ -1310,8 +1348,7 @@ Model.setMethod(function save(data, _options, _callback) {
|
|
|
1310
1348
|
return iter.hasNext();
|
|
1311
1349
|
}, function saveData(next) {
|
|
1312
1350
|
|
|
1313
|
-
var document = iter.next().value
|
|
1314
|
-
temp;
|
|
1351
|
+
var document = iter.next().value;
|
|
1315
1352
|
|
|
1316
1353
|
// Skip invalid items
|
|
1317
1354
|
if (!document) {
|
|
@@ -1360,9 +1397,9 @@ Model.setMethod(function save(data, _options, _callback) {
|
|
|
1360
1397
|
* Create a new document.
|
|
1361
1398
|
* If data is given, the document is populated
|
|
1362
1399
|
*
|
|
1363
|
-
* @author Jelle De Loecker <jelle@
|
|
1400
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1364
1401
|
* @since 0.2.0
|
|
1365
|
-
* @version 1.
|
|
1402
|
+
* @version 1.3.0
|
|
1366
1403
|
*
|
|
1367
1404
|
* @param {Object} data Optional data
|
|
1368
1405
|
* @param {Object} options
|
|
@@ -1371,13 +1408,15 @@ Model.setMethod(function save(data, _options, _callback) {
|
|
|
1371
1408
|
*/
|
|
1372
1409
|
Model.setMethod(function createDocument(data, options) {
|
|
1373
1410
|
|
|
1374
|
-
|
|
1411
|
+
let original_record;
|
|
1375
1412
|
|
|
1376
1413
|
if (!options) {
|
|
1377
1414
|
options = {};
|
|
1378
1415
|
}
|
|
1379
1416
|
|
|
1380
1417
|
if (data && this.constructor.Document.isDocument(data)) {
|
|
1418
|
+
original_record = data.$attributes.original_record;
|
|
1419
|
+
|
|
1381
1420
|
data = {
|
|
1382
1421
|
[data.$model_alias] : data.$main,
|
|
1383
1422
|
};
|
|
@@ -1385,7 +1424,11 @@ Model.setMethod(function createDocument(data, options) {
|
|
|
1385
1424
|
|
|
1386
1425
|
options.model = this;
|
|
1387
1426
|
|
|
1388
|
-
doc = new this.constructor.Document(data, options);
|
|
1427
|
+
let doc = new this.constructor.Document(data, options);
|
|
1428
|
+
|
|
1429
|
+
if (original_record) {
|
|
1430
|
+
doc.$attributes.original_record = original_record;
|
|
1431
|
+
}
|
|
1389
1432
|
|
|
1390
1433
|
return doc;
|
|
1391
1434
|
});
|
|
@@ -1477,11 +1520,14 @@ Model.setProperty(function model_info() {
|
|
|
1477
1520
|
return {};
|
|
1478
1521
|
}
|
|
1479
1522
|
|
|
1480
|
-
|
|
1481
|
-
hawkejs.scene.exposed.model_info[name] = {};
|
|
1482
|
-
}
|
|
1523
|
+
let config;
|
|
1483
1524
|
|
|
1484
|
-
|
|
1525
|
+
for (config of hawkejs.scene.exposed.model_info) {
|
|
1526
|
+
if (config.name === name) {
|
|
1527
|
+
data = config;
|
|
1528
|
+
break;
|
|
1529
|
+
}
|
|
1530
|
+
}
|
|
1485
1531
|
} else {
|
|
1486
1532
|
|
|
1487
1533
|
let MainClass = Blast.Classes.Alchemy.Model[name];
|
|
@@ -1699,7 +1745,7 @@ Model.setMethod(function compose(data, options) {
|
|
|
1699
1745
|
*
|
|
1700
1746
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
1701
1747
|
* @since 0.2.0
|
|
1702
|
-
* @version 1.
|
|
1748
|
+
* @version 1.3.1
|
|
1703
1749
|
*
|
|
1704
1750
|
* @param {Object} data The record data to check
|
|
1705
1751
|
* @param {Object} options
|
|
@@ -1725,6 +1771,10 @@ Model.setMethod(function createRecord(data, options, callback) {
|
|
|
1725
1771
|
return callback(err);
|
|
1726
1772
|
}
|
|
1727
1773
|
|
|
1774
|
+
if (!that.datasource) {
|
|
1775
|
+
return callback(new Error('Model "' + that.model_name + '" has no datasource'));
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1728
1778
|
that.datasource.create(that, data, options, function afterCreate(err, result) {
|
|
1729
1779
|
|
|
1730
1780
|
if (err != null) {
|
|
@@ -1743,7 +1793,7 @@ Model.setMethod(function createRecord(data, options, callback) {
|
|
|
1743
1793
|
*
|
|
1744
1794
|
* @author Jelle De Loecker <jelle@develry.be>
|
|
1745
1795
|
* @since 0.2.0
|
|
1746
|
-
* @version 1.
|
|
1796
|
+
* @version 1.3.1
|
|
1747
1797
|
*
|
|
1748
1798
|
* @param {Object} data The record data to check
|
|
1749
1799
|
* @param {Object} options
|
|
@@ -1770,6 +1820,10 @@ Model.setMethod(function updateRecord(data, options, callback) {
|
|
|
1770
1820
|
return callback(err);
|
|
1771
1821
|
}
|
|
1772
1822
|
|
|
1823
|
+
if (!that.datasource) {
|
|
1824
|
+
return callback(new Error('Model "' + that.model_name + '" has no datasource'));
|
|
1825
|
+
}
|
|
1826
|
+
|
|
1773
1827
|
that.datasource.update(that, data, options, function afterUpdate(err, result) {
|
|
1774
1828
|
|
|
1775
1829
|
if (err != null) {
|
|
@@ -60,13 +60,13 @@ Validator.setMethod(function toDry() {
|
|
|
60
60
|
*
|
|
61
61
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
62
62
|
* @since 1.1.0
|
|
63
|
-
* @version 1.1
|
|
63
|
+
* @version 1.3.1
|
|
64
64
|
*
|
|
65
65
|
* @param {FieldValue} fv The FieldValue instance
|
|
66
66
|
*
|
|
67
67
|
* @return {String}
|
|
68
68
|
*/
|
|
69
|
-
Validator.
|
|
69
|
+
Validator.setTypedMethod([Types.Alchemy.FieldValue], function getInvalidFieldMessage(fv) {
|
|
70
70
|
|
|
71
71
|
let field = fv.field,
|
|
72
72
|
value = fv.value;
|
|
@@ -105,20 +105,52 @@ Validator.setMethod(function createFieldViolation(fv) {
|
|
|
105
105
|
return result;
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Validate a value
|
|
110
|
+
*
|
|
111
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
112
|
+
* @since 1.3.1
|
|
113
|
+
* @version 1.3.1
|
|
114
|
+
*
|
|
115
|
+
* @param {Field} field
|
|
116
|
+
* @param {*} value
|
|
117
|
+
*
|
|
118
|
+
* @return {Promise<Violation|undefined>}
|
|
119
|
+
*/
|
|
120
|
+
Validator.setTypedMethod([Types.Alchemy.Field, Types.Any], function validateFieldValue(field, value) {
|
|
121
|
+
return this.validateFieldValue(field, field.path, value);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Validate a value
|
|
126
|
+
*
|
|
127
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
128
|
+
* @since 1.3.1
|
|
129
|
+
* @version 1.3.1
|
|
130
|
+
*
|
|
131
|
+
* @param {Field} field
|
|
132
|
+
* @param {String} path
|
|
133
|
+
* @param {*} value
|
|
134
|
+
*
|
|
135
|
+
* @return {Promise<Violation|undefined>}
|
|
136
|
+
*/
|
|
137
|
+
Validator.setTypedMethod([Types.Alchemy.Field, Types.String, Types.Any], function validateFieldValue(field, path, value) {
|
|
138
|
+
let fv = new Classes.Alchemy.FieldValue(field, path, value);
|
|
139
|
+
return this.validateFieldValue(fv);
|
|
140
|
+
});
|
|
141
|
+
|
|
108
142
|
/**
|
|
109
143
|
* Validate a value
|
|
110
144
|
*
|
|
111
145
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
112
146
|
* @since 1.1.0
|
|
113
|
-
* @version 1.1
|
|
147
|
+
* @version 1.3.1
|
|
114
148
|
*
|
|
115
149
|
* @param {FieldValue[]} field_values The FieldValue instance(s)
|
|
116
150
|
*
|
|
117
151
|
* @return {Promise<Violation|undefined>}
|
|
118
152
|
*/
|
|
119
|
-
Validator.
|
|
120
|
-
|
|
121
|
-
field_values = Array.cast(field_values);
|
|
153
|
+
Validator.setTypedMethod([Types.Alchemy.FieldValue.array()], async function validateFieldValue(field_values) {
|
|
122
154
|
|
|
123
155
|
let violation,
|
|
124
156
|
passes,
|
|
@@ -10,9 +10,7 @@
|
|
|
10
10
|
*
|
|
11
11
|
* @param {Object} options
|
|
12
12
|
*/
|
|
13
|
-
|
|
14
|
-
NotEmpty.super.call(this, options);
|
|
15
|
-
});
|
|
13
|
+
const NotEmpty = Function.inherits('Alchemy.Validator', 'NotEmpty');
|
|
16
14
|
|
|
17
15
|
/**
|
|
18
16
|
* The message to use inside errors
|
package/lib/app/routes.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
1
|
Router.add(['get', 'post'], 'APIResource', '/api/{action}', 'Api#{action}');
|
|
2
2
|
Router.get('AlchemyInfo', '/alchemy-info', 'AlchemyInfo#info');
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
Router.POSTPONED_ROUTE = Router.add({
|
|
5
|
+
name : 'AlchemyInfo#postponed',
|
|
6
|
+
paths : '/alchemy/postponed/{id}',
|
|
7
|
+
methods : ['get'],
|
|
8
|
+
can_be_postponed : false,
|
|
9
|
+
});
|
package/lib/bootstrap.js
CHANGED
|
@@ -160,6 +160,7 @@ hawkejs_options.server = false;
|
|
|
160
160
|
* @version 1.1.0
|
|
161
161
|
*/
|
|
162
162
|
alchemy.hawkejs.load(libpath.resolve(PATH_CORE, 'class', 'path_definition.js'), hawkejs_options);
|
|
163
|
+
alchemy.hawkejs.load(libpath.resolve(PATH_CORE, 'class', 'path_param_definition.js'), hawkejs_options);
|
|
163
164
|
|
|
164
165
|
/**
|
|
165
166
|
* Require the element class on the client side too
|