actioncable 7.1.6 → 7.2.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +38 -135
- data/README.md +1 -1
- data/app/assets/javascripts/action_cable.js +3 -3
- data/app/assets/javascripts/actioncable.esm.js +3 -3
- data/app/assets/javascripts/actioncable.js +3 -3
- data/lib/action_cable/channel/base.rb +104 -86
- data/lib/action_cable/channel/broadcasting.rb +25 -18
- data/lib/action_cable/channel/callbacks.rb +27 -25
- data/lib/action_cable/channel/naming.rb +9 -8
- data/lib/action_cable/channel/periodic_timers.rb +7 -7
- data/lib/action_cable/channel/streams.rb +79 -64
- data/lib/action_cable/channel/test_case.rb +112 -86
- data/lib/action_cable/connection/authorization.rb +4 -1
- data/lib/action_cable/connection/base.rb +53 -38
- data/lib/action_cable/connection/callbacks.rb +20 -18
- data/lib/action_cable/connection/client_socket.rb +3 -1
- data/lib/action_cable/connection/identification.rb +9 -5
- data/lib/action_cable/connection/internal_channel.rb +5 -2
- data/lib/action_cable/connection/message_buffer.rb +4 -1
- data/lib/action_cable/connection/stream.rb +2 -0
- data/lib/action_cable/connection/stream_event_loop.rb +4 -3
- data/lib/action_cable/connection/subscriptions.rb +6 -3
- data/lib/action_cable/connection/tagged_logger_proxy.rb +7 -4
- data/lib/action_cable/connection/test_case.rb +66 -59
- data/lib/action_cable/connection/web_socket.rb +10 -8
- data/lib/action_cable/deprecator.rb +2 -0
- data/lib/action_cable/engine.rb +5 -3
- data/lib/action_cable/gem_version.rb +5 -3
- data/lib/action_cable/helpers/action_cable_helper.rb +21 -19
- data/lib/action_cable/remote_connections.rb +19 -16
- data/lib/action_cable/server/base.rb +27 -15
- data/lib/action_cable/server/broadcasting.rb +23 -17
- data/lib/action_cable/server/configuration.rb +17 -14
- data/lib/action_cable/server/connections.rb +11 -5
- data/lib/action_cable/server/worker/active_record_connection_management.rb +2 -0
- data/lib/action_cable/server/worker.rb +4 -2
- data/lib/action_cable/subscription_adapter/async.rb +2 -0
- data/lib/action_cable/subscription_adapter/base.rb +2 -0
- data/lib/action_cable/subscription_adapter/channel_prefix.rb +2 -0
- data/lib/action_cable/subscription_adapter/inline.rb +2 -0
- data/lib/action_cable/subscription_adapter/postgresql.rb +7 -6
- data/lib/action_cable/subscription_adapter/redis.rb +5 -2
- data/lib/action_cable/subscription_adapter/subscriber_map.rb +2 -0
- data/lib/action_cable/subscription_adapter/test.rb +8 -5
- data/lib/action_cable/test_case.rb +2 -0
- data/lib/action_cable/test_helper.rb +51 -48
- data/lib/action_cable/version.rb +3 -1
- data/lib/action_cable.rb +12 -6
- data/lib/rails/generators/channel/channel_generator.rb +4 -2
- data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
- metadata +11 -11
- /data/lib/rails/generators/channel/templates/application_cable/{channel.rb → channel.rb.tt} +0 -0
- /data/lib/rails/generators/channel/templates/application_cable/{connection.rb → connection.rb.tt} +0 -0
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 88ac9e42c67cca041d0460a69d0c15d4ae29a219701f121f04204a70b750c038
|
|
4
|
+
data.tar.gz: 2a6d999d4e3249b3b0bc8ea7434df42de661b18b34242ab2b35c4971d0701baa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 745a91a5943da893641b66f9246657695e3ddbfb8713e1d258612d59f628d5680f86c8e069be0530cad1209d9447b7aecd2833d9ee89eefaf3d2a28084fa4a64
|
|
7
|
+
data.tar.gz: e7948ddcc1ff1a16ec75a80f4e3c3dbed53037eaac8211eb79df326136f65b15c493b3614ca9f36230548ebfa117af4323c425ac17a7d0b298b71ee2ac65312b
|
data/CHANGELOG.md
CHANGED
|
@@ -1,188 +1,91 @@
|
|
|
1
|
-
## Rails 7.
|
|
1
|
+
## Rails 7.2.3 (October 28, 2025) ##
|
|
2
2
|
|
|
3
3
|
* Fixed compatibility with `redis` gem `5.4.1`
|
|
4
4
|
|
|
5
5
|
*Jean Boussier*
|
|
6
6
|
|
|
7
|
+
* Fixed a possible race condition in `stream_from`.
|
|
7
8
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
* No changes.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
## Rails 7.1.5.1 (December 10, 2024) ##
|
|
14
|
-
|
|
15
|
-
* No changes.
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
## Rails 7.1.5 (October 30, 2024) ##
|
|
19
|
-
|
|
20
|
-
* No changes.
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
## Rails 7.1.4.2 (October 23, 2024) ##
|
|
24
|
-
|
|
25
|
-
* No changes.
|
|
9
|
+
*OuYangJinTing*
|
|
26
10
|
|
|
11
|
+
* Ensure the Postgresql adapter always use a dedicated connection even during system tests.
|
|
27
12
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
* No changes.
|
|
13
|
+
Fix an issue with the Action Cable Postgresql adapter causing deadlock or various weird
|
|
14
|
+
pg client error during system tests.
|
|
31
15
|
|
|
32
|
-
|
|
33
|
-
## Rails 7.1.4 (August 22, 2024) ##
|
|
34
|
-
|
|
35
|
-
* No changes.
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
## Rails 7.1.3.4 (June 04, 2024) ##
|
|
39
|
-
|
|
40
|
-
* No changes.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
## Rails 7.1.3.3 (May 16, 2024) ##
|
|
44
|
-
|
|
45
|
-
* No changes.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
## Rails 7.1.3.2 (February 21, 2024) ##
|
|
49
|
-
|
|
50
|
-
* No changes.
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
## Rails 7.1.3.1 (February 21, 2024) ##
|
|
54
|
-
|
|
55
|
-
* No changes.
|
|
16
|
+
*Jean Boussier*
|
|
56
17
|
|
|
57
18
|
|
|
58
|
-
## Rails 7.
|
|
19
|
+
## Rails 7.2.2.2 (August 13, 2025) ##
|
|
59
20
|
|
|
60
21
|
* No changes.
|
|
61
22
|
|
|
62
23
|
|
|
63
|
-
## Rails 7.
|
|
24
|
+
## Rails 7.2.2.1 (December 10, 2024) ##
|
|
64
25
|
|
|
65
26
|
* No changes.
|
|
66
27
|
|
|
67
28
|
|
|
68
|
-
## Rails 7.
|
|
29
|
+
## Rails 7.2.2 (October 30, 2024) ##
|
|
69
30
|
|
|
70
31
|
* No changes.
|
|
71
32
|
|
|
72
33
|
|
|
73
|
-
## Rails 7.1.
|
|
34
|
+
## Rails 7.2.1.2 (October 23, 2024) ##
|
|
74
35
|
|
|
75
36
|
* No changes.
|
|
76
37
|
|
|
77
38
|
|
|
78
|
-
## Rails 7.1.
|
|
39
|
+
## Rails 7.2.1.1 (October 15, 2024) ##
|
|
79
40
|
|
|
80
41
|
* No changes.
|
|
81
42
|
|
|
82
43
|
|
|
83
|
-
## Rails 7.1
|
|
44
|
+
## Rails 7.2.1 (August 22, 2024) ##
|
|
84
45
|
|
|
85
46
|
* No changes.
|
|
86
47
|
|
|
87
48
|
|
|
88
|
-
## Rails 7.
|
|
89
|
-
|
|
90
|
-
* Add a `@server` instance variable referencing the `ActionCable.server`
|
|
91
|
-
singleton to `ActionCable::Channel::ConnectionStub`
|
|
92
|
-
|
|
93
|
-
This lets us delegate the `pubsub` and `config` method calls
|
|
94
|
-
to the server. This fixes `NoMethodError` errors when testing
|
|
95
|
-
channel logic that call `pubsub` (e.g. `stop_stream_for`).
|
|
96
|
-
|
|
97
|
-
*Julian Foo*
|
|
49
|
+
## Rails 7.2.0 (August 09, 2024) ##
|
|
98
50
|
|
|
99
|
-
*
|
|
100
|
-
mount a given health check rack app on a given path.
|
|
101
|
-
Useful when mounting Action Cable standalone.
|
|
51
|
+
* Bring `ActionCable::Connection::TestCookieJar` in alignment with `ActionDispatch::Cookies::CookieJar` in regards to setting the cookie value.
|
|
102
52
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
* Introduce the `capture_broadcasts` test helper.
|
|
106
|
-
|
|
107
|
-
Returns all messages broadcast in a block.
|
|
53
|
+
Before:
|
|
108
54
|
|
|
109
55
|
```ruby
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
ActionCable.server.broadcast "test", { message: "two" }
|
|
113
|
-
end
|
|
114
|
-
assert_equal 2, messages.length
|
|
115
|
-
assert_equal({ "message" => "one" }, messages.first)
|
|
116
|
-
assert_equal({ "message" => "two" }, messages.last)
|
|
56
|
+
cookies[:foo] = { value: "bar" }
|
|
57
|
+
puts cookies[:foo] # => { value: "bar" }
|
|
117
58
|
```
|
|
118
59
|
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
* Display broadcasted messages on error message when using `assert_broadcast_on`
|
|
122
|
-
|
|
123
|
-
*Stéphane Robino*
|
|
124
|
-
|
|
125
|
-
* The Action Cable client now supports subprotocols to allow passing arbitrary data
|
|
126
|
-
to the server.
|
|
127
|
-
|
|
128
|
-
```js
|
|
129
|
-
const consumer = ActionCable.createConsumer()
|
|
130
|
-
|
|
131
|
-
consumer.addSubProtocol('custom-protocol')
|
|
60
|
+
After:
|
|
132
61
|
|
|
133
|
-
|
|
62
|
+
```ruby
|
|
63
|
+
cookies[:foo] = { value: "bar" }
|
|
64
|
+
puts cookies[:foo] # => "bar"
|
|
134
65
|
```
|
|
135
66
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols
|
|
67
|
+
*Justin Ko*
|
|
139
68
|
|
|
140
|
-
|
|
69
|
+
* Record ping on every Action Cable message.
|
|
141
70
|
|
|
142
|
-
|
|
71
|
+
Previously only `ping` and `welcome` message types were keeping the connection active.
|
|
72
|
+
Now every Action Cable message updates the `pingedAt` value, preventing the connection
|
|
73
|
+
from being marked as stale.
|
|
143
74
|
|
|
144
|
-
*
|
|
75
|
+
*yauhenininjia*
|
|
145
76
|
|
|
146
|
-
*
|
|
147
|
-
|
|
77
|
+
* Add two new assertion methods for Action Cable test cases: `assert_has_no_stream`
|
|
78
|
+
and `assert_has_no_stream_for`.
|
|
148
79
|
|
|
149
|
-
|
|
150
|
-
|
|
80
|
+
These methods can be used to assert that a stream has been stopped, e.g. via
|
|
81
|
+
`stop_stream` or `stop_stream_for`. They complement the already existing
|
|
82
|
+
`assert_has_stream` and `assert_has_stream_for` methods.
|
|
151
83
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
...
|
|
156
|
-
} else {
|
|
157
|
-
...
|
|
158
|
-
}
|
|
159
|
-
}
|
|
160
|
-
})
|
|
84
|
+
```ruby
|
|
85
|
+
assert_has_no_stream "messages"
|
|
86
|
+
assert_has_no_stream_for User.find(42)
|
|
161
87
|
```
|
|
162
88
|
|
|
163
|
-
*
|
|
164
|
-
|
|
165
|
-
* The Redis adapter is now compatible with redis-rb 5.0
|
|
166
|
-
|
|
167
|
-
Compatibility with redis-rb 3.x was dropped.
|
|
168
|
-
|
|
169
|
-
*Jean Boussier*
|
|
170
|
-
|
|
171
|
-
* The Action Cable server is now mounted with `anchor: true`.
|
|
172
|
-
|
|
173
|
-
This means that routes that also start with `/cable` will no longer clash with Action Cable.
|
|
174
|
-
|
|
175
|
-
*Alex Ghiculescu*
|
|
176
|
-
|
|
177
|
-
* `ActionCable.server.remote_connections.where(...).disconnect` now sends `disconnect` message
|
|
178
|
-
before closing the connection with the reconnection strategy specified (defaults to `true`).
|
|
179
|
-
|
|
180
|
-
*Vladimir Dementyev*
|
|
181
|
-
|
|
182
|
-
* Added command callbacks to `ActionCable::Connection::Base`.
|
|
183
|
-
|
|
184
|
-
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.
|
|
185
|
-
|
|
186
|
-
*Vladimir Dementyev*
|
|
89
|
+
*Sebastian Pöll*, *Junichi Sato*
|
|
187
90
|
|
|
188
|
-
Please check [7-
|
|
91
|
+
Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/actioncable/CHANGELOG.md) for previous changes.
|
data/README.md
CHANGED
|
@@ -19,6 +19,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
|
|
|
19
19
|
|
|
20
20
|
* https://github.com/rails/rails/issues
|
|
21
21
|
|
|
22
|
-
Feature requests should be discussed on the
|
|
22
|
+
Feature requests should be discussed on the rubyonrails-core forum here:
|
|
23
23
|
|
|
24
24
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
|
@@ -43,12 +43,11 @@
|
|
|
43
43
|
isRunning() {
|
|
44
44
|
return this.startedAt && !this.stoppedAt;
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
recordMessage() {
|
|
47
47
|
this.pingedAt = now();
|
|
48
48
|
}
|
|
49
49
|
recordConnect() {
|
|
50
50
|
this.reconnectAttempts = 0;
|
|
51
|
-
this.recordPing();
|
|
52
51
|
delete this.disconnectedAt;
|
|
53
52
|
logger.log("ConnectionMonitor recorded connect");
|
|
54
53
|
}
|
|
@@ -236,6 +235,7 @@
|
|
|
236
235
|
return;
|
|
237
236
|
}
|
|
238
237
|
const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
|
|
238
|
+
this.monitor.recordMessage();
|
|
239
239
|
switch (type) {
|
|
240
240
|
case message_types.welcome:
|
|
241
241
|
if (this.triedToReconnect()) {
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
});
|
|
252
252
|
|
|
253
253
|
case message_types.ping:
|
|
254
|
-
return
|
|
254
|
+
return null;
|
|
255
255
|
|
|
256
256
|
case message_types.confirmation:
|
|
257
257
|
this.subscriptions.confirmSubscription(identifier);
|
|
@@ -42,12 +42,11 @@ class ConnectionMonitor {
|
|
|
42
42
|
isRunning() {
|
|
43
43
|
return this.startedAt && !this.stoppedAt;
|
|
44
44
|
}
|
|
45
|
-
|
|
45
|
+
recordMessage() {
|
|
46
46
|
this.pingedAt = now();
|
|
47
47
|
}
|
|
48
48
|
recordConnect() {
|
|
49
49
|
this.reconnectAttempts = 0;
|
|
50
|
-
this.recordPing();
|
|
51
50
|
delete this.disconnectedAt;
|
|
52
51
|
logger.log("ConnectionMonitor recorded connect");
|
|
53
52
|
}
|
|
@@ -244,6 +243,7 @@ Connection.prototype.events = {
|
|
|
244
243
|
return;
|
|
245
244
|
}
|
|
246
245
|
const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
|
|
246
|
+
this.monitor.recordMessage();
|
|
247
247
|
switch (type) {
|
|
248
248
|
case message_types.welcome:
|
|
249
249
|
if (this.triedToReconnect()) {
|
|
@@ -259,7 +259,7 @@ Connection.prototype.events = {
|
|
|
259
259
|
});
|
|
260
260
|
|
|
261
261
|
case message_types.ping:
|
|
262
|
-
return
|
|
262
|
+
return null;
|
|
263
263
|
|
|
264
264
|
case message_types.confirmation:
|
|
265
265
|
this.subscriptions.confirmSubscription(identifier);
|
|
@@ -43,12 +43,11 @@
|
|
|
43
43
|
isRunning() {
|
|
44
44
|
return this.startedAt && !this.stoppedAt;
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
recordMessage() {
|
|
47
47
|
this.pingedAt = now();
|
|
48
48
|
}
|
|
49
49
|
recordConnect() {
|
|
50
50
|
this.reconnectAttempts = 0;
|
|
51
|
-
this.recordPing();
|
|
52
51
|
delete this.disconnectedAt;
|
|
53
52
|
logger.log("ConnectionMonitor recorded connect");
|
|
54
53
|
}
|
|
@@ -236,6 +235,7 @@
|
|
|
236
235
|
return;
|
|
237
236
|
}
|
|
238
237
|
const {identifier: identifier, message: message, reason: reason, reconnect: reconnect, type: type} = JSON.parse(event.data);
|
|
238
|
+
this.monitor.recordMessage();
|
|
239
239
|
switch (type) {
|
|
240
240
|
case message_types.welcome:
|
|
241
241
|
if (this.triedToReconnect()) {
|
|
@@ -251,7 +251,7 @@
|
|
|
251
251
|
});
|
|
252
252
|
|
|
253
253
|
case message_types.ping:
|
|
254
|
-
return
|
|
254
|
+
return null;
|
|
255
255
|
|
|
256
256
|
case message_types.confirmation:
|
|
257
257
|
this.subscriptions.confirmSubscription(identifier);
|
|
@@ -1,102 +1,112 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
3
5
|
require "set"
|
|
4
6
|
require "active_support/rescuable"
|
|
5
7
|
require "active_support/parameter_filter"
|
|
6
8
|
|
|
7
9
|
module ActionCable
|
|
8
10
|
module Channel
|
|
9
|
-
#
|
|
11
|
+
# # Action Cable Channel Base
|
|
10
12
|
#
|
|
11
|
-
# The channel provides the basic structure of grouping behavior into logical
|
|
12
|
-
#
|
|
13
|
-
#
|
|
13
|
+
# The channel provides the basic structure of grouping behavior into logical
|
|
14
|
+
# units when communicating over the WebSocket connection. You can think of a
|
|
15
|
+
# channel like a form of controller, but one that's capable of pushing content
|
|
16
|
+
# to the subscriber in addition to simply responding to the subscriber's direct
|
|
17
|
+
# requests.
|
|
14
18
|
#
|
|
15
|
-
# Channel instances are long-lived. A channel object will be instantiated when
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
+
# Channel instances are long-lived. A channel object will be instantiated when
|
|
20
|
+
# the cable consumer becomes a subscriber, and then lives until the consumer
|
|
21
|
+
# disconnects. This may be seconds, minutes, hours, or even days. That means you
|
|
22
|
+
# have to take special care not to do anything silly in a channel that would
|
|
23
|
+
# balloon its memory footprint or whatever. The references are forever, so they
|
|
24
|
+
# won't be released as is normally the case with a controller instance that gets
|
|
25
|
+
# thrown away after every request.
|
|
19
26
|
#
|
|
20
|
-
# Long-lived channels (and connections) also mean you're responsible for
|
|
21
|
-
#
|
|
27
|
+
# Long-lived channels (and connections) also mean you're responsible for
|
|
28
|
+
# ensuring that the data is fresh. If you hold a reference to a user record, but
|
|
29
|
+
# the name is changed while that reference is held, you may be sending stale
|
|
30
|
+
# data if you don't take precautions to avoid it.
|
|
22
31
|
#
|
|
23
|
-
# The upside of long-lived channel instances is that you can use instance
|
|
24
|
-
#
|
|
32
|
+
# The upside of long-lived channel instances is that you can use instance
|
|
33
|
+
# variables to keep reference to objects that future subscriber requests can
|
|
34
|
+
# interact with. Here's a quick example:
|
|
25
35
|
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
36
|
+
# class ChatChannel < ApplicationCable::Channel
|
|
37
|
+
# def subscribed
|
|
38
|
+
# @room = Chat::Room[params[:room_number]]
|
|
39
|
+
# end
|
|
30
40
|
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
41
|
+
# def speak(data)
|
|
42
|
+
# @room.speak data, user: current_user
|
|
43
|
+
# end
|
|
33
44
|
# end
|
|
34
|
-
# end
|
|
35
45
|
#
|
|
36
|
-
# The #speak action simply uses the Chat::Room object that was created when the
|
|
37
|
-
#
|
|
46
|
+
# The #speak action simply uses the Chat::Room object that was created when the
|
|
47
|
+
# channel was first subscribed to by the consumer when that subscriber wants to
|
|
48
|
+
# say something in the room.
|
|
38
49
|
#
|
|
39
|
-
#
|
|
50
|
+
# ## Action processing
|
|
40
51
|
#
|
|
41
52
|
# Unlike subclasses of ActionController::Base, channels do not follow a RESTful
|
|
42
53
|
# constraint form for their actions. Instead, Action Cable operates through a
|
|
43
|
-
# remote-procedure call model. You can declare any public method on the
|
|
44
|
-
#
|
|
45
|
-
#
|
|
54
|
+
# remote-procedure call model. You can declare any public method on the channel
|
|
55
|
+
# (optionally taking a `data` argument), and this method is automatically
|
|
56
|
+
# exposed as callable to the client.
|
|
46
57
|
#
|
|
47
58
|
# Example:
|
|
48
59
|
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
# def unsubscribed
|
|
55
|
-
# current_user.disappear @connection_token
|
|
56
|
-
# end
|
|
60
|
+
# class AppearanceChannel < ApplicationCable::Channel
|
|
61
|
+
# def subscribed
|
|
62
|
+
# @connection_token = generate_connection_token
|
|
63
|
+
# end
|
|
57
64
|
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
65
|
+
# def unsubscribed
|
|
66
|
+
# current_user.disappear @connection_token
|
|
67
|
+
# end
|
|
61
68
|
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
69
|
+
# def appear(data)
|
|
70
|
+
# current_user.appear @connection_token, on: data['appearing_on']
|
|
71
|
+
# end
|
|
65
72
|
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# SecureRandom.hex(36)
|
|
73
|
+
# def away
|
|
74
|
+
# current_user.away @connection_token
|
|
69
75
|
# end
|
|
70
|
-
# end
|
|
71
76
|
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
77
|
+
# private
|
|
78
|
+
# def generate_connection_token
|
|
79
|
+
# SecureRandom.hex(36)
|
|
80
|
+
# end
|
|
81
|
+
# end
|
|
82
|
+
#
|
|
83
|
+
# In this example, the subscribed and unsubscribed methods are not callable
|
|
84
|
+
# methods, as they were already declared in ActionCable::Channel::Base, but
|
|
85
|
+
# `#appear` and `#away` are. `#generate_connection_token` is also not callable,
|
|
86
|
+
# since it's a private method. You'll see that appear accepts a data parameter,
|
|
87
|
+
# which it then uses as part of its model call. `#away` does not, since it's
|
|
88
|
+
# simply a trigger action.
|
|
78
89
|
#
|
|
79
|
-
# Also note that in this example,
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
90
|
+
# Also note that in this example, `current_user` is available because it was
|
|
91
|
+
# marked as an identifying attribute on the connection. All such identifiers
|
|
92
|
+
# will automatically create a delegation method of the same name on the channel
|
|
93
|
+
# instance.
|
|
83
94
|
#
|
|
84
|
-
#
|
|
95
|
+
# ## Rejecting subscription requests
|
|
85
96
|
#
|
|
86
97
|
# A channel can reject a subscription request in the #subscribed callback by
|
|
87
98
|
# invoking the #reject method:
|
|
88
99
|
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
100
|
+
# class ChatChannel < ApplicationCable::Channel
|
|
101
|
+
# def subscribed
|
|
102
|
+
# @room = Chat::Room[params[:room_number]]
|
|
103
|
+
# reject unless current_user.can_access?(@room)
|
|
104
|
+
# end
|
|
93
105
|
# end
|
|
94
|
-
# end
|
|
95
106
|
#
|
|
96
|
-
# In this example, the subscription will be rejected if the
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
# the server rejects the subscription request.
|
|
107
|
+
# In this example, the subscription will be rejected if the `current_user` does
|
|
108
|
+
# not have access to the chat room. On the client-side, the `Channel#rejected`
|
|
109
|
+
# callback will get invoked when the server rejects the subscription request.
|
|
100
110
|
class Base
|
|
101
111
|
include Callbacks
|
|
102
112
|
include PeriodicTimers
|
|
@@ -109,14 +119,13 @@ module ActionCable
|
|
|
109
119
|
delegate :logger, to: :connection
|
|
110
120
|
|
|
111
121
|
class << self
|
|
112
|
-
# A list of method names that should be considered actions. This
|
|
113
|
-
#
|
|
114
|
-
# any
|
|
115
|
-
#
|
|
116
|
-
# itself.
|
|
122
|
+
# A list of method names that should be considered actions. This includes all
|
|
123
|
+
# public instance methods on a channel, less any internal methods (defined on
|
|
124
|
+
# Base), adding back in any methods that are internal, but still exist on the
|
|
125
|
+
# class itself.
|
|
117
126
|
#
|
|
118
|
-
#
|
|
119
|
-
# *
|
|
127
|
+
# #### Returns
|
|
128
|
+
# * `Set` - A set of all methods that should be considered actions.
|
|
120
129
|
def action_methods
|
|
121
130
|
@action_methods ||= begin
|
|
122
131
|
# All public instance methods of this class, including ancestors
|
|
@@ -130,9 +139,9 @@ module ActionCable
|
|
|
130
139
|
end
|
|
131
140
|
|
|
132
141
|
private
|
|
133
|
-
# action_methods are cached and there is sometimes need to refresh
|
|
134
|
-
#
|
|
135
|
-
#
|
|
142
|
+
# action_methods are cached and there is sometimes need to refresh them.
|
|
143
|
+
# ::clear_action_methods! allows you to do that, so next time you run
|
|
144
|
+
# action_methods, they will be recalculated.
|
|
136
145
|
def clear_action_methods! # :doc:
|
|
137
146
|
@action_methods = nil
|
|
138
147
|
end
|
|
@@ -157,13 +166,14 @@ module ActionCable
|
|
|
157
166
|
|
|
158
167
|
@reject_subscription = nil
|
|
159
168
|
@subscription_confirmation_sent = nil
|
|
169
|
+
@unsubscribed = false
|
|
160
170
|
|
|
161
171
|
delegate_connection_identifiers
|
|
162
172
|
end
|
|
163
173
|
|
|
164
|
-
# Extract the action name from the passed data and process it via the channel.
|
|
165
|
-
# that the action requested is a public method on the
|
|
166
|
-
# like #subscribed).
|
|
174
|
+
# Extract the action name from the passed data and process it via the channel.
|
|
175
|
+
# The process will ensure that the action requested is a public method on the
|
|
176
|
+
# channel declared by the user (so not one of the callbacks like #subscribed).
|
|
167
177
|
def perform_action(data)
|
|
168
178
|
action = extract_action(data)
|
|
169
179
|
|
|
@@ -177,8 +187,8 @@ module ActionCable
|
|
|
177
187
|
end
|
|
178
188
|
end
|
|
179
189
|
|
|
180
|
-
# This method is called after subscription has been added to the connection
|
|
181
|
-
#
|
|
190
|
+
# This method is called after subscription has been added to the connection and
|
|
191
|
+
# confirms or rejects the subscription.
|
|
182
192
|
def subscribe_to_channel
|
|
183
193
|
run_callbacks :subscribe do
|
|
184
194
|
subscribed
|
|
@@ -188,29 +198,37 @@ module ActionCable
|
|
|
188
198
|
ensure_confirmation_sent
|
|
189
199
|
end
|
|
190
200
|
|
|
191
|
-
# Called by the cable connection when it's cut, so the channel has a chance to
|
|
192
|
-
# This method is not intended to be called directly by
|
|
201
|
+
# Called by the cable connection when it's cut, so the channel has a chance to
|
|
202
|
+
# cleanup with callbacks. This method is not intended to be called directly by
|
|
203
|
+
# the user. Instead, override the #unsubscribed callback.
|
|
193
204
|
def unsubscribe_from_channel # :nodoc:
|
|
205
|
+
@unsubscribed = true
|
|
194
206
|
run_callbacks :unsubscribe do
|
|
195
207
|
unsubscribed
|
|
196
208
|
end
|
|
197
209
|
end
|
|
198
210
|
|
|
211
|
+
def unsubscribed? # :nodoc:
|
|
212
|
+
@unsubscribed
|
|
213
|
+
end
|
|
214
|
+
|
|
199
215
|
private
|
|
200
|
-
# Called once a consumer has become a subscriber of the channel. Usually the
|
|
201
|
-
# you want this channel to be sending to the
|
|
216
|
+
# Called once a consumer has become a subscriber of the channel. Usually the
|
|
217
|
+
# place to set up any streams you want this channel to be sending to the
|
|
218
|
+
# subscriber.
|
|
202
219
|
def subscribed # :doc:
|
|
203
220
|
# Override in subclasses
|
|
204
221
|
end
|
|
205
222
|
|
|
206
|
-
# Called once a consumer has cut its cable connection. Can be used for cleaning
|
|
207
|
-
# users as offline or the like.
|
|
223
|
+
# Called once a consumer has cut its cable connection. Can be used for cleaning
|
|
224
|
+
# up connections or marking users as offline or the like.
|
|
208
225
|
def unsubscribed # :doc:
|
|
209
226
|
# Override in subclasses
|
|
210
227
|
end
|
|
211
228
|
|
|
212
|
-
# Transmit a hash of data to the subscriber. The hash will automatically be
|
|
213
|
-
# the proper channel identifier marked as the
|
|
229
|
+
# Transmit a hash of data to the subscriber. The hash will automatically be
|
|
230
|
+
# wrapped in a JSON envelope with the proper channel identifier marked as the
|
|
231
|
+
# recipient.
|
|
214
232
|
def transmit(data, via: nil) # :doc:
|
|
215
233
|
logger.debug do
|
|
216
234
|
status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
|