message_bus 2.0.2 → 2.0.3
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.
Potentially problematic release.
This version of message_bus might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/.rubocop.yml +104 -0
- data/CHANGELOG +9 -0
- data/README.md +62 -11
- data/lib/message_bus.rb +25 -25
- data/lib/message_bus/backends/memory.rb +14 -13
- data/lib/message_bus/backends/postgres.rb +34 -29
- data/lib/message_bus/backends/redis.rb +13 -15
- data/lib/message_bus/client.rb +21 -11
- data/lib/message_bus/connection_manager.rb +4 -3
- data/lib/message_bus/message.rb +7 -5
- data/lib/message_bus/rack/diagnostics.rb +5 -4
- data/lib/message_bus/rack/middleware.rb +19 -18
- data/lib/message_bus/rack/thin_ext.rb +2 -1
- data/lib/message_bus/rails/railtie.rb +16 -3
- data/lib/message_bus/timer_thread.rb +18 -14
- data/lib/message_bus/version.rb +2 -1
- data/message_bus.gemspec +2 -1
- data/spec/lib/message_bus/backends/postgres_spec.rb +1 -1
- data/spec/lib/message_bus/backends/redis_spec.rb +1 -1
- data/spec/lib/message_bus/client_spec.rb +24 -1
- data/spec/lib/message_bus/connection_manager_spec.rb +4 -4
- data/spec/lib/message_bus/rack/middleware_spec.rb +10 -2
- data/spec/lib/message_bus_spec.rb +1 -1
- data/vendor/assets/javascripts/message-bus-ajax.js +1 -0
- data/vendor/assets/javascripts/message-bus.js +1 -0
- metadata +14 -13
- data/vendor/assets/javascripts/message-bus-ajax.js +0 -44
- data/vendor/assets/javascripts/message-bus.js +0 -431
@@ -1,44 +0,0 @@
|
|
1
|
-
// A bare-bones implementation of $.ajax that MessageBus will use
|
2
|
-
// as a fallback if jQuery is not present
|
3
|
-
//
|
4
|
-
// Only implements methods & options used by MessageBus
|
5
|
-
(function(global, undefined) {
|
6
|
-
'use strict';
|
7
|
-
if (!global.MessageBus){
|
8
|
-
throw new Error("MessageBus must be loaded before the ajax adapter");
|
9
|
-
}
|
10
|
-
|
11
|
-
var cacheBuster = Math.random() * 10000 | 0;
|
12
|
-
|
13
|
-
global.MessageBus.ajax = function(options){
|
14
|
-
var XHRImpl = (global.MessageBus && global.MessageBus.xhrImplementation) || global.XMLHttpRequest;
|
15
|
-
var xhr = new XHRImpl();
|
16
|
-
xhr.dataType = options.dataType;
|
17
|
-
var url = options.url;
|
18
|
-
if (!options.cache){
|
19
|
-
url += ((-1 == url.indexOf('?')) ? '?' : '&') + '_=' + (cacheBuster++)
|
20
|
-
}
|
21
|
-
xhr.open('POST', url);
|
22
|
-
for (var name in options.headers){
|
23
|
-
xhr.setRequestHeader(name, options.headers[name]);
|
24
|
-
}
|
25
|
-
xhr.setRequestHeader('Content-Type', 'application/json');
|
26
|
-
if (options.messageBus.chunked){
|
27
|
-
options.messageBus.onProgressListener(xhr);
|
28
|
-
}
|
29
|
-
xhr.onreadystatechange = function(){
|
30
|
-
if (xhr.readyState === 4){
|
31
|
-
var status = xhr.status;
|
32
|
-
if (status >= 200 && status < 300 || status === 304){
|
33
|
-
options.success(xhr.responseText);
|
34
|
-
} else {
|
35
|
-
options.error(xhr, xhr.statusText);
|
36
|
-
}
|
37
|
-
options.complete();
|
38
|
-
}
|
39
|
-
}
|
40
|
-
xhr.send(JSON.stringify(options.data));
|
41
|
-
return xhr;
|
42
|
-
};
|
43
|
-
|
44
|
-
})(window);
|
@@ -1,431 +0,0 @@
|
|
1
|
-
/*jshint bitwise: false*/
|
2
|
-
(function(global, document, undefined) {
|
3
|
-
'use strict';
|
4
|
-
var previousMessageBus = global.MessageBus;
|
5
|
-
|
6
|
-
// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
|
7
|
-
var callbacks, clientId, failCount, shouldLongPoll, queue, responseCallbacks, uniqueId, baseUrl;
|
8
|
-
var me, started, stopped, longPoller, pollTimeout, paused, later, jQuery, interval, chunkedBackoff;
|
9
|
-
|
10
|
-
uniqueId = function() {
|
11
|
-
return 'xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
12
|
-
var r, v;
|
13
|
-
r = Math.random() * 16 | 0;
|
14
|
-
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
15
|
-
return v.toString(16);
|
16
|
-
});
|
17
|
-
};
|
18
|
-
|
19
|
-
clientId = uniqueId();
|
20
|
-
responseCallbacks = {};
|
21
|
-
callbacks = [];
|
22
|
-
queue = [];
|
23
|
-
interval = null;
|
24
|
-
failCount = 0;
|
25
|
-
baseUrl = "/";
|
26
|
-
paused = false;
|
27
|
-
later = [];
|
28
|
-
chunkedBackoff = 0;
|
29
|
-
jQuery = global.jQuery;
|
30
|
-
var hiddenProperty;
|
31
|
-
|
32
|
-
(function(){
|
33
|
-
var prefixes = ["","webkit","ms","moz"];
|
34
|
-
for(var i=0; i<prefixes.length; i++) {
|
35
|
-
var prefix = prefixes[i];
|
36
|
-
var check = prefix + (prefix === "" ? "hidden" : "Hidden");
|
37
|
-
if(document[check] !== undefined ){
|
38
|
-
hiddenProperty = check;
|
39
|
-
}
|
40
|
-
}
|
41
|
-
})();
|
42
|
-
|
43
|
-
var isHidden = function() {
|
44
|
-
if (hiddenProperty !== undefined){
|
45
|
-
return document[hiddenProperty];
|
46
|
-
} else {
|
47
|
-
return !document.hasFocus;
|
48
|
-
}
|
49
|
-
};
|
50
|
-
|
51
|
-
var hasonprogress = (new XMLHttpRequest()).onprogress === null;
|
52
|
-
var allowChunked = function(){
|
53
|
-
return me.enableChunkedEncoding && hasonprogress;
|
54
|
-
};
|
55
|
-
|
56
|
-
shouldLongPoll = function() {
|
57
|
-
return me.alwaysLongPoll || !isHidden();
|
58
|
-
};
|
59
|
-
|
60
|
-
var totalAjaxFailures = 0;
|
61
|
-
var totalAjaxCalls = 0;
|
62
|
-
var lastAjax;
|
63
|
-
|
64
|
-
var processMessages = function(messages) {
|
65
|
-
var gotData = false;
|
66
|
-
if (!messages) return false; // server unexpectedly closed connection
|
67
|
-
|
68
|
-
for (var i=0; i<messages.length; i++) {
|
69
|
-
var message = messages[i];
|
70
|
-
gotData = true;
|
71
|
-
for (var j=0; j<callbacks.length; j++) {
|
72
|
-
var callback = callbacks[j];
|
73
|
-
if (callback.channel === message.channel) {
|
74
|
-
callback.last_id = message.message_id;
|
75
|
-
try {
|
76
|
-
callback.func(message.data, message.global_id, message.message_id);
|
77
|
-
}
|
78
|
-
catch(e){
|
79
|
-
if(console.log) {
|
80
|
-
console.log("MESSAGE BUS FAIL: callback " + callback.channel + " caused exception " + e.message);
|
81
|
-
}
|
82
|
-
}
|
83
|
-
}
|
84
|
-
if (message.channel === "/__status") {
|
85
|
-
if (message.data[callback.channel] !== undefined) {
|
86
|
-
callback.last_id = message.data[callback.channel];
|
87
|
-
}
|
88
|
-
}
|
89
|
-
}
|
90
|
-
}
|
91
|
-
|
92
|
-
return gotData;
|
93
|
-
};
|
94
|
-
|
95
|
-
var reqSuccess = function(messages) {
|
96
|
-
failCount = 0;
|
97
|
-
if (paused) {
|
98
|
-
if (messages) {
|
99
|
-
for (var i=0; i<messages.length; i++) {
|
100
|
-
later.push(messages[i]);
|
101
|
-
}
|
102
|
-
}
|
103
|
-
} else {
|
104
|
-
return processMessages(messages);
|
105
|
-
}
|
106
|
-
return false;
|
107
|
-
};
|
108
|
-
|
109
|
-
longPoller = function(poll,data){
|
110
|
-
var gotData = false;
|
111
|
-
var aborted = false;
|
112
|
-
lastAjax = new Date();
|
113
|
-
totalAjaxCalls += 1;
|
114
|
-
data.__seq = totalAjaxCalls;
|
115
|
-
|
116
|
-
var longPoll = shouldLongPoll() && me.enableLongPolling;
|
117
|
-
var chunked = longPoll && allowChunked();
|
118
|
-
if (chunkedBackoff > 0) {
|
119
|
-
chunkedBackoff--;
|
120
|
-
chunked = false;
|
121
|
-
}
|
122
|
-
|
123
|
-
var headers = {
|
124
|
-
'X-SILENCE-LOGGER': 'true'
|
125
|
-
};
|
126
|
-
for (var name in me.headers){
|
127
|
-
headers[name] = me.headers[name];
|
128
|
-
}
|
129
|
-
|
130
|
-
if (!chunked){
|
131
|
-
headers["Dont-Chunk"] = 'true';
|
132
|
-
}
|
133
|
-
|
134
|
-
var dataType = chunked ? "text" : "json";
|
135
|
-
|
136
|
-
var handle_progress = function(payload, position) {
|
137
|
-
|
138
|
-
var separator = "\r\n|\r\n";
|
139
|
-
var endChunk = payload.indexOf(separator, position);
|
140
|
-
|
141
|
-
if (endChunk === -1) {
|
142
|
-
return position;
|
143
|
-
}
|
144
|
-
|
145
|
-
var chunk = payload.substring(position, endChunk);
|
146
|
-
chunk = chunk.replace(/\r\n\|\|\r\n/g, separator);
|
147
|
-
|
148
|
-
try {
|
149
|
-
reqSuccess(JSON.parse(chunk));
|
150
|
-
} catch(e) {
|
151
|
-
if (console.log) {
|
152
|
-
console.log("FAILED TO PARSE CHUNKED REPLY");
|
153
|
-
console.log(data);
|
154
|
-
}
|
155
|
-
}
|
156
|
-
|
157
|
-
return handle_progress(payload, endChunk + separator.length);
|
158
|
-
}
|
159
|
-
|
160
|
-
var disableChunked = function(){
|
161
|
-
if (me.longPoll) {
|
162
|
-
me.longPoll.abort();
|
163
|
-
chunkedBackoff = 30;
|
164
|
-
}
|
165
|
-
};
|
166
|
-
|
167
|
-
var setOnProgressListener = function(xhr) {
|
168
|
-
var position = 0;
|
169
|
-
// if it takes longer than 3000 ms to get first chunk, we have some proxy
|
170
|
-
// this is messing with us, so just backoff from using chunked for now
|
171
|
-
var chunkedTimeout = setTimeout(disableChunked,3000);
|
172
|
-
xhr.onprogress = function () {
|
173
|
-
clearTimeout(chunkedTimeout);
|
174
|
-
if(xhr.getResponseHeader('Content-Type') === 'application/json; charset=utf-8') {
|
175
|
-
// not chunked we are sending json back
|
176
|
-
chunked = false;
|
177
|
-
return;
|
178
|
-
}
|
179
|
-
position = handle_progress(xhr.responseText, position);
|
180
|
-
}
|
181
|
-
};
|
182
|
-
if (!me.ajax){
|
183
|
-
throw new Error("Either jQuery or the ajax adapter must be loaded");
|
184
|
-
}
|
185
|
-
var req = me.ajax({
|
186
|
-
url: me.baseUrl + "message-bus/" + me.clientId + "/poll" + (!longPoll ? "?dlp=t" : ""),
|
187
|
-
data: data,
|
188
|
-
cache: false,
|
189
|
-
async: true,
|
190
|
-
dataType: dataType,
|
191
|
-
type: 'POST',
|
192
|
-
headers: headers,
|
193
|
-
messageBus: {
|
194
|
-
chunked: chunked,
|
195
|
-
onProgressListener: function(xhr) {
|
196
|
-
var position = 0;
|
197
|
-
// if it takes longer than 3000 ms to get first chunk, we have some proxy
|
198
|
-
// this is messing with us, so just backoff from using chunked for now
|
199
|
-
var chunkedTimeout = setTimeout(disableChunked,3000);
|
200
|
-
return xhr.onprogress = function () {
|
201
|
-
clearTimeout(chunkedTimeout);
|
202
|
-
if(xhr.getResponseHeader('Content-Type') === 'application/json; charset=utf-8') {
|
203
|
-
chunked = false; // not chunked, we are sending json back
|
204
|
-
} else {
|
205
|
-
position = handle_progress(xhr.responseText, position);
|
206
|
-
}
|
207
|
-
}
|
208
|
-
}
|
209
|
-
},
|
210
|
-
xhr: function() {
|
211
|
-
var xhr = jQuery.ajaxSettings.xhr();
|
212
|
-
if (!chunked) {
|
213
|
-
return xhr;
|
214
|
-
}
|
215
|
-
this.messageBus.onProgressListener(xhr);
|
216
|
-
return xhr;
|
217
|
-
},
|
218
|
-
success: function(messages) {
|
219
|
-
if (!chunked) {
|
220
|
-
// we may have requested text so jQuery will not parse
|
221
|
-
if (typeof(messages) === "string") {
|
222
|
-
messages = JSON.parse(messages);
|
223
|
-
}
|
224
|
-
gotData = reqSuccess(messages);
|
225
|
-
}
|
226
|
-
},
|
227
|
-
error: function(xhr, textStatus, err) {
|
228
|
-
if(textStatus === "abort") {
|
229
|
-
aborted = true;
|
230
|
-
} else {
|
231
|
-
failCount += 1;
|
232
|
-
totalAjaxFailures += 1;
|
233
|
-
}
|
234
|
-
},
|
235
|
-
complete: function() {
|
236
|
-
var interval;
|
237
|
-
try {
|
238
|
-
if (gotData || aborted) {
|
239
|
-
interval = 100;
|
240
|
-
} else {
|
241
|
-
interval = me.callbackInterval;
|
242
|
-
if (failCount > 2) {
|
243
|
-
interval = interval * failCount;
|
244
|
-
} else if (!shouldLongPoll()) {
|
245
|
-
interval = me.backgroundCallbackInterval;
|
246
|
-
}
|
247
|
-
if (interval > me.maxPollInterval) {
|
248
|
-
interval = me.maxPollInterval;
|
249
|
-
}
|
250
|
-
|
251
|
-
interval -= (new Date() - lastAjax);
|
252
|
-
|
253
|
-
if (interval < 100) {
|
254
|
-
interval = 100;
|
255
|
-
}
|
256
|
-
}
|
257
|
-
} catch(e) {
|
258
|
-
if(console.log && e.message) {
|
259
|
-
console.log("MESSAGE BUS FAIL: " + e.message);
|
260
|
-
}
|
261
|
-
}
|
262
|
-
|
263
|
-
pollTimeout = setTimeout(function(){pollTimeout=null; poll();}, interval);
|
264
|
-
me.longPoll = null;
|
265
|
-
}
|
266
|
-
});
|
267
|
-
|
268
|
-
return req;
|
269
|
-
};
|
270
|
-
|
271
|
-
me = {
|
272
|
-
enableChunkedEncoding: true,
|
273
|
-
enableLongPolling: true,
|
274
|
-
callbackInterval: 15000,
|
275
|
-
backgroundCallbackInterval: 60000,
|
276
|
-
maxPollInterval: 3 * 60 * 1000,
|
277
|
-
callbacks: callbacks,
|
278
|
-
clientId: clientId,
|
279
|
-
alwaysLongPoll: false,
|
280
|
-
baseUrl: baseUrl,
|
281
|
-
headers: {},
|
282
|
-
ajax: (jQuery && jQuery.ajax),
|
283
|
-
noConflict: function(){
|
284
|
-
global.MessageBus = global.MessageBus.previousMessageBus;
|
285
|
-
return this;
|
286
|
-
},
|
287
|
-
diagnostics: function(){
|
288
|
-
console.log("Stopped: " + stopped + " Started: " + started);
|
289
|
-
console.log("Current callbacks");
|
290
|
-
console.log(callbacks);
|
291
|
-
console.log("Total ajax calls: " + totalAjaxCalls + " Recent failure count: " + failCount + " Total failures: " + totalAjaxFailures);
|
292
|
-
console.log("Last ajax call: " + (new Date() - lastAjax) / 1000 + " seconds ago") ;
|
293
|
-
},
|
294
|
-
|
295
|
-
pause: function() {
|
296
|
-
paused = true;
|
297
|
-
},
|
298
|
-
|
299
|
-
resume: function() {
|
300
|
-
paused = false;
|
301
|
-
processMessages(later);
|
302
|
-
later = [];
|
303
|
-
},
|
304
|
-
|
305
|
-
stop: function() {
|
306
|
-
stopped = true;
|
307
|
-
started = false;
|
308
|
-
},
|
309
|
-
|
310
|
-
// Start polling
|
311
|
-
start: function() {
|
312
|
-
var poll, delayPollTimeout;
|
313
|
-
|
314
|
-
if (started) return;
|
315
|
-
started = true;
|
316
|
-
stopped = false;
|
317
|
-
|
318
|
-
poll = function() {
|
319
|
-
var data;
|
320
|
-
|
321
|
-
if(stopped) {
|
322
|
-
return;
|
323
|
-
}
|
324
|
-
|
325
|
-
if (callbacks.length === 0) {
|
326
|
-
if(!delayPollTimeout) {
|
327
|
-
delayPollTimeout = setTimeout(function(){ delayPollTimeout = null; poll();}, 500);
|
328
|
-
}
|
329
|
-
return;
|
330
|
-
}
|
331
|
-
|
332
|
-
data = {};
|
333
|
-
for (var i=0;i<callbacks.length;i++) {
|
334
|
-
data[callbacks[i].channel] = callbacks[i].last_id;
|
335
|
-
}
|
336
|
-
|
337
|
-
me.longPoll = longPoller(poll,data);
|
338
|
-
};
|
339
|
-
|
340
|
-
|
341
|
-
// monitor visibility, issue a new long poll when the page shows
|
342
|
-
if(document.addEventListener && 'hidden' in document){
|
343
|
-
me.visibilityEvent = global.document.addEventListener('visibilitychange', function(){
|
344
|
-
if(!document.hidden && !me.longPoll && pollTimeout){
|
345
|
-
clearTimeout(pollTimeout);
|
346
|
-
pollTimeout = null;
|
347
|
-
poll();
|
348
|
-
}
|
349
|
-
});
|
350
|
-
}
|
351
|
-
|
352
|
-
poll();
|
353
|
-
},
|
354
|
-
|
355
|
-
"status": function() {
|
356
|
-
if (paused) {
|
357
|
-
return "paused";
|
358
|
-
} else if (started) {
|
359
|
-
return "started";
|
360
|
-
} else if (stopped) {
|
361
|
-
return "stopped";
|
362
|
-
} else {
|
363
|
-
throw "Cannot determine current status";
|
364
|
-
}
|
365
|
-
},
|
366
|
-
|
367
|
-
// Subscribe to a channel
|
368
|
-
subscribe: function(channel, func, lastId) {
|
369
|
-
|
370
|
-
if(!started && !stopped){
|
371
|
-
me.start();
|
372
|
-
}
|
373
|
-
|
374
|
-
if (typeof(lastId) !== "number" || lastId < -1){
|
375
|
-
lastId = -1;
|
376
|
-
}
|
377
|
-
callbacks.push({
|
378
|
-
channel: channel,
|
379
|
-
func: func,
|
380
|
-
last_id: lastId
|
381
|
-
});
|
382
|
-
if (me.longPoll) {
|
383
|
-
me.longPoll.abort();
|
384
|
-
}
|
385
|
-
|
386
|
-
return func;
|
387
|
-
},
|
388
|
-
|
389
|
-
// Unsubscribe from a channel
|
390
|
-
unsubscribe: function(channel, func) {
|
391
|
-
// TODO allow for globbing in the middle of a channel name
|
392
|
-
// like /something/*/something
|
393
|
-
// at the moment we only support globbing /something/*
|
394
|
-
var glob;
|
395
|
-
if (channel.indexOf("*", channel.length - 1) !== -1) {
|
396
|
-
channel = channel.substr(0, channel.length - 1);
|
397
|
-
glob = true;
|
398
|
-
}
|
399
|
-
|
400
|
-
var removed = false;
|
401
|
-
|
402
|
-
for (var i=callbacks.length-1; i>=0; i--) {
|
403
|
-
|
404
|
-
var callback = callbacks[i];
|
405
|
-
var keep;
|
406
|
-
|
407
|
-
if (glob) {
|
408
|
-
keep = callback.channel.substr(0, channel.length) !== channel;
|
409
|
-
} else {
|
410
|
-
keep = callback.channel !== channel;
|
411
|
-
}
|
412
|
-
|
413
|
-
if(!keep && func && callback.func !== func){
|
414
|
-
keep = true;
|
415
|
-
}
|
416
|
-
|
417
|
-
if (!keep) {
|
418
|
-
callbacks.splice(i,1);
|
419
|
-
removed = true;
|
420
|
-
}
|
421
|
-
}
|
422
|
-
|
423
|
-
if (removed && me.longPoll) {
|
424
|
-
me.longPoll.abort();
|
425
|
-
}
|
426
|
-
|
427
|
-
return removed;
|
428
|
-
}
|
429
|
-
};
|
430
|
-
global.MessageBus = me;
|
431
|
-
})(window, document);
|