@rails/actioncable 6.1.4 → 6.1.5

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.
@@ -291,6 +291,7 @@
291
291
  return this.monitor.recordPing();
292
292
 
293
293
  case message_types.confirmation:
294
+ this.subscriptions.confirmSubscription(identifier);
294
295
  return this.subscriptions.notify(identifier, "connected");
295
296
 
296
297
  case message_types.rejection:
@@ -360,10 +361,52 @@
360
361
  };
361
362
  return Subscription;
362
363
  }();
364
+ var SubscriptionGuarantor = function() {
365
+ function SubscriptionGuarantor(subscriptions) {
366
+ classCallCheck(this, SubscriptionGuarantor);
367
+ this.subscriptions = subscriptions;
368
+ this.pendingSubscriptions = [];
369
+ }
370
+ SubscriptionGuarantor.prototype.guarantee = function guarantee(subscription) {
371
+ if (this.pendingSubscriptions.indexOf(subscription) == -1) {
372
+ logger.log("SubscriptionGuarantor guaranteeing " + subscription.identifier);
373
+ this.pendingSubscriptions.push(subscription);
374
+ } else {
375
+ logger.log("SubscriptionGuarantor already guaranteeing " + subscription.identifier);
376
+ }
377
+ this.startGuaranteeing();
378
+ };
379
+ SubscriptionGuarantor.prototype.forget = function forget(subscription) {
380
+ logger.log("SubscriptionGuarantor forgetting " + subscription.identifier);
381
+ this.pendingSubscriptions = this.pendingSubscriptions.filter(function(s) {
382
+ return s !== subscription;
383
+ });
384
+ };
385
+ SubscriptionGuarantor.prototype.startGuaranteeing = function startGuaranteeing() {
386
+ this.stopGuaranteeing();
387
+ this.retrySubscribing();
388
+ };
389
+ SubscriptionGuarantor.prototype.stopGuaranteeing = function stopGuaranteeing() {
390
+ clearTimeout(this.retryTimeout);
391
+ };
392
+ SubscriptionGuarantor.prototype.retrySubscribing = function retrySubscribing() {
393
+ var _this = this;
394
+ this.retryTimeout = setTimeout(function() {
395
+ if (_this.subscriptions && typeof _this.subscriptions.subscribe === "function") {
396
+ _this.pendingSubscriptions.map(function(subscription) {
397
+ logger.log("SubscriptionGuarantor resubscribing " + subscription.identifier);
398
+ _this.subscriptions.subscribe(subscription);
399
+ });
400
+ }
401
+ }, 500);
402
+ };
403
+ return SubscriptionGuarantor;
404
+ }();
363
405
  var Subscriptions = function() {
364
406
  function Subscriptions(consumer) {
365
407
  classCallCheck(this, Subscriptions);
366
408
  this.consumer = consumer;
409
+ this.guarantor = new SubscriptionGuarantor(this);
367
410
  this.subscriptions = [];
368
411
  }
369
412
  Subscriptions.prototype.create = function create(channelName, mixin) {
@@ -378,7 +421,7 @@
378
421
  this.subscriptions.push(subscription);
379
422
  this.consumer.ensureActiveConnection();
380
423
  this.notify(subscription, "initialized");
381
- this.sendCommand(subscription, "subscribe");
424
+ this.subscribe(subscription);
382
425
  return subscription;
383
426
  };
384
427
  Subscriptions.prototype.remove = function remove(subscription) {
@@ -397,6 +440,7 @@
397
440
  });
398
441
  };
399
442
  Subscriptions.prototype.forget = function forget(subscription) {
443
+ this.guarantor.forget(subscription);
400
444
  this.subscriptions = this.subscriptions.filter(function(s) {
401
445
  return s !== subscription;
402
446
  });
@@ -410,7 +454,7 @@
410
454
  Subscriptions.prototype.reload = function reload() {
411
455
  var _this2 = this;
412
456
  return this.subscriptions.map(function(subscription) {
413
- return _this2.sendCommand(subscription, "subscribe");
457
+ return _this2.subscribe(subscription);
414
458
  });
415
459
  };
416
460
  Subscriptions.prototype.notifyAll = function notifyAll(callbackName) {
@@ -436,6 +480,18 @@
436
480
  return typeof subscription[callbackName] === "function" ? subscription[callbackName].apply(subscription, args) : undefined;
437
481
  });
438
482
  };
