alchemymvc 1.3.20 → 1.3.21
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/helper/alchemy_helper.js +8 -4
- package/lib/app/helper_controller/controller.js +1 -0
- package/lib/app/helper_field/big_int_field.js +2 -2
- package/lib/app/helper_field/mixed_field.js +73 -0
- package/lib/app/helper_field/schema_field.js +110 -35
- package/lib/app/helper_model/criteria.js +13 -0
- package/lib/app/helper_model/document.js +229 -0
- package/lib/app/helper_model/model.js +67 -8
- package/lib/class/conduit.js +2 -2
- package/lib/class/document.js +27 -0
- package/lib/class/field.js +110 -0
- package/lib/class/model.js +47 -12
- package/lib/class/route.js +4 -1
- package/lib/class/router.js +4 -2
- package/lib/class/schema_client.js +163 -20
- package/lib/class/task.js +48 -2
- package/lib/class/task_service.js +5 -1
- package/lib/core/client_alchemy.js +77 -1
- package/lib/core/middleware.js +27 -7
- package/lib/init/alchemy.js +4 -1
- package/package.json +3 -3
|
@@ -30,7 +30,7 @@ Alchemy.setStatic(function onScene(scene, options) {
|
|
|
30
30
|
*
|
|
31
31
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
32
32
|
* @since 0.0.1
|
|
33
|
-
* @version 1.
|
|
33
|
+
* @version 1.3.21
|
|
34
34
|
*
|
|
35
35
|
* @param {String|Object} options
|
|
36
36
|
* @param {Object} data
|
|
@@ -122,10 +122,14 @@ Alchemy.setMethod(function getResource(options, data, callback) {
|
|
|
122
122
|
|
|
123
123
|
config = this.view.helpers.Router.routeConfig(options.name);
|
|
124
124
|
|
|
125
|
-
if (config
|
|
126
|
-
if (config.methods.indexOf('get') == -1) {
|
|
125
|
+
if (config) {
|
|
126
|
+
if (config.methods && config.methods.indexOf('get') == -1) {
|
|
127
127
|
method = config.methods[0];
|
|
128
128
|
}
|
|
129
|
+
|
|
130
|
+
if (config.cache != null && options.cache == null) {
|
|
131
|
+
options.cache = config.cache;
|
|
132
|
+
}
|
|
129
133
|
}
|
|
130
134
|
|
|
131
135
|
// Get the url to the resource
|
|
@@ -150,7 +154,7 @@ Alchemy.setMethod(function getResource(options, data, callback) {
|
|
|
150
154
|
options[method] = true;
|
|
151
155
|
}
|
|
152
156
|
|
|
153
|
-
pledge =
|
|
157
|
+
pledge = this.view.fetch(options);
|
|
154
158
|
pledge.done(callback);
|
|
155
159
|
|
|
156
160
|
return pledge;
|
|
@@ -359,6 +359,7 @@ Controller.setMethod(async function doAction(name, args) {
|
|
|
359
359
|
if (route) {
|
|
360
360
|
|
|
361
361
|
if (route.options?.title) {
|
|
362
|
+
// @TODO: Add support for objects with language keys
|
|
362
363
|
let title = route.options.title;
|
|
363
364
|
|
|
364
365
|
if (alchemy.settings && alchemy.settings.title_suffix) {
|
|
@@ -50,7 +50,7 @@ BigIntField.setMethod(function cast(value) {
|
|
|
50
50
|
*
|
|
51
51
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
52
52
|
* @since 1.3.6
|
|
53
|
-
* @version 1.3.
|
|
53
|
+
* @version 1.3.21
|
|
54
54
|
*
|
|
55
55
|
* @param {Mixed} value
|
|
56
56
|
* @param {Array} field_paths The path to the field
|
|
@@ -60,7 +60,7 @@ BigIntField.setMethod(function cast(value) {
|
|
|
60
60
|
BigIntField.setMethod(function _castCondition(value, field_paths) {
|
|
61
61
|
|
|
62
62
|
value = this.cast(value);
|
|
63
|
-
value = this.convertBigIntForDatasource(value);
|
|
63
|
+
value = this.datasource.convertBigIntForDatasource(value);
|
|
64
64
|
|
|
65
65
|
return value;
|
|
66
66
|
});
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Mixed Field class
|
|
3
|
+
*
|
|
4
|
+
* @constructor
|
|
5
|
+
*
|
|
6
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
7
|
+
* @since 1.3.21
|
|
8
|
+
* @version 1.3.21
|
|
9
|
+
*/
|
|
10
|
+
const MixedField = Function.inherits('Alchemy.Field', 'Mixed');
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Set the datatype name
|
|
14
|
+
*
|
|
15
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
16
|
+
* @since 1.3.21
|
|
17
|
+
* @version 1.3.21
|
|
18
|
+
*/
|
|
19
|
+
MixedField.setDatatype('object');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This field value is self-contained
|
|
23
|
+
*
|
|
24
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
25
|
+
* @since 1.3.21
|
|
26
|
+
* @version 1.3.21
|
|
27
|
+
*/
|
|
28
|
+
MixedField.setSelfContained(true);
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Store objects as strings, if wanted
|
|
32
|
+
*
|
|
33
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
34
|
+
* @since 1.3.21
|
|
35
|
+
* @version 1.3.21
|
|
36
|
+
*
|
|
37
|
+
* @param {Mixed} value The field's own value
|
|
38
|
+
* @param {Object} data The main record
|
|
39
|
+
* @param {Datasource} datasource The datasource instance
|
|
40
|
+
*
|
|
41
|
+
* @return {Mixed}
|
|
42
|
+
*/
|
|
43
|
+
MixedField.setMethod(function _toDatasource(value, data, datasource, callback) {
|
|
44
|
+
|
|
45
|
+
if (value && typeof value == 'object' && !Object.isPlainObject(value)) {
|
|
46
|
+
if (!(value instanceof Date)) {
|
|
47
|
+
value = JSON.toDryObject(value);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Blast.nextTick(callback, null, null, value);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Convert from database to app
|
|
56
|
+
*
|
|
57
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
58
|
+
* @since 1.3.21
|
|
59
|
+
* @version 1.3.21
|
|
60
|
+
*
|
|
61
|
+
* @param {Object} query The original query
|
|
62
|
+
* @param {Object} options The original query options
|
|
63
|
+
* @param {Mixed} value The field value, as stored in the DB
|
|
64
|
+
* @param {Function} callback
|
|
65
|
+
*/
|
|
66
|
+
MixedField.setMethod(function _toApp(query, options, value, callback) {
|
|
67
|
+
|
|
68
|
+
if (value && typeof value == 'object' && typeof value.dry == 'string') {
|
|
69
|
+
value = JSON.unDry(value);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
callback(null, value);
|
|
73
|
+
});
|
|
@@ -110,7 +110,7 @@ SchemaField.setProperty(function requires_translating() {
|
|
|
110
110
|
*
|
|
111
111
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
112
112
|
* @since 0.2.0
|
|
113
|
-
* @version 1.3.
|
|
113
|
+
* @version 1.3.21
|
|
114
114
|
*
|
|
115
115
|
* @param {Object} record This *should* be the schema context (might not be the root)
|
|
116
116
|
* @param {String} some_path Some path to a field in the wanted schema
|
|
@@ -124,56 +124,131 @@ SchemaField.setMethod(function getSubschema(record, some_path) {
|
|
|
124
124
|
// If schema is a string,
|
|
125
125
|
// it needs to be extracted from another field's value
|
|
126
126
|
if (typeof schema == 'string') {
|
|
127
|
+
schema = this.resolveSchemaPath(this.schema, record, some_path, schema);
|
|
128
|
+
}
|
|
127
129
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
+
return schema;
|
|
131
|
+
});
|
|
130
132
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
+
/**
|
|
134
|
+
* Get the subschema of a given schema
|
|
135
|
+
*
|
|
136
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
137
|
+
* @since 1.3.21
|
|
138
|
+
* @version 1.3.21
|
|
139
|
+
*
|
|
140
|
+
* @param {Schema} context_schema The schema context (probably the schema this field is in)
|
|
141
|
+
* @param {Object} record This *should* be the schema context (might not be the root)
|
|
142
|
+
* @param {String} some_path Some path to a field in the wanted schema
|
|
143
|
+
* @param {String} schema The schema path to resolve
|
|
144
|
+
*
|
|
145
|
+
* @return {Schema}
|
|
146
|
+
*/
|
|
147
|
+
SchemaField.setMethod(function resolveSchemaPath(context_schema, record, field_path, schema_path) {
|
|
133
148
|
|
|
134
|
-
|
|
135
|
-
|
|
149
|
+
if (typeof schema_path != 'string') {
|
|
150
|
+
return schema_path;
|
|
151
|
+
}
|
|
136
152
|
|
|
137
|
-
|
|
138
|
-
let field = this.schema.getField(external_field_name);
|
|
153
|
+
let schema;
|
|
139
154
|
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
155
|
+
// When there are 2 pieces, the second piece is the property name
|
|
156
|
+
let pieces = schema_path.split('.');
|
|
144
157
|
|
|
145
|
-
|
|
146
|
-
|
|
158
|
+
// The first piece is the external field name
|
|
159
|
+
let external_field_name = pieces.shift();
|
|
147
160
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
161
|
+
// The rest of the pieces is the property path
|
|
162
|
+
let property_name = pieces.join('.') || 'schema';
|
|
151
163
|
|
|
152
|
-
|
|
153
|
-
|
|
164
|
+
// Get that other field by its name
|
|
165
|
+
let field = context_schema.getField(external_field_name);
|
|
154
166
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
167
|
+
if (!field) {
|
|
168
|
+
let association = context_schema.getAssociation(external_field_name);
|
|
169
|
+
return this.getSubschemaFromAssociation(record, property_name, association);
|
|
170
|
+
}
|
|
159
171
|
|
|
160
|
-
|
|
161
|
-
|
|
172
|
+
if (!field) {
|
|
173
|
+
console.error('Failed to get subschema', external_field_name, 'of', context_schema, field_path);
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
162
176
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
177
|
+
// Get the values that field can have (probably an enum)
|
|
178
|
+
let values = field.getValues();
|
|
179
|
+
|
|
180
|
+
if (values == null) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Now get the actual external value from the record
|
|
185
|
+
let record_value = field.getRecordValue(record);
|
|
186
|
+
|
|
187
|
+
// I'm not sure if this will help
|
|
188
|
+
if (record_value == null) {
|
|
189
|
+
record_value = field.getRecordValue(record, field_path);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// Get the correct field value
|
|
193
|
+
let enum_value = values.get(record_value);
|
|
194
|
+
|
|
195
|
+
if (!enum_value) {
|
|
196
|
+
schema = null;
|
|
197
|
+
} else if (enum_value.schema) {
|
|
198
|
+
schema = enum_value.schema;
|
|
199
|
+
} else if (enum_value.value) {
|
|
200
|
+
schema = enum_value.value[property_name] || enum_value.value.schema;
|
|
201
|
+
} else {
|
|
202
|
+
console.log('Could not find', schema_path, 'in', record, 'enum values:', enum_value, 'of field', field)
|
|
172
203
|
}
|
|
173
204
|
|
|
174
205
|
return schema;
|
|
175
206
|
});
|
|
176
207
|
|
|
208
|
+
/**
|
|
209
|
+
* Get the subschema via an association
|
|
210
|
+
* @TODO: Work in progress!
|
|
211
|
+
*
|
|
212
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
213
|
+
* @since 0.2.0
|
|
214
|
+
* @version 1.3.21
|
|
215
|
+
*
|
|
216
|
+
* @param {Object} record This *should* be the schema context (might not be the root)
|
|
217
|
+
* @param {String} path The path inside the associated schema
|
|
218
|
+
* @param {Object} association The association object
|
|
219
|
+
*
|
|
220
|
+
* @return {Schema} Response might be a promise
|
|
221
|
+
*/
|
|
222
|
+
SchemaField.setMethod(function getSubschemaFromAssociation(record, path, association) {
|
|
223
|
+
|
|
224
|
+
let local_value = record[association.options.local_key];
|
|
225
|
+
|
|
226
|
+
if (!local_value) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const associated_model = alchemy.getModel(association.model_name);
|
|
231
|
+
|
|
232
|
+
if (!associated_model) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return Pledge.Swift.execute(async () => {
|
|
237
|
+
|
|
238
|
+
let result = await associated_model.findByValues({
|
|
239
|
+
[association.options.foreign_key]: local_value,
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
if (!result) {
|
|
243
|
+
return null;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
let found_schema = this.resolveSchemaPath(associated_model.schema, result, path, path);
|
|
247
|
+
|
|
248
|
+
return found_schema;
|
|
249
|
+
});
|
|
250
|
+
});
|
|
251
|
+
|
|
177
252
|
/**
|
|
178
253
|
* Cast all the subschema values using their _toDatasource method
|
|
179
254
|
*
|
|
@@ -1116,6 +1116,19 @@ Criteria.setMethod(function and(value) {
|
|
|
1116
1116
|
return this._applyGroup('and');
|
|
1117
1117
|
});
|
|
1118
1118
|
|
|
1119
|
+
/**
|
|
1120
|
+
* Alias for `ne`: Not equals check
|
|
1121
|
+
*
|
|
1122
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1123
|
+
* @since 1.3.21
|
|
1124
|
+
* @version 1.3.2
|
|
1125
|
+
*
|
|
1126
|
+
* @param {*} value
|
|
1127
|
+
*/
|
|
1128
|
+
Criteria.setMethod(function notEquals(value) {
|
|
1129
|
+
return this.ne(value);
|
|
1130
|
+
});
|
|
1131
|
+
|
|
1119
1132
|
Criteria.addValueExpression('equals');
|
|
1120
1133
|
Criteria.addValueExpression('gte');
|
|
1121
1134
|
Criteria.addValueExpression('gt');
|
|
@@ -131,6 +131,32 @@ Document.setStatic(function setMethod(key, method, on_server) {
|
|
|
131
131
|
return Blast.Collection.Function.setMethod(this, key, method);
|
|
132
132
|
});
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Set a getter for this computed field
|
|
136
|
+
*
|
|
137
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
138
|
+
* @since 1.3.21
|
|
139
|
+
* @version 1.3.21
|
|
140
|
+
*
|
|
141
|
+
* @param {String} name Name of the property
|
|
142
|
+
* @param {Boolean} on_server Also set on the server implementation
|
|
143
|
+
*/
|
|
144
|
+
Document.setStatic(function setComputedFieldGetter(name, on_server) {
|
|
145
|
+
this.setProperty(name, function getComputedFieldValue() {
|
|
146
|
+
this.recomputeFieldIfNecessary(name);
|
|
147
|
+
return this.$main[name];
|
|
148
|
+
}, function setComputedFieldValue(value) {
|
|
149
|
+
|
|
150
|
+
const field = this.$model.schema.getField(name);
|
|
151
|
+
|
|
152
|
+
if (field?.options?.allow_manual_set) {
|
|
153
|
+
return this.$main[name] = value;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.error('Can not set computed field "' + name + '" to', value);
|
|
157
|
+
}, on_server);
|
|
158
|
+
});
|
|
159
|
+
|
|
134
160
|
/**
|
|
135
161
|
* Set a getter for this field
|
|
136
162
|
*
|
|
@@ -858,6 +884,209 @@ Document.setMethod(function setValues(values) {
|
|
|
858
884
|
|
|
859
885
|
});
|
|
860
886
|
|
|
887
|
+
/**
|
|
888
|
+
* Recompute the given field if required
|
|
889
|
+
*
|
|
890
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
891
|
+
* @since 1.3.21
|
|
892
|
+
* @version 1.3.21
|
|
893
|
+
*
|
|
894
|
+
* @param {string|Alchemy.Field} name
|
|
895
|
+
* @param {boolean} force
|
|
896
|
+
*
|
|
897
|
+
* @return {Pledge|undefined}
|
|
898
|
+
*/
|
|
899
|
+
Document.setMethod(function recomputeFieldIfNecessary(name, force = false) {
|
|
900
|
+
|
|
901
|
+
const field = this.$model.schema.getField(name),
|
|
902
|
+
original = this.$main[name];
|
|
903
|
+
|
|
904
|
+
if (!field) {
|
|
905
|
+
return original;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
let options = field.options;
|
|
909
|
+
|
|
910
|
+
if (!options.compute_method) {
|
|
911
|
+
return original;
|
|
912
|
+
}
|
|
913
|
+
|
|
914
|
+
let required_field_count = options.required_fields?.length || 0,
|
|
915
|
+
optional_field_count = options.optional_fields?.length || 0;
|
|
916
|
+
|
|
917
|
+
if (!force && original == null) {
|
|
918
|
+
// If the value is null, it hasn't been computed yet
|
|
919
|
+
// and we need to compute it
|
|
920
|
+
force = true;
|
|
921
|
+
}
|
|
922
|
+
|
|
923
|
+
if (!force) {
|
|
924
|
+
let has_changed = false;
|
|
925
|
+
|
|
926
|
+
if (required_field_count) {
|
|
927
|
+
for (let required_field of options.required_fields) {
|
|
928
|
+
if (this.hasChanged(required_field)) {
|
|
929
|
+
has_changed = true;
|
|
930
|
+
break;
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (!has_changed && optional_field_count) {
|
|
936
|
+
for (let optional_field of options.optional_fields) {
|
|
937
|
+
if (this.hasChanged(optional_field)) {
|
|
938
|
+
has_changed = true;
|
|
939
|
+
break;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
if (!has_changed) {
|
|
945
|
+
return original;
|
|
946
|
+
}
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
// Make sure the required fields are set
|
|
950
|
+
if (required_field_count) {
|
|
951
|
+
let has_value = true;
|
|
952
|
+
|
|
953
|
+
for (let required_field of options.required_fields) {
|
|
954
|
+
if (this[required_field] == null) {
|
|
955
|
+
has_value = false;
|
|
956
|
+
break;
|
|
957
|
+
}
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
if (!has_value) {
|
|
961
|
+
// If not all required field values are set,
|
|
962
|
+
// the result will also be undefined
|
|
963
|
+
return this._setComputedFieldValue(name, undefined);
|
|
964
|
+
}
|
|
965
|
+
}
|
|
966
|
+
|
|
967
|
+
let compute_method = options.compute_method;
|
|
968
|
+
|
|
969
|
+
if (typeof compute_method == 'string') {
|
|
970
|
+
let fnc = this[compute_method];
|
|
971
|
+
|
|
972
|
+
if (typeof fnc == 'function') {
|
|
973
|
+
compute_method = fnc;
|
|
974
|
+
} else {
|
|
975
|
+
// Handle special cases in the browser
|
|
976
|
+
compute_method = alchemy.getCustomHandler('recompute_field');
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
if (!compute_method) {
|
|
981
|
+
return original;
|
|
982
|
+
}
|
|
983
|
+
|
|
984
|
+
let result = compute_method.call(this, this, field);
|
|
985
|
+
|
|
986
|
+
return this._setComputedFieldValue(name, result);
|
|
987
|
+
});
|
|
988
|
+
|
|
989
|
+
/**
|
|
990
|
+
* Set a computed field to a specific value
|
|
991
|
+
*
|
|
992
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
993
|
+
* @since 1.3.21
|
|
994
|
+
* @version 1.3.21
|
|
995
|
+
*
|
|
996
|
+
* @param {string|Alchemy.Field} name
|
|
997
|
+
* @param {*} value
|
|
998
|
+
*
|
|
999
|
+
* @return {Pledge|undefined}
|
|
1000
|
+
*/
|
|
1001
|
+
Document.setMethod(function _setComputedFieldValue(name, value) {
|
|
1002
|
+
|
|
1003
|
+
const field = this.$model.schema.getField(name);
|
|
1004
|
+
|
|
1005
|
+
if (value == null && field?.options?.allow_manual_set) {
|
|
1006
|
+
return this.$main[name];
|
|
1007
|
+
}
|
|
1008
|
+
|
|
1009
|
+
if (Pledge.isThenable(value)) {
|
|
1010
|
+
value.then(value => {
|
|
1011
|
+
this.$main[name] = value;
|
|
1012
|
+
});
|
|
1013
|
+
} else {
|
|
1014
|
+
this.$main[name] = value;
|
|
1015
|
+
}
|
|
1016
|
+
|
|
1017
|
+
return value;
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
/**
|
|
1021
|
+
* Recompute values of computed fields.
|
|
1022
|
+
* This might be async. If it is, a pledge will be returned.
|
|
1023
|
+
*
|
|
1024
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1025
|
+
* @since 1.3.21
|
|
1026
|
+
* @version 1.3.21
|
|
1027
|
+
*
|
|
1028
|
+
* @return {Pledge|undefined}
|
|
1029
|
+
*/
|
|
1030
|
+
Document.setMethod(function recomputeValues() {
|
|
1031
|
+
|
|
1032
|
+
const schema = this.$model.schema;
|
|
1033
|
+
|
|
1034
|
+
if (!schema.has_computed_fields) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
|
|
1038
|
+
let promises = [];
|
|
1039
|
+
|
|
1040
|
+
for (let key in schema.computed_fields) {
|
|
1041
|
+
let field = schema.computed_fields[key];
|
|
1042
|
+
let options = field.options;
|
|
1043
|
+
|
|
1044
|
+
// Make sure the required fields are set
|
|
1045
|
+
if (options.required_fields?.length) {
|
|
1046
|
+
let has_value = true;
|
|
1047
|
+
|
|
1048
|
+
for (let required_field of options.required_fields) {
|
|
1049
|
+
if (this[required_field] == null) {
|
|
1050
|
+
has_value = false;
|
|
1051
|
+
break;
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
if (!has_value) {
|
|
1056
|
+
// If not all required field values are set,
|
|
1057
|
+
// the result will also be undefined
|
|
1058
|
+
this._setComputedFieldValue(key, undefined);
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
|
|
1063
|
+
let compute_method = options.compute_method;
|
|
1064
|
+
|
|
1065
|
+
if (typeof compute_method == 'string') {
|
|
1066
|
+
let fnc = this[compute_method];
|
|
1067
|
+
|
|
1068
|
+
if (typeof fnc == 'function') {
|
|
1069
|
+
compute_method = fnc;
|
|
1070
|
+
} else {
|
|
1071
|
+
// Handle special cases in the browser
|
|
1072
|
+
compute_method = alchemy.getCustomHandler('recompute_field');
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
|
|
1076
|
+
if (!compute_method) {
|
|
1077
|
+
continue;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
let result = compute_method.call(this, this, field);
|
|
1081
|
+
|
|
1082
|
+
this._setComputedFieldValue(key, result);
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
if (promises.length) {
|
|
1086
|
+
return Pledge.all(promises);
|
|
1087
|
+
}
|
|
1088
|
+
});
|
|
1089
|
+
|
|
861
1090
|
/**
|
|
862
1091
|
* Get the clean options
|
|
863
1092
|
*
|
|
@@ -629,7 +629,7 @@ Model.setMethod(function getAliasModel(alias) {
|
|
|
629
629
|
*
|
|
630
630
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
631
631
|
* @since 0.0.1
|
|
632
|
-
* @version 1.3.
|
|
632
|
+
* @version 1.3.21
|
|
633
633
|
*
|
|
634
634
|
* @param {String} type The type of find (first, all)
|
|
635
635
|
* @param {Criteria} criteria The criteria object
|
|
@@ -797,6 +797,27 @@ Model.setMethod(function find(type, criteria, callback) {
|
|
|
797
797
|
next();
|
|
798
798
|
}
|
|
799
799
|
});
|
|
800
|
+
}, function recomputeAfterFind(next) {
|
|
801
|
+
|
|
802
|
+
if (!that.schema.has_recompute_after_find || criteria.options.document === false) {
|
|
803
|
+
return next();
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
let tasks = [];
|
|
807
|
+
|
|
808
|
+
for (let document of records) {
|
|
809
|
+
tasks.push((next) => {
|
|
810
|
+
let promise = document.recomputeValues();
|
|
811
|
+
|
|
812
|
+
if (!promise) {
|
|
813
|
+
return next();
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
Pledge.done(promise, next);
|
|
817
|
+
});
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
Function.parallel(8, tasks, next);
|
|
800
821
|
}, function done(err) {
|
|
801
822
|
|
|
802
823
|
var i;
|
|
@@ -1773,7 +1794,7 @@ Model.setMethod(function compose(data, options) {
|
|
|
1773
1794
|
*
|
|
1774
1795
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1775
1796
|
* @since 0.2.0
|
|
1776
|
-
* @version 1.3.
|
|
1797
|
+
* @version 1.3.21
|
|
1777
1798
|
*
|
|
1778
1799
|
* @param {Document} document
|
|
1779
1800
|
* @param {Object} options
|
|
@@ -1786,10 +1807,17 @@ Model.setMethod(function createRecord(document, options, callback) {
|
|
|
1786
1807
|
// Normalize & clone the data, set default values, ...
|
|
1787
1808
|
let data = this.compose(document, options);
|
|
1788
1809
|
|
|
1789
|
-
|
|
1790
|
-
|
|
1810
|
+
document = createDocumentForSaving(this, document, data);
|
|
1811
|
+
|
|
1812
|
+
Function.series(function recompute(next) {
|
|
1813
|
+
let result = document.recomputeValues();
|
|
1791
1814
|
|
|
1792
|
-
|
|
1815
|
+
if (result) {
|
|
1816
|
+
result.done(next);
|
|
1817
|
+
} else {
|
|
1818
|
+
next();
|
|
1819
|
+
}
|
|
1820
|
+
}, function doBeforeValidate(next) {
|
|
1793
1821
|
that.callOrNext('beforeValidate', [document, options], next);
|
|
1794
1822
|
}, function validate(next) {
|
|
1795
1823
|
|
|
@@ -1823,12 +1851,35 @@ Model.setMethod(function createRecord(document, options, callback) {
|
|
|
1823
1851
|
})
|
|
1824
1852
|
});
|
|
1825
1853
|
|
|
1854
|
+
/**
|
|
1855
|
+
* Create a document used for saving
|
|
1856
|
+
*
|
|
1857
|
+
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1858
|
+
* @since 1.3.21
|
|
1859
|
+
* @version 1.3.21
|
|
1860
|
+
*
|
|
1861
|
+
* @param {Model} model
|
|
1862
|
+
* @param {Document|Object} original_input
|
|
1863
|
+
* @param {Object} data
|
|
1864
|
+
*/
|
|
1865
|
+
function createDocumentForSaving(model, original_input, data) {
|
|
1866
|
+
|
|
1867
|
+
// Turn it into a new document
|
|
1868
|
+
let document = model.createDocument(data);
|
|
1869
|
+
|
|
1870
|
+
if (original_input?.$attributes.original_record) {
|
|
1871
|
+
document.$attributes.original_record = original_input.$attributes.original_record;
|
|
1872
|
+
}
|
|
1873
|
+
|
|
1874
|
+
return document;
|
|
1875
|
+
}
|
|
1876
|
+
|
|
1826
1877
|
/**
|
|
1827
1878
|
* Update a record in the database
|
|
1828
1879
|
*
|
|
1829
1880
|
* @author Jelle De Loecker <jelle@elevenways.be>
|
|
1830
1881
|
* @since 0.2.0
|
|
1831
|
-
* @version 1.3.
|
|
1882
|
+
* @version 1.3.21
|
|
1832
1883
|
*
|
|
1833
1884
|
* @param {Document} document
|
|
1834
1885
|
* @param {Object} options
|
|
@@ -1842,9 +1893,17 @@ Model.setMethod(function updateRecord(document, options, callback) {
|
|
|
1842
1893
|
let data = this.compose(document, Object.assign({update: true}, options));
|
|
1843
1894
|
|
|
1844
1895
|
// Turn it into a new document
|
|
1845
|
-
document = this
|
|
1896
|
+
document = createDocumentForSaving(this, document, data);
|
|
1897
|
+
|
|
1898
|
+
Function.series(function recompute(next) {
|
|
1899
|
+
let result = document.recomputeValues();
|
|
1846
1900
|
|
|
1847
|
-
|
|
1901
|
+
if (result) {
|
|
1902
|
+
result.done(next);
|
|
1903
|
+
} else {
|
|
1904
|
+
next();
|
|
1905
|
+
}
|
|
1906
|
+
}, function doBeforeValidate(next) {
|
|
1848
1907
|
that.callOrNext('beforeValidate', [document, options], next);
|
|
1849
1908
|
}, function validate(next) {
|
|
1850
1909
|
|