alchemymvc 1.1.7 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/lib/app/behaviour/sluggable_behaviour.js +2 -4
  2. package/lib/app/conduit/http_conduit.js +173 -192
  3. package/lib/app/conduit/loopback_conduit.js +0 -0
  4. package/lib/app/conduit/socket_conduit.js +620 -620
  5. package/lib/app/datasource/mongo_datasource.js +75 -10
  6. package/lib/app/element/time_ago.js +0 -0
  7. package/lib/app/helper/socket_helper.js +613 -613
  8. package/lib/app/helper_datasource/00-nosql_datasource.js +73 -19
  9. package/lib/app/helper_datasource/remote_datasource.js +0 -0
  10. package/lib/app/helper_field/05-string_field.js +0 -0
  11. package/lib/app/helper_field/schema_field.js +7 -2
  12. package/lib/app/helper_model/criteria.js +76 -18
  13. package/lib/app/helper_model/field_config.js +24 -5
  14. package/lib/app/helper_model/model.js +65 -7
  15. package/lib/app/model/alchemy_migration_model.js +33 -0
  16. package/lib/bootstrap.js +9 -0
  17. package/lib/class/conduit.js +2474 -2412
  18. package/lib/class/datasource.js +8 -8
  19. package/lib/class/field.js +7 -1
  20. package/lib/class/inode.js +62 -0
  21. package/lib/class/inode_dir.js +115 -0
  22. package/lib/class/inode_file.js +112 -111
  23. package/lib/class/migration.js +138 -0
  24. package/lib/class/model.js +1765 -1772
  25. package/lib/class/reciprocal.js +8 -2
  26. package/lib/class/router.js +0 -0
  27. package/lib/class/schema.js +14 -12
  28. package/lib/core/base.js +37 -10
  29. package/lib/core/client_base.js +1 -9
  30. package/lib/core/middleware.js +4 -8
  31. package/lib/core/socket.js +159 -159
  32. package/lib/init/alchemy.js +1779 -1779
  33. package/lib/init/devwatch.js +1 -1
  34. package/lib/init/functions.js +2 -56
  35. package/lib/init/load_functions.js +8 -2
  36. package/lib/init/requirements.js +101 -96
  37. package/lib/stages.js +12 -0
  38. package/package.json +74 -76
  39. package/CHANGELOG.md +0 -445
@@ -1,11 +1,17 @@
1
1
  /**
2
- * The Reciprocal class
2
+ * A Reciprocal instance creates a named channel on an IPC link.
3
+ *
4
+ * This link instance has to implement the `message` event and the `send` method,
5
+ * this will most probably be a ChildProcess but can also be used for socket.io
3
6
  *
4
7
  * @constructor
5
8
  *
6
- * @author Jelle De Loecker <jelle@develry.be>
9
+ * @author Jelle De Loecker <jelle@elevenways.be>
7
10
  * @since 1.1.0
8
11
  * @version 1.1.0
12
+ *
13
+ * @param {ChildProcess} link
14
+ * @param {string} identifier
9
15
  */
