alchemymvc 1.3.21 → 1.3.22

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.
@@ -262,6 +262,31 @@ Syncable.setStatic(function setSyncMethod(types, method) {
262
262
  });
263
263
  });
264
264
 
265
+ /**
266
+ * Add a method that will only be called on the client.
267
+ * No response will be returned, since it is sent to multiple clients.
268
+ *
269
+ * @author Jelle De Loecker <jelle@elevenways.be>
270
+ * @since 1.3.22
271
+ * @version 1.3.22
272
+ *
273
+ * @param {Object} data
274
+ *
275
+ * @return {Syncable}
276
+ */
277
+ Syncable.setStatic(function setClientMethod(types, method) {
278
+ return this.setHandledMethod(types, method, function handler(method, args) {
279
+
280
+ if (this.is_server) {
281
+ this.addLog('call', [method.name, args]);
282
+ return;
283
+ }
284
+
285
+ let result = method.apply(this, args);
286
+ return result;
287
+ });
288
+ });
289
+
265
290
  /**
266
291
  * Add a method that fetches info from the server.
267
292
  * The response is always a promise.
@@ -34,7 +34,7 @@ ObjectField.setSelfContained(true);
34
34
  *
35
35
  * @author Jelle De Loecker <jelle@develry.be>
36
36
  * @since 0.5.0
37
- * @version 0.5.0
37
+ * @version 1.3.22
38
38
  *
39
39
  * @param {Mixed} value
40
40
  *
@@ -42,7 +42,7 @@ ObjectField.setSelfContained(true);
42
42
  */
