message_bus 3.3.4 → 3.3.8

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 (61) hide show
  1. checksums.yaml +4 -4
  2. data/.eslintrc.js +21 -0
  3. data/.github/workflows/ci.yml +99 -0
  4. data/.gitignore +2 -0
  5. data/.prettierrc +1 -0
  6. data/.rubocop.yml +3 -1
  7. data/CHANGELOG +30 -8
  8. data/DEV.md +7 -0
  9. data/Gemfile +0 -25
  10. data/LICENSE +1 -1
  11. data/README.md +34 -15
  12. data/Rakefile +13 -8
  13. data/assets/message-bus-ajax.js +4 -10
  14. data/assets/message-bus.js +69 -76
  15. data/bench/codecs/all_codecs.rb +39 -0
  16. data/bench/codecs/marshal.rb +11 -0
  17. data/bench/codecs/packed_string.rb +67 -0
  18. data/bench/codecs/string_hack.rb +47 -0
  19. data/bench/codecs_large_user_list.rb +29 -0
  20. data/bench/codecs_standard_message.rb +29 -0
  21. data/examples/bench/bench.lua +2 -2
  22. data/lib/message_bus/backends/base.rb +8 -5
  23. data/lib/message_bus/backends/memory.rb +6 -2
  24. data/lib/message_bus/backends/postgres.rb +27 -18
  25. data/lib/message_bus/backends/redis.rb +9 -6
  26. data/lib/message_bus/client.rb +6 -7
  27. data/lib/message_bus/codec/base.rb +18 -0
  28. data/lib/message_bus/codec/json.rb +15 -0
  29. data/lib/message_bus/codec/oj.rb +21 -0
  30. data/lib/message_bus/connection_manager.rb +1 -1
  31. data/lib/message_bus/distributed_cache.rb +3 -1
  32. data/lib/message_bus/http_client.rb +2 -2
  33. data/lib/message_bus/rack/diagnostics.rb +30 -8
  34. data/lib/message_bus/rack/middleware.rb +22 -16
  35. data/lib/message_bus/rack/thin_ext.rb +2 -1
  36. data/lib/message_bus/version.rb +1 -1
  37. data/lib/message_bus.rb +42 -22
  38. data/message_bus.gemspec +21 -3
  39. data/package-lock.json +3744 -0
  40. data/package.json +15 -8
  41. data/spec/assets/SpecHelper.js +6 -5
  42. data/spec/assets/message-bus.spec.js +9 -6
  43. data/spec/helpers.rb +23 -7
  44. data/spec/integration/http_client_spec.rb +1 -1
  45. data/spec/lib/fake_async_middleware.rb +1 -0
  46. data/spec/lib/message_bus/backend_spec.rb +13 -44
  47. data/spec/lib/message_bus/client_spec.rb +7 -6
  48. data/spec/lib/message_bus/connection_manager_spec.rb +4 -0
  49. data/spec/lib/message_bus/distributed_cache_spec.rb +5 -7
  50. data/spec/lib/message_bus/multi_process_spec.rb +19 -9
  51. data/spec/lib/message_bus/rack/middleware_spec.rb +18 -6
  52. data/spec/lib/message_bus_spec.rb +13 -8
  53. data/spec/spec_helper.rb +8 -9
  54. data/spec/support/jasmine-browser.json +16 -0
  55. data/vendor/assets/javascripts/message-bus-ajax.js +4 -10
  56. data/vendor/assets/javascripts/message-bus.js +69 -76
  57. metadata +231 -11
  58. data/.travis.yml +0 -17
  59. data/lib/message_bus/em_ext.rb +0 -6
  60. data/spec/assets/support/jasmine.yml +0 -126
  61. data/spec/assets/support/jasmine_helper.rb +0 -11
@@ -0,0 +1,16 @@
1
+ {
2
+ "srcDir": "assets",
3
+ "srcFiles": [
4
+ "message-bus.js",
5
+ "message-bus-ajax.js"
6
+ ],
7
+ "specDir": "spec/assets",
8
+ "specFiles": [
9
+ "message-bus.spec.js"
10
+ ],
11
+ "helpers": [
12
+ "SpecHelper.js"
13
+ ],
14
+ "random": true,
15
+ "browser": "headlessChrome"
16
+ }
@@ -2,27 +2,21 @@
2
2
  // as a fallback if jQuery is not present
3
3
  //
4
4
  // Only implements methods & options used by MessageBus
5
- (function(global, undefined) {
5
+ (function(global) {
6
6
  'use strict';
7
7
  if (!global.MessageBus){
8
8
  throw new Error("MessageBus must be loaded before the ajax adapter");
9
9
  }
10
10
 
11
- var cacheBuster = Math.random() * 10000 | 0;
12
-
13
11
  global.MessageBus.ajax = function(options){
14
12
  var XHRImpl = (global.MessageBus && global.MessageBus.xhrImplementation) || global.XMLHttpRequest;
15
13
  var xhr = new XHRImpl();
16
14
  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);
15
+ xhr.open('POST', options.url);
22
16
  for (var name in options.headers){
23
17
  xhr.setRequestHeader(name, options.headers[name]);
24
18
  }
25
- xhr.setRequestHeader('Content-Type', 'application/json');
19
+ xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
26
20
  if (options.messageBus.chunked){
27
21
  options.messageBus.onProgressListener(xhr);
28
22
  }
@@ -37,7 +31,7 @@
37
31
  options.complete();
38
32
  }
39
33
  }
40
- xhr.send(JSON.stringify(options.data));
34
+ xhr.send(new URLSearchParams(options.data).toString());
41
35
  return xhr;
42
36
  };
43
37
 
@@ -1,9 +1,9 @@
1
- /*jshint bitwise: false*/
1
+ /*global define, jQuery*/
2
2
 
3
3
  (function (root, factory) {
4
- if (typeof define === 'function' && define.amd) {
4
+ if (typeof define === "function" && define.amd) {
5
5
  // AMD. Register as an anonymous module.
6
- define([], function (b) {
6
+ define([], function () {
7
7
  // Also create a global in case some scripts
8
8
  // that are loaded still are looking for
9
9
  // a global even when an AMD loader is in use.
@@ -13,12 +13,12 @@
13
13
  // Browser globals
14
14
  root.MessageBus = factory();
15
15
  }
16
- }(typeof self !== 'undefined' ? self : this, function () {
16
+ })(typeof self !== "undefined" ? self : this, function () {
17
17
  "use strict";
18
18
 
19
19
  // http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
20
- var uniqueId = function() {
21
- return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function(c) {
20
+ var uniqueId = function () {
21
+ return "xxxxxxxxxxxx4xxxyxxxxxxxxxxxxxxx".replace(/[xy]/g, function (c) {
22
22
  var r = (Math.random() * 16) | 0;
23
23
  var v = c === "x" ? r : (r & 0x3) | 0x8;
24
24
  return v.toString(16);
@@ -31,8 +31,6 @@
31
31
  var started = false;
32
32
  var clientId = uniqueId();
33
33
  var callbacks = [];
34
- var queue = [];
35
- var interval = null;
36
34
  var failCount = 0;
37
35
  var baseUrl = "/";
38
36
  var paused = false;
@@ -44,7 +42,7 @@
44
42
  var totalAjaxCalls = 0;
45
43
  var lastAjax;
46
44
 
47
- var isHidden = (function() {
45
+ var isHidden = (function () {
48
46
  var prefixes = ["", "webkit", "ms", "moz"];
49
47
  var hiddenProperty;
50
48
  for (var i = 0; i < prefixes.length; i++) {
@@ -55,7 +53,7 @@
55
53
  }
56
54
  }
57
55
 
58
- return function() {
56
+ return function () {
59
57
  if (hiddenProperty !== undefined) {
60
58
  return document[hiddenProperty];
61
59
  } else {
@@ -64,7 +62,7 @@
64
62
  };
65
63
  })();
66
64
 
67
- var hasLocalStorage = (function() {
65
+ var hasLocalStorage = (function () {
68
66
  try {
69
67
  localStorage.setItem("mbTestLocalStorage", Date.now());
70
68
  localStorage.removeItem("mbTestLocalStorage");
@@ -74,13 +72,13 @@
74
72
  }
75
73
  })();
76
74
 
77
- var updateLastAjax = function() {
75
+ var updateLastAjax = function () {
78
76
  if (hasLocalStorage) {
79
77
  localStorage.setItem("__mbLastAjax", Date.now());
80
78
  }
81
79
  };
82
80
 
83
- var hiddenTabShouldWait = function() {
81
+ var hiddenTabShouldWait = function () {
84
82
  if (hasLocalStorage && isHidden()) {
85
83
  var lastAjaxCall = parseInt(localStorage.getItem("__mbLastAjax"), 10);
86
84
  var deltaAjax = Date.now() - lastAjaxCall;
@@ -91,20 +89,21 @@
91
89
  };
92
90
 
93
91
  var hasonprogress = new XMLHttpRequest().onprogress === null;
94
- var allowChunked = function() {
92
+ var allowChunked = function () {
95
93
  return me.enableChunkedEncoding && hasonprogress;
96
94
  };
97
95
 
98
- var shouldLongPoll = function() {
96
+ var shouldLongPoll = function () {
99
97
  return (
100
98
  me.alwaysLongPoll ||
101
99
  (me.shouldLongPollCallback ? me.shouldLongPollCallback() : !isHidden())
102
100
  );
103
101
  };
104
102
 
105
- var processMessages = function(messages) {
106
- var gotData = false;
107
- if ((!messages) || (messages.length === 0)) { return false; }
103
+ var processMessages = function (messages) {
104
+ if (!messages || messages.length === 0) {
105
+ return false;
106
+ }
108
107
 
109
108
  for (var i = 0; i < messages.length; i++) {
110
109
  var message = messages[i];
@@ -136,7 +135,7 @@
136
135
  return true;
137
136
  };
138
137
 
139
- var reqSuccess = function(messages) {
138
+ var reqSuccess = function (messages) {
140
139
  failCount = 0;
141
140
  if (paused) {
142
141
  if (messages) {
@@ -150,7 +149,7 @@
150
149
  return false;
151
150
  };
152
151
 
153
- var longPoller = function(poll, data) {
152
+ var longPoller = function (poll, data) {
154
153
  if (ajaxInProgress) {
155
154
  // never allow concurrent ajax reqs
156
155
  return;
@@ -183,7 +182,7 @@
183
182
 
184
183
  var dataType = chunked ? "text" : "json";
185
184
 
186
- var handle_progress = function(payload, position) {
185
+ var handle_progress = function (payload, position) {
187
186
  var separator = "\r\n|\r\n";
188
187
  var endChunk = payload.indexOf(separator, position);
189
188
 
@@ -206,31 +205,13 @@
206
205
  return handle_progress(payload, endChunk + separator.length);
207
206
  };
208
207
 
209
- var disableChunked = function() {
208
+ var disableChunked = function () {
210
209
  if (me.longPoll) {
211
210
  me.longPoll.abort();
212
211
  chunkedBackoff = 30;
213
212
  }
214
213
  };
215
214
 
216
- var setOnProgressListener = function(xhr) {
217
- var position = 0;
218
- // if it takes longer than 3000 ms to get first chunk, we have some proxy
219
- // this is messing with us, so just backoff from using chunked for now
220
- var chunkedTimeout = setTimeout(disableChunked, 3000);
221
- xhr.onprogress = function() {
222
- clearTimeout(chunkedTimeout);
223
- if (
224
- xhr.getResponseHeader("Content-Type") ===
225
- "application/json; charset=utf-8"
226
- ) {
227
- // not chunked we are sending json back
228
- chunked = false;
229
- return;
230
- }
231
- position = handle_progress(xhr.responseText, position);
232
- };
233
- };
234
215
  if (!me.ajax) {
235
216
  throw new Error("Either jQuery or the ajax adapter must be loaded");
236
217
  }
@@ -246,19 +227,18 @@
246
227
  "/poll" +
247
228
  (!longPoll ? "?dlp=t" : ""),
248
229
  data: data,
249
- cache: false,
250
230
  async: true,
251
231
  dataType: dataType,
252
232
  type: "POST",
253
233
  headers: headers,
254
234
  messageBus: {
255
235
  chunked: chunked,
256
- onProgressListener: function(xhr) {
236
+ onProgressListener: function (xhr) {
257
237
  var position = 0;
258
238
  // if it takes longer than 3000 ms to get first chunk, we have some proxy
259
239
  // this is messing with us, so just backoff from using chunked for now
260
240
  var chunkedTimeout = setTimeout(disableChunked, 3000);
261
- return (xhr.onprogress = function() {
241
+ return (xhr.onprogress = function () {
262
242
  clearTimeout(chunkedTimeout);
263
243
  if (
264
244
  xhr.getResponseHeader("Content-Type") ===
@@ -269,9 +249,9 @@
269
249
  position = handle_progress(xhr.responseText, position);
270
250
  }
271
251
  });
272
- }
252
+ },
273
253
  },
274
- xhr: function() {
254
+ xhr: function () {
275
255
  var xhr = jQuery.ajaxSettings.xhr();
276
256
  if (!chunked) {
277
257
  return xhr;
@@ -279,7 +259,7 @@
279
259
  this.messageBus.onProgressListener(xhr);
280
260
  return xhr;
281
261
  },
282
- success: function(messages) {
262
+ success: function (messages) {
283
263
  if (!chunked) {
284
264
  // we may have requested text so jQuery will not parse
285
265
  if (typeof messages === "string") {
@@ -288,7 +268,7 @@
288
268
  gotData = reqSuccess(messages);
289
269
  }
290
270
  },
291
- error: function(xhr, textStatus, err) {
271
+ error: function (xhr, textStatus) {
292
272
  if (xhr.status === 429) {
293
273
  var tryAfter =
294
274
  parseInt(
@@ -307,7 +287,7 @@
307
287
  totalAjaxFailures += 1;
308
288
  }
309
289
  },
310
- complete: function() {
290
+ complete: function () {
311
291
  ajaxInProgress = false;
312
292
 
313
293
  var interval;
@@ -345,14 +325,14 @@
345
325
  }
346
326
 
347
327
  if (started) {
348
- pollTimeout = setTimeout(function() {
328
+ pollTimeout = setTimeout(function () {
349
329
  pollTimeout = null;
350
330
  poll();
351
331
  }, interval);
352
332
  }
353
333
 
354
334
  me.longPoll = null;
355
- }
335
+ },
356
336
  });
357
337
 
358
338
  return req;
@@ -374,7 +354,7 @@
374
354
  baseUrl: baseUrl,
375
355
  headers: {},
376
356
  ajax: typeof jQuery !== "undefined" && jQuery.ajax,
377
- diagnostics: function() {
357
+ diagnostics: function () {
378
358
  console.log("Stopped: " + stopped + " Started: " + started);
379
359
  console.log("Current callbacks");
380
360
  console.log(callbacks);
@@ -391,42 +371,50 @@
391
371
  );
392
372
  },
393
373
 
394
- pause: function() {
374
+ pause: function () {
395
375
  paused = true;
396
376
  },
397
377
 
398
- resume: function() {
378
+ resume: function () {
399
379
  paused = false;
400
380
  processMessages(later);
401
381
  later = [];
402
382
  },
403
383
 
404
- stop: function() {
384
+ stop: function () {
405
385
  stopped = true;
406
386
  started = false;
407
387
  if (delayPollTimeout) {
408
388
  clearTimeout(delayPollTimeout);
409
389
  delayPollTimeout = null;
410
390
  }
391
+ if (pollTimeout) {
392
+ clearTimeout(pollTimeout);
393
+ pollTimeout = null;
394
+ }
411
395
  if (me.longPoll) {
412
396
  me.longPoll.abort();
413
397
  }
398
+ if (me.onVisibilityChange) {
399
+ document.removeEventListener("visibilitychange", me.onVisibilityChange);
400
+ me.onVisibilityChange = null;
401
+ }
414
402
  },
415
403
 
416
404
  // Start polling
417
- start: function() {
405
+ start: function () {
418
406
  if (started) return;
419
407
  started = true;
420
408
  stopped = false;
421
409
 
422
- var poll = function() {
410
+ var poll = function () {
423
411
  if (stopped) {
424
412
  return;
425
413
  }
426
414
 
427
415
  if (callbacks.length === 0 || hiddenTabShouldWait()) {
428
416
  if (!delayPollTimeout) {
429
- delayPollTimeout = setTimeout(function() {
417
+ delayPollTimeout = setTimeout(function () {
430
418
  delayPollTimeout = null;
431
419
  poll();
432
420
  }, parseInt(500 + Math.random() * 500));
@@ -448,25 +436,28 @@
448
436
 
449
437
  // monitor visibility, issue a new long poll when the page shows
450
438
  if (document.addEventListener && "hidden" in document) {
451
- me.visibilityEvent = document.addEventListener(
452
- "visibilitychange",
453
- function() {
454
- if (!document.hidden && !me.longPoll && pollTimeout) {
455
- clearTimeout(pollTimeout);
456
- clearTimeout(delayPollTimeout);
457
-
458
- delayPollTimeout = null;
459
- pollTimeout = null;
460
- poll();
461
- }
439
+ me.onVisibilityChange = function () {
440
+ if (
441
+ !document.hidden &&
442
+ !me.longPoll &&
443
+ (pollTimeout || delayPollTimeout)
444
+ ) {
445
+ clearTimeout(pollTimeout);
446
+ clearTimeout(delayPollTimeout);
447
+
448
+ delayPollTimeout = null;
449
+ pollTimeout = null;
450
+ poll();
462
451
  }
463
- );
452
+ };
453
+
454
+ document.addEventListener("visibilitychange", me.onVisibilityChange);
464
455
  }
465
456
 
466
457
  poll();
467
458
  },
468
459
 
469
- status: function() {
460
+ status: function () {
470
461
  if (paused) {
471
462
  return "paused";
472
463
  } else if (started) {
@@ -485,7 +476,7 @@
485
476
  // -2 will recieve last message + all new messages
486
477
  // -3 will recieve last 2 messages + all new messages
487
478
  // if undefined will default to -1
488
- subscribe: function(channel, func, lastId) {
479
+ subscribe: function (channel, func, lastId) {
489
480
  if (!started && !stopped) {
490
481
  me.start();
491
482
  }
@@ -493,7 +484,9 @@
493
484
  if (lastId === null || typeof lastId === "undefined") {
494
485
  lastId = -1;
495
486
  } else if (typeof lastId !== "number") {
496
- throw "lastId has type " + typeof lastId + " but a number was expected.";
487
+ throw (
488
+ "lastId has type " + typeof lastId + " but a number was expected."
489
+ );
497
490
  }
498
491
 
499
492
  if (typeof channel !== "string") {
@@ -503,7 +496,7 @@
503
496
  callbacks.push({
504
497
  channel: channel,
505
498
  func: func,
506
- last_id: lastId
499
+ last_id: lastId,
507
500
  });
508
501
  if (me.longPoll) {
509
502
  me.longPoll.abort();
@@ -513,7 +506,7 @@
513
506
  },
514
507
 
515
508
  // Unsubscribe from a channel
516
- unsubscribe: function(channel, func) {
509
+ unsubscribe: function (channel, func) {
517
510
  // TODO allow for globbing in the middle of a channel name
518
511
  // like /something/*/something
519
512
  // at the moment we only support globbing /something/*
@@ -550,7 +543,7 @@
550
543
  }
551
544
 
552
545
  return removed;
553
- }
546
+ },
554
547
  };
555
548
  return me;
556
- }));
549
+ });