actioncable 7.0.8.7 → 7.1.0.beta1
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +58 -171
- data/MIT-LICENSE +1 -1
- data/README.md +4 -4
- data/app/assets/javascripts/action_cable.js +25 -4
- data/app/assets/javascripts/actioncable.esm.js +25 -4
- data/app/assets/javascripts/actioncable.js +25 -4
- data/lib/action_cable/channel/base.rb +17 -5
- data/lib/action_cable/channel/broadcasting.rb +3 -1
- data/lib/action_cable/channel/callbacks.rb +16 -0
- data/lib/action_cable/channel/naming.rb +4 -2
- data/lib/action_cable/channel/streams.rb +2 -0
- data/lib/action_cable/channel/test_case.rb +6 -1
- data/lib/action_cable/connection/authorization.rb +1 -1
- data/lib/action_cable/connection/base.rb +18 -5
- data/lib/action_cable/connection/callbacks.rb +51 -0
- data/lib/action_cable/connection/internal_channel.rb +3 -1
- data/lib/action_cable/connection/stream.rb +1 -3
- data/lib/action_cable/connection/stream_event_loop.rb +0 -1
- data/lib/action_cable/connection/subscriptions.rb +2 -0
- data/lib/action_cable/connection/tagged_logger_proxy.rb +6 -4
- data/lib/action_cable/connection/test_case.rb +3 -1
- data/lib/action_cable/connection/web_socket.rb +2 -0
- data/lib/action_cable/deprecator.rb +7 -0
- data/lib/action_cable/engine.rb +13 -5
- data/lib/action_cable/gem_version.rb +4 -4
- data/lib/action_cable/remote_connections.rb +11 -2
- data/lib/action_cable/server/base.rb +3 -0
- data/lib/action_cable/server/broadcasting.rb +2 -0
- data/lib/action_cable/server/configuration.rb +12 -2
- data/lib/action_cable/server/connections.rb +3 -1
- data/lib/action_cable/server/worker.rb +0 -1
- data/lib/action_cable/subscription_adapter/async.rb +0 -2
- data/lib/action_cable/subscription_adapter/postgresql.rb +0 -1
- data/lib/action_cable/subscription_adapter/redis.rb +3 -6
- data/lib/action_cable/subscription_adapter/test.rb +3 -5
- data/lib/action_cable/test_helper.rb +53 -22
- data/lib/action_cable/version.rb +1 -1
- data/lib/action_cable.rb +24 -12
- data/lib/rails/generators/channel/USAGE +14 -8
- data/lib/rails/generators/channel/channel_generator.rb +21 -7
- metadata +31 -19
- data/lib/action_cable/channel.rb +0 -17
- data/lib/action_cable/connection.rb +0 -22
- data/lib/action_cable/server.rb +0 -16
- data/lib/action_cable/subscription_adapter.rb +0 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ccb9a2f59fc62c2ab7db13d614444a13b987954a4ccf840c0dc63a232a3af0c2
|
4
|
+
data.tar.gz: c5d380823470f2b257050785a1e2d65a734bce3d89589e2c0d47f761755e28e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ed5b02acbf05dcc57a03b420019afd733d0f8ce867ec55be7b6f7fafdd97083bfe903808f34b5193f451b67d4bffee1d96e586768c1be3c9a85a4763f9176efe
|
7
|
+
data.tar.gz: 8a5eff431fae0f83c0962e67aaaeabdee3dc7235484a005c94dd39ddaf523c5c94139e248085910cfff239c9c46d5c8b1c197b636545458b5d119c991512b7eb
|
data/CHANGELOG.md
CHANGED
@@ -1,93 +1,79 @@
|
|
1
|
-
## Rails 7.0.
|
1
|
+
## Rails 7.1.0.beta1 (September 13, 2023) ##
|
2
2
|
|
3
|
-
*
|
3
|
+
* Add a `@server` instance variable referencing the `ActionCable.server`
|
4
|
+
singleton to `ActionCable::Channel::ConnectionStub`
|
4
5
|
|
6
|
+
This lets us delegate the `pubsub` and `config` method calls
|
7
|
+
to the server. This fixes `NoMethodError` errors when testing
|
8
|
+
channel logic that call `pubsub` (e.g. `stop_stream_for`).
|
5
9
|
|
6
|
-
|
10
|
+
*Julian Foo*
|
7
11
|
|
8
|
-
*
|
12
|
+
* Added `health_check_path` and `health_check_application` config to
|
13
|
+
mount a given health check rack app on a given path.
|
14
|
+
Useful when mounting Action Cable standalone.
|
9
15
|
|
16
|
+
*Joé Dupuis*
|
10
17
|
|
11
|
-
|
18
|
+
* Introduce the `capture_broadcasts` test helper.
|
12
19
|
|
13
|
-
|
20
|
+
Returns all messages broadcast in a block.
|
14
21
|
|
22
|
+
```ruby
|
23
|
+
messages = capture_broadcasts("test") do
|
24
|
+
ActionCable.server.broadcast "test", { message: "one" }
|
25
|
+
ActionCable.server.broadcast "test", { message: "two" }
|
26
|
+
end
|
27
|
+
assert_equal 2, messages.length
|
28
|
+
assert_equal({ "message" => "one" }, messages.first)
|
29
|
+
assert_equal({ "message" => "two" }, messages.last)
|
30
|
+
```
|
15
31
|
|
16
|
-
|
17
|
-
|
18
|
-
* No changes.
|
19
|
-
|
20
|
-
|
21
|
-
## Rails 7.0.8.3 (May 17, 2024) ##
|
22
|
-
|
23
|
-
* No changes.
|
24
|
-
|
25
|
-
|
26
|
-
## Rails 7.0.8.2 (May 16, 2024) ##
|
27
|
-
|
28
|
-
* No changes.
|
29
|
-
|
30
|
-
|
31
|
-
## Rails 7.0.8.1 (February 21, 2024) ##
|
32
|
-
|
33
|
-
* No changes.
|
34
|
-
|
35
|
-
|
36
|
-
## Rails 7.0.8 (September 09, 2023) ##
|
37
|
-
|
38
|
-
* No changes.
|
39
|
-
|
40
|
-
|
41
|
-
## Rails 7.0.7.2 (August 22, 2023) ##
|
42
|
-
|
43
|
-
* No changes.
|
44
|
-
|
45
|
-
|
46
|
-
## Rails 7.0.7.1 (August 22, 2023) ##
|
47
|
-
|
48
|
-
* No changes.
|
49
|
-
|
50
|
-
|
51
|
-
## Rails 7.0.7 (August 09, 2023) ##
|
52
|
-
|
53
|
-
* No changes.
|
32
|
+
*Alex Ghiculescu*
|
54
33
|
|
34
|
+
* Display broadcasted messages on error message when using `assert_broadcast_on`
|
55
35
|
|
56
|
-
|
36
|
+
*Stéphane Robino*
|
57
37
|
|
58
|
-
*
|
38
|
+
* The Action Cable client now supports subprotocols to allow passing arbitrary data
|
39
|
+
to the server.
|
59
40
|
|
60
|
-
|
41
|
+
```js
|
42
|
+
const consumer = ActionCable.createConsumer()
|
61
43
|
|
44
|
+
consumer.addSubProtocol('custom-protocol')
|
62
45
|
|
63
|
-
|
46
|
+
consumer.connect()
|
47
|
+
```
|
64
48
|
|
65
|
-
|
49
|
+
See also:
|
66
50
|
|
51
|
+
* https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols
|
67
52
|
|
68
|
-
|
53
|
+
*Guillaume Hain*
|
69
54
|
|
70
|
-
*
|
55
|
+
* Redis pub/sub adapter now automatically reconnects when Redis connection is lost.
|
71
56
|
|
72
57
|
*Vladimir Dementyev*
|
73
58
|
|
59
|
+
* The `connected()` callback can now take a `{reconnected}` parameter to differentiate
|
60
|
+
connections from reconnections.
|
74
61
|
|
75
|
-
|
76
|
-
|
77
|
-
* No changes.
|
78
|
-
|
79
|
-
|
80
|
-
## Rails 7.0.4.2 (January 24, 2023) ##
|
81
|
-
|
82
|
-
* No changes.
|
62
|
+
```js
|
63
|
+
import consumer from "./consumer"
|
83
64
|
|
65
|
+
consumer.subscriptions.create("ExampleChannel", {
|
66
|
+
connected({reconnected}) {
|
67
|
+
if (reconnected) {
|
68
|
+
...
|
69
|
+
} else {
|
70
|
+
...
|
71
|
+
}
|
72
|
+
}
|
73
|
+
})
|
74
|
+
```
|
84
75
|
|
85
|
-
|
86
|
-
|
87
|
-
* No changes.
|
88
|
-
|
89
|
-
|
90
|
-
## Rails 7.0.4 (September 09, 2022) ##
|
76
|
+
*Mansa Keïta*
|
91
77
|
|
92
78
|
* The Redis adapter is now compatible with redis-rb 5.0
|
93
79
|
|
@@ -101,114 +87,15 @@
|
|
101
87
|
|
102
88
|
*Alex Ghiculescu*
|
103
89
|
|
90
|
+
* `ActionCable.server.remote_connections.where(...).disconnect` now sends `disconnect` message
|
91
|
+
before closing the connection with the reconnection strategy specified (defaults to `true`).
|
104
92
|
|
105
|
-
|
106
|
-
|
107
|
-
* No changes.
|
108
|
-
|
109
|
-
|
110
|
-
## Rails 7.0.3 (May 09, 2022) ##
|
111
|
-
|
112
|
-
* No changes.
|
113
|
-
|
114
|
-
|
115
|
-
## Rails 7.0.2.4 (April 26, 2022) ##
|
116
|
-
|
117
|
-
* No changes.
|
118
|
-
|
119
|
-
|
120
|
-
## Rails 7.0.2.3 (March 08, 2022) ##
|
121
|
-
|
122
|
-
* No changes.
|
123
|
-
|
124
|
-
|
125
|
-
## Rails 7.0.2.2 (February 11, 2022) ##
|
126
|
-
|
127
|
-
* No changes.
|
128
|
-
|
129
|
-
|
130
|
-
## Rails 7.0.2.1 (February 11, 2022) ##
|
131
|
-
|
132
|
-
* No changes.
|
133
|
-
|
134
|
-
|
135
|
-
## Rails 7.0.2 (February 08, 2022) ##
|
136
|
-
|
137
|
-
* No changes.
|
138
|
-
|
139
|
-
|
140
|
-
## Rails 7.0.1 (January 06, 2022) ##
|
141
|
-
|
142
|
-
* No changes.
|
143
|
-
|
144
|
-
|
145
|
-
## Rails 7.0.0 (December 15, 2021) ##
|
146
|
-
|
147
|
-
* No changes.
|
148
|
-
|
149
|
-
|
150
|
-
## Rails 7.0.0.rc3 (December 14, 2021) ##
|
151
|
-
|
152
|
-
* No changes.
|
153
|
-
|
154
|
-
|
155
|
-
## Rails 7.0.0.rc2 (December 14, 2021) ##
|
156
|
-
|
157
|
-
* No changes.
|
158
|
-
|
159
|
-
## Rails 7.0.0.rc1 (December 06, 2021) ##
|
160
|
-
|
161
|
-
* The Action Cable client now ensures successful channel subscriptions:
|
162
|
-
|
163
|
-
* The client maintains a set of pending subscriptions until either
|
164
|
-
the server confirms the subscription or the channel is torn down.
|
165
|
-
* Rectifies the race condition where an unsubscribe is rapidly followed
|
166
|
-
by a subscribe (on the same channel identifier) and the requests are
|
167
|
-
handled out of order by the ActionCable server, thereby ignoring the
|
168
|
-
subscribe command.
|
169
|
-
|
170
|
-
*Daniel Spinosa*
|
171
|
-
|
172
|
-
|
173
|
-
## Rails 7.0.0.alpha2 (September 15, 2021) ##
|
174
|
-
|
175
|
-
* No changes.
|
176
|
-
|
177
|
-
|
178
|
-
## Rails 7.0.0.alpha1 (September 15, 2021) ##
|
179
|
-
|
180
|
-
* Compile ESM package that can be used directly in the browser as actioncable.esm.js.
|
181
|
-
|
182
|
-
*DHH*
|
183
|
-
|
184
|
-
* Move action_cable.js to actioncable.js to match naming convention used for other Rails frameworks, and use JS console to communicate the deprecation.
|
185
|
-
|
186
|
-
*DHH*
|
187
|
-
|
188
|
-
* Stop transpiling the UMD package generated as actioncable.js and drop the IE11 testing that relied on that.
|
189
|
-
|
190
|
-
*DHH*
|
191
|
-
|
192
|
-
* Truncate broadcast logging messages.
|
193
|
-
|
194
|
-
*J Smith*
|
195
|
-
|
196
|
-
* OpenSSL constants are now used for Digest computations.
|
197
|
-
|
198
|
-
*Dirkjan Bussink*
|
199
|
-
|
200
|
-
* The Action Cable client now includes safeguards to prevent a "thundering
|
201
|
-
herd" of client reconnects after server connectivity loss:
|
93
|
+
*Vladimir Dementyev*
|
202
94
|
|
203
|
-
|
204
|
-
threshold after the server's last ping before making the first
|
205
|
-
reconnection attempt.
|
206
|
-
* Subsequent reconnection attempts now use exponential backoff instead of
|
207
|
-
logarithmic backoff. To allow the delay between reconnection attempts to
|
208
|
-
increase slowly at first, the default exponentiation base is < 2.
|
209
|
-
* Random jitter is applied to each delay between reconnection attempts.
|
95
|
+
* Added command callbacks to `ActionCable::Connection::Base`.
|
210
96
|
|
211
|
-
|
97
|
+
Now you can define `before_command`, `after_command`, and `around_command` to be invoked before, after or around any command received by a client respectively.
|
212
98
|
|
99
|
+
*Vladimir Dementyev*
|
213
100
|
|
214
|
-
Please check [
|
101
|
+
Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/actioncable/CHANGELOG.md) for previous changes.
|
data/MIT-LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
# Action Cable – Integrated WebSockets for Rails
|
1
|
+
# Action Cable – Integrated WebSockets for \Rails
|
2
2
|
|
3
|
-
Action Cable seamlessly integrates WebSockets with the rest of your Rails application.
|
3
|
+
Action Cable seamlessly integrates WebSockets with the rest of your \Rails application.
|
4
4
|
It allows for real-time features to be written in Ruby in the same style
|
5
|
-
and form as the rest of your Rails application, while still being performant
|
5
|
+
and form as the rest of your \Rails application, while still being performant
|
6
6
|
and scalable. It's a full-stack offering that provides both a client-side
|
7
7
|
JavaScript framework and a server-side Ruby framework. You have access to your full
|
8
8
|
domain model written with Active Record or your ORM of choice.
|
9
9
|
|
10
|
-
You can read more about Action Cable in the [Action Cable Overview](https://
|
10
|
+
You can read more about Action Cable in the [Action Cable Overview](https://guides.rubyonrails.org/action_cable_overview.html) guide.
|
11
11
|
|
12
12
|
## Support
|
13
13
|
|
@@ -121,7 +121,8 @@
|
|
121
121
|
disconnect_reasons: {
|
122
122
|
unauthorized: "unauthorized",
|
123
123
|
invalid_request: "invalid_request",
|
124
|
-
server_restart: "server_restart"
|
124
|
+
server_restart: "server_restart",
|
125
|
+
remote: "remote"
|
125
126
|
},
|
126
127
|
default_mount_path: "/cable",
|
127
128
|
protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
|
@@ -150,11 +151,12 @@
|
|
150
151
|
logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
|
151
152
|
return false;
|
152
153
|
} else {
|
153
|
-
|
154
|
+
const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
|
155
|
+
logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
|
154
156
|
if (this.webSocket) {
|
155
157
|
this.uninstallEventHandlers();
|
156
158
|
}
|
157
|
-
this.webSocket = new adapters.WebSocket(this.consumer.url,
|
159
|
+
this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
|
158
160
|
this.installEventHandlers();
|
159
161
|
this.monitor.start();
|
160
162
|
return true;
|
@@ -196,6 +198,9 @@
|
|
196
198
|
isActive() {
|
197
199
|
return this.isState("open", "connecting");
|
198
200
|
}
|
201
|
+
triedToReconnect() {
|
202
|
+
return this.monitor.reconnectAttempts > 0;
|
203
|
+
}
|
199
204
|
isProtocolSupported() {
|
200
205
|
return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
|
201
206
|
}
|
@@ -233,6 +238,9 @@
|
|
233
238
|
const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
|
234
239
|
switch (type) {
|
235
240
|
case message_types.welcome:
|
241
|
+
if (this.triedToReconnect()) {
|
242
|
+
this.reconnectAttempted = true;
|
243
|
+
}
|
236
244
|
this.monitor.recordConnect();
|
237
245
|
return this.subscriptions.reload();
|
238
246
|
|
@@ -247,7 +255,16 @@
|
|
247
255
|
|
248
256
|
case message_types.confirmation:
|
249
257
|
this.subscriptions.confirmSubscription(identifier);
|
250
|
-
|
258
|
+
if (this.reconnectAttempted) {
|
259
|
+
this.reconnectAttempted = false;
|
260
|
+
return this.subscriptions.notify(identifier, "connected", {
|
261
|
+
reconnected: true
|
262
|
+
});
|
263
|
+
} else {
|
264
|
+
return this.subscriptions.notify(identifier, "connected", {
|
265
|
+
reconnected: false
|
266
|
+
});
|
267
|
+
}
|
251
268
|
|
252
269
|
case message_types.rejection:
|
253
270
|
return this.subscriptions.reject(identifier);
|
@@ -427,6 +444,7 @@
|
|
427
444
|
this._url = url;
|
428
445
|
this.subscriptions = new Subscriptions(this);
|
429
446
|
this.connection = new Connection(this);
|
447
|
+
this.subprotocols = [];
|
430
448
|
}
|
431
449
|
get url() {
|
432
450
|
return createWebSocketURL(this._url);
|
@@ -447,6 +465,9 @@
|
|
447
465
|
return this.connection.open();
|
448
466
|
}
|
449
467
|
}
|
468
|
+
addSubProtocol(subprotocol) {
|
469
|
+
this.subprotocols = [ ...this.subprotocols, subprotocol ];
|
470
|
+
}
|
450
471
|
}
|
451
472
|
function createWebSocketURL(url) {
|
452
473
|
if (typeof url === "function") {
|
@@ -123,7 +123,8 @@ var INTERNAL = {
|
|
123
123
|
disconnect_reasons: {
|
124
124
|
unauthorized: "unauthorized",
|
125
125
|
invalid_request: "invalid_request",
|
126
|
-
server_restart: "server_restart"
|
126
|
+
server_restart: "server_restart",
|
127
|
+
remote: "remote"
|
127
128
|
},
|
128
129
|
default_mount_path: "/cable",
|
129
130
|
protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
|
@@ -156,11 +157,12 @@ class Connection {
|
|
156
157
|
logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
|
157
158
|
return false;
|
158
159
|
} else {
|
159
|
-
|
160
|
+
const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
|
161
|
+
logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
|
160
162
|
if (this.webSocket) {
|
161
163
|
this.uninstallEventHandlers();
|
162
164
|
}
|
163
|
-
this.webSocket = new adapters.WebSocket(this.consumer.url,
|
165
|
+
this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
|
164
166
|
this.installEventHandlers();
|
165
167
|
this.monitor.start();
|
166
168
|
return true;
|
@@ -202,6 +204,9 @@ class Connection {
|
|
202
204
|
isActive() {
|
203
205
|
return this.isState("open", "connecting");
|
204
206
|
}
|
207
|
+
triedToReconnect() {
|
208
|
+
return this.monitor.reconnectAttempts > 0;
|
209
|
+
}
|
205
210
|
isProtocolSupported() {
|
206
211
|
return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
|
207
212
|
}
|
@@ -241,6 +246,9 @@ Connection.prototype.events = {
|
|
241
246
|
const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
|
242
247
|
switch (type) {
|
243
248
|
case message_types.welcome:
|
249
|
+
if (this.triedToReconnect()) {
|
250
|
+
this.reconnectAttempted = true;
|
251
|
+
}
|
244
252
|
this.monitor.recordConnect();
|
245
253
|
return this.subscriptions.reload();
|
246
254
|
|
@@ -255,7 +263,16 @@ Connection.prototype.events = {
|
|
255
263
|
|
256
264
|
case message_types.confirmation:
|
257
265
|
this.subscriptions.confirmSubscription(identifier);
|
258
|
-
|
266
|
+
if (this.reconnectAttempted) {
|
267
|
+
this.reconnectAttempted = false;
|
268
|
+
return this.subscriptions.notify(identifier, "connected", {
|
269
|
+
reconnected: true
|
270
|
+
});
|
271
|
+
} else {
|
272
|
+
return this.subscriptions.notify(identifier, "connected", {
|
273
|
+
reconnected: false
|
274
|
+
});
|
275
|
+
}
|
259
276
|
|
260
277
|
case message_types.rejection:
|
261
278
|
return this.subscriptions.reject(identifier);
|
@@ -440,6 +457,7 @@ class Consumer {
|
|
440
457
|
this._url = url;
|
441
458
|
this.subscriptions = new Subscriptions(this);
|
442
459
|
this.connection = new Connection(this);
|
460
|
+
this.subprotocols = [];
|
443
461
|
}
|
444
462
|
get url() {
|
445
463
|
return createWebSocketURL(this._url);
|
@@ -460,6 +478,9 @@ class Consumer {
|
|
460
478
|
return this.connection.open();
|
461
479
|
}
|
462
480
|
}
|
481
|
+
addSubProtocol(subprotocol) {
|
482
|
+
this.subprotocols = [ ...this.subprotocols, subprotocol ];
|
483
|
+
}
|
463
484
|
}
|
464
485
|
|
465
486
|
function createWebSocketURL(url) {
|
@@ -121,7 +121,8 @@
|
|
121
121
|
disconnect_reasons: {
|
122
122
|
unauthorized: "unauthorized",
|
123
123
|
invalid_request: "invalid_request",
|
124
|
-
server_restart: "server_restart"
|
124
|
+
server_restart: "server_restart",
|
125
|
+
remote: "remote"
|
125
126
|
},
|
126
127
|
default_mount_path: "/cable",
|
127
128
|
protocols: [ "actioncable-v1-json", "actioncable-unsupported" ]
|
@@ -150,11 +151,12 @@
|
|
150
151
|
logger.log(`Attempted to open WebSocket, but existing socket is ${this.getState()}`);
|
151
152
|
return false;
|
152
153
|
} else {
|
153
|
-
|
154
|
+
const socketProtocols = [ ...protocols, ...this.consumer.subprotocols || [] ];
|
155
|
+
logger.log(`Opening WebSocket, current state is ${this.getState()}, subprotocols: ${socketProtocols}`);
|
154
156
|
if (this.webSocket) {
|
155
157
|
this.uninstallEventHandlers();
|
156
158
|
}
|
157
|
-
this.webSocket = new adapters.WebSocket(this.consumer.url,
|
159
|
+
this.webSocket = new adapters.WebSocket(this.consumer.url, socketProtocols);
|
158
160
|
this.installEventHandlers();
|
159
161
|
this.monitor.start();
|
160
162
|
return true;
|
@@ -196,6 +198,9 @@
|
|
196
198
|
isActive() {
|
197
199
|
return this.isState("open", "connecting");
|
198
200
|
}
|
201
|
+
triedToReconnect() {
|
202
|
+
return this.monitor.reconnectAttempts > 0;
|
203
|
+
}
|
199
204
|
isProtocolSupported() {
|
200
205
|
return indexOf.call(supportedProtocols, this.getProtocol()) >= 0;
|
201
206
|
}
|
@@ -233,6 +238,9 @@
|
|
233
238
|
const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
|
234
239
|
switch (type) {
|
235
240
|
case message_types.welcome:
|
241
|
+
if (this.triedToReconnect()) {
|
242
|
+
this.reconnectAttempted = true;
|
243
|
+
}
|
236
244
|
this.monitor.recordConnect();
|
237
245
|
return this.subscriptions.reload();
|
238
246
|
|
@@ -247,7 +255,16 @@
|
|
247
255
|
|
248
256
|
case message_types.confirmation:
|
249
257
|
this.subscriptions.confirmSubscription(identifier);
|
250
|
-
|
258
|
+
if (this.reconnectAttempted) {
|
259
|
+
this.reconnectAttempted = false;
|
260
|
+
return this.subscriptions.notify(identifier, "connected", {
|
261
|
+
reconnected: true
|
262
|
+
});
|
263
|
+
} else {
|
264
|
+
return this.subscriptions.notify(identifier, "connected", {
|
265
|
+
reconnected: false
|
266
|
+
});
|
267
|
+
}
|
251
268
|
|
252
269
|
case message_types.rejection:
|
253
270
|
return this.subscriptions.reject(identifier);
|
@@ -427,6 +444,7 @@
|
|
427
444
|
this._url = url;
|
428
445
|
this.subscriptions = new Subscriptions(this);
|
429
446
|
this.connection = new Connection(this);
|
447
|
+
this.subprotocols = [];
|
430
448
|
}
|
431
449
|
get url() {
|
432
450
|
return createWebSocketURL(this._url);
|
@@ -447,6 +465,9 @@
|
|
447
465
|
return this.connection.open();
|
448
466
|
}
|
449
467
|
}
|
468
|
+
addSubProtocol(subprotocol) {
|
469
|
+
this.subprotocols = [ ...this.subprotocols, subprotocol ];
|
470
|
+
}
|
450
471
|
}
|
451
472
|
function createWebSocketURL(url) {
|
452
473
|
if (typeof url === "function") {
|
@@ -2,9 +2,12 @@
|
|
2
2
|
|
3
3
|
require "set"
|
4
4
|
require "active_support/rescuable"
|
5
|
+
require "active_support/parameter_filter"
|
5
6
|
|
6
7
|
module ActionCable
|
7
8
|
module Channel
|
9
|
+
# = Action Cable \Channel \Base
|
10
|
+
#
|
8
11
|
# The channel provides the basic structure of grouping behavior into logical units when communicating over the WebSocket connection.
|
9
12
|
# You can think of a channel like a form of controller, but one that's capable of pushing content to the subscriber in addition to simply
|
10
13
|
# responding to the subscriber's direct requests.
|
@@ -209,9 +212,11 @@ module ActionCable
|
|
209
212
|
# Transmit a hash of data to the subscriber. The hash will automatically be wrapped in a JSON envelope with
|
210
213
|
# the proper channel identifier marked as the recipient.
|
211
214
|
def transmit(data, via: nil) # :doc:
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
+
logger.debug do
|
216
|
+
status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
|
217
|
+
status += " (via #{via})" if via
|
218
|
+
status
|
219
|
+
end
|
215
220
|
|
216
221
|
payload = { channel_class: self.class.name, data: data, via: via }
|
217
222
|
ActiveSupport::Notifications.instrument("transmit.action_cable", payload) do
|
@@ -262,7 +267,7 @@ module ActionCable
|
|
262
267
|
end
|
263
268
|
|
264
269
|
def dispatch_action(action, data)
|
265
|
-
logger.
|
270
|
+
logger.debug action_signature(action, data)
|
266
271
|
|
267
272
|
if method(action).arity == 1
|
268
273
|
public_send action, data
|
@@ -275,12 +280,19 @@ module ActionCable
|
|
275
280
|
|
276
281
|
def action_signature(action, data)
|
277
282
|
(+"#{self.class.name}##{action}").tap do |signature|
|
278
|
-
|
283
|
+
arguments = data.except("action")
|
284
|
+
|
285
|
+
if arguments.any?
|
286
|
+
arguments = parameter_filter.filter(arguments)
|
279
287
|
signature << "(#{arguments.inspect})"
|
280
288
|
end
|
281
289
|
end
|
282
290
|
end
|
283
291
|
|
292
|
+
def parameter_filter
|
293
|
+
@parameter_filter ||= ActiveSupport::ParameterFilter.new(connection.config.filter_parameters)
|
294
|
+
end
|
295
|
+
|
284
296
|
def transmit_subscription_confirmation
|
285
297
|
unless subscription_confirmation_sent?
|
286
298
|
logger.debug "#{self.class.name} is transmitting the subscription confirmation"
|
@@ -7,7 +7,9 @@ module ActionCable
|
|
7
7
|
module Broadcasting
|
8
8
|
extend ActiveSupport::Concern
|
9
9
|
|
10
|
-
|
10
|
+
included do
|
11
|
+
delegate :broadcasting_for, :broadcast_to, to: :class
|
12
|
+
end
|
11
13
|
|
12
14
|
module ClassMethods
|
13
15
|
# Broadcast a hash to a unique broadcasting for this <tt>model</tt> in this channel.
|
@@ -4,6 +4,22 @@ require "active_support/callbacks"
|
|
4
4
|
|
5
5
|
module ActionCable
|
6
6
|
module Channel
|
7
|
+
# = Action Cable \Channel \Callbacks
|
8
|
+
#
|
9
|
+
# Action Cable Channel provides hooks during the life cycle of a channel subscription.
|
10
|
+
# Callbacks allow triggering logic during this cycle. Available callbacks are:
|
11
|
+
#
|
12
|
+
# * <tt>before_subscribe</tt>
|
13
|
+
# * <tt>after_subscribe</tt> (also aliased as: <tt>on_subscribe</tt>)
|
14
|
+
# * <tt>before_unsubscribe</tt>
|
15
|
+
# * <tt>after_unsubscribe</tt> (also aliased as: <tt>on_unsubscribe</tt>)
|
16
|
+
#
|
17
|
+
# NOTE: the <tt>after_subscribe</tt> callback is triggered whenever
|
18
|
+
# the <tt>subscribed</tt> method is called, even if subscription was rejected
|
19
|
+
# with the <tt>reject</tt> method.
|
20
|
+
# To trigger <tt>after_subscribe</tt> only on successful subscriptions,
|
21
|
+
# use <tt>after_subscribe :my_method_name, unless: :subscription_rejected?</tt>
|
22
|
+
#
|
7
23
|
module Callbacks
|
8
24
|
extend ActiveSupport::Concern
|
9
25
|
include ActiveSupport::Callbacks
|
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
module ActionCable
|
4
4
|
module Channel
|
5
|
+
# = Action Cable \Channel \Streams
|
6
|
+
#
|
5
7
|
# Streams allow channels to route broadcastings to the subscriber. A broadcasting is, as discussed elsewhere, a pubsub queue where any data
|
6
8
|
# placed into it is automatically sent to the clients that are connected at that time. It's purely an online queue, though. If you're not
|
7
9
|
# streaming a broadcasting at the very moment it sends out an update, you will not get that update, even if you connect after it has been sent.
|
@@ -15,6 +15,8 @@ module ActionCable
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
# = Action Cable \Channel Stub
|
19
|
+
#
|
18
20
|
# Stub +stream_from+ to track streams for the channel.
|
19
21
|
# Add public aliases for +subscription_confirmation_sent?+ and
|
20
22
|
# +subscription_rejected?+.
|
@@ -45,9 +47,12 @@ module ActionCable
|
|
45
47
|
end
|
46
48
|
|
47
49
|
class ConnectionStub
|
48
|
-
attr_reader :transmissions, :identifiers, :subscriptions, :logger
|
50
|
+
attr_reader :server, :transmissions, :identifiers, :subscriptions, :logger
|
51
|
+
|
52
|
+
delegate :pubsub, :config, to: :server
|
49
53
|
|
50
54
|
def initialize(identifiers = {})
|
55
|
+
@server = ActionCable.server
|
51
56
|
@transmissions = []
|
52
57
|
|
53
58
|
identifiers.each do |identifier, val|
|