43
43
  ObjectField.setMethod(function cast(value) {
44
44
 
45
- if (typeof value == 'string') {
45
+ if (this.options.store_as_string && typeof value == 'string') {
46
46
  return JSON.undry(value);
47
47
  }
48
48
 
@@ -49,7 +49,7 @@ Model.constitute(function setModelName() {
49
49
  *
50
50
  * @author Jelle De Loecker <jelle@elevenways.be>
51
51
  * @since 1.2.5
52
- * @version 1.2.5
52
+ * @version 1.3.22
53
53
  */
54
54
  Model.mapEventToMethod({
55
55
  saved : 'afterSave',
@@ -58,6 +58,7 @@ Model.mapEventToMethod({
58
58
  associated : 'afterAssociated',
59
59
  found : 'afterData',
60
60
  foundDocuments : 'afterFind',
61
+ removed : 'afterRemove',
61
62
  });
62
63
 
63
64
  /**
@@ -1806,6 +1806,10 @@ Conduit.setMethod(function end(message) {
1806
1806
 
1807
1807
  if (typeof message !== 'string') {
1808
1808
 
1809
+ if (message && message instanceof Classes.Stream.Stream) {
1810
+ return this._endWithStream(message);
1811
+ }
1812
+
1809
1813
  // Use regular JSON if DRY has been disabled in settings
1810
1814
  if (alchemy.settings.json_dry_response === false || this.json_dry === false) {
1811
1815
  json_type = 'json';
@@ -1878,12 +1882,29 @@ Conduit.setMethod(function end(message) {
1878
1882
  this._end(message, 'utf-8');
1879
1883
  });
1880
1884
 
1885
+ /**
1886
+ * End with a stream
1887
+ *
1888
+ * @author Jelle De Loecker <jelle@elevenways.be>
1889
+ * @since 1.3.22
1890
+ * @version 1.3.22
1891
+ */
1892
+ Conduit.setMethod(function _endWithStream(stream) {
1893
+
1894
+ this.ended = new Date();
1895
+ this.emit('ending');
1896
+
1897
+ this.flushHeaders();
1898
+
1899
+ stream.pipe(this.response);
1900
+ });
1901
+
1881
1902
  /**
1882
1903
  * Call the actual end method
1883
1904
  *
1884
1905
  * @author Jelle De Loecker <jelle@elevenways.be>
1885
1906
  * @since 0.2.0
1886
- * @version 1.3.14
1907
+ * @version 1.3.22
1887
1908
  */
1888
1909
  Conduit.setMethod(function _end(message, encoding = 'utf-8') {
1889
1910
 
@@ -1906,18 +1927,42 @@ Conduit.setMethod(function _end(message, encoding = 'utf-8') {
1906
1927
  return;
1907
1928
  }
1908
1929
 
1909
- let headers = [],
1910
- value,
1911
- key;
1930
+ // Set the content-length if it hasn't been set yet
1931
+ if (arguments.length > 0 && !this.response_headers['content-length']) {
1932
+ this.response_headers['content-length'] = Buffer.byteLength(message);
1933
+ }
1934
+
1935
+ this.flushHeaders();
1936
+
1937
+ if (arguments.length === 0) {
1938
+ return this.response.end();
1939
+ }
1940
+
1941
+ // End the response
1942
+ return this.response.end(message, encoding);
1943
+ });
1944
+
1945
+ /**
1946
+ * Flush the headers
1947
+ *
1948
+ * @author Jelle De Loecker <jelle@elevenways.be>
1949
+ * @since 1.3.22
1950
+ * @version 1.3.22
1951
+ */
1952
+ Conduit.setMethod(function flushHeaders() {
1953
+
1954
+ if (this._flushed) {
1955
+ return;
1956
+ }
1957
+
1958
+ this._flushed = true;
1912
1959
 
1913
1960
  if (this.status) {
1914
1961
  this.response.statusCode = this.status;
1915
1962
  }
1916
1963
 
1917
- // Set the content-length if it hasn't been set yet
1918
- if (arguments.length > 0 && !this.response_headers['content-length']) {
1919
- this.response_headers['content-length'] = Buffer.byteLength(message);
1920
- }
1964
+ let value,
1965
+ key;
1921
1966
 
1922
1967
  for (key in this.response_headers) {
1923
1968
  value = this.response_headers[key];
@@ -1935,13 +1980,6 @@ Conduit.setMethod(function _end(message, encoding = 'utf-8') {
1935
1980
 
1936
1981
  // Write the actual headers
1937
1982
  this.response.writeHead(this.status);
1938
-
1939
- if (arguments.length === 0) {
1940
- return this.response.end();
1941
- }
1942
-
1943
- // End the response
1944
- return this.response.end(message, encoding);
1945
1983
  });
1946
1984
 
1947
1985
  /**
@@ -338,4 +338,29 @@ File.setMethod(function readString(encoding) {
338
338
  }
339
339
 
340
340
  return this.read({encoding: encoding});
341
+ });
342
+
343
+ /**
344
+ * Write the given data to the file
345
+ *
346
+ * @author Jelle De Loecker <jelle@elevenways.be>
347
+ * @since 1.3.22
348
+ * @version 1.3.22
349
+ */
350
+ File.setMethod(function overwrite(contents, options = {}) {
351
+
352
+ let pledge = new Pledge();
353
+
354
+ options.flag = 'w';
355
+
356
+ fs.writeFile(this.path, contents, options, function done(err) {
357
+
358
+ if (err) {
359
+ return pledge.reject(err);
360
+ }
361
+
362
+ pledge.resolve();
363
+ });
364
+
365
+ return pledge;
341
366
  });
@@ -1287,7 +1287,7 @@ Model.setMethod(async function executeMongoPipeline(pipeline) {
1287
1287
  *
1288
1288
  * @author Jelle De Loecker <jelle@develry.be>
1289
1289
  * @since 0.0.1
1290
- * @version 1.3.21
1290
+ * @version 1.3.22
1291
1291
  *
1292
1292
  * @param {String} id The object id
1293
1293
  * @param {Function} callback
@@ -1313,12 +1313,33 @@ Model.setMethod(function remove(id, callback) {
1313
1313
  id = String(id);
1314
1314
  }
1315
1315
 
1316
- let query = {};
1317
- query[this.primary_key] = id;
1316
+ let query = {
1317
+ [this.primary_key] : id,
1318
+ };
1318
1319
 
1319
- Function.series(function recompute(next) {
1320
- that.callOrNext('beforeRemove', [id], next);
1321
- }, function done(err) {
1320
+ let has_remove_events = typeof this.beforeRemove == 'function' || typeof this.afterRemove == 'function' || this.listeners('removed').length;
1321
+ let doc;
1322
+ let tasks = [];
1323
+
1324
+ if (has_remove_events) {
1325
+
1326
+ // Get the actual document
1327
+ tasks.push(async next => {
1328
+ doc = await this.findByPk(id);
1329
+
1330
+ if (!doc) {
1331
+ return next(new Error('Unable to find document with id ' + id));
1332
+ }
1333
+
1334
+ next();
1335
+ });
1336
+
1337
+ tasks.push(next => {
1338
+ this.callOrNext('beforeRemove', [doc], next);
1339
+ });
1340
+ }
1341
+
1342
+ Function.series(tasks, function done(err) {
1322
1343
 
1323
1344
  if (err) {
1324
1345
  pledge.reject(err);
@@ -1330,10 +1351,13 @@ Model.setMethod(function remove(id, callback) {
1330
1351
  if (err != null) {
1331
1352
  return pledge.reject(err);
1332
1353
  }
1333
-
1334
- that.emit('removed', result, {}, false, function afterRemovedEvent() {
1354
+
1355
+ if (has_remove_events) {
1356
+ that.issueEvent('removed', [doc, result], () => pledge.resolve(result));
1357
+ } else {
1335
1358
  pledge.resolve(result);
1336
- });
1359
+ }
1360
+
1337
1361
  });
1338
1362
  });
1339
1363
 
@@ -294,7 +294,7 @@ ClientBase.setMethod(function getModel(name, init, options) {
294
294
  *
295
295
  * @author Jelle De Loecker <jelle@elevenways.be>
296
296
  * @since 1.1.1
297
- * @version 1.3.2
297
+ * @version 1.3.22
298
298
  *
299
299
  * @param {String} name The name of the event to emit
300
300
  * @param {Array} args The parameters to pass to the event
@@ -323,7 +323,17 @@ ClientBase.setMethod(function issueEvent(name, args, next) {
323
323
  }
324
324
 
325
325
  if (method_name && typeof this[method_name] == 'function') {
326
- promise = this[method_name].apply(this, args);
326
+ try {
327
+ promise = this[method_name].apply(this, args);
328
+ } catch (err) {
329
+ pledge.reject(err);
330
+
331
+ if (next) {
332
+ next(err);
333
+ }
334
+
335
+ return pledge;
336
+ }
327
337
  }
328
338
 
329
339
  Pledge.done(promise, function doneEventPromise(err, result) {
@@ -924,7 +924,7 @@ Alchemy.setMethod(function ready(callback) {
924
924
  *
925
925
  * @author Jelle De Loecker <jelle@elevenways.be>
926
926
  * @since 1.1.2
927
- * @version 1.3.3
927
+ * @version 1.3.22
928
928
  */
929
929
  Alchemy.setMethod(async function doPreload() {
930
930
 
@@ -952,7 +952,8 @@ Alchemy.setMethod(async function doPreload() {
952
952
  Blast.getClientPath({
953
953
  modify_prototypes : true,
954
954
  create_source_map : alchemy.settings.source_map,
955
- enable_coverage : !!global.__coverage__
955
+ enable_coverage : !!global.__coverage__,
956
+ debug : alchemy.settings.debug,
956
957
  });
957
958
  }
958
959
  });
@@ -441,26 +441,49 @@ Alchemy.setMethod(function createDir(target, callback) {
441
441
  /**
442
442
  * Return the key:items of the first object that are no longer in the second
443
443
  *
444
- * @author Jelle De Loecker <jelle@develry.be>
444
+ * @author Jelle De Loecker <jelle@elevenways.be>
445
445
  * @since 0.0.1
446
- * @version 0.4.0
446
+ * @version 1.3.22
447
447
  *
448
- * @param {Object}
449
- * @param {Object}
448
+ * @param {Object|Map} first
449
+ * @param {Object|Map} second
450
450
  *
451
- * @return {Object} The items in the first item that are not in the second
451
+ * @return {Object} The items in the first item that are not in the second
452
452
  */
453
453
  Alchemy.setMethod(function getDifference(first, second) {
454
454
 
455
- var key,
456
- result = {};
455
+ let first_is_map = first instanceof Map,
456
+ second_is_map = second instanceof Map,
457
+ first_keys,
458
+ result = {},
459
+ first_val,
460
+ key,
461
+ val;
462
+
463
+ if (first_is_map) {
464
+ first_keys = [...first.keys()];
465
+ } else {
466
+ first_keys = Object.keys(first);
467
+ }
468
+
469
+ for (key of first_keys) {
457
470
 
458
- for (key in first) {
471
+ if (second_is_map) {
472
+ val = second.get(key);
473
+ } else {
474
+ val = second[key];
475
+ }
459
476
 
460
477
  // If the key in the first item doesn't show up in the second item,
461
478
  // add it to the result
462
- if (typeof second[key] === 'undefined') {
463
- result[key] = first[key];
479
+ if (typeof val === 'undefined') {
480
+ if (first_is_map) {
481
+ first_val = first.get(key);
482
+ } else {
483
+ first_val = first[key];
484
+ }
485
+
486
+ result[key] = first_val;
464
487
  }
465
488
  }
466
489
 
@@ -470,24 +493,42 @@ Alchemy.setMethod(function getDifference(first, second) {
470
493
  /**
471
494
  * Return the shared items
472
495
  *
473
- * @author Jelle De Loecker <jelle@develry.be>
496
+ * @author Jelle De Loecker <jelle@elevenways.be>
474
497
  * @since 0.0.1
475
- * @version 0.4.0
498
+ * @version 1.3.22
476
499
  *
477
- * @param {Object}
478
- * @param {Object}
500
+ * @param {Object|Map} first
501
+ * @param {Object|Map} second
479
502
  *
480
503
  * @return {Object} The items in the second item that are also in the first
481
504
  */
482
505
  Alchemy.setMethod(function getShared(first, second) {
483
506
 
484
- var key,
485
- result = {};
507
+ let first_is_map = first instanceof Map,
508
+ second_is_map = second instanceof Map,
509
+ first_keys,
510
+ result = {},
511
+ key,
512
+ val;
513
+
514
+ if (first_is_map) {
515
+ first_keys = [...first.keys()];
516
+ } else {
517
+ first_keys = Object.keys(first);
518
+ }
486
519
 
487
- for (key in first) {
520
+ for (key of first_keys) {
488
521
 
489
- if (typeof second[key] !== 'undefined') {
490
- result[key] = second[key];
522
+ if (second_is_map) {
523
+ val = second.get(key);
524
+ } else {
525
+ val = second[key];
526
+ }
527
+
528
+ // If the key in the first item doesn't show up in the second item,
529
+ // add it to the result
530
+ if (typeof val !== 'undefined') {
531
+ result[key] = val;
491
532
  }
492
533
  }
493
534
 
@@ -19,7 +19,7 @@ var fs = alchemy.use('fs'),
19
19
  *
20
20
  * @author Jelle De Loecker <jelle@develry.be>
21
21
  * @since 0.0.1
22
- * @version 1.1.5
22
+ * @version 1.3.22
23
23
  *
24
24
  * @param {String} dirPath The path to load
25
25
  * @param {Object} options
@@ -44,6 +44,7 @@ Alchemy.setMethod(function usePath(dirPath, options) {
44
44
 
45
45
  options._level++;
46
46
  useOptions._level = options._level;
47
+ useOptions.skip = options.skip;
47
48
 
48
49
  // Always ignore .git directories
49
50
  modularRegex.push(/^.git$/);
@@ -166,7 +167,7 @@ Alchemy.setProperty('default_use_path_options', {
166
167
  *
167
168
  * @author Jelle De Loecker <jelle@develry.be>
168
169
  * @since 0.0.1
169
- * @version 1.2.0
170
+ * @version 1.3.22
170
171
  *
171
172
  * @param {String} dir_path
172
173
  * @param {Object} options
@@ -214,6 +215,10 @@ Alchemy.setMethod(function _usePath(dir_path, options) {
214
215
  for (i = 0; i < files.length; i++) {
215
216
  file_name = files[i];
216
217
 
218
+ if (file_name == 'routes.js' && options.skip?.length && options.skip.includes('routes')) {
219
+ continue;
220
+ }
221
+
217
222
  // Skip hidden files or the "empty" file
218
223
  if (file_name[0] == '.' || file_name == 'empty') {
219
224
  continue;
@@ -289,7 +294,8 @@ Alchemy.setMethod(function _usePath(dir_path, options) {
289
294
  ignore : options.ignore,
290
295
  recursive : options.recursive - 1,
291
296
  _level : options._level,
292
- modularParent : options.modularParent
297
+ modularParent : options.modularParent,
298
+ skip : options.skip,
293
299
  });
294
300
  }
295
301
 
package/lib/stages.js CHANGED
@@ -156,11 +156,11 @@ alchemy.sputnik.add(function plugins() {
156
156
  *
157
157
  * @author Jelle De Loecker <jelle@develry.be>
158
158
  * @since 0.0.1
159
- * @version 0.0.1
159
+ * @version 1.3.22
160
160
  */
161
161
  alchemy.sputnik.add(function base_app() {
162
162
  // Load in the app
163
- alchemy.usePath(PATH_APP, {weight: 20});
163
+ alchemy.usePath(PATH_APP, {weight: 20, skip: ['routes']});
164
164
  });
165
165
 
166
166
  /**
@@ -185,7 +185,7 @@ alchemy.sputnik.add(function define_debug() {
185
185
  *
186
186
  * @author Jelle De Loecker <jelle@elevenways.be>
187
187
  * @since 0.0.1
188
- * @version 1.3.3
188
+ * @version 1.3.22
189
189
  */
190
190
  alchemy.sputnik.add(function hawkejs_setup() {
191
191
 
@@ -202,7 +202,8 @@ alchemy.sputnik.add(function hawkejs_setup() {
202
202
  modify_prototypes : true,
203
203
  ua : req.conduit.headers.useragent,
204
204
  create_source_map : alchemy.settings.source_map,
205
- enable_coverage : !!global.__coverage__
205
+ enable_coverage : !!global.__coverage__,
206
+ debug : alchemy.settings.debug,
206
207
  }).done(gotClientFile);
207
208
 
208
209
  function gotClientFile(err, path) {
@@ -244,6 +245,7 @@ alchemy.sputnik.add(function hawkejs_setup() {
244
245
  modify_prototypes : true,
245
246
  ua : req.conduit.headers.useragent,
246
247
  create_source_map : alchemy.settings.source_map,
248
+ debug : alchemy.settings.debug,
247
249
  }).done(gotClientFile);
248
250
  }
249
251
  });
@@ -362,15 +364,15 @@ alchemy.sputnik.add(function middleware() {
362
364
  *
363
365
  * @author Jelle De Loecker <jelle@develry.be>
364
366
  * @since 0.0.1
365
- * @version 0.4.0
367
+ * @version 1.3.22
366
368
  */
367
369
  alchemy.sputnik.add(function routes() {
368
370
  try {
369
- alchemy.useOnce(path.resolve(PATH_APP, 'config', 'routes'));
371
+ alchemy.useOnce(path.resolve(PATH_APP, 'config', 'routes.js'));
370
372
  } catch (err) {
371
373
  // Only output warning when not in client mode
372
374
  if (!alchemy.settings.client_mode) {
373
- log.warn('No route config was found');
375
+ log.warn('No app routes were found:', err);
374
376
  }
375
377
  }
376
378
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "alchemymvc",
3
3
  "description": "MVC framework for Node.js",
4
- "version": "1.3.21",
4
+ "version": "1.3.22",
5
5
  "author": "Jelle De Loecker <jelle@elevenways.be>",
6
6
  "keywords": [
7
7
  "alchemy",