10
16
  const Reciprocal = Function.inherits('Alchemy.Base', 'Alchemy.Reciprocal', function Reciprocal(link, identifier) {
11
17
 
File without changes
@@ -184,7 +184,7 @@ Schema.setMethod(function eachAlternateIndex(data, fnc) {
184
184
  *
185
185
  * @author Jelle De Loecker <jelle@develry.be>
186
186
  * @since 1.1.0
187
- * @version 1.1.0
187
+ * @version 1.2.0
188
188
  *
189
189
  * @return {Pledge}
190
190
  */
@@ -192,20 +192,24 @@ Schema.setMethod(function getDatasource() {
192
192
 
193
193
  var that = this;
194
194
 
195
- return Function.parallel(function waitForModelClass(next) {
196
- that.afterOnce('has_model_class', function hasModelClass() {
197
- next();
198
- });
195
+ return Function.series(function waitForModelClass(next) {
196
+ that.afterOnce('has_model_class', next);
199
197
  }, function waitForDatasources(next) {
200
198
 
201
199
  alchemy.sputnik.after('datasources', function afterDs() {
202
200
 
203
- var datasource = Datasource.get(that.model_class.prototype.dbConfig);
201
+ let datasource;
204
202
 
205
- if (!datasource) {
206
- console.log('Failed to get', that.model_class.prototype.dbConfig, 'datasource?');
207
- return next(new Error('Could not get datasource "' + that.model_class.prototype.dbConfig + '"'));
208
- }
203
+ if (that.model_class) {
204
+ datasource = Datasource.get(that.model_class.prototype.dbConfig);
205
+
206
+ if (!datasource) {
207
+ console.log('Failed to get', that.model_class.prototype.dbConfig, 'datasource?');
208
+ return next(new Error('Could not get datasource "' + that.model_class.prototype.dbConfig + '"'));
209
+ }
210
+ } else {
211
+ return next(new Error('Unable to get datasource for this sub-schema'));
212
+ }
209
213
 
210
214
  next(null, datasource);
211
215
  });
@@ -218,8 +222,6 @@ Schema.setMethod(function getDatasource() {
218
222
 
219
223
  return result[1];
220
224
  });
221
-
222
- return pledge;
223
225
  });
224
226
 
225
227
  /**
package/lib/core/base.js CHANGED
@@ -1,4 +1,5 @@
1
1
  'use strict';
2
+ const CONDUIT = Symbol('conduit');
2
3
 
3
4
  /**
4
5
  * Alchemy's Base class
@@ -339,6 +340,40 @@ Base.setStatic('is_abstract_class', true, false);
339
340
  */
340
341
  Base.setStatic('starts_new_group', false);
341
342
 
343
+ /**
344
+ * Get the attached conduit
345
+ *
346
+ * @author Jelle De Loecker <jelle@elevenways.be>
347
+ * @since 1.1.8
348
+ * @version 1.1.8
349
+ *
350
+ * @type {Conduit}
351
+ */
352
+ Base.setProperty(function conduit() {
353
+
354
+ if (this[CONDUIT]) {
355
+ return this[CONDUIT];
356
+ }
357
+
358
+ let renderer = this.renderer || this.view || this.hawkejs_view,
359
+ result;
360
+
361
+ if (renderer && renderer.conduit) {
362
+ result = renderer.conduit;
363
+ } else if (renderer && renderer.root_renderer && renderer.root_renderer.conduit) {
364
+ result = renderer.root_renderer.conduit;
365
+ }
366
+
367
+ if (!conduit && renderer && renderer.server_var) {
368
+ result = renderer.server_var('conduit');
369
+ }
370
+
371
+ return result;
372
+
373
+ }, function setConduit(conduit) {
374
+ return this[CONDUIT] = conduit;
375
+ });
376
+
342
377
  /**
343
378
  * Enable all inherited classes to get a class group
344
379
  *
@@ -420,9 +455,9 @@ Base.setMethod(function attachConduit(conduit) {
420
455
  /**
421
456
  * Get a model, attach a conduit if possible
422
457
  *
423
- * @author Jelle De Loecker <jelle@develry.be>
458
+ * @author Jelle De Loecker <jelle@elevenways.be>
424
459
  * @since 0.3.0
425
- * @version 1.1.0
460
+ * @version 1.1.8
426
461
  *
427
462
  * @param {String} name The name of the model to get
428
463
  * @param {Boolean} init Initialize the class [true]
@@ -457,14 +492,6 @@ Base.setMethod(function getModel(name, init, options) {
457
492
 
458
493
  let conduit = this.conduit;
459
494
 
460
- if (!conduit) {
461
- if (this.view && this.view.conduit) {
462
- conduit = this.view.conduit;
463
- } else if (this.view && this.view.root_renderer && this.view.root_renderer.conduit) {
464
- conduit = this.view.root_renderer.conduit;
465
- }
466
- }
467
-
468
495
  // If an instance already exists on this item,
469
496
  // and it has the same conduit (or none), return that
470
497
  if (instance && (instance.conduit == conduit)) {
@@ -233,7 +233,7 @@ ClientBase.setStatic(function mapEventToMethod(event_name, method_name) {
233
233
  *
234
234
  * @author Jelle De Loecker <jelle@develry.be>
235
235
  * @since 1.0.0
236
- * @version 1.1.3
236
+ * @version 1.1.8
237
237
  *
238
238
  * @param {String} name
239
239
  * @param {Object} options
@@ -272,14 +272,6 @@ ClientBase.setMethod(function getModel(name, init, options) {
272
272
 
273
273
  let conduit = this.conduit;
274
274
 
275
- if (!conduit) {
276
- if (this.view && this.view.conduit) {
277
- conduit = this.view.conduit;
278
- } else if (this.view && this.view.root_renderer && this.view.root_renderer.conduit) {
279
- conduit = this.view.root_renderer.conduit;
280
- }
281
- }
282
-
283
275
  if (conduit) {
284
276
  instance.conduit = conduit;
285
277
  }
@@ -22,19 +22,15 @@ var publicDirs = alchemy.shared('public.directories', new Deck()),
22
22
  postcss,
23
23
  babel,
24
24
  sass,
25
- temp = alchemy.use('temp'),
26
25
  less,
27
26
  fs = alchemy.use('fs');
28
27
 
29
- // Remove temporary files on exit
30
- temp.track();
31
-
32
28
  if (alchemy.settings.css_less !== false) {
33
29
  less = alchemy.use('less');
34
30
  }
35
31
 
36
32
  if (alchemy.settings.css_sass) {
37
- sass = alchemy.use('node-sass');
33
+ sass = alchemy.use('sass');
38
34
  }
39
35
 
40
36
  if (alchemy.settings.css_post !== false) {
@@ -410,7 +406,7 @@ Alchemy.setMethod(function minifyScript(path, options, callback) {
410
406
  }
411
407
 
412
408
  // Open a temp js file
413
- temp.open({suffix: '.js'}, function getTempFile(err, info) {
409
+ Blast.openTempFile({suffix: '.js'}, function getTempFile(err, info) {
414
410
 
415
411
  var js_buffer;
416
412
 
@@ -900,7 +896,7 @@ Alchemy.setMethod(function getCompiledLessPath(lessPath, options, callback) {
900
896
  // @todo: add minify options
901
897
  css = cssTree.css;
902
898
 
903
- temp.open({suffix: '.css'}, function getTempFile(err, info) {
899
+ Blast.openTempFile({suffix: '.css'}, function getTempFile(err, info) {
904
900
 
905
901
  var cssBuffer;
906
902
 
@@ -989,7 +985,7 @@ Alchemy.setMethod(function getCompiledSassPath(sassPath, options, callback) {
989
985
  });
990
986
 
991
987
  function writeToTemp(content) {
992
- temp.open({suffix: '.css'}, function getTempFile(err, info) {
988
+ Blast.openTempFile({suffix: '.css'}, function getTempFile(err, info) {
993
989
 
994
990
  if (err) {
995
991
  return callback(err);
@@ -1,160 +1,160 @@
1
- 'use strict';
2
-
3
- var types = alchemy.shared('Socket.types'),
4
- iostream,
5
- path = alchemy.use('path'),
6
- fs = alchemy.use('fs');
7
-
8
- /**
9
- * The "socket" stage:
10
- *
11
- * Create the socket.io listener
12
- *
13
- * @author Jelle De Loecker <jelle@develry.be>
14
- * @since 0.1.0
15
- * @version 1.1.3
16
- */
17
- alchemy.sputnik.add(function socket() {
18
-
19
- var clientPath,
20
- streamPath,
21
- io;
22
-
23
- if (!alchemy.settings.websockets) {
24
- log.info('Websockets have been disabled');
25
- return;
26
- } else {
27
- if (alchemy.settings.websockets == 'optional') {
28
- log.info('Websockets have been enabled optionally');
29
- } else {
30
- log.info('Websockets have been enabled, clients will automatically connect');
31
- }
32
- }
33
-
34
- io = alchemy.use('socket.io');
35
-
36
- if (!io) {
37
- return log.error('Could not load socket.io!');
38
- }
39
-
40
- iostream = alchemy.use('socket.io-stream');
41
-
42
- // Create the Socket.io listener
43
- alchemy.io = io.listen(alchemy.server, {serveClient: false});
44
-
45
- // Get the core client path
46
- clientPath = path.dirname(alchemy.findModule('socket.io-client').module_path);
47
- clientPath = path.resolve(clientPath, '..', 'dist', 'socket.io.slim.js');
48
-
49
- // Get the stream client path
50
- streamPath = path.dirname(alchemy.findModule('socket.io-stream').module_path);
51
- streamPath = path.resolve(streamPath, 'socket.io-stream.js');
52
-
53
- // Serve the socket io core file
54
- Router.use('/scripts/socket.io.js', function getSocketIo(req, res, next) {
55
- alchemy.minifyScript(clientPath, function gotMinifiedPath(err, mpath) {
56
- req.conduit.serveFile(mpath || clientPath);
57
- });
58
- });
59
-
60
- // Serve the socket io stream file
61
- Router.use('/scripts/socket.io-stream.js', function getSocketStream(req, res, next) {
62
- alchemy.minifyScript(streamPath, function gotMinifiedPath(err, mpath) {
63
- req.conduit.serveFile(mpath || streamPath);
64
- });
65
- });
66
-
67
- /**
68
- * Handle connections
69
- *
70
- * @author Jelle De Loecker <jelle@develry.be>
71
- * @since 0.0.1
72
- * @version 0.2.0
73
- */
74
- alchemy.io.sockets.on('connection', function onConnect(socket){
75
-
76
- var syncs = {},
77
- latencies = [],
78
- latency_avg = 2,
79
- offset = 0;
80
-
81
- socket.on('timesync', function gotTimesyncRequest(data) {
82
-
83
- var received = Date.now(),
84
- latency;
85
-
86
- // This is the initial request
87
- if (data.count == null) {
88
- data.count = 0;
89
- data.latency_trip = 0;
90
-
91
- // Reset the latencies array
92
- latencies.length = 0;
93
- }
94
-
95
- // Do 8 round trips to determine latency
96
- if (data.latency_trip <= 8) {
97
-
98
- if (data.last_sent) {
99
-
100
- // Latency is the timestamp when we received the response
101
- // minus the timestamp when we sent the request
102
- latency = received - data.last_sent;
103
- latencies.push(latency);
104
- }
105
-
106
- // Wait before responding
107
- setTimeout(function waitForLatency() {
108
- data.last_sent = Date.now();
109
- socket.emit('timesync', data);
110
- }, 100 + (data.latency_trip * 150));
111
-
112
- data.latency_trip++;
113
- } else if (data.latency_trip) {
114
-
115
- latency_avg = ~~(Math.median(latencies) / 2);
116
- offset = (received - latency_avg) - data.client_time;
117
-
118
- socket.emit('timesync', {offset: offset, latency: latency_avg});
119
- }
120
- });
121
-
122
- // Wait for the announcement
123
- socket.once('announce', function gotAnnouncement(data) {
124
-
125
- var SocketClass,
126
- class_name;
127
-
128
- // Try getting the socket class of this type
129
- if (typeof data.type == 'string') {
130
- class_name = data.type.classify();
131
- SocketClass = Classes.Alchemy.Conduit[class_name + 'Socket'];
132
- }
133
-
134
- // If no socket class was found get the regular class
135
- if (!SocketClass) {
136
- SocketClass = Classes.Alchemy.Conduit.Socket;
137
- }
138
-
139
- new SocketClass(socket, data);
140
- });
141
- });
142
- });
143
-
144
- /**
145
- * Connect to another alchemy instance
146
- *
147
- * @author Jelle De Loecker <jelle@develry.be>
148
- * @since 0.1.0
149
- * @version 0.4.0
150
- */
151
- Alchemy.setMethod(function callServer(address, data, callback) {
152
-
153
- var server = new Classes.ClientSocket();
154
-
155
- server.reconnect = false;
156
-
157
- server.connect(address, data, callback);
158
-
159
- return server;
1
+ 'use strict';
2
+
3
+ var types = alchemy.shared('Socket.types'),
4
+ iostream,
5
+ path = alchemy.use('path'),
6
+ fs = alchemy.use('fs');
7
+
8
+ /**
9
+ * The "socket" stage:
10
+ *
11
+ * Create the socket.io listener
12
+ *
13
+ * @author Jelle De Loecker <jelle@develry.be>
14
+ * @since 0.1.0
15
+ * @version 1.1.3
16
+ */
17
+ alchemy.sputnik.add(function socket() {
18
+
19
+ var clientPath,
20
+ streamPath,
21
+ io;
22
+
23
+ if (!alchemy.settings.websockets) {
24
+ log.info('Websockets have been disabled');
25
+ return;
26
+ } else {
27
+ if (alchemy.settings.websockets == 'optional') {
28
+ log.info('Websockets have been enabled optionally');
29
+ } else {
30
+ log.info('Websockets have been enabled, clients will automatically connect');
31
+ }
32
+ }
33
+
34
+ io = alchemy.use('socket.io');
35
+
36
+ if (!io) {
37
+ return log.error('Could not load socket.io!');
38
+ }
39
+
40
+ iostream = alchemy.use('socket.io-stream');
41
+
42
+ // Create the Socket.io listener
43
+ alchemy.io = io.listen(alchemy.server, {serveClient: false});
44
+
45
+ // Get the core client path
46
+ clientPath = path.dirname(alchemy.findModule('socket.io-client').module_path);
47
+ clientPath = path.resolve(clientPath, '..', 'dist', 'socket.io.slim.js');
48
+
49
+ // Get the stream client path
50
+ streamPath = path.dirname(alchemy.findModule('@11ways/socket.io-stream').module_path);
51
+ streamPath = path.resolve(streamPath, 'socket.io-stream.js');
52
+
53
+ // Serve the socket io core file
54
+ Router.use('/scripts/socket.io.js', function getSocketIo(req, res, next) {
55
+ alchemy.minifyScript(clientPath, function gotMinifiedPath(err, mpath) {
56
+ req.conduit.serveFile(mpath || clientPath);
57
+ });
58
+ });
59
+
60
+ // Serve the socket io stream file
61
+ Router.use('/scripts/socket.io-stream.js', function getSocketStream(req, res, next) {
62
+ alchemy.minifyScript(streamPath, function gotMinifiedPath(err, mpath) {
63
+ req.conduit.serveFile(mpath || streamPath);
64
+ });
65
+ });
66
+
67
+ /**
68
+ * Handle connections
69
+ *
70
+ * @author Jelle De Loecker <jelle@develry.be>
71
+ * @since 0.0.1
72
+ * @version 0.2.0
73
+ */
74
+ alchemy.io.sockets.on('connection', function onConnect(socket){
75
+
76
+ var syncs = {},
77
+ latencies = [],
78
+ latency_avg = 2,
79
+ offset = 0;
80
+
81
+ socket.on('timesync', function gotTimesyncRequest(data) {
82
+
83
+ var received = Date.now(),
84
+ latency;
85
+
86
+ // This is the initial request
87
+ if (data.count == null) {
88
+ data.count = 0;
89
+ data.latency_trip = 0;
90
+
91
+ // Reset the latencies array
92
+ latencies.length = 0;
93
+ }
94
+
95
+ // Do 8 round trips to determine latency
96
+ if (data.latency_trip <= 8) {
97
+
98
+ if (data.last_sent) {
99
+
100
+ // Latency is the timestamp when we received the response
101
+ // minus the timestamp when we sent the request
102
+ latency = received - data.last_sent;
103
+ latencies.push(latency);
104
+ }
105
+
106
+ // Wait before responding
107
+ setTimeout(function waitForLatency() {
108
+ data.last_sent = Date.now();
109
+ socket.emit('timesync', data);
110
+ }, 100 + (data.latency_trip * 150));
111
+
112
+ data.latency_trip++;
113
+ } else if (data.latency_trip) {
114
+
115
+ latency_avg = ~~(Math.median(latencies) / 2);
116
+ offset = (received - latency_avg) - data.client_time;
117
+
118
+ socket.emit('timesync', {offset: offset, latency: latency_avg});
119
+ }
120
+ });
121
+
122
+ // Wait for the announcement
123
+ socket.once('announce', function gotAnnouncement(data) {
124
+
125
+ var SocketClass,
126
+ class_name;
127
+
128
+ // Try getting the socket class of this type
129
+ if (typeof data.type == 'string') {
130
+ class_name = data.type.classify();
131
+ SocketClass = Classes.Alchemy.Conduit[class_name + 'Socket'];
132
+ }
133
+
134
+ // If no socket class was found get the regular class
135
+ if (!SocketClass) {
136
+ SocketClass = Classes.Alchemy.Conduit.Socket;
137
+ }
138
+
139
+ new SocketClass(socket, data);
140
+ });
141
+ });
142
+ });
143
+
144
+ /**
145
+ * Connect to another alchemy instance
146
+ *
147
+ * @author Jelle De Loecker <jelle@develry.be>
148
+ * @since 0.1.0
149
+ * @version 0.4.0
150
+ */
151
+ Alchemy.setMethod(function callServer(address, data, callback) {
152
+
153
+ var server = new Classes.ClientSocket();
154
+
155
+ server.reconnect = false;
156
+
157
+ server.connect(address, data, callback);
158
+
159
+ return server;
160
160
  });