483
+ Subscriptions.prototype.subscribe = function subscribe(subscription) {
484
+ if (this.sendCommand(subscription, "subscribe")) {
485
+ this.guarantor.guarantee(subscription);
486
+ }
487
+ };
488
+ Subscriptions.prototype.confirmSubscription = function confirmSubscription(identifier) {
489
+ var _this4 = this;
490
+ logger.log("Subscription confirmed " + identifier);
491
+ this.findAll(identifier).map(function(subscription) {
492
+ return _this4.guarantor.forget(subscription);
493
+ });
494
+ };
439
495
  Subscriptions.prototype.sendCommand = function sendCommand(subscription, command) {
440
496
  var identifier = subscription.identifier;
441
497
  return this.consumer.send({
@@ -506,6 +562,7 @@
506
562
  exports.INTERNAL = INTERNAL;
507
563
  exports.Subscription = Subscription;
508
564
  exports.Subscriptions = Subscriptions;
565
+ exports.SubscriptionGuarantor = SubscriptionGuarantor;
509
566
  exports.adapters = adapters;
510
567
  exports.createWebSocketURL = createWebSocketURL;
511
568
  exports.logger = logger;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rails/actioncable",
3
- "version": "6.1.4",
3
+ "version": "6.1.5",
4
4
  "description": "WebSocket framework for Ruby on Rails.",
5
5
  "main": "app/assets/javascripts/action_cable.js",
6
6
  "files": [
package/src/connection.js CHANGED
@@ -132,6 +132,7 @@ Connection.prototype.events = {
132
132
  case message_types.ping:
133
133
  return this.monitor.recordPing()
134
134
  case message_types.confirmation:
135
+ this.subscriptions.confirmSubscription(identifier)
135
136
  return this.subscriptions.notify(identifier, "connected")
136
137
  case message_types.rejection:
137
138
  return this.subscriptions.reject(identifier)
package/src/index.js CHANGED
@@ -4,6 +4,7 @@ import Consumer, { createWebSocketURL } from "./consumer"
4
4
  import INTERNAL from "./internal"
5
5
  import Subscription from "./subscription"
6
6
  import Subscriptions from "./subscriptions"
7
+ import SubscriptionGuarantor from "./subscription_guarantor"
7
8
  import adapters from "./adapters"
8
9
  import logger from "./logger"
9
10
 
@@ -14,6 +15,7 @@ export {
14
15
  INTERNAL,
15
16
  Subscription,
16
17
  Subscriptions,
18
+ SubscriptionGuarantor,
17
19
  adapters,
18
20
  createWebSocketURL,
19
21
  logger,
@@ -0,0 +1,50 @@
1
+ import logger from "./logger"
2
+
3
+ // Responsible for ensuring channel subscribe command is confirmed, retrying until confirmation is received.
4
+ // Internal class, not intended for direct user manipulation.
5
+
6
+ class SubscriptionGuarantor {
7
+ constructor(subscriptions) {
8
+ this.subscriptions = subscriptions
9
+ this.pendingSubscriptions = []
10
+ }
11
+
12
+ guarantee(subscription) {
13
+ if(this.pendingSubscriptions.indexOf(subscription) == -1){
14
+ logger.log(`SubscriptionGuarantor guaranteeing ${subscription.identifier}`)
15
+ this.pendingSubscriptions.push(subscription)
16
+ }
17
+ else {
18
+ logger.log(`SubscriptionGuarantor already guaranteeing ${subscription.identifier}`)
19
+ }
20
+ this.startGuaranteeing()
21
+ }
22
+
23
+ forget(subscription) {
24
+ logger.log(`SubscriptionGuarantor forgetting ${subscription.identifier}`)
25
+ this.pendingSubscriptions = (this.pendingSubscriptions.filter((s) => s !== subscription))
26
+ }
27
+
28
+ startGuaranteeing() {
29
+ this.stopGuaranteeing()
30
+ this.retrySubscribing()
31
+ }
32
+
33
+ stopGuaranteeing() {
34
+ clearTimeout(this.retryTimeout)
35
+ }
36
+
37
+ retrySubscribing() {
38
+ this.retryTimeout = setTimeout(() => {
39
+ if (this.subscriptions && typeof(this.subscriptions.subscribe) === "function") {
40
+ this.pendingSubscriptions.map((subscription) => {
41
+ logger.log(`SubscriptionGuarantor resubscribing ${subscription.identifier}`)
42
+ this.subscriptions.subscribe(subscription)
43
+ })
44
+ }
45
+ }
46
+ , 500)
47
+ }
48
+ }
49
+
50
+ export default SubscriptionGuarantor
@@ -1,4 +1,6 @@
1
1
  import Subscription from "./subscription"
2
+ import SubscriptionGuarantor from "./subscription_guarantor"
3
+ import logger from "./logger"
2
4
 
3
5
  // Collection class for creating (and internally managing) channel subscriptions.
4
6
  // The only method intended to be triggered by the user is ActionCable.Subscriptions#create,
@@ -13,6 +15,7 @@ import Subscription from "./subscription"
13
15
  export default class Subscriptions {
14
16
  constructor(consumer) {
15
17
  this.consumer = consumer
18
+ this.guarantor = new SubscriptionGuarantor(this)
16
19
  this.subscriptions = []
17
20
  }
18
21
 
@@ -29,7 +32,7 @@ export default class Subscriptions {
29
32
  this.subscriptions.push(subscription)
30
33
  this.consumer.ensureActiveConnection()
31
34
  this.notify(subscription, "initialized")
32
- this.sendCommand(subscription, "subscribe")
35
+ this.subscribe(subscription)
33
36
  return subscription
34
37
  }
35
38
 
@@ -50,6 +53,7 @@ export default class Subscriptions {
50
53
  }
51
54
 
52
55
  forget(subscription) {
56
+ this.guarantor.forget(subscription)
53
57
  this.subscriptions = (this.subscriptions.filter((s) => s !== subscription))
54
58
  return subscription
55
59
  }
@@ -60,7 +64,7 @@ export default class Subscriptions {
60
64
 
61
65
  reload() {
62
66
  return this.subscriptions.map((subscription) =>
63
- this.sendCommand(subscription, "subscribe"))
67
+ this.subscribe(subscription))
64
68
  }
65
69
 
66
70
  notifyAll(callbackName, ...args) {
@@ -80,6 +84,18 @@ export default class Subscriptions {
80
84
  (typeof subscription[callbackName] === "function" ? subscription[callbackName](...args) : undefined))
81
85
  }
82
86
 
87
+ subscribe(subscription) {
88
+ if (this.sendCommand(subscription, "subscribe")) {
89
+ this.guarantor.guarantee(subscription)
90
+ }
91
+ }
92
+
93
+ confirmSubscription(identifier) {
94
+ logger.log(`Subscription confirmed ${identifier}`)
95
+ this.findAll(identifier).map((subscription) =>
96
+ this.guarantor.forget(subscription))
97
+ }
98
+
83
99
  sendCommand(subscription, command) {
84
100
  const {identifier} = subscription
85
101
  return this.consumer.send({command, identifier})
package/CHANGELOG.md DELETED
@@ -1,77 +0,0 @@
1
- ## Rails 6.1.4 (June 24, 2021) ##
2
-
3
- * Fix `ArgumentError` with ruby 3.0 on `RemoteConnection#disconnect`.
4
-
5
- *Vladislav*
6
-
7
-
8
- ## Rails 6.1.3.2 (May 05, 2021) ##
9
-
10
- * No changes.
11
-
12
-
13
- ## Rails 6.1.3.1 (March 26, 2021) ##
14
-
15
- * No changes.
16
-
17
-
18
- ## Rails 6.1.3 (February 17, 2021) ##
19
-
20
- * No changes.
21
-
22
-
23
- ## Rails 6.1.2.1 (February 10, 2021) ##
24
-
25
- * No changes.
26
-
27
-
28
- ## Rails 6.1.2 (February 09, 2021) ##
29
-
30
- * No changes.
31
-
32
-
33
- ## Rails 6.1.1 (January 07, 2021) ##
34
-
35
- * No changes.
36
-
37
-
38
- ## Rails 6.1.0 (December 09, 2020) ##
39
-
40
- * `ActionCable::Connection::Base` now allows intercepting unhandled exceptions
41
- with `rescue_from` before they are logged, which is useful for error reporting
42
- tools and other integrations.
43
-
44
- *Justin Talbott*
45
-
46
- * Add `ActionCable::Channel#stream_or_reject_for` to stream if record is present, otherwise reject the connection
47
-
48
- *Atul Bhosale*
49
-
50
- * Add `ActionCable::Channel#stop_stream_from` and `#stop_stream_for` to unsubscribe from a specific stream.
51
-
52
- *Zhang Kang*
53
-
54
- * Add PostgreSQL subscription connection identificator.
55
-
56
- Now you can distinguish Action Cable PostgreSQL subscription connections among others.
57
- Also, you can set custom `id` in `cable.yml` configuration.
58
-
59
- ```sql
60
- SELECT application_name FROM pg_stat_activity;
61
- /*
62
- application_name
63
- ------------------------
64
- psql
65
- ActionCable-PID-42
66
- (2 rows)
67
- */
68
- ```
69
-
70
- *Sergey Ponomarev*
71
-
72
- * Subscription confirmations and rejections are now logged at the `DEBUG` level instead of `INFO`.
73
-
74
- *DHH*
75
-
76
-
77
- Please check [6-0-stable](https://github.com/rails/rails/blob/6-0-stable/actioncable/CHANGELOG.md) for previous changes.