alchemymvc 1.1.9 → 1.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,138 @@
1
+ const libpath = alchemy.use('path');
2
+
3
+ /**
4
+ * Migrations
5
+ *
6
+ * @constructor
7
+ *
8
+ * @author Jelle De Loecker <jelle@elevenways.be>
9
+ * @since 1.2.0
10
+ * @version 1.2.0
11
+ */
12
+ const Migration = Function.inherits('Alchemy.Base', function Migration(document) {
13
+ this.document = document;
14
+ });
15
+
16
+ /**
17
+ * Start the necesary migrations
18
+ *
19
+ * @constructor
20
+ *
21
+ * @author Jelle De Loecker <jelle@elevenways.be>
22
+ * @since 1.2.0
23
+ * @version 1.2.0
24
+ */
25
+ Migration.setStatic(async function start() {
26
+
27
+ const AlchemyMigration = Model.get('AlchemyMigration');
28
+
29
+ console.log('Starting migration task...');
30
+
31
+ let dir = new Classes.Alchemy.Inode.Directory(libpath.resolve(PATH_APP, 'migrations'));
32
+
33
+ await dir.loadContents();
34
+
35
+ for (let entry of dir) {
36
+
37
+ let name = entry.name.beforeLast('.js');
38
+
39
+ if (!name) {
40
+ continue;
41
+ }
42
+
43
+ let crit = AlchemyMigration.find();
44
+ crit.where('name').equals(name);
45
+
46
+ let record = await AlchemyMigration.find('first', crit);
47
+
48
+ if (record) {
49
+ if (record.ended) {
50
+ console.log(' »» Migration "' + name + '" already finished on ' + record.ended.format('Y-m-d H:i'));
51
+ } else if (record.error) {
52
+ console.log(' »» Migration "' + name + '" failed with error: ' + record.error);
53
+ console.log('Migrations stopped');
54
+ return;
55
+ } else {
56
+ console.log(' »» Migration "' + name + '" has not yet finished!');
57
+ console.log('Migrations stopped');
58
+ return;
59
+ }
60
+
61
+ continue;
62
+ }
63
+
64
+ record = AlchemyMigration.createDocument();
65
+ record.name = name;
66
+ record.path = entry.path;
67
+
68
+ await record.save();
69
+
70
+ try {
71
+
72
+ console.log(' »» Starting migration "' + name + '"');
73
+
74
+ let migration = new Migration(record);
75
+
76
+ await Blast.require(entry.path, {
77
+ client: false,
78
+ async : true,
79
+ arguments: {
80
+ names : [
81
+ 'Blast',
82
+ 'Collection',
83
+ 'Bound',
84
+ 'Obj',
85
+ 'Fn',
86
+ 'migration'
87
+ ],
88
+ values : [
89
+ Blast,
90
+ Blast.Collection,
91
+ Blast.Bound,
92
+ Blast.Bound.Object,
93
+ Blast.Collection.Function,
94
+ migration,
95
+ ]
96
+ }
97
+ });
98
+
99
+ record.ended = new Date();
100
+ await record.save();
101
+
102
+ } catch (err) {
103
+ console.log(' »» Migration error:', err);
104
+ record.error = err.message + '\n' + err.stack;
105
+ await record.save();
106
+ console.log('Migrations stopped');
107
+ return;
108
+ }
109
+ }
110
+
111
+ console.log('Executed all migrations');
112
+ });
113
+
114
+ /**
115
+ * Migrate methods
116
+ *
117
+ * @constructor
118
+ *
119
+ * @author Jelle De Loecker <jelle@elevenways.be>
120
+ * @since 1.2.0
121
+ * @version 1.2.0
122
+ */
123
+ Migration.setMethod(function processRecords(model_name, fnc) {
124
+
125
+ const model = Model.get(model_name);
126
+
127
+ let options = {
128
+ document : false,
129
+ parallel_limit : 1,
130
+ };
131
+
132
+ return model.eachRecord(options, async (record, index, next) => {
133
+ await fnc(model, record[model_name], index);
134
+ next();
135
+ }, (err) => {
136
+ // Done!
137
+ });
138
+ });
@@ -800,7 +800,7 @@ Model.setMethod(function ensureIds(list, callback) {
800
800
  *
801
801
  * @author Jelle De Loecker <jelle@develry.be>
802
802
  * @since 0.0.1
803
- * @version 1.1.0
803
+ * @version 1.2.0
804
804
  *
805
805
  * @param {Document} document
806
806
  * @param {Object} options
@@ -853,7 +853,14 @@ Model.setMethod(function saveRecord(document, options, callback) {
853
853
  }, function doBeforeSave(next) {
854
854
 
855
855
  if (typeof that.beforeSave == 'function') {
856
- that.beforeSave(document, options, next);
856
+ let promise = that.beforeSave(document, options, next);
857
+
858
+ if (promise) {
859
+ Pledge.done(promise, next);
860
+ } else if (that.beforeSave.length < 3) {
861
+ // If the method accepts no `next` callback, call it now
862
+ next();
863
+ }
857
864
  } else {
858
865
  next();
859
866
  }
@@ -1133,21 +1140,6 @@ Model.setMethod(function processDatasourceFormat(ds_data, options, callback) {
1133
1140
  return pledge;
1134
1141
  });
1135
1142
 
1136
- /**
1137
- * Get a field instance from the schema
1138
- *
1139
- * @author Jelle De Loecker <jelle@develry.be>
1140
- * @since 0.2.0
1141
- * @version 0.2.0
1142
- *
1143
- * @param {String} name The name of the field
1144
- *
1145
- * @return {FieldType}
1146
- */
1147
- Model.setMethod(function getField(name) {
1148
- return this.schema.getField(name);
1149
- });
1150
-
1151
1143
  /**
1152
1144
  * Get the title to display for this record
1153
1145
  *
@@ -1317,7 +1309,7 @@ Model.setMethod(function remove(id, callback) {
1317
1309
  *
1318
1310
  * @author Jelle De Loecker <jelle@develry.be>
1319
1311
  * @since 0.5.0
1320
- * @version 1.0.5
1312
+ * @version 1.2.0
1321
1313
  *
1322
1314
  * @param {Object} options Find options
1323
1315
  * @param {Function} task Task to perform on each record
@@ -1382,7 +1374,7 @@ Model.setMethod(function eachRecord(options, task, callback) {
1382
1374
 
1383
1375
  var record_index = index++;
1384
1376
 
1385
- last_id = record[that.primary_key];
1377
+ last_id = record[that.model_name][that.primary_key];
1386
1378
 
1387
1379
  tasks.push(function doSave(next) {
1388
1380
  pledge.reportProgress(((record_index - 1) / available) * 100);
@@ -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
  /**
@@ -1066,4 +1066,18 @@ Alchemy.setMethod(function syncDataWithServer() {
1066
1066
  });
1067
1067
 
1068
1068
  return Function.parallel(tasks);
1069
+ });
1070
+
1071
+ /**
1072
+ * Create a new schema on the client
1073
+ *
1074
+ * @author Jelle De Loecker <jelle@elevenways.be>
1075
+ * @since 1.2.1
1076
+ * @version 1.2.1
1077
+ *
1078
+ * @param {*} parent
1079
+ */
1080
+ Alchemy.setMethod(function createSchema(parent) {
1081
+ let schema = new Classes.Alchemy.Client.Schema(parent);
1082
+ return schema;
1069
1083
  });
@@ -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
  });