alchemymvc 1.2.5 → 1.2.6
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/LICENSE +0 -0
- package/README.md +0 -0
- package/lib/app/assets/scripts/.gitkeep +0 -0
- package/lib/app/assets/stylesheets/alchemy-info.less +0 -0
- package/lib/app/behaviour/publishable_behaviour.js +0 -0
- package/lib/app/behaviour/revision_behaviour.js +0 -0
- package/lib/app/behaviour/sluggable_behaviour.js +0 -0
- package/lib/app/component/.gitkeep +0 -0
- package/lib/app/conduit/electron_conduit.js +0 -0
- package/lib/app/conduit/http_conduit.js +173 -173
- package/lib/app/conduit/socket_conduit.js +620 -620
- package/lib/app/controller/alchemy_info_controller.js +0 -0
- package/lib/app/datasource/mongo_datasource.js +0 -0
- package/lib/app/helper/client_collection.js +0 -0
- package/lib/app/helper/pagination_helper.js +0 -0
- package/lib/app/helper/router_helper.js +0 -0
- package/lib/app/helper/socket_helper.js +613 -613
- package/lib/app/helper_component/paginate_component.js +0 -0
- package/lib/app/helper_controller/component.js +0 -0
- package/lib/app/helper_controller/conduit.js +0 -0
- package/lib/app/helper_controller/controller.js +0 -0
- package/lib/app/helper_datasource/00-nosql_datasource.js +0 -0
- package/lib/app/helper_datasource/05-fallback_datasource.js +0 -0
- package/lib/app/helper_datasource/idb_datasource.js +0 -0
- package/lib/app/helper_datasource/indexed_db.js +0 -0
- package/lib/app/helper_field/00-objectid_field.js +0 -0
- package/lib/app/helper_field/06-text_field.js +0 -0
- package/lib/app/helper_field/10-number_field.js +0 -0
- package/lib/app/helper_field/boolean_field.js +0 -0
- package/lib/app/helper_field/date_field.js +0 -0
- package/lib/app/helper_field/datetime_field.js +0 -0
- package/lib/app/helper_field/enum_field.js +0 -0
- package/lib/app/helper_field/geopoint_field.js +0 -0
- package/lib/app/helper_field/habtm_field.js +0 -0
- package/lib/app/helper_field/hasoneparent_field.js +0 -0
- package/lib/app/helper_field/html_field.js +0 -0
- package/lib/app/helper_field/integer_field.js +0 -0
- package/lib/app/helper_field/object_field.js +0 -0
- package/lib/app/helper_field/regexp_field.js +0 -0
- package/lib/app/helper_field/schema_field.js +23 -2
- package/lib/app/helper_field/time_field.js +0 -0
- package/lib/app/helper_field/url_field.js +0 -0
- package/lib/app/helper_model/criteria.js +0 -0
- package/lib/app/helper_model/db_query.js +0 -0
- package/lib/app/helper_model/document_list.js +0 -0
- package/lib/app/model/alchemy_task_model.js +0 -0
- package/lib/app/routes.js +0 -0
- package/lib/app/view/alchemy/info.ejs +0 -0
- package/lib/app/view/error/unknown.ejs +0 -0
- package/lib/app/view/paginate/navlist.ejs +0 -0
- package/lib/bootstrap.js +0 -0
- package/lib/class/behaviour.js +0 -0
- package/lib/class/component.js +0 -0
- package/lib/class/conduit.js +2555 -2552
- package/lib/class/controller.js +4 -1
- package/lib/class/document_list.js +0 -0
- package/lib/class/helper.js +0 -0
- package/lib/class/inode.js +0 -0
- package/lib/class/inode_dir.js +0 -0
- package/lib/class/inode_file.js +112 -112
- package/lib/class/inode_list.js +0 -0
- package/lib/class/model.js +1772 -1769
- package/lib/class/path_definition.js +0 -0
- package/lib/class/route.js +0 -0
- package/lib/class/session.js +0 -0
- package/lib/class/task.js +0 -0
- package/lib/core/base.js +50 -9
- package/lib/core/discovery.js +0 -0
- package/lib/core/routing.js +0 -0
- package/lib/core/socket.js +159 -159
- package/lib/init/alchemy.js +1823 -1823
- package/lib/init/constants.js +0 -0
- package/lib/init/functions.js +8 -4
- package/lib/init/load_functions.js +0 -0
- package/lib/init/requirements.js +101 -101
- package/package.json +74 -74
package/lib/class/model.js
CHANGED
|
@@ -1,1770 +1,1773 @@
|
|
|
1
|
-
var nameCache = {},
|
|
2
|
-
mongo = alchemy.use('mongodb'),
|
|
3
|
-
all_prefixes = alchemy.shared('Routing.prefixes'),
|
|
4
|
-
fs = alchemy.use('fs'),
|
|
5
|
-
createdModel;
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* The Model class
|
|
9
|
-
*
|
|
10
|
-
* @constructor
|
|
11
|
-
*
|
|
12
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
13
|
-
* @since 0.0.1
|
|
14
|
-
* @version 1.1.0
|
|
15
|
-
*/
|
|
16
|
-
var Model = Function.inherits('Alchemy.Base', 'Alchemy.Model', function Model(options) {
|
|
17
|
-
this.init(options);
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Set the modelName property after class creation
|
|
22
|
-
*
|
|
23
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
24
|
-
* @since 0.2.0
|
|
25
|
-
* @version 1.1.0
|
|
26
|
-
*/
|
|
27
|
-
Model.constitute(function setModelName() {
|
|
28
|
-
this.model_name = this.name;
|
|
29
|
-
this.setProperty('model_name', this.model_name);
|
|
30
|
-
|
|
31
|
-
if (!this.table) {
|
|
32
|
-
this.table = this.model_name.tableize();
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* This is a model constructor
|
|
38
|
-
*
|
|
39
|
-
* @type {Boolean}
|
|
40
|
-
*/
|
|
41
|
-
Model.setStaticProperty('model', true);
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* The cache duration static getter/setter
|
|
45
|
-
*
|
|
46
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
47
|
-
* @since 0.1.0
|
|
48
|
-
* @version 1.0.3
|
|
49
|
-
*
|
|
50
|
-
* @property cache_duration
|
|
51
|
-
* @type {String}
|
|
52
|
-
*/
|
|
53
|
-
Model.setStaticProperty(function cache_duration() {
|
|
54
|
-
|
|
55
|
-
if (this._cache_duration == null) {
|
|
56
|
-
this._cache_duration = alchemy.settings.model_query_cache_duration;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
return this._cache_duration;
|
|
60
|
-
}, function setCacheDuration(duration) {
|
|
61
|
-
this._cache_duration = duration;
|
|
62
|
-
|
|
63
|
-
// @todo: reset cache
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* Get the cache object
|
|
68
|
-
*
|
|
69
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
70
|
-
* @since 0.1.0
|
|
71
|
-
* @version 1.0.3
|
|
72
|
-
*
|
|
73
|
-
* @property cache
|
|
74
|
-
* @type {Object}
|
|
75
|
-
*/
|
|
76
|
-
Model.setStaticProperty(function cache() {
|
|
77
|
-
|
|
78
|
-
if (this.cache_duration) {
|
|
79
|
-
|
|
80
|
-
if (this._cache) {
|
|
81
|
-
return this._cache;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
this._cache = alchemy.getCache(this.name, this.cache_duration);
|
|
85
|
-
return this._cache;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return false;
|
|
89
|
-
}, function setCache(value) {
|
|
90
|
-
return this._cache = value;
|
|
91
|
-
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
/**
|
|
95
|
-
* Is this an abstract model?
|
|
96
|
-
*
|
|
97
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
98
|
-
* @since 1.1.0
|
|
99
|
-
* @version 1.1.0
|
|
100
|
-
*
|
|
101
|
-
* @type {Boolean}
|
|
102
|
-
*/
|
|
103
|
-
Model.setStaticProperty(function is_abstract() {
|
|
104
|
-
|
|
105
|
-
// Do simple is_abstract_class check
|
|
106
|
-
if (this.is_abstract_class != null) {
|
|
107
|
-
return !!this.is_abstract_class;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
// If we need to load an external schema, it's also not abstract
|
|
111
|
-
if (this.prototype.load_external_schema) {
|
|
112
|
-
return false;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// See if this model has other fields than the default ones
|
|
116
|
-
let field_count = this.schema.array.length;
|
|
117
|
-
|
|
118
|
-
if (this.schema.has('_id')) {
|
|
119
|
-
field_count--;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
if (this.schema.has('created')) {
|
|
123
|
-
field_count--;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
if (this.schema.has('updated')) {
|
|
127
|
-
field_count--;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return field_count < 1;
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Get the document class constructor
|
|
136
|
-
*
|
|
137
|
-
* @type {Alchemy.Document}
|
|
138
|
-
*/
|
|
139
|
-
Model.prepareStaticProperty('Document', function getDocumentClass() {
|
|
140
|
-
return Classes.Alchemy.Document.Document.getDocumentClass(this);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Get the client document class constructor
|
|
145
|
-
*
|
|
146
|
-
* @type {Hawkejs.Document}
|
|
147
|
-
*/
|
|
148
|
-
Model.prepareStaticProperty('ClientDocument', function getClientDocumentClass() {
|
|
149
|
-
return this.Document.getClientDocumentClass();
|
|
150
|
-
});
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* Set the static per-model schema
|
|
154
|
-
*
|
|
155
|
-
* @version 1.1.0
|
|
156
|
-
*
|
|
157
|
-
* @type {Schema}
|
|
158
|
-
*/
|
|
159
|
-
Model.staticCompose('schema', function createSchema(doNext) {
|
|
160
|
-
|
|
161
|
-
var that = this,
|
|
162
|
-
model = this.compositorParent,
|
|
163
|
-
schema = new Classes.Alchemy.Schema();
|
|
164
|
-
|
|
165
|
-
// The base Model does not have a schema
|
|
166
|
-
if (model.name == 'Model') {
|
|
167
|
-
return false;
|
|
168
|
-
} else {
|
|
169
|
-
|
|
170
|
-
// Link the schema to this model
|
|
171
|
-
schema.setModel(model);
|
|
172
|
-
|
|
173
|
-
// Set the schema name
|
|
174
|
-
schema.setName(model.name);
|
|
175
|
-
|
|
176
|
-
if (model.prototype.add_basic_fields !== false) {
|
|
177
|
-
|
|
178
|
-
// Set default model fields immediately after this function ends
|
|
179
|
-
// This has to be scheduled next, because addField would call createSchema
|
|
180
|
-
// again, resulting in an infinite loop
|
|
181
|
-
doNext(function addSchemaBasics() {
|
|
182
|
-
model.addField('_id', 'ObjectId', {default: Field.createPathEvaluator('alchemy.ObjectId')});
|
|
183
|
-
model.addField('created', 'Datetime', {default: Field.createPathEvaluator('Date.create')});
|
|
184
|
-
model.addField('updated', 'Datetime', {default: Field.createPathEvaluator('Date.create')});
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
return schema;
|
|
190
|
-
}, [
|
|
191
|
-
'addEnumValues',
|
|
192
|
-
'setEnumValues',
|
|
193
|
-
'belongsTo',
|
|
194
|
-
'hasOneParent',
|
|
195
|
-
'hasAndBelongsToMany',
|
|
196
|
-
'hasMany',
|
|
197
|
-
'hasOneChild',
|
|
198
|
-
'addIndex',
|
|
199
|
-
'addRule',
|
|
200
|
-
]);
|
|
201
|
-
|
|
202
|
-
Model.setDeprecatedProperty('modelName', 'model_name');
|
|
203
|
-
Model.setDeprecatedProperty('blueprint', 'schema');
|
|
204
|
-
|
|
205
|
-
/**
|
|
206
|
-
* The default database config to use
|
|
207
|
-
*
|
|
208
|
-
* @type {String}
|
|
209
|
-
*/
|
|
210
|
-
Model.setProperty('dbConfig', 'default');
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* The default field to use as display
|
|
214
|
-
*
|
|
215
|
-
* @type {String}
|
|
216
|
-
*/
|
|
217
|
-
Model.setProperty('displayField', 'title');
|
|
218
|
-
|
|
219
|
-
/**
|
|
220
|
-
* Translate is on by default
|
|
221
|
-
*
|
|
222
|
-
* @type {Boolean}
|
|
223
|
-
*/
|
|
224
|
-
Model.setProperty('translate', true);
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Set the name of the primary key field
|
|
228
|
-
*
|
|
229
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
230
|
-
* @since 0.1.0
|
|
231
|
-
* @version 0.1.0
|
|
232
|
-
*/
|
|
233
|
-
Model.setProperty('primary_key', '_id');
|
|
234
|
-
|
|
235
|
-
/**
|
|
236
|
-
* Should we load the schema from the database?
|
|
237
|
-
*
|
|
238
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
239
|
-
* @since 1.1.0
|
|
240
|
-
* @version 1.1.0
|
|
241
|
-
*/
|
|
242
|
-
Model.setProperty('load_external_schema', false);
|
|
243
|
-
|
|
244
|
-
/**
|
|
245
|
-
* Object where behaviours are stored
|
|
246
|
-
*
|
|
247
|
-
* @type {Object}
|
|
248
|
-
*/
|
|
249
|
-
Model.prepareProperty('behaviours', Object);
|
|
250
|
-
|
|
251
|
-
/**
|
|
252
|
-
* Associations
|
|
253
|
-
*
|
|
254
|
-
* @type {Object}
|
|
255
|
-
*/
|
|
256
|
-
Model.setProperty(function associations() {
|
|
257
|
-
return this.schema.associations;
|
|
258
|
-
});
|
|
259
|
-
|
|
260
|
-
/**
|
|
261
|
-
* Instance access to static cache
|
|
262
|
-
*
|
|
263
|
-
* @type {Expirable}
|
|
264
|
-
*/
|
|
265
|
-
Model.prepareProperty('cache', function cache() {
|
|
266
|
-
return this.constructor.cache;
|
|
267
|
-
});
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* Instance access to static schema
|
|
271
|
-
*
|
|
272
|
-
* @type {Schema}
|
|
273
|
-
*/
|
|
274
|
-
Model.setProperty(function schema() {
|
|
275
|
-
return this.constructor.schema;
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
/**
|
|
279
|
-
* Is this an abstract model?
|
|
280
|
-
*
|
|
281
|
-
* @type {Boolean}
|
|
282
|
-
*/
|
|
283
|
-
Model.setProperty(function is_abstract() {
|
|
284
|
-
return this.constructor.is_abstract;
|
|
285
|
-
});
|
|
286
|
-
|
|
287
|
-
/**
|
|
288
|
-
* This is a wrapper class
|
|
289
|
-
*/
|
|
290
|
-
Model.makeAbstractClass();
|
|
291
|
-
|
|
292
|
-
/**
|
|
293
|
-
* This wrapper class starts a new group
|
|
294
|
-
*/
|
|
295
|
-
Model.startNewGroup();
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* The connection
|
|
299
|
-
*
|
|
300
|
-
* @type {Object}
|
|
301
|
-
*/
|
|
302
|
-
Model.prepareProperty('datasource', function datasource() {
|
|
303
|
-
if (this.table) return Datasource.get(this.dbConfig);
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
/**
|
|
307
|
-
* The default sort options
|
|
308
|
-
*
|
|
309
|
-
* @type {Object}
|
|
310
|
-
*/
|
|
311
|
-
Model.prepareProperty('sort', function sort() {
|
|
312
|
-
return {created: 1};
|
|
313
|
-
});
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Check a url value
|
|
317
|
-
*
|
|
318
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
319
|
-
* @since 1.0.0
|
|
320
|
-
* @version 1.2.5
|
|
321
|
-
*
|
|
322
|
-
* @param {String} value The value in the url
|
|
323
|
-
* @param {String} name The name of the url parameter
|
|
324
|
-
* @param {String} field_name The name of the field to check
|
|
325
|
-
* @param {Conduit} conduit The optional conduit
|
|
326
|
-
*
|
|
327
|
-
* @return {Pledge}
|
|
328
|
-
*/
|
|
329
|
-
Model.setStatic(async function checkPathValue(value, name, field_name, conduit) {
|
|
330
|
-
|
|
331
|
-
var instance,
|
|
332
|
-
pledge,
|
|
333
|
-
crit;
|
|
334
|
-
|
|
335
|
-
if (!field_name) {
|
|
336
|
-
if (name == 'id') {
|
|
337
|
-
field_name = this.prototype.primary_key;
|
|
338
|
-
} else {
|
|
339
|
-
field_name = name;
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
if (conduit) {
|
|
344
|
-
instance = conduit.getModel(this);
|
|
345
|
-
} else {
|
|
346
|
-
instance = new this;
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
// Create new criteria instance
|
|
350
|
-
crit = instance.find();
|
|
351
|
-
|
|
352
|
-
// Look for the wanted field
|
|
353
|
-
crit.where(field_name).equals(value);
|
|
354
|
-
|
|
355
|
-
let result = await instance.find('first', crit);
|
|
356
|
-
|
|
357
|
-
if (result) {
|
|
358
|
-
let found_value = result[field_name];
|
|
359
|
-
|
|
360
|
-
if (found_value != value && !Object.alike(value, found_value)) {
|
|
361
|
-
conduit.rewriteRequestRouteParam(name, found_value);
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
return result;
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
/**
|
|
369
|
-
* Add a field to this model's schema
|
|
370
|
-
*
|
|
371
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
372
|
-
* @since 0.2.0
|
|
373
|
-
* @version 1.2.4
|
|
374
|
-
*
|
|
375
|
-
* @return {Alchemy.Field}
|
|
376
|
-
*/
|
|
377
|
-
Model.setStatic(function addField(name, type, options) {
|
|
378
|
-
|
|
379
|
-
var field,
|
|
380
|
-
is_new;
|
|
381
|
-
|
|
382
|
-
is_new = !this.schema.has(name);
|
|
383
|
-
|
|
384
|
-
// Add it to the schema
|
|
385
|
-
field = this.schema.addField(name, type, options);
|
|
386
|
-
|
|
387
|
-
if (is_new) {
|
|
388
|
-
// Add it to the Document class
|
|
389
|
-
this.Document.setFieldGetter(name);
|
|
390
|
-
|
|
391
|
-
// False means it should not be set on the server implementation
|
|
392
|
-
// (because that's where it's coming from)
|
|
393
|
-
// Yes, this also sets private fields on the server-side client document.
|
|
394
|
-
this.ClientDocument.setFieldGetter(name, null, null, false);
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
return field;
|
|
398
|
-
});
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
/**
|
|
402
|
-
* Add a behaviour to this model
|
|
403
|
-
*
|
|
404
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
405
|
-
* @since 0.2.0
|
|
406
|
-
* @version 0.2.0
|
|
407
|
-
*/
|
|
408
|
-
Model.setStatic(function addBehaviour(behaviour_name, options) {
|
|
409
|
-
return this.schema.addBehaviour(behaviour_name, options);
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Add an association to this model's schema
|
|
414
|
-
* and set it on the Document as a getter
|
|
415
|
-
*
|
|
416
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
417
|
-
* @since 0.2.0
|
|
418
|
-
* @version 1.2.4
|
|
419
|
-
*/
|
|
420
|
-
Model.setStatic(function addAssociation(type, alias, model_name, options) {
|
|
421
|
-
var data = this.schema.addAssociation(type, alias, model_name, options);
|
|
422
|
-
this.Document.setAliasGetter(data.alias);
|
|
423
|
-
|
|
424
|
-
// False means it should not be set on the server implementation
|
|
425
|
-
// (because that's where it's coming from)
|
|
426
|
-
// Yes, this also sets private fields on the server-side client document.
|
|
427
|
-
this.ClientDocument.setAliasGetter(name);
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
/**
|
|
431
|
-
* Set a method on the document class
|
|
432
|
-
*
|
|
433
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
434
|
-
* @since 0.2.0
|
|
435
|
-
* @version 1.0.6
|
|
436
|
-
*/
|
|
437
|
-
Model.setStatic(function setDocumentMethod(name, fnc) {
|
|
438
|
-
|
|
439
|
-
var that = this;
|
|
440
|
-
|
|
441
|
-
if (typeof name == 'function') {
|
|
442
|
-
fnc = name;
|
|
443
|
-
name = fnc.name;
|
|
444
|
-
}
|
|
445
|
-
|
|
446
|
-
Blast.loaded(function whenLoaded() {
|
|
447
|
-
that.Document.setMethod(name, fnc);
|
|
448
|
-
});
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
/**
|
|
452
|
-
* Set a property on the document class
|
|
453
|
-
*
|
|
454
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
455
|
-
* @since 0.2.0
|
|
456
|
-
* @version 1.0.6
|
|
457
|
-
*/
|
|
458
|
-
Model.setStatic(function setDocumentProperty(name, fnc) {
|
|
459
|
-
|
|
460
|
-
var that = this,
|
|
461
|
-
args = arguments;
|
|
462
|
-
|
|
463
|
-
Blast.loaded(function whenLoaded() {
|
|
464
|
-
that.Document.setProperty.apply(that.Document, args);
|
|
465
|
-
});
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
/**
|
|
469
|
-
* Get a field
|
|
470
|
-
*
|
|
471
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
472
|
-
* @since 0.2.0
|
|
473
|
-
* @version 0.2.0
|
|
474
|
-
*
|
|
475
|
-
* @return {FieldType}
|
|
476
|
-
*/
|
|
477
|
-
Model.setStatic(function getField(name) {
|
|
478
|
-
|
|
479
|
-
var fieldPath,
|
|
480
|
-
alias,
|
|
481
|
-
model,
|
|
482
|
-
split;
|
|
483
|
-
|
|
484
|
-
if (name.indexOf('.') > -1) {
|
|
485
|
-
split = name.split('.');
|
|
486
|
-
|
|
487
|
-
alias = name[0];
|
|
488
|
-
|
|
489
|
-
if (this.schema.associations[alias] == null) {
|
|
490
|
-
model = this;
|
|
491
|
-
fieldPath = name;
|
|
492
|
-
} else {
|
|
493
|
-
model = Model.get(this.schema.associations[alias].modelName).constructor;
|
|
494
|
-
split.shift();
|
|
495
|
-
fieldPath = split.join('.');
|
|
496
|
-
}
|
|
497
|
-
} else {
|
|
498
|
-
model = this;
|
|
499
|
-
fieldPath = name;
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
return model.schema.get(fieldPath);
|
|
503
|
-
});
|
|
504
|
-
|
|
505
|
-
/**
|
|
506
|
-
* Get the model's public configuration
|
|
507
|
-
* (This is used to create the client-side Model instances)
|
|
508
|
-
*
|
|
509
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
510
|
-
* @since 1.0.0
|
|
511
|
-
* @version 1.1.0
|
|
512
|
-
*/
|
|
513
|
-
Model.setStatic(function getClientConfig() {
|
|
514
|
-
|
|
515
|
-
var result = {
|
|
516
|
-
name : this.model_name,
|
|
517
|
-
schema : this.schema,
|
|
518
|
-
primary_key : this.prototype.primary_key
|
|
519
|
-
};
|
|
520
|
-
|
|
521
|
-
if (this.super.name != 'Model') {
|
|
522
|
-
result.parent = this.super.name;
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
return result;
|
|
526
|
-
});
|
|
527
|
-
|
|
528
|
-
/**
|
|
529
|
-
* Initialize behaviours
|
|
530
|
-
*
|
|
531
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
532
|
-
* @since 0.2.0
|
|
533
|
-
* @version 0.2.0
|
|
534
|
-
*
|
|
535
|
-
* @return {Document}
|
|
536
|
-
*/
|
|
537
|
-
Model.setMethod(function initBehaviours() {
|
|
538
|
-
|
|
539
|
-
var behaviour,
|
|
540
|
-
key;
|
|
541
|
-
|
|
542
|
-
this.behaviours = {};
|
|
543
|
-
|
|
544
|
-
for (key in this.schema.behaviours) {
|
|
545
|
-
behaviour = this.schema.behaviours[key];
|
|
546
|
-
|
|
547
|
-
this.behaviours[key] = new behaviour.constructor(this, behaviour.options);
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
});
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Enable a behaviour on-the-fly
|
|
554
|
-
*
|
|
555
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
556
|
-
* @since 0.0.1
|
|
557
|
-
* @version 0.2.0
|
|
558
|
-
*/
|
|
559
|
-
Model.setMethod(function addBehaviour(behaviourname, options) {
|
|
560
|
-
|
|
561
|
-
var instance;
|
|
562
|
-
|
|
563
|
-
if (!options) {
|
|
564
|
-
options = {};
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
instance = Behaviour.get(behaviourname, this, options);
|
|
568
|
-
this.behaviours[behaviourname] = instance;
|
|
569
|
-
|
|
570
|
-
return instance;
|
|
571
|
-
});
|
|
572
|
-
|
|
573
|
-
/**
|
|
574
|
-
* Get a behaviour instance
|
|
575
|
-
*
|
|
576
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
577
|
-
* @since 1.0.3
|
|
578
|
-
* @version 1.0.3
|
|
579
|
-
*
|
|
580
|
-
* @param {String} name
|
|
581
|
-
*
|
|
582
|
-
* @return {Behaviour}
|
|
583
|
-
*/
|
|
584
|
-
Model.setMethod(function getBehaviour(name) {
|
|
585
|
-
|
|
586
|
-
name = name.camelize();
|
|
587
|
-
|
|
588
|
-
if (!name.endsWith('Behaviour')) {
|
|
589
|
-
name += 'Behaviour';
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
return this.behaviours[name];
|
|
593
|
-
});
|
|
594
|
-
|
|
595
|
-
/**
|
|
596
|
-
* Enable translations
|
|
597
|
-
*
|
|
598
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
599
|
-
* @since 0.2.0
|
|
600
|
-
* @version 0.2.0
|
|
601
|
-
*/
|
|
602
|
-
Model.setMethod(function enableTranslations() {
|
|
603
|
-
this.translate = true;
|
|
604
|
-
});
|
|
605
|
-
|
|
606
|
-
/**
|
|
607
|
-
* Disable translations
|
|
608
|
-
*
|
|
609
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
610
|
-
* @since 0.2.0
|
|
611
|
-
* @version 0.2.0
|
|
612
|
-
*/
|
|
613
|
-
Model.setMethod(function disableTranslations() {
|
|
614
|
-
this.translate = false;
|
|
615
|
-
});
|
|
616
|
-
|
|
617
|
-
/**
|
|
618
|
-
* Aggregate
|
|
619
|
-
*
|
|
620
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
621
|
-
* @since 0.5.0
|
|
622
|
-
* @version 0.5.0
|
|
623
|
-
*
|
|
624
|
-
* @param {Array} pipeline
|
|
625
|
-
* @param {Function} callback
|
|
626
|
-
*/
|
|
627
|
-
Model.setMethod(function aggregate(pipeline, callback) {
|
|
628
|
-
|
|
629
|
-
this.datasource.collection(this.table, function gotCollection(err, collection) {
|
|
630
|
-
|
|
631
|
-
if (err) {
|
|
632
|
-
return callback(err);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
collection.aggregate(pipeline, callback);
|
|
636
|
-
});
|
|
637
|
-
});
|
|
638
|
-
|
|
639
|
-
/**
|
|
640
|
-
* Translate the given records
|
|
641
|
-
*
|
|
642
|
-
* @author Jelle De Loecker <jelle@develry.be>
|
|
643
|
-
* @since 0.2.0
|
|
644
|
-
* @version 1.
|
|
645
|
-
*
|
|
646
|
-
* @param {Array} items
|
|
647
|
-
* @param {Object} options Optional options object
|
|
648
|
-
* @param {Function} callback
|
|
649
|
-
*/
|
|
650
|
-
Model.setMethod(function translateItems(items, options, callback) {
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
//
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
}
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
*
|
|
763
|
-
*
|
|
764
|
-
*
|
|
765
|
-
* @
|
|
766
|
-
* @
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
});
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
*
|
|
807
|
-
*
|
|
808
|
-
*
|
|
809
|
-
* @
|
|
810
|
-
* @
|
|
811
|
-
*
|
|
812
|
-
*
|
|
813
|
-
* @
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
next
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
//
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
*
|
|
949
|
-
*
|
|
950
|
-
*
|
|
951
|
-
* @
|
|
952
|
-
* @
|
|
953
|
-
*
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
*
|
|
1077
|
-
*
|
|
1078
|
-
*
|
|
1079
|
-
* @
|
|
1080
|
-
* @
|
|
1081
|
-
*
|
|
1082
|
-
*
|
|
1083
|
-
* @
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
data = this.
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
*
|
|
1117
|
-
*
|
|
1118
|
-
*
|
|
1119
|
-
* @
|
|
1120
|
-
* @
|
|
1121
|
-
*
|
|
1122
|
-
*
|
|
1123
|
-
* @
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1154
|
-
* @
|
|
1155
|
-
* @
|
|
1156
|
-
*
|
|
1157
|
-
* @
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
}
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
*
|
|
1211
|
-
*
|
|
1212
|
-
*
|
|
1213
|
-
* @
|
|
1214
|
-
* @
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
*
|
|
1268
|
-
*
|
|
1269
|
-
*
|
|
1270
|
-
* @
|
|
1271
|
-
* @
|
|
1272
|
-
*
|
|
1273
|
-
* @
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
});
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
*
|
|
1316
|
-
*
|
|
1317
|
-
*
|
|
1318
|
-
* @
|
|
1319
|
-
* @
|
|
1320
|
-
*
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
//
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
//
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
next_options
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
});
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1415
|
-
* @
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
}
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
*
|
|
1454
|
-
*
|
|
1455
|
-
*
|
|
1456
|
-
* @
|
|
1457
|
-
* @
|
|
1458
|
-
*
|
|
1459
|
-
* @
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
})
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
*
|
|
1504
|
-
*
|
|
1505
|
-
*
|
|
1506
|
-
* @
|
|
1507
|
-
* @
|
|
1508
|
-
*
|
|
1509
|
-
* @
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
size = buffer.
|
|
1572
|
-
buffer = buffer.slice(
|
|
1573
|
-
} else if (current_type ==
|
|
1574
|
-
size = buffer.readUInt32BE(1);
|
|
1575
|
-
buffer = buffer.slice(5);
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
}
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
}
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
}
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
}
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
*
|
|
1677
|
-
*
|
|
1678
|
-
*
|
|
1679
|
-
* @
|
|
1680
|
-
* @
|
|
1681
|
-
*
|
|
1682
|
-
* @
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
// The
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
}
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
*
|
|
1766
|
-
*
|
|
1767
|
-
*
|
|
1768
|
-
* @
|
|
1769
|
-
|
|
1
|
+
var nameCache = {},
|
|
2
|
+
mongo = alchemy.use('mongodb'),
|
|
3
|
+
all_prefixes = alchemy.shared('Routing.prefixes'),
|
|
4
|
+
fs = alchemy.use('fs'),
|
|
5
|
+
createdModel;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The Model class
|
|
9
|
+
*
|
|
10
|
+
* @constructor
|
|
11
|
+
*
|
|
12
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
13
|
+
* @since 0.0.1
|
|
14
|
+
* @version 1.1.0
|
|
15
|
+
*/
|
|
16
|
+
var Model = Function.inherits('Alchemy.Base', 'Alchemy.Model', function Model(options) {
|
|
17
|
+
this.init(options);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Set the modelName property after class creation
|
|
22
|
+
*
|
|
23
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
24
|
+
* @since 0.2.0
|
|
25
|
+
* @version 1.1.0
|
|
26
|
+
*/
|
|
27
|
+
Model.constitute(function setModelName() {
|
|
28
|
+
this.model_name = this.name;
|
|
29
|
+
this.setProperty('model_name', this.model_name);
|
|
30
|
+
|
|
31
|
+
if (!this.table) {
|
|
32
|
+
this.table = this.model_name.tableize();
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* This is a model constructor
|
|
38
|
+
*
|
|
39
|
+
* @type {Boolean}
|
|
40
|
+
*/
|
|
41
|
+
Model.setStaticProperty('model', true);
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The cache duration static getter/setter
|
|
45
|
+
*
|
|
46
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
47
|
+
* @since 0.1.0
|
|
48
|
+
* @version 1.0.3
|
|
49
|
+
*
|
|
50
|
+
* @property cache_duration
|
|
51
|
+
* @type {String}
|
|
52
|
+
*/
|
|
53
|
+
Model.setStaticProperty(function cache_duration() {
|
|
54
|
+
|
|
55
|
+
if (this._cache_duration == null) {
|
|
56
|
+
this._cache_duration = alchemy.settings.model_query_cache_duration;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return this._cache_duration;
|
|
60
|
+
}, function setCacheDuration(duration) {
|
|
61
|
+
this._cache_duration = duration;
|
|
62
|
+
|
|
63
|
+
// @todo: reset cache
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Get the cache object
|
|
68
|
+
*
|
|
69
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
70
|
+
* @since 0.1.0
|
|
71
|
+
* @version 1.0.3
|
|
72
|
+
*
|
|
73
|
+
* @property cache
|
|
74
|
+
* @type {Object}
|
|
75
|
+
*/
|
|
76
|
+
Model.setStaticProperty(function cache() {
|
|
77
|
+
|
|
78
|
+
if (this.cache_duration) {
|
|
79
|
+
|
|
80
|
+
if (this._cache) {
|
|
81
|
+
return this._cache;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
this._cache = alchemy.getCache(this.name, this.cache_duration);
|
|
85
|
+
return this._cache;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return false;
|
|
89
|
+
}, function setCache(value) {
|
|
90
|
+
return this._cache = value;
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Is this an abstract model?
|
|
96
|
+
*
|
|
97
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
98
|
+
* @since 1.1.0
|
|
99
|
+
* @version 1.1.0
|
|
100
|
+
*
|
|
101
|
+
* @type {Boolean}
|
|
102
|
+
*/
|
|
103
|
+
Model.setStaticProperty(function is_abstract() {
|
|
104
|
+
|
|
105
|
+
// Do simple is_abstract_class check
|
|
106
|
+
if (this.is_abstract_class != null) {
|
|
107
|
+
return !!this.is_abstract_class;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// If we need to load an external schema, it's also not abstract
|
|
111
|
+
if (this.prototype.load_external_schema) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// See if this model has other fields than the default ones
|
|
116
|
+
let field_count = this.schema.array.length;
|
|
117
|
+
|
|
118
|
+
if (this.schema.has('_id')) {
|
|
119
|
+
field_count--;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (this.schema.has('created')) {
|
|
123
|
+
field_count--;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (this.schema.has('updated')) {
|
|
127
|
+
field_count--;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return field_count < 1;
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get the document class constructor
|
|
136
|
+
*
|
|
137
|
+
* @type {Alchemy.Document}
|
|
138
|
+
*/
|
|
139
|
+
Model.prepareStaticProperty('Document', function getDocumentClass() {
|
|
140
|
+
return Classes.Alchemy.Document.Document.getDocumentClass(this);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Get the client document class constructor
|
|
145
|
+
*
|
|
146
|
+
* @type {Hawkejs.Document}
|
|
147
|
+
*/
|
|
148
|
+
Model.prepareStaticProperty('ClientDocument', function getClientDocumentClass() {
|
|
149
|
+
return this.Document.getClientDocumentClass();
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Set the static per-model schema
|
|
154
|
+
*
|
|
155
|
+
* @version 1.1.0
|
|
156
|
+
*
|
|
157
|
+
* @type {Schema}
|
|
158
|
+
*/
|
|
159
|
+
Model.staticCompose('schema', function createSchema(doNext) {
|
|
160
|
+
|
|
161
|
+
var that = this,
|
|
162
|
+
model = this.compositorParent,
|
|
163
|
+
schema = new Classes.Alchemy.Schema();
|
|
164
|
+
|
|
165
|
+
// The base Model does not have a schema
|
|
166
|
+
if (model.name == 'Model') {
|
|
167
|
+
return false;
|
|
168
|
+
} else {
|
|
169
|
+
|
|
170
|
+
// Link the schema to this model
|
|
171
|
+
schema.setModel(model);
|
|
172
|
+
|
|
173
|
+
// Set the schema name
|
|
174
|
+
schema.setName(model.name);
|
|
175
|
+
|
|
176
|
+
if (model.prototype.add_basic_fields !== false) {
|
|
177
|
+
|
|
178
|
+
// Set default model fields immediately after this function ends
|
|
179
|
+
// This has to be scheduled next, because addField would call createSchema
|
|
180
|
+
// again, resulting in an infinite loop
|
|
181
|
+
doNext(function addSchemaBasics() {
|
|
182
|
+
model.addField('_id', 'ObjectId', {default: Field.createPathEvaluator('alchemy.ObjectId')});
|
|
183
|
+
model.addField('created', 'Datetime', {default: Field.createPathEvaluator('Date.create')});
|
|
184
|
+
model.addField('updated', 'Datetime', {default: Field.createPathEvaluator('Date.create')});
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return schema;
|
|
190
|
+
}, [
|
|
191
|
+
'addEnumValues',
|
|
192
|
+
'setEnumValues',
|
|
193
|
+
'belongsTo',
|
|
194
|
+
'hasOneParent',
|
|
195
|
+
'hasAndBelongsToMany',
|
|
196
|
+
'hasMany',
|
|
197
|
+
'hasOneChild',
|
|
198
|
+
'addIndex',
|
|
199
|
+
'addRule',
|
|
200
|
+
]);
|
|
201
|
+
|
|
202
|
+
Model.setDeprecatedProperty('modelName', 'model_name');
|
|
203
|
+
Model.setDeprecatedProperty('blueprint', 'schema');
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* The default database config to use
|
|
207
|
+
*
|
|
208
|
+
* @type {String}
|
|
209
|
+
*/
|
|
210
|
+
Model.setProperty('dbConfig', 'default');
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* The default field to use as display
|
|
214
|
+
*
|
|
215
|
+
* @type {String}
|
|
216
|
+
*/
|
|
217
|
+
Model.setProperty('displayField', 'title');
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Translate is on by default
|
|
221
|
+
*
|
|
222
|
+
* @type {Boolean}
|
|
223
|
+
*/
|
|
224
|
+
Model.setProperty('translate', true);
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Set the name of the primary key field
|
|
228
|
+
*
|
|
229
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
230
|
+
* @since 0.1.0
|
|
231
|
+
* @version 0.1.0
|
|
232
|
+
*/
|
|
233
|
+
Model.setProperty('primary_key', '_id');
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Should we load the schema from the database?
|
|
237
|
+
*
|
|
238
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
239
|
+
* @since 1.1.0
|
|
240
|
+
* @version 1.1.0
|
|
241
|
+
*/
|
|
242
|
+
Model.setProperty('load_external_schema', false);
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Object where behaviours are stored
|
|
246
|
+
*
|
|
247
|
+
* @type {Object}
|
|
248
|
+
*/
|
|
249
|
+
Model.prepareProperty('behaviours', Object);
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* Associations
|
|
253
|
+
*
|
|
254
|
+
* @type {Object}
|
|
255
|
+
*/
|
|
256
|
+
Model.setProperty(function associations() {
|
|
257
|
+
return this.schema.associations;
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Instance access to static cache
|
|
262
|
+
*
|
|
263
|
+
* @type {Expirable}
|
|
264
|
+
*/
|
|
265
|
+
Model.prepareProperty('cache', function cache() {
|
|
266
|
+
return this.constructor.cache;
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Instance access to static schema
|
|
271
|
+
*
|
|
272
|
+
* @type {Schema}
|
|
273
|
+
*/
|
|
274
|
+
Model.setProperty(function schema() {
|
|
275
|
+
return this.constructor.schema;
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Is this an abstract model?
|
|
280
|
+
*
|
|
281
|
+
* @type {Boolean}
|
|
282
|
+
*/
|
|
283
|
+
Model.setProperty(function is_abstract() {
|
|
284
|
+
return this.constructor.is_abstract;
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* This is a wrapper class
|
|
289
|
+
*/
|
|
290
|
+
Model.makeAbstractClass();
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* This wrapper class starts a new group
|
|
294
|
+
*/
|
|
295
|
+
Model.startNewGroup();
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* The connection
|
|
299
|
+
*
|
|
300
|
+
* @type {Object}
|
|
301
|
+
*/
|
|
302
|
+
Model.prepareProperty('datasource', function datasource() {
|
|
303
|
+
if (this.table) return Datasource.get(this.dbConfig);
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* The default sort options
|
|
308
|
+
*
|
|
309
|
+
* @type {Object}
|
|
310
|
+
*/
|
|
311
|
+
Model.prepareProperty('sort', function sort() {
|
|
312
|
+
return {created: 1};
|
|
313
|
+
});
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Check a url value
|
|
317
|
+
*
|
|
318
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
319
|
+
* @since 1.0.0
|
|
320
|
+
* @version 1.2.5
|
|
321
|
+
*
|
|
322
|
+
* @param {String} value The value in the url
|
|
323
|
+
* @param {String} name The name of the url parameter
|
|
324
|
+
* @param {String} field_name The name of the field to check
|
|
325
|
+
* @param {Conduit} conduit The optional conduit
|
|
326
|
+
*
|
|
327
|
+
* @return {Pledge}
|
|
328
|
+
*/
|
|
329
|
+
Model.setStatic(async function checkPathValue(value, name, field_name, conduit) {
|
|
330
|
+
|
|
331
|
+
var instance,
|
|
332
|
+
pledge,
|
|
333
|
+
crit;
|
|
334
|
+
|
|
335
|
+
if (!field_name) {
|
|
336
|
+
if (name == 'id') {
|
|
337
|
+
field_name = this.prototype.primary_key;
|
|
338
|
+
} else {
|
|
339
|
+
field_name = name;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (conduit) {
|
|
344
|
+
instance = conduit.getModel(this);
|
|
345
|
+
} else {
|
|
346
|
+
instance = new this;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Create new criteria instance
|
|
350
|
+
crit = instance.find();
|
|
351
|
+
|
|
352
|
+
// Look for the wanted field
|
|
353
|
+
crit.where(field_name).equals(value);
|
|
354
|
+
|
|
355
|
+
let result = await instance.find('first', crit);
|
|
356
|
+
|
|
357
|
+
if (result) {
|
|
358
|
+
let found_value = result[field_name];
|
|
359
|
+
|
|
360
|
+
if (found_value != value && !Object.alike(value, found_value)) {
|
|
361
|
+
conduit.rewriteRequestRouteParam(name, found_value);
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
return result;
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Add a field to this model's schema
|
|
370
|
+
*
|
|
371
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
372
|
+
* @since 0.2.0
|
|
373
|
+
* @version 1.2.4
|
|
374
|
+
*
|
|
375
|
+
* @return {Alchemy.Field}
|
|
376
|
+
*/
|
|
377
|
+
Model.setStatic(function addField(name, type, options) {
|
|
378
|
+
|
|
379
|
+
var field,
|
|
380
|
+
is_new;
|
|
381
|
+
|
|
382
|
+
is_new = !this.schema.has(name);
|
|
383
|
+
|
|
384
|
+
// Add it to the schema
|
|
385
|
+
field = this.schema.addField(name, type, options);
|
|
386
|
+
|
|
387
|
+
if (is_new) {
|
|
388
|
+
// Add it to the Document class
|
|
389
|
+
this.Document.setFieldGetter(name);
|
|
390
|
+
|
|
391
|
+
// False means it should not be set on the server implementation
|
|
392
|
+
// (because that's where it's coming from)
|
|
393
|
+
// Yes, this also sets private fields on the server-side client document.
|
|
394
|
+
this.ClientDocument.setFieldGetter(name, null, null, false);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
return field;
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
/**
|
|
402
|
+
* Add a behaviour to this model
|
|
403
|
+
*
|
|
404
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
405
|
+
* @since 0.2.0
|
|
406
|
+
* @version 0.2.0
|
|
407
|
+
*/
|
|
408
|
+
Model.setStatic(function addBehaviour(behaviour_name, options) {
|
|
409
|
+
return this.schema.addBehaviour(behaviour_name, options);
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Add an association to this model's schema
|
|
414
|
+
* and set it on the Document as a getter
|
|
415
|
+
*
|
|
416
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
417
|
+
* @since 0.2.0
|
|
418
|
+
* @version 1.2.4
|
|
419
|
+
*/
|
|
420
|
+
Model.setStatic(function addAssociation(type, alias, model_name, options) {
|
|
421
|
+
var data = this.schema.addAssociation(type, alias, model_name, options);
|
|
422
|
+
this.Document.setAliasGetter(data.alias);
|
|
423
|
+
|
|
424
|
+
// False means it should not be set on the server implementation
|
|
425
|
+
// (because that's where it's coming from)
|
|
426
|
+
// Yes, this also sets private fields on the server-side client document.
|
|
427
|
+
this.ClientDocument.setAliasGetter(name);
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Set a method on the document class
|
|
432
|
+
*
|
|
433
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
434
|
+
* @since 0.2.0
|
|
435
|
+
* @version 1.0.6
|
|
436
|
+
*/
|
|
437
|
+
Model.setStatic(function setDocumentMethod(name, fnc) {
|
|
438
|
+
|
|
439
|
+
var that = this;
|
|
440
|
+
|
|
441
|
+
if (typeof name == 'function') {
|
|
442
|
+
fnc = name;
|
|
443
|
+
name = fnc.name;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
Blast.loaded(function whenLoaded() {
|
|
447
|
+
that.Document.setMethod(name, fnc);
|
|
448
|
+
});
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Set a property on the document class
|
|
453
|
+
*
|
|
454
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
455
|
+
* @since 0.2.0
|
|
456
|
+
* @version 1.0.6
|
|
457
|
+
*/
|
|
458
|
+
Model.setStatic(function setDocumentProperty(name, fnc) {
|
|
459
|
+
|
|
460
|
+
var that = this,
|
|
461
|
+
args = arguments;
|
|
462
|
+
|
|
463
|
+
Blast.loaded(function whenLoaded() {
|
|
464
|
+
that.Document.setProperty.apply(that.Document, args);
|
|
465
|
+
});
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Get a field
|
|
470
|
+
*
|
|
471
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
472
|
+
* @since 0.2.0
|
|
473
|
+
* @version 0.2.0
|
|
474
|
+
*
|
|
475
|
+
* @return {FieldType}
|
|
476
|
+
*/
|
|
477
|
+
Model.setStatic(function getField(name) {
|
|
478
|
+
|
|
479
|
+
var fieldPath,
|
|
480
|
+
alias,
|
|
481
|
+
model,
|
|
482
|
+
split;
|
|
483
|
+
|
|
484
|
+
if (name.indexOf('.') > -1) {
|
|
485
|
+
split = name.split('.');
|
|
486
|
+
|
|
487
|
+
alias = name[0];
|
|
488
|
+
|
|
489
|
+
if (this.schema.associations[alias] == null) {
|
|
490
|
+
model = this;
|
|
491
|
+
fieldPath = name;
|
|
492
|
+
} else {
|
|
493
|
+
model = Model.get(this.schema.associations[alias].modelName).constructor;
|
|
494
|
+
split.shift();
|
|
495
|
+
fieldPath = split.join('.');
|
|
496
|
+
}
|
|
497
|
+
} else {
|
|
498
|
+
model = this;
|
|
499
|
+
fieldPath = name;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
return model.schema.get(fieldPath);
|
|
503
|
+
});
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* Get the model's public configuration
|
|
507
|
+
* (This is used to create the client-side Model instances)
|
|
508
|
+
*
|
|
509
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
510
|
+
* @since 1.0.0
|
|
511
|
+
* @version 1.1.0
|
|
512
|
+
*/
|
|
513
|
+
Model.setStatic(function getClientConfig() {
|
|
514
|
+
|
|
515
|
+
var result = {
|
|
516
|
+
name : this.model_name,
|
|
517
|
+
schema : this.schema,
|
|
518
|
+
primary_key : this.prototype.primary_key
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
if (this.super.name != 'Model') {
|
|
522
|
+
result.parent = this.super.name;
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
return result;
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Initialize behaviours
|
|
530
|
+
*
|
|
531
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
532
|
+
* @since 0.2.0
|
|
533
|
+
* @version 0.2.0
|
|
534
|
+
*
|
|
535
|
+
* @return {Document}
|
|
536
|
+
*/
|
|
537
|
+
Model.setMethod(function initBehaviours() {
|
|
538
|
+
|
|
539
|
+
var behaviour,
|
|
540
|
+
key;
|
|
541
|
+
|
|
542
|
+
this.behaviours = {};
|
|
543
|
+
|
|
544
|
+
for (key in this.schema.behaviours) {
|
|
545
|
+
behaviour = this.schema.behaviours[key];
|
|
546
|
+
|
|
547
|
+
this.behaviours[key] = new behaviour.constructor(this, behaviour.options);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Enable a behaviour on-the-fly
|
|
554
|
+
*
|
|
555
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
556
|
+
* @since 0.0.1
|
|
557
|
+
* @version 0.2.0
|
|
558
|
+
*/
|
|
559
|
+
Model.setMethod(function addBehaviour(behaviourname, options) {
|
|
560
|
+
|
|
561
|
+
var instance;
|
|
562
|
+
|
|
563
|
+
if (!options) {
|
|
564
|
+
options = {};
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
instance = Behaviour.get(behaviourname, this, options);
|
|
568
|
+
this.behaviours[behaviourname] = instance;
|
|
569
|
+
|
|
570
|
+
return instance;
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* Get a behaviour instance
|
|
575
|
+
*
|
|
576
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
577
|
+
* @since 1.0.3
|
|
578
|
+
* @version 1.0.3
|
|
579
|
+
*
|
|
580
|
+
* @param {String} name
|
|
581
|
+
*
|
|
582
|
+
* @return {Behaviour}
|
|
583
|
+
*/
|
|
584
|
+
Model.setMethod(function getBehaviour(name) {
|
|
585
|
+
|
|
586
|
+
name = name.camelize();
|
|
587
|
+
|
|
588
|
+
if (!name.endsWith('Behaviour')) {
|
|
589
|
+
name += 'Behaviour';
|
|
590
|
+
}
|
|
591
|
+
|
|
592
|
+
return this.behaviours[name];
|
|
593
|
+
});
|
|
594
|
+
|
|
595
|
+
/**
|
|
596
|
+
* Enable translations
|
|
597
|
+
*
|
|
598
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
599
|
+
* @since 0.2.0
|
|
600
|
+
* @version 0.2.0
|
|
601
|
+
*/
|
|
602
|
+
Model.setMethod(function enableTranslations() {
|
|
603
|
+
this.translate = true;
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
/**
|
|
607
|
+
* Disable translations
|
|
608
|
+
*
|
|
609
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
610
|
+
* @since 0.2.0
|
|
611
|
+
* @version 0.2.0
|
|
612
|
+
*/
|
|
613
|
+
Model.setMethod(function disableTranslations() {
|
|
614
|
+
this.translate = false;
|
|
615
|
+
});
|
|
616
|
+
|
|
617
|
+
/**
|
|
618
|
+
* Aggregate
|
|
619
|
+
*
|
|
620
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
621
|
+
* @since 0.5.0
|
|
622
|
+
* @version 0.5.0
|
|
623
|
+
*
|
|
624
|
+
* @param {Array} pipeline
|
|
625
|
+
* @param {Function} callback
|
|
626
|
+
*/
|
|
627
|
+
Model.setMethod(function aggregate(pipeline, callback) {
|
|
628
|
+
|
|
629
|
+
this.datasource.collection(this.table, function gotCollection(err, collection) {
|
|
630
|
+
|
|
631
|
+
if (err) {
|
|
632
|
+
return callback(err);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
collection.aggregate(pipeline, callback);
|
|
636
|
+
});
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
/**
|
|
640
|
+
* Translate the given records
|
|
641
|
+
*
|
|
642
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
643
|
+
* @since 0.2.0
|
|
644
|
+
* @version 1.2.6
|
|
645
|
+
*
|
|
646
|
+
* @param {Array} items
|
|
647
|
+
* @param {Object} options Optional options object
|
|
648
|
+
* @param {Function} callback
|
|
649
|
+
*/
|
|
650
|
+
Model.setMethod(function translateItems(items, options, callback) {
|
|
651
|
+
|
|
652
|
+
if (options && options instanceof Classes.Alchemy.Criteria.Criteria) {
|
|
653
|
+
options = options.options;
|
|
654
|
+
}
|
|
655
|
+
|
|
656
|
+
// No items to translate
|
|
657
|
+
if (!items.length) {
|
|
658
|
+
return callback();
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
// No fields in this schema are translatable
|
|
662
|
+
if (!this.schema.hasTranslations) {
|
|
663
|
+
return callback();
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// Do nothing if there are no translatable fields
|
|
667
|
+
// or translate is disabled
|
|
668
|
+
if (!this.translate || (!this.conduit && !options.locale)) {
|
|
669
|
+
return callback();
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
let collection,
|
|
673
|
+
fieldName,
|
|
674
|
+
prefixes,
|
|
675
|
+
prefix,
|
|
676
|
+
record,
|
|
677
|
+
found,
|
|
678
|
+
item,
|
|
679
|
+
key,
|
|
680
|
+
i,
|
|
681
|
+
j;
|
|
682
|
+
|
|
683
|
+
// Get the alias we need to translate
|
|
684
|
+
let alias = options.forAlias || this.name;
|
|
685
|
+
|
|
686
|
+
// Get the (optional) attached conduit
|
|
687
|
+
let conduit = this.conduit;
|
|
688
|
+
|
|
689
|
+
// If prefixes are given as an option, only use those
|
|
690
|
+
if (options.prefixes) {
|
|
691
|
+
prefix = options.prefixes;
|
|
692
|
+
} else {
|
|
693
|
+
// Possible prefixes
|
|
694
|
+
prefix = [];
|
|
695
|
+
|
|
696
|
+
// Prefixes set in the options get precedence
|
|
697
|
+
if (options.locale && options.locale !== true) {
|
|
698
|
+
prefix.include(options.locale);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
// Append the visited prefix after that (if there is one)
|
|
702
|
+
if (conduit && conduit.prefix) {
|
|
703
|
+
prefix.include(conduit.prefix);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
// Append all the allowed locales after that
|
|
707
|
+
if (conduit && conduit.locales) {
|
|
708
|
+
prefix.include(conduit.locales);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
// Add all available prefixes last
|
|
712
|
+
for (key in all_prefixes) {
|
|
713
|
+
prefix.push(key);
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
// The fallback prefix
|
|
717
|
+
prefix.push('__');
|
|
718
|
+
|
|
719
|
+
// @DEPRECATED: empty keys should no longer be allowed
|
|
720
|
+
prefix.push('');
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
for (i = 0; i < items.length; i++) {
|
|
724
|
+
item = items[i];
|
|
725
|
+
|
|
726
|
+
if (!options.ungrouped_items) {
|
|
727
|
+
item = item[alias];
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
// Don't translate items twice
|
|
731
|
+
if (item?.$translated_fields && !Object.isEmpty(item.$translated_fields)) {
|
|
732
|
+
continue;
|
|
733
|
+
}
|
|
734
|
+
|
|
735
|
+
collection = Array.cast(item);
|
|
736
|
+
|
|
737
|
+
// Clone the prefixes
|
|
738
|
+
prefixes = prefix.slice(0);
|
|
739
|
+
|
|
740
|
+
// If one of the query conditions searched through a translatable field,
|
|
741
|
+
// the prefix found should get preference
|
|
742
|
+
if (options.use_found_prefix && items.item_prefixes && items.item_prefixes[i]) {
|
|
743
|
+
prefixes.unshift(items.item_prefixes[i]);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
let field;
|
|
747
|
+
|
|
748
|
+
for (j = 0; j < collection.length; j++) {
|
|
749
|
+
record = collection[j];
|
|
750
|
+
|
|
751
|
+
for (fieldName in this.schema.translatableFields) {
|
|
752
|
+
field = this.schema.translatableFields[fieldName];
|
|
753
|
+
field.translateRecord(prefixes, record, options.allow_empty);
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
callback();
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
/**
|
|
762
|
+
* Create the given record if the id does not exist in the database
|
|
763
|
+
*
|
|
764
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
765
|
+
* @since 0.0.1
|
|
766
|
+
* @version 1.1.0
|
|
767
|
+
*
|
|
768
|
+
* @param {Array} list A list of all the records that need to be in the db
|
|
769
|
+
* @param {Function} callback
|
|
770
|
+
*/
|
|
771
|
+
Model.setMethod(function ensureIds(list, callback) {
|
|
772
|
+
|
|
773
|
+
var that = this;
|
|
774
|
+
|
|
775
|
+
list = Array.cast(list);
|
|
776
|
+
|
|
777
|
+
return Function.forEach.parallel(list, function checkEntry(entry, key, next) {
|
|
778
|
+
var id;
|
|
779
|
+
|
|
780
|
+
id = entry[that.primary_key];
|
|
781
|
+
|
|
782
|
+
if (!id && entry[that.name]) {
|
|
783
|
+
id = entry[that.name][that.primary_key];
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
if (!id) {
|
|
787
|
+
return next(new Classes.Alchemy.Error.Model('`Model#ensureIds()` can\'t ensure an entry without an _id'));
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
that.findById(id, function gotItem(err, result) {
|
|
791
|
+
|
|
792
|
+
if (err) {
|
|
793
|
+
return next(err);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
if (result) {
|
|
797
|
+
return next();
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
that.save(entry, {create: true, document: false}, next);
|
|
801
|
+
});
|
|
802
|
+
}, callback);
|
|
803
|
+
});
|
|
804
|
+
|
|
805
|
+
/**
|
|
806
|
+
* Save one record
|
|
807
|
+
*
|
|
808
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
809
|
+
* @since 0.0.1
|
|
810
|
+
* @version 1.2.0
|
|
811
|
+
*
|
|
812
|
+
* @param {Document} document
|
|
813
|
+
* @param {Object} options
|
|
814
|
+
* @param {Function} callback
|
|
815
|
+
*
|
|
816
|
+
* @return {Pledge}
|
|
817
|
+
*/
|
|
818
|
+
Model.setMethod(function saveRecord(document, options, callback) {
|
|
819
|
+
|
|
820
|
+
var that = this,
|
|
821
|
+
saved_record,
|
|
822
|
+
creating,
|
|
823
|
+
results,
|
|
824
|
+
pledge,
|
|
825
|
+
main,
|
|
826
|
+
iter;
|
|
827
|
+
|
|
828
|
+
if (!document) {
|
|
829
|
+
pledge = Pledge.reject(new Error('Unable to save record: given document is undefined'));
|
|
830
|
+
pledge.done(callback);
|
|
831
|
+
return pledge;
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
// Normalize the arguments
|
|
835
|
+
if (typeof options == 'function') {
|
|
836
|
+
callback = options;
|
|
837
|
+
}
|
|
838
|
+
|
|
839
|
+
if (typeof options !== 'object') {
|
|
840
|
+
options = {};
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
pledge = Function.series(function doAudit(next) {
|
|
844
|
+
|
|
845
|
+
if (Object.isPlainObject(document)) {
|
|
846
|
+
return next(new Error('Model#saveRecord() expects a Document, not a plain object'));
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
// Look through unique indexes if no _id is present
|
|
850
|
+
that.auditRecord(document, options, function afterAudit(err, doc) {
|
|
851
|
+
|
|
852
|
+
if (err) {
|
|
853
|
+
return next(err);
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
// Is a new record being created?
|
|
857
|
+
creating = options.create || doc[that.primary_key] == null;
|
|
858
|
+
next();
|
|
859
|
+
});
|
|
860
|
+
}, function doBeforeSave(next) {
|
|
861
|
+
|
|
862
|
+
if (typeof that.beforeSave == 'function') {
|
|
863
|
+
let promise = that.beforeSave(document, options, next);
|
|
864
|
+
|
|
865
|
+
if (promise) {
|
|
866
|
+
Pledge.done(promise, next);
|
|
867
|
+
} else if (that.beforeSave.length < 3) {
|
|
868
|
+
// If the method accepts no `next` callback, call it now
|
|
869
|
+
next();
|
|
870
|
+
}
|
|
871
|
+
} else {
|
|
872
|
+
next();
|
|
873
|
+
}
|
|
874
|
+
|
|
875
|
+
}, function emitSavingEvent(next) {
|
|
876
|
+
that.emit('saving', document, options, creating, function afterSavingEvent(err, stopped) {
|
|
877
|
+
return next(err);
|
|
878
|
+
});
|
|
879
|
+
}, function doDatabase(next) {
|
|
880
|
+
|
|
881
|
+
if (options.debug) {
|
|
882
|
+
console.log('Saving document', document, 'Creating?', creating);
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
function gotRecord(err, result) {
|
|
886
|
+
if (err) {
|
|
887
|
+
return next(err);
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
saved_record = result;
|
|
891
|
+
next();
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
if (creating) {
|
|
895
|
+
that.createRecord(document, options, gotRecord);
|
|
896
|
+
} else {
|
|
897
|
+
that.updateRecord(document, options, gotRecord);
|
|
898
|
+
}
|
|
899
|
+
}, function doAssociated(next) {
|
|
900
|
+
|
|
901
|
+
var tasks = [],
|
|
902
|
+
assoc,
|
|
903
|
+
entry,
|
|
904
|
+
key;
|
|
905
|
+
|
|
906
|
+
Object.each(document.$record, function eachEntry(entry, key) {
|
|
907
|
+
|
|
908
|
+
// Skip our own record
|
|
909
|
+
if (key == that.name) {
|
|
910
|
+
return;
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
// Get the association configuration
|
|
914
|
+
assoc = that.schema.associations[key];
|
|
915
|
+
|
|
916
|
+
// If the association doesn't exist, do nothing
|
|
917
|
+
if (!assoc) {
|
|
918
|
+
return;
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
// Add the saved _id
|
|
922
|
+
//@TODO: this throws an error sometimes.
|
|
923
|
+
entry[assoc.options.foreignKey] = saved_record[assoc.options.localKey];
|
|
924
|
+
|
|
925
|
+
// Add the task
|
|
926
|
+
tasks.push(function doSave(next) {
|
|
927
|
+
var a_model = that.getModel(assoc.modelName);
|
|
928
|
+
a_model.save(entry, next);
|
|
929
|
+
});
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
Function.parallel(tasks, next);
|
|
933
|
+
}, function done(err) {
|
|
934
|
+
|
|
935
|
+
if (err) {
|
|
936
|
+
return;
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
return saved_record;
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
pledge.handleCallback(callback);
|
|
943
|
+
|
|
944
|
+
return pledge;
|
|
945
|
+
});
|
|
946
|
+
|
|
947
|
+
/**
|
|
948
|
+
* Look for the record id by checking the indexes
|
|
949
|
+
*
|
|
950
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
951
|
+
* @since 0.1.0
|
|
952
|
+
* @version 1.1.0
|
|
953
|
+
*
|
|
954
|
+
* @param {Document} document
|
|
955
|
+
* @param {Object} options
|
|
956
|
+
* @param {Function} callback
|
|
957
|
+
*/
|
|
958
|
+
Model.setMethod(function auditRecord(document, options, callback) {
|
|
959
|
+
|
|
960
|
+
var that = this,
|
|
961
|
+
results,
|
|
962
|
+
schema,
|
|
963
|
+
tasks;
|
|
964
|
+
|
|
965
|
+
if (!document) {
|
|
966
|
+
return callback(new Error('No record was given to audit'));
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
schema = this.schema;
|
|
970
|
+
|
|
971
|
+
if (schema && document[this.primary_key] == null && options.audit !== false) {
|
|
972
|
+
tasks = {};
|
|
973
|
+
results = {};
|
|
974
|
+
|
|
975
|
+
if (options.debug) {
|
|
976
|
+
console.log('Pre-save audit record', document);
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
schema.eachAlternateIndex(document, function iterIndex(index, indexName) {
|
|
980
|
+
|
|
981
|
+
if (options.debug) {
|
|
982
|
+
console.log('Checking alternate index', indexName);
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
tasks[indexName] = function auditIndex(next) {
|
|
986
|
+
var query = {},
|
|
987
|
+
fieldName;
|
|
988
|
+
|
|
989
|
+
for (fieldName in index.fields) {
|
|
990
|
+
if (document[fieldName] != null) {
|
|
991
|
+
query[fieldName] = document[fieldName];
|
|
992
|
+
|
|
993
|
+
// @todo: should run through the FieldType instance
|
|
994
|
+
if (String(query[fieldName]).isObjectId()) {
|
|
995
|
+
query[fieldName] = alchemy.castObjectId(query[fieldName]);
|
|
996
|
+
}
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
1000
|
+
that.datasource.read(that, query, {}, function gotRecordInfo(err, records) {
|
|
1001
|
+
|
|
1002
|
+
if (err != null) {
|
|
1003
|
+
return next(err);
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
if (records[0] != null) {
|
|
1007
|
+
results[indexName] = records[0];
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
next();
|
|
1011
|
+
});
|
|
1012
|
+
|
|
1013
|
+
};
|
|
1014
|
+
});
|
|
1015
|
+
|
|
1016
|
+
Function.parallel(tasks, function doneAudit(err) {
|
|
1017
|
+
|
|
1018
|
+
var indexName,
|
|
1019
|
+
record,
|
|
1020
|
+
count,
|
|
1021
|
+
ids;
|
|
1022
|
+
|
|
1023
|
+
if (err != null) {
|
|
1024
|
+
return callback(err);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
if (!Object.isEmpty(results)) {
|
|
1028
|
+
|
|
1029
|
+
count = 0;
|
|
1030
|
+
ids = {};
|
|
1031
|
+
|
|
1032
|
+
for (indexName in results) {
|
|
1033
|
+
record = results[indexName];
|
|
1034
|
+
|
|
1035
|
+
// First make sure this index is allowed during the audit
|
|
1036
|
+
// If it's not, this means it should be considered a duplicate
|
|
1037
|
+
if (options.allowedIndexes != null && !Object.hasValue(options.allowedIndexes, indexName)) {
|
|
1038
|
+
if (callback) callback(new Error('Duplicate index found other than _id: ' + indexName), null);
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
|
|
1042
|
+
// Add the id a first time
|
|
1043
|
+
if (ids[record[that.primary_key]] == null) {
|
|
1044
|
+
count++;
|
|
1045
|
+
ids[record[that.primary_key]] = true;
|
|
1046
|
+
}
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
// If more than 1 ids are found, we can't update the item
|
|
1050
|
+
// because we don't know which record is the actual owner
|
|
1051
|
+
if (count > 1) {
|
|
1052
|
+
if (callback) callback(new Error('Multiple unique records found'));
|
|
1053
|
+
return;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// Use the last found record to get the id
|
|
1057
|
+
document[that.primary_key] = record[that.primary_key];
|
|
1058
|
+
}
|
|
1059
|
+
|
|
1060
|
+
if (options.debug) {
|
|
1061
|
+
console.log('Audit done, found pk:', document[that.primary_key]);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
callback(null, document);
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
setImmediate(function skippedAudit() {
|
|
1071
|
+
callback(null, document);
|
|
1072
|
+
});
|
|
1073
|
+
});
|
|
1074
|
+
|
|
1075
|
+
/**
|
|
1076
|
+
* Turn a record into something the database will understand
|
|
1077
|
+
*
|
|
1078
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1079
|
+
* @since 1.0.3
|
|
1080
|
+
* @version 1.0.4
|
|
1081
|
+
*
|
|
1082
|
+
* @param {Document} record
|
|
1083
|
+
* @param {Object} options
|
|
1084
|
+
* @param {Function} callback
|
|
1085
|
+
*
|
|
1086
|
+
* @return {Pledge}
|
|
1087
|
+
*/
|
|
1088
|
+
Model.setMethod(function convertRecordToDatasourceFormat(record, options, callback) {
|
|
1089
|
+
|
|
1090
|
+
var that = this,
|
|
1091
|
+
pledge,
|
|
1092
|
+
data;
|
|
1093
|
+
|
|
1094
|
+
if (typeof options == 'function') {
|
|
1095
|
+
callback = options;
|
|
1096
|
+
options = {};
|
|
1097
|
+
}
|
|
1098
|
+
|
|
1099
|
+
if (!options) {
|
|
1100
|
+
options = {};
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
data = record[this.name] || record;
|
|
1104
|
+
|
|
1105
|
+
// Normalize the data
|
|
1106
|
+
data = this.compose(data, options);
|
|
1107
|
+
|
|
1108
|
+
pledge = this.datasource.toDatasource(this, data);
|
|
1109
|
+
|
|
1110
|
+
pledge.handleCallback(callback);
|
|
1111
|
+
|
|
1112
|
+
return pledge;
|
|
1113
|
+
});
|
|
1114
|
+
|
|
1115
|
+
/**
|
|
1116
|
+
* Process an object of datasource format
|
|
1117
|
+
*
|
|
1118
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1119
|
+
* @since 1.0.3
|
|
1120
|
+
* @version 1.0.3
|
|
1121
|
+
*
|
|
1122
|
+
* @param {Object} ds_data
|
|
1123
|
+
* @param {Object} options
|
|
1124
|
+
* @param {Function} callback
|
|
1125
|
+
*
|
|
1126
|
+
* @return {Pledge}
|
|
1127
|
+
*/
|
|
1128
|
+
Model.setMethod(function processDatasourceFormat(ds_data, options, callback) {
|
|
1129
|
+
|
|
1130
|
+
var that = this,
|
|
1131
|
+
pledge,
|
|
1132
|
+
data;
|
|
1133
|
+
|
|
1134
|
+
if (typeof options == 'function') {
|
|
1135
|
+
callback = options;
|
|
1136
|
+
options = {};
|
|
1137
|
+
}
|
|
1138
|
+
|
|
1139
|
+
if (!options) {
|
|
1140
|
+
options = {};
|
|
1141
|
+
}
|
|
1142
|
+
|
|
1143
|
+
pledge = this.datasource.toApp(this.schema, {}, options, ds_data);
|
|
1144
|
+
|
|
1145
|
+
pledge.handleCallback(callback);
|
|
1146
|
+
|
|
1147
|
+
return pledge;
|
|
1148
|
+
});
|
|
1149
|
+
|
|
1150
|
+
/**
|
|
1151
|
+
* Get the title to display for this record
|
|
1152
|
+
*
|
|
1153
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1154
|
+
* @since 0.0.1
|
|
1155
|
+
* @version 0.1.0
|
|
1156
|
+
*
|
|
1157
|
+
* @param {Object} item The record item of this model
|
|
1158
|
+
* @param {String|Array} fallbacks Extra fallbacks to use
|
|
1159
|
+
*
|
|
1160
|
+
* @return {String} The display title to use
|
|
1161
|
+
*/
|
|
1162
|
+
Model.setMethod(function getDisplayTitle(item, fallbacks) {
|
|
1163
|
+
|
|
1164
|
+
var fields,
|
|
1165
|
+
field,
|
|
1166
|
+
main,
|
|
1167
|
+
val,
|
|
1168
|
+
i;
|
|
1169
|
+
|
|
1170
|
+
if (!item) {
|
|
1171
|
+
return 'Undefined item';
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
if (item[this.modelName]) {
|
|
1175
|
+
main = item[this.modelName];
|
|
1176
|
+
} else {
|
|
1177
|
+
main = item;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
if (!main) {
|
|
1181
|
+
return 'Undefined item';
|
|
1182
|
+
}
|
|
1183
|
+
|
|
1184
|
+
fields = Array.cast(this.displayField);
|
|
1185
|
+
|
|
1186
|
+
if (fallbacks) {
|
|
1187
|
+
fields = fields.concat(fallbacks);
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
for (i = 0; i < fields.length; i++) {
|
|
1191
|
+
val = main[fields[i]];
|
|
1192
|
+
|
|
1193
|
+
if (Object.isObject(val)) {
|
|
1194
|
+
field = this.getField(fields[i]);
|
|
1195
|
+
|
|
1196
|
+
if (field && field.isTranslatable) {
|
|
1197
|
+
val = alchemy.pickTranslation(this.conduit, val).result;
|
|
1198
|
+
}
|
|
1199
|
+
}
|
|
1200
|
+
|
|
1201
|
+
if (val && typeof val == 'string') {
|
|
1202
|
+
return val;
|
|
1203
|
+
}
|
|
1204
|
+
}
|
|
1205
|
+
|
|
1206
|
+
return main[this.primary_key] || '';
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
/**
|
|
1210
|
+
* Clear the cache of this and all associated models
|
|
1211
|
+
*
|
|
1212
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1213
|
+
* @since 0.0.1
|
|
1214
|
+
* @version 1.1.6
|
|
1215
|
+
*
|
|
1216
|
+
* @param {Boolean} associated Also nuke associated models
|
|
1217
|
+
* @param {Branch} parent
|
|
1218
|
+
*/
|
|
1219
|
+
Model.setMethod(function nukeCache(associated, parent) {
|
|
1220
|
+
|
|
1221
|
+
let model_name,
|
|
1222
|
+
branch,
|
|
1223
|
+
alias;
|
|
1224
|
+
|
|
1225
|
+
// Nuke associated caches by default
|
|
1226
|
+
if (typeof associated == 'undefined') {
|
|
1227
|
+
associated = true;
|
|
1228
|
+
}
|
|
1229
|
+
|
|
1230
|
+
// Create the parent branch object
|
|
1231
|
+
if (!parent) {
|
|
1232
|
+
branch = parent = new Classes.Branch.Data(this.name);
|
|
1233
|
+
}
|
|
1234
|
+
|
|
1235
|
+
if (branch || !parent.root.seen(this.name)) {
|
|
1236
|
+
|
|
1237
|
+
if (!branch) {
|
|
1238
|
+
branch = parent.append(this.name);
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
if (this.cache) {
|
|
1242
|
+
this.cache.reset();
|
|
1243
|
+
}
|
|
1244
|
+
|
|
1245
|
+
// Also nuke the cache of the client model, if it exists
|
|
1246
|
+
if (Classes.Alchemy.Client.Model[this.constructor.name] && Classes.Alchemy.Client.Model[this.constructor.name].cache) {
|
|
1247
|
+
Classes.Alchemy.Client.Model[this.constructor.name].cache.reset();
|
|
1248
|
+
}
|
|
1249
|
+
}
|
|
1250
|
+
|
|
1251
|
+
// Return if we don't need to nuke associated models
|
|
1252
|
+
if (!associated) {
|
|
1253
|
+
return;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
for (alias in this.associations) {
|
|
1257
|
+
model_name = this.associations[alias].modelName;
|
|
1258
|
+
|
|
1259
|
+
if (!parent.root.seen(model_name)) {
|
|
1260
|
+
let assoc_model = this.getModel(model_name);
|
|
1261
|
+
assoc_model.nukeCache(true, branch);
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
});
|
|
1265
|
+
|
|
1266
|
+
/**
|
|
1267
|
+
* Delete the given record id
|
|
1268
|
+
*
|
|
1269
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1270
|
+
* @since 0.0.1
|
|
1271
|
+
* @version 1.0.7
|
|
1272
|
+
*
|
|
1273
|
+
* @param {String} id The object id
|
|
1274
|
+
* @param {Function} callback
|
|
1275
|
+
*
|
|
1276
|
+
* @return {Pledge}
|
|
1277
|
+
*/
|
|
1278
|
+
Model.setMethod(function remove(id, callback) {
|
|
1279
|
+
|
|
1280
|
+
var that = this,
|
|
1281
|
+
pledge = new Pledge(),
|
|
1282
|
+
id;
|
|
1283
|
+
|
|
1284
|
+
pledge.handleCallback(callback);
|
|
1285
|
+
|
|
1286
|
+
if (!id) {
|
|
1287
|
+
pledge.reject(new Error('Invalid id given!'));
|
|
1288
|
+
return pledge;
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
if (this.datasource.supports('objectid')) {
|
|
1292
|
+
id = alchemy.castObjectId(id);
|
|
1293
|
+
} else {
|
|
1294
|
+
id = String(id);
|
|
1295
|
+
}
|
|
1296
|
+
|
|
1297
|
+
let query = {};
|
|
1298
|
+
query[this.primary_key] = id;
|
|
1299
|
+
|
|
1300
|
+
this.datasource.remove(this, query, {}, function afterRemove(err, result) {
|
|
1301
|
+
|
|
1302
|
+
if (err != null) {
|
|
1303
|
+
return pledge.reject(err);
|
|
1304
|
+
}
|
|
1305
|
+
|
|
1306
|
+
that.emit('removed', result, {}, false, function afterRemovedEvent() {
|
|
1307
|
+
pledge.resolve(result);
|
|
1308
|
+
});
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
return pledge;
|
|
1312
|
+
});
|
|
1313
|
+
|
|
1314
|
+
/**
|
|
1315
|
+
* Get all the records and perform the given task on them
|
|
1316
|
+
*
|
|
1317
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1318
|
+
* @since 0.5.0
|
|
1319
|
+
* @version 1.2.0
|
|
1320
|
+
*
|
|
1321
|
+
* @param {Object} options Find options
|
|
1322
|
+
* @param {Function} task Task to perform on each record
|
|
1323
|
+
* @param {Function} callback Function to call when done
|
|
1324
|
+
*/
|
|
1325
|
+
Model.setMethod(function eachRecord(options, task, callback) {
|
|
1326
|
+
|
|
1327
|
+
var that = this,
|
|
1328
|
+
parallel_limit,
|
|
1329
|
+
available = null,
|
|
1330
|
+
last_id = null,
|
|
1331
|
+
pledge = new Classes.Pledge(),
|
|
1332
|
+
index = 0;
|
|
1333
|
+
|
|
1334
|
+
if (typeof options == 'function') {
|
|
1335
|
+
callback = task;
|
|
1336
|
+
task = options;
|
|
1337
|
+
options = {};
|
|
1338
|
+
} else if (!options) {
|
|
1339
|
+
options = {};
|
|
1340
|
+
}
|
|
1341
|
+
|
|
1342
|
+
if (!callback) {
|
|
1343
|
+
callback = Function.thrower;
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
// Apply default limit of 50 records per fetch
|
|
1347
|
+
options = Object.assign({}, {limit: 50}, options);
|
|
1348
|
+
|
|
1349
|
+
// Get amount of tasks to do in parallel
|
|
1350
|
+
parallel_limit = options.parallel_limit || 8;
|
|
1351
|
+
|
|
1352
|
+
// Sort by _id ascending
|
|
1353
|
+
if (!options.sort) {
|
|
1354
|
+
options.sort = {};
|
|
1355
|
+
options.sort[this.primary_key] = 1;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
// Make sure there is a conditions object
|
|
1359
|
+
if (!options.conditions) {
|
|
1360
|
+
options.conditions = {};
|
|
1361
|
+
}
|
|
1362
|
+
|
|
1363
|
+
this.find('all', options, function gotRecords(err, result) {
|
|
1364
|
+
|
|
1365
|
+
var tasks = [];
|
|
1366
|
+
|
|
1367
|
+
if (!result.length) {
|
|
1368
|
+
pledge.reportProgress(100);
|
|
1369
|
+
pledge.resolve();
|
|
1370
|
+
return callback(null);
|
|
1371
|
+
}
|
|
1372
|
+
|
|
1373
|
+
if (available == null) {
|
|
1374
|
+
available = result.available;
|
|
1375
|
+
options.available = false;
|
|
1376
|
+
} else {
|
|
1377
|
+
result.available = available;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
result.forEach(function eachRecord(record) {
|
|
1381
|
+
|
|
1382
|
+
var record_index = index++;
|
|
1383
|
+
|
|
1384
|
+
last_id = record[that.model_name][that.primary_key];
|
|
1385
|
+
|
|
1386
|
+
tasks.push(function doSave(next) {
|
|
1387
|
+
pledge.reportProgress(((record_index - 1) / available) * 100);
|
|
1388
|
+
task.call(that, record, record_index, next);
|
|
1389
|
+
});
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
Function.parallel(parallel_limit, tasks, function done(err) {
|
|
1393
|
+
|
|
1394
|
+
if (err) {
|
|
1395
|
+
pledge.reject(err);
|
|
1396
|
+
return callback(err);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1399
|
+
let next_options = Object.assign({}, options);
|
|
1400
|
+
|
|
1401
|
+
// Get records with a bigger _id than the last found
|
|
1402
|
+
next_options.conditions[that.primary_key] = {$gt: last_id};
|
|
1403
|
+
|
|
1404
|
+
that.find('all', next_options, gotRecords);
|
|
1405
|
+
});
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
return pledge;
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
/**
|
|
1412
|
+
* Strip out private fields
|
|
1413
|
+
*
|
|
1414
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1415
|
+
* @since 1.0.0
|
|
1416
|
+
* @version 1.0.0
|
|
1417
|
+
*
|
|
1418
|
+
* @param {Array} records
|
|
1419
|
+
*/
|
|
1420
|
+
Model.setMethod(function removePrivateFields(records) {
|
|
1421
|
+
|
|
1422
|
+
var has_private_fields,
|
|
1423
|
+
fields = this.schema.getSorted(false),
|
|
1424
|
+
record,
|
|
1425
|
+
field,
|
|
1426
|
+
i,
|
|
1427
|
+
j;
|
|
1428
|
+
|
|
1429
|
+
records = Array.cast(records);
|
|
1430
|
+
|
|
1431
|
+
for (i = 0; i < records.length; i++) {
|
|
1432
|
+
record = records[i];
|
|
1433
|
+
|
|
1434
|
+
for (j = 0; j < fields.length; j++) {
|
|
1435
|
+
field = fields[j];
|
|
1436
|
+
|
|
1437
|
+
if (field.is_private) {
|
|
1438
|
+
has_private_fields = true;
|
|
1439
|
+
delete record[field.name];
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
// If there are no private fields, break loop
|
|
1444
|
+
if (!has_private_fields) {
|
|
1445
|
+
break;
|
|
1446
|
+
}
|
|
1447
|
+
}
|
|
1448
|
+
|
|
1449
|
+
return records;
|
|
1450
|
+
});
|
|
1451
|
+
|
|
1452
|
+
/**
|
|
1453
|
+
* Create an export stream
|
|
1454
|
+
*
|
|
1455
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1456
|
+
* @since 1.0.5
|
|
1457
|
+
* @version 1.0.5
|
|
1458
|
+
*
|
|
1459
|
+
* @param {Stream} output
|
|
1460
|
+
* @param {Object} options
|
|
1461
|
+
*
|
|
1462
|
+
* @return {Pledge}
|
|
1463
|
+
*/
|
|
1464
|
+
Model.setMethod(function exportToStream(output, options) {
|
|
1465
|
+
|
|
1466
|
+
if (!alchemy.isStream(output)) {
|
|
1467
|
+
if (!options) {
|
|
1468
|
+
options = output;
|
|
1469
|
+
output = null;
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1472
|
+
output = options.output;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
if (!output) {
|
|
1476
|
+
return Pledge.reject(new Error('No target output stream has been given'));
|
|
1477
|
+
}
|
|
1478
|
+
|
|
1479
|
+
if (!options) {
|
|
1480
|
+
options = {};
|
|
1481
|
+
}
|
|
1482
|
+
|
|
1483
|
+
// Only allow 1 task to run at a time
|
|
1484
|
+
options.parallel_limit = 1;
|
|
1485
|
+
|
|
1486
|
+
let that = this,
|
|
1487
|
+
name_buf = Buffer.from(this.model_name),
|
|
1488
|
+
head_buf;
|
|
1489
|
+
|
|
1490
|
+
// 0x01 is a model
|
|
1491
|
+
head_buf = Buffer.concat([Buffer.from([0x01, name_buf.length]), name_buf]);
|
|
1492
|
+
|
|
1493
|
+
output.write(head_buf);
|
|
1494
|
+
|
|
1495
|
+
return this.eachRecord(options, function eachRecord(record, index, next) {
|
|
1496
|
+
record.exportToStream(output).done(next);
|
|
1497
|
+
}, function done(err) {
|
|
1498
|
+
|
|
1499
|
+
});
|
|
1500
|
+
});
|
|
1501
|
+
|
|
1502
|
+
/**
|
|
1503
|
+
* Import from a stream
|
|
1504
|
+
*
|
|
1505
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1506
|
+
* @since 1.0.5
|
|
1507
|
+
* @version 1.0.5
|
|
1508
|
+
*
|
|
1509
|
+
* @param {Stream} input
|
|
1510
|
+
* @param {Object} options
|
|
1511
|
+
*
|
|
1512
|
+
* @return {Pledge}
|
|
1513
|
+
*/
|
|
1514
|
+
Model.setMethod(function importFromStream(input, options) {
|
|
1515
|
+
|
|
1516
|
+
if (!alchemy.isStream(input)) {
|
|
1517
|
+
if (!options) {
|
|
1518
|
+
options = input;
|
|
1519
|
+
input = null;
|
|
1520
|
+
}
|
|
1521
|
+
|
|
1522
|
+
input = options.input;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1525
|
+
if (!input) {
|
|
1526
|
+
return Pledge.reject(new Error('No source input stream has been given'));
|
|
1527
|
+
}
|
|
1528
|
+
|
|
1529
|
+
let that = this,
|
|
1530
|
+
current_type = null,
|
|
1531
|
+
extra_stream,
|
|
1532
|
+
pledge = new Pledge(),
|
|
1533
|
+
stopped,
|
|
1534
|
+
paused,
|
|
1535
|
+
buffer,
|
|
1536
|
+
value,
|
|
1537
|
+
seen = 0,
|
|
1538
|
+
left,
|
|
1539
|
+
size,
|
|
1540
|
+
doc;
|
|
1541
|
+
|
|
1542
|
+
input.on('data', function onData(data) {
|
|
1543
|
+
|
|
1544
|
+
if (stopped) {
|
|
1545
|
+
return;
|
|
1546
|
+
}
|
|
1547
|
+
|
|
1548
|
+
if (buffer) {
|
|
1549
|
+
buffer = Buffer.concat([buffer, data]);
|
|
1550
|
+
} else {
|
|
1551
|
+
buffer = data;
|
|
1552
|
+
}
|
|
1553
|
+
|
|
1554
|
+
handleBuffer();
|
|
1555
|
+
});
|
|
1556
|
+
|
|
1557
|
+
function handleBuffer() {
|
|
1558
|
+
|
|
1559
|
+
if (paused) {
|
|
1560
|
+
return;
|
|
1561
|
+
}
|
|
1562
|
+
|
|
1563
|
+
if (!current_type && buffer.length < 2) {
|
|
1564
|
+
return;
|
|
1565
|
+
}
|
|
1566
|
+
|
|
1567
|
+
if (!current_type) {
|
|
1568
|
+
current_type = buffer.readUInt8(0);
|
|
1569
|
+
|
|
1570
|
+
if (current_type == 0x01) {
|
|
1571
|
+
size = buffer.readUInt8(1);
|
|
1572
|
+
buffer = buffer.slice(2);
|
|
1573
|
+
} else if (current_type == 0x02 && buffer.length >= 5) {
|
|
1574
|
+
size = buffer.readUInt32BE(1);
|
|
1575
|
+
buffer = buffer.slice(5);
|
|
1576
|
+
} else if (current_type == 0xFF) {
|
|
1577
|
+
size = buffer.readUInt32BE(1);
|
|
1578
|
+
buffer = buffer.slice(5);
|
|
1579
|
+
seen = 0;
|
|
1580
|
+
|
|
1581
|
+
if (!doc) {
|
|
1582
|
+
stopped = true;
|
|
1583
|
+
pledge.reject(new Error('Found extra import data, but no active document'));
|
|
1584
|
+
} else {
|
|
1585
|
+
extra_stream = new require('stream').PassThrough();
|
|
1586
|
+
doc.extraImportFromStream(extra_stream);
|
|
1587
|
+
}
|
|
1588
|
+
} else {
|
|
1589
|
+
// Not enough data? Wait
|
|
1590
|
+
current_type = null;
|
|
1591
|
+
return;
|
|
1592
|
+
}
|
|
1593
|
+
}
|
|
1594
|
+
|
|
1595
|
+
handleRest();
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
function handleRest() {
|
|
1599
|
+
|
|
1600
|
+
if (current_type == 0xFF) {
|
|
1601
|
+
left = size - seen;
|
|
1602
|
+
value = buffer.slice(0, left);
|
|
1603
|
+
|
|
1604
|
+
seen += value.length;
|
|
1605
|
+
|
|
1606
|
+
if (value.length == buffer.length) {
|
|
1607
|
+
buffer = null;
|
|
1608
|
+
} else if (value.length < buffer.length) {
|
|
1609
|
+
buffer = buffer.slice(left);
|
|
1610
|
+
}
|
|
1611
|
+
|
|
1612
|
+
extra_stream.write(value);
|
|
1613
|
+
|
|
1614
|
+
if (value.length == left) {
|
|
1615
|
+
extra_stream.end();
|
|
1616
|
+
current_type = null;
|
|
1617
|
+
|
|
1618
|
+
if (buffer) {
|
|
1619
|
+
handleBuffer();
|
|
1620
|
+
}
|
|
1621
|
+
}
|
|
1622
|
+
|
|
1623
|
+
return;
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
if (buffer.length >= size) {
|
|
1627
|
+
value = buffer.slice(0, size);
|
|
1628
|
+
buffer = buffer.slice(size);
|
|
1629
|
+
} else {
|
|
1630
|
+
// Wait for next call
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
|
|
1634
|
+
if (current_type == 0x01) {
|
|
1635
|
+
value = value.toString();
|
|
1636
|
+
|
|
1637
|
+
if (value == that.model_name) {
|
|
1638
|
+
// Found name!
|
|
1639
|
+
current_type = null;
|
|
1640
|
+
size = 0;
|
|
1641
|
+
} else {
|
|
1642
|
+
stopped = true;
|
|
1643
|
+
return pledge.reject(new Error('Model names do not match'));
|
|
1644
|
+
}
|
|
1645
|
+
} else if (current_type == 0x02) {
|
|
1646
|
+
doc = that.createDocument();
|
|
1647
|
+
input.pause();
|
|
1648
|
+
paused = true;
|
|
1649
|
+
|
|
1650
|
+
doc.importFromBuffer(value).done(function done(err, result) {
|
|
1651
|
+
|
|
1652
|
+
if (err) {
|
|
1653
|
+
stopped = true;
|
|
1654
|
+
return pledge.reject(err);
|
|
1655
|
+
}
|
|
1656
|
+
|
|
1657
|
+
current_type = null;
|
|
1658
|
+
paused = false;
|
|
1659
|
+
input.resume();
|
|
1660
|
+
|
|
1661
|
+
handleBuffer();
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1664
|
+
return;
|
|
1665
|
+
}
|
|
1666
|
+
|
|
1667
|
+
if (buffer && buffer.length) {
|
|
1668
|
+
handleBuffer();
|
|
1669
|
+
}
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
return pledge;
|
|
1673
|
+
});
|
|
1674
|
+
|
|
1675
|
+
/**
|
|
1676
|
+
* Get a model
|
|
1677
|
+
*
|
|
1678
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1679
|
+
* @since 0.0.1
|
|
1680
|
+
* @version 1.1.0
|
|
1681
|
+
*
|
|
1682
|
+
* @param {String} name
|
|
1683
|
+
* @param {Boolean} init
|
|
1684
|
+
*
|
|
1685
|
+
* @return {Model}
|
|
1686
|
+
*/
|
|
1687
|
+
Model.get = function get(name, init, options) {
|
|
1688
|
+
|
|
1689
|
+
var constructor,
|
|
1690
|
+
namespace,
|
|
1691
|
+
pieces,
|
|
1692
|
+
path,
|
|
1693
|
+
obj;
|
|
1694
|
+
|
|
1695
|
+
if (typeof name == 'function') {
|
|
1696
|
+
name = name.name;
|
|
1697
|
+
}
|
|
1698
|
+
|
|
1699
|
+
if (!name) {
|
|
1700
|
+
throw new TypeError('Model name should be a non-empty string');
|
|
1701
|
+
}
|
|
1702
|
+
|
|
1703
|
+
if (init && typeof init == 'object') {
|
|
1704
|
+
options = init;
|
|
1705
|
+
init = true;
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
if (!options) {
|
|
1709
|
+
options = {};
|
|
1710
|
+
}
|
|
1711
|
+
|
|
1712
|
+
if (nameCache[name]) {
|
|
1713
|
+
if (init === false) {
|
|
1714
|
+
return nameCache[name];
|
|
1715
|
+
} else {
|
|
1716
|
+
return new nameCache[name];
|
|
1717
|
+
}
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
pieces = name.split('.');
|
|
1721
|
+
|
|
1722
|
+
if (pieces.length > 1) {
|
|
1723
|
+
// The first part is the namespace
|
|
1724
|
+
namespace = pieces.shift();
|
|
1725
|
+
|
|
1726
|
+
// The rest should be the path
|
|
1727
|
+
path = pieces.join('.');
|
|
1728
|
+
|
|
1729
|
+
obj = Classes[namespace];
|
|
1730
|
+
|
|
1731
|
+
if (!obj) {
|
|
1732
|
+
if (init === false) {
|
|
1733
|
+
return null;
|
|
1734
|
+
}
|
|
1735
|
+
|
|
1736
|
+
throw new TypeError('Namespace "' + namespace + '" could not be found');
|
|
1737
|
+
}
|
|
1738
|
+
} else {
|
|
1739
|
+
path = name;
|
|
1740
|
+
obj = Classes.Alchemy.Model;
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1743
|
+
constructor = Object.path(obj, path) || obj[String(path).modelName()];
|
|
1744
|
+
|
|
1745
|
+
if (constructor == null) {
|
|
1746
|
+
if (init === false) {
|
|
1747
|
+
return null;
|
|
1748
|
+
}
|
|
1749
|
+
|
|
1750
|
+
throw new Error('Could not find model "' + name + '"');
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
// Store this name in the cache,
|
|
1754
|
+
// so we don't have to perform the expensive #modelName() method again
|
|
1755
|
+
nameCache[name] = constructor;
|
|
1756
|
+
|
|
1757
|
+
if (init === false) {
|
|
1758
|
+
return constructor;
|
|
1759
|
+
} else {
|
|
1760
|
+
return new constructor;
|
|
1761
|
+
}
|
|
1762
|
+
};
|
|
1763
|
+
|
|
1764
|
+
/**
|
|
1765
|
+
* Make the base Model class a global
|
|
1766
|
+
*
|
|
1767
|
+
* @author Jelle De Loecker <jelle@develry.be>
|
|
1768
|
+
* @since 0.0.1
|
|
1769
|
+
* @version 0.2.0
|
|
1770
|
+
*
|
|
1771
|
+
* @type {Object}
|
|
1772
|
+
*/
|
|
1770
1773
|
global.Model = Model;
|