actioncable 7.1.5.2 → 8.1.2.1
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 +9 -164
- 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 +115 -90
- data/lib/action_cable/channel/broadcasting.rb +29 -22
- data/lib/action_cable/channel/callbacks.rb +34 -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 +81 -66
- 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 +8 -6
- 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 +5 -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 +6 -4
- data/lib/action_cable/helpers/action_cable_helper.rb +21 -19
- data/lib/action_cable/remote_connections.rb +47 -45
- data/lib/action_cable/server/base.rb +27 -15
- data/lib/action_cable/server/broadcasting.rb +25 -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 +5 -3
- data/lib/action_cable/subscription_adapter/async.rb +2 -0
- data/lib/action_cable/subscription_adapter/base.rb +4 -1
- 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 +13 -5
- 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 +7 -5
- data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
- metadata +12 -12
- /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: c27f8d404e63bd07562b04a3ebe2108fce4c84d0144b0b52966b0a1eaabb7c94
|
|
4
|
+
data.tar.gz: b247a11c1597988efd6c88760af657bffc59caee794fd4946a69068e833a796a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9849f9b34982208eea70213bc006bd1860726e366d76c0942c100edb9107fa20126b35246beda8f27b3d73e6f27e7ea3a95738595247a07d10f6dfa490cea051
|
|
7
|
+
data.tar.gz: bc2a3283c30781e7e6ad93bd4a9f4ce3669fd06899b5bf3d11ab669d1d4de46a975677c5bd066e77cf763cf14d6711b81c0778358296b0215cb432312c250eae
|
data/CHANGELOG.md
CHANGED
|
@@ -1,181 +1,26 @@
|
|
|
1
|
-
## Rails
|
|
1
|
+
## Rails 8.1.2.1 (March 23, 2026) ##
|
|
2
2
|
|
|
3
3
|
* No changes.
|
|
4
4
|
|
|
5
5
|
|
|
6
|
-
## Rails
|
|
6
|
+
## Rails 8.1.2 (January 08, 2026) ##
|
|
7
7
|
|
|
8
8
|
* No changes.
|
|
9
9
|
|
|
10
10
|
|
|
11
|
-
## Rails
|
|
11
|
+
## Rails 8.1.1 (October 28, 2025) ##
|
|
12
12
|
|
|
13
13
|
* No changes.
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
## Rails
|
|
16
|
+
## Rails 8.1.0 (October 22, 2025) ##
|
|
17
17
|
|
|
18
|
-
*
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## Rails 7.1.4.1 (October 15, 2024) ##
|
|
22
|
-
|
|
23
|
-
* No changes.
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
## Rails 7.1.4 (August 22, 2024) ##
|
|
27
|
-
|
|
28
|
-
* No changes.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
## Rails 7.1.3.4 (June 04, 2024) ##
|
|
32
|
-
|
|
33
|
-
* No changes.
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
## Rails 7.1.3.3 (May 16, 2024) ##
|
|
37
|
-
|
|
38
|
-
* No changes.
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
## Rails 7.1.3.2 (February 21, 2024) ##
|
|
42
|
-
|
|
43
|
-
* No changes.
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
## Rails 7.1.3.1 (February 21, 2024) ##
|
|
47
|
-
|
|
48
|
-
* No changes.
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
## Rails 7.1.3 (January 16, 2024) ##
|
|
52
|
-
|
|
53
|
-
* No changes.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
## Rails 7.1.2 (November 10, 2023) ##
|
|
57
|
-
|
|
58
|
-
* No changes.
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
## Rails 7.1.1 (October 11, 2023) ##
|
|
62
|
-
|
|
63
|
-
* No changes.
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
## Rails 7.1.0 (October 05, 2023) ##
|
|
67
|
-
|
|
68
|
-
* No changes.
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
## Rails 7.1.0.rc2 (October 01, 2023) ##
|
|
72
|
-
|
|
73
|
-
* No changes.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
## Rails 7.1.0.rc1 (September 27, 2023) ##
|
|
77
|
-
|
|
78
|
-
* No changes.
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
## Rails 7.1.0.beta1 (September 13, 2023) ##
|
|
82
|
-
|
|
83
|
-
* Add a `@server` instance variable referencing the `ActionCable.server`
|
|
84
|
-
singleton to `ActionCable::Channel::ConnectionStub`
|
|
85
|
-
|
|
86
|
-
This lets us delegate the `pubsub` and `config` method calls
|
|
87
|
-
to the server. This fixes `NoMethodError` errors when testing
|
|
88
|
-
channel logic that call `pubsub` (e.g. `stop_stream_for`).
|
|
89
|
-
|
|
90
|
-
*Julian Foo*
|
|
91
|
-
|
|
92
|
-
* Added `health_check_path` and `health_check_application` config to
|
|
93
|
-
mount a given health check rack app on a given path.
|
|
94
|
-
Useful when mounting Action Cable standalone.
|
|
95
|
-
|
|
96
|
-
*Joé Dupuis*
|
|
97
|
-
|
|
98
|
-
* Introduce the `capture_broadcasts` test helper.
|
|
99
|
-
|
|
100
|
-
Returns all messages broadcast in a block.
|
|
101
|
-
|
|
102
|
-
```ruby
|
|
103
|
-
messages = capture_broadcasts("test") do
|
|
104
|
-
ActionCable.server.broadcast "test", { message: "one" }
|
|
105
|
-
ActionCable.server.broadcast "test", { message: "two" }
|
|
106
|
-
end
|
|
107
|
-
assert_equal 2, messages.length
|
|
108
|
-
assert_equal({ "message" => "one" }, messages.first)
|
|
109
|
-
assert_equal({ "message" => "two" }, messages.last)
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
*Alex Ghiculescu*
|
|
113
|
-
|
|
114
|
-
* Display broadcasted messages on error message when using `assert_broadcast_on`
|
|
115
|
-
|
|
116
|
-
*Stéphane Robino*
|
|
117
|
-
|
|
118
|
-
* The Action Cable client now supports subprotocols to allow passing arbitrary data
|
|
119
|
-
to the server.
|
|
120
|
-
|
|
121
|
-
```js
|
|
122
|
-
const consumer = ActionCable.createConsumer()
|
|
123
|
-
|
|
124
|
-
consumer.addSubProtocol('custom-protocol')
|
|
125
|
-
|
|
126
|
-
consumer.connect()
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
See also:
|
|
130
|
-
|
|
131
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols
|
|
132
|
-
|
|
133
|
-
*Guillaume Hain*
|
|
134
|
-
|
|
135
|
-
* Redis pub/sub adapter now automatically reconnects when Redis connection is lost.
|
|
136
|
-
|
|
137
|
-
*Vladimir Dementyev*
|
|
138
|
-
|
|
139
|
-
* The `connected()` callback can now take a `{reconnected}` parameter to differentiate
|
|
140
|
-
connections from reconnections.
|
|
141
|
-
|
|
142
|
-
```js
|
|
143
|
-
import consumer from "./consumer"
|
|
144
|
-
|
|
145
|
-
consumer.subscriptions.create("ExampleChannel", {
|
|
146
|
-
connected({reconnected}) {
|
|
147
|
-
if (reconnected) {
|
|
148
|
-
...
|
|
149
|
-
} else {
|
|
150
|
-
...
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
})
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
*Mansa Keïta*
|
|
157
|
-
|
|
158
|
-
* The Redis adapter is now compatible with redis-rb 5.0
|
|
159
|
-
|
|
160
|
-
Compatibility with redis-rb 3.x was dropped.
|
|
161
|
-
|
|
162
|
-
*Jean Boussier*
|
|
163
|
-
|
|
164
|
-
* The Action Cable server is now mounted with `anchor: true`.
|
|
165
|
-
|
|
166
|
-
This means that routes that also start with `/cable` will no longer clash with Action Cable.
|
|
167
|
-
|
|
168
|
-
*Alex Ghiculescu*
|
|
169
|
-
|
|
170
|
-
* `ActionCable.server.remote_connections.where(...).disconnect` now sends `disconnect` message
|
|
171
|
-
before closing the connection with the reconnection strategy specified (defaults to `true`).
|
|
172
|
-
|
|
173
|
-
*Vladimir Dementyev*
|
|
18
|
+
* Allow passing composite channels to `ActionCable::Channel#stream_for` – e.g. `stream_for [ group, group.owner ]`
|
|
174
19
|
|
|
175
|
-
*
|
|
20
|
+
*hey-leon*
|
|
176
21
|
|
|
177
|
-
|
|
22
|
+
* Allow setting nil as subscription connection identifier for Redis.
|
|
178
23
|
|
|
179
|
-
*
|
|
24
|
+
*Nguyen Nguyen*
|
|
180
25
|
|
|
181
|
-
Please check [
|
|
26
|
+
Please check [8-0-stable](https://github.com/rails/rails/blob/8-0-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,111 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
# :markup: markdown
|
|
4
|
+
|
|
4
5
|
require "active_support/rescuable"
|
|
5
6
|
require "active_support/parameter_filter"
|
|
6
7
|
|
|
7
8
|
module ActionCable
|
|
8
9
|
module Channel
|
|
9
|
-
#
|
|
10
|
+
# # Action Cable Channel Base
|
|
10
11
|
#
|
|
11
|
-
# The channel provides the basic structure of grouping behavior into logical
|
|
12
|
-
#
|
|
13
|
-
#
|
|
12
|
+
# The channel provides the basic structure of grouping behavior into logical
|
|
13
|
+
# units when communicating over the WebSocket connection. You can think of a
|
|
14
|
+
# channel like a form of controller, but one that's capable of pushing content
|
|
15
|
+
# to the subscriber in addition to simply responding to the subscriber's direct
|
|
16
|
+
# requests.
|
|
14
17
|
#
|
|
15
|
-
# Channel instances are long-lived. A channel object will be instantiated when
|
|
16
|
-
#
|
|
17
|
-
#
|
|
18
|
-
#
|
|
18
|
+
# Channel instances are long-lived. A channel object will be instantiated when
|
|
19
|
+
# the cable consumer becomes a subscriber, and then lives until the consumer
|
|
20
|
+
# disconnects. This may be seconds, minutes, hours, or even days. That means you
|
|
21
|
+
# have to take special care not to do anything silly in a channel that would
|
|
22
|
+
# balloon its memory footprint or whatever. The references are forever, so they
|
|
23
|
+
# won't be released as is normally the case with a controller instance that gets
|
|
24
|
+
# thrown away after every request.
|
|
19
25
|
#
|
|
20
|
-
# Long-lived channels (and connections) also mean you're responsible for
|
|
21
|
-
#
|
|
26
|
+
# Long-lived channels (and connections) also mean you're responsible for
|
|
27
|
+
# ensuring that the data is fresh. If you hold a reference to a user record, but
|
|
28
|
+
# the name is changed while that reference is held, you may be sending stale
|
|
29
|
+
# data if you don't take precautions to avoid it.
|
|
22
30
|
#
|
|
23
|
-
# The upside of long-lived channel instances is that you can use instance
|
|
24
|
-
#
|
|
31
|
+
# The upside of long-lived channel instances is that you can use instance
|
|
32
|
+
# variables to keep reference to objects that future subscriber requests can
|
|
33
|
+
# interact with. Here's a quick example:
|
|
25
34
|
#
|
|
26
|
-
#
|
|
27
|
-
#
|
|
28
|
-
#
|
|
29
|
-
#
|
|
35
|
+
# class ChatChannel < ApplicationCable::Channel
|
|
36
|
+
# def subscribed
|
|
37
|
+
# @room = Chat::Room[params[:room_number]]
|
|
38
|
+
# end
|
|
30
39
|
#
|
|
31
|
-
#
|
|
32
|
-
#
|
|
40
|
+
# def speak(data)
|
|
41
|
+
# @room.speak data, user: current_user
|
|
42
|
+
# end
|
|
33
43
|
# end
|
|
34
|
-
# end
|
|
35
44
|
#
|
|
36
|
-
# The #speak action simply uses the Chat::Room object that was created when the
|
|
37
|
-
#
|
|
45
|
+
# The #speak action simply uses the Chat::Room object that was created when the
|
|
46
|
+
# channel was first subscribed to by the consumer when that subscriber wants to
|
|
47
|
+
# say something in the room.
|
|
38
48
|
#
|
|
39
|
-
#
|
|
49
|
+
# ## Action processing
|
|
40
50
|
#
|
|
41
51
|
# Unlike subclasses of ActionController::Base, channels do not follow a RESTful
|
|
42
52
|
# 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
|
-
#
|
|
53
|
+
# remote-procedure call model. You can declare any public method on the channel
|
|
54
|
+
# (optionally taking a `data` argument), and this method is automatically
|
|
55
|
+
# exposed as callable to the client.
|
|
46
56
|
#
|
|
47
57
|
# Example:
|
|
48
58
|
#
|
|
49
|
-
#
|
|
50
|
-
#
|
|
51
|
-
#
|
|
52
|
-
#
|
|
53
|
-
#
|
|
54
|
-
# def unsubscribed
|
|
55
|
-
# current_user.disappear @connection_token
|
|
56
|
-
# end
|
|
59
|
+
# class AppearanceChannel < ApplicationCable::Channel
|
|
60
|
+
# def subscribed
|
|
61
|
+
# @connection_token = generate_connection_token
|
|
62
|
+
# end
|
|
57
63
|
#
|
|
58
|
-
#
|
|
59
|
-
#
|
|
60
|
-
#
|
|
64
|
+
# def unsubscribed
|
|
65
|
+
# current_user.disappear @connection_token
|
|
66
|
+
# end
|
|
61
67
|
#
|
|
62
|
-
#
|
|
63
|
-
#
|
|
64
|
-
#
|
|
68
|
+
# def appear(data)
|
|
69
|
+
# current_user.appear @connection_token, on: data['appearing_on']
|
|
70
|
+
# end
|
|
65
71
|
#
|
|
66
|
-
#
|
|
67
|
-
#
|
|
68
|
-
# SecureRandom.hex(36)
|
|
72
|
+
# def away
|
|
73
|
+
# current_user.away @connection_token
|
|
69
74
|
# end
|
|
70
|
-
# end
|
|
71
75
|
#
|
|
72
|
-
#
|
|
73
|
-
#
|
|
74
|
-
#
|
|
75
|
-
#
|
|
76
|
-
#
|
|
77
|
-
#
|
|
76
|
+
# private
|
|
77
|
+
# def generate_connection_token
|
|
78
|
+
# SecureRandom.hex(36)
|
|
79
|
+
# end
|
|
80
|
+
# end
|
|
81
|
+
#
|
|
82
|
+
# In this example, the subscribed and unsubscribed methods are not callable
|
|
83
|
+
# methods, as they were already declared in ActionCable::Channel::Base, but
|
|
84
|
+
# `#appear` and `#away` are. `#generate_connection_token` is also not callable,
|
|
85
|
+
# since it's a private method. You'll see that appear accepts a data parameter,
|
|
86
|
+
# which it then uses as part of its model call. `#away` does not, since it's
|
|
87
|
+
# simply a trigger action.
|
|
78
88
|
#
|
|
79
|
-
# Also note that in this example,
|
|
80
|
-
#
|
|
81
|
-
#
|
|
82
|
-
#
|
|
89
|
+
# Also note that in this example, `current_user` is available because it was
|
|
90
|
+
# marked as an identifying attribute on the connection. All such identifiers
|
|
91
|
+
# will automatically create a delegation method of the same name on the channel
|
|
92
|
+
# instance.
|
|
83
93
|
#
|
|
84
|
-
#
|
|
94
|
+
# ## Rejecting subscription requests
|
|
85
95
|
#
|
|
86
96
|
# A channel can reject a subscription request in the #subscribed callback by
|
|
87
97
|
# invoking the #reject method:
|
|
88
98
|
#
|
|
89
|
-
#
|
|
90
|
-
#
|
|
91
|
-
#
|
|
92
|
-
#
|
|
99
|
+
# class ChatChannel < ApplicationCable::Channel
|
|
100
|
+
# def subscribed
|
|
101
|
+
# @room = Chat::Room[params[:room_number]]
|
|
102
|
+
# reject unless current_user.can_access?(@room)
|
|
103
|
+
# end
|
|
93
104
|
# end
|
|
94
|
-
# end
|
|
95
105
|
#
|
|
96
|
-
# In this example, the subscription will be rejected if the
|
|
97
|
-
#
|
|
98
|
-
#
|
|
99
|
-
# the server rejects the subscription request.
|
|
106
|
+
# In this example, the subscription will be rejected if the `current_user` does
|
|
107
|
+
# not have access to the chat room. On the client-side, the `Channel#rejected`
|
|
108
|
+
# callback will get invoked when the server rejects the subscription request.
|
|
100
109
|
class Base
|
|
101
110
|
include Callbacks
|
|
102
111
|
include PeriodicTimers
|
|
@@ -109,14 +118,13 @@ module ActionCable
|
|
|
109
118
|
delegate :logger, to: :connection
|
|
110
119
|
|
|
111
120
|
class << self
|
|
112
|
-
# A list of method names that should be considered actions. This
|
|
113
|
-
#
|
|
114
|
-
# any
|
|
115
|
-
#
|
|
116
|
-
# itself.
|
|
121
|
+
# A list of method names that should be considered actions. This includes all
|
|
122
|
+
# public instance methods on a channel, less any internal methods (defined on
|
|
123
|
+
# Base), adding back in any methods that are internal, but still exist on the
|
|
124
|
+
# class itself.
|
|
117
125
|
#
|
|
118
|
-
#
|
|
119
|
-
# *
|
|
126
|
+
# #### Returns
|
|
127
|
+
# * `Set` - A set of all methods that should be considered actions.
|
|
120
128
|
def action_methods
|
|
121
129
|
@action_methods ||= begin
|
|
122
130
|
# All public instance methods of this class, including ancestors
|
|
@@ -124,15 +132,19 @@ module ActionCable
|
|
|
124
132
|
# Except for public instance methods of Base and its ancestors
|
|
125
133
|
ActionCable::Channel::Base.public_instance_methods(true) +
|
|
126
134
|
# Be sure to include shadowed public instance methods of this class
|
|
127
|
-
public_instance_methods(false)
|
|
135
|
+
public_instance_methods(false) -
|
|
136
|
+
# Except the internal methods
|
|
137
|
+
internal_methods).uniq
|
|
138
|
+
|
|
139
|
+
methods.map!(&:name)
|
|
128
140
|
methods.to_set
|
|
129
141
|
end
|
|
130
142
|
end
|
|
131
143
|
|
|
132
144
|
private
|
|
133
|
-
# action_methods are cached and there is sometimes need to refresh
|
|
134
|
-
#
|
|
135
|
-
#
|
|
145
|
+
# action_methods are cached and there is sometimes need to refresh them.
|
|
146
|
+
# ::clear_action_methods! allows you to do that, so next time you run
|
|
147
|
+
# action_methods, they will be recalculated.
|
|
136
148
|
def clear_action_methods! # :doc:
|
|
137
149
|
@action_methods = nil
|
|
138
150
|
end
|
|
@@ -142,6 +154,10 @@ module ActionCable
|
|
|
142
154
|
super
|
|
143
155
|
clear_action_methods!
|
|
144
156
|
end
|
|
157
|
+
|
|
158
|
+
def internal_methods
|
|
159
|
+
super
|
|
160
|
+
end
|
|
145
161
|
end
|
|
146
162
|
|
|
147
163
|
def initialize(connection, identifier, params = {})
|
|
@@ -157,13 +173,14 @@ module ActionCable
|
|
|
157
173
|
|
|
158
174
|
@reject_subscription = nil
|
|
159
175
|
@subscription_confirmation_sent = nil
|
|
176
|
+
@unsubscribed = false
|
|
160
177
|
|
|
161
178
|
delegate_connection_identifiers
|
|
162
179
|
end
|
|
163
180
|
|
|
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).
|
|
181
|
+
# Extract the action name from the passed data and process it via the channel.
|
|
182
|
+
# The process will ensure that the action requested is a public method on the
|
|
183
|
+
# channel declared by the user (so not one of the callbacks like #subscribed).
|
|
167
184
|
def perform_action(data)
|
|
168
185
|
action = extract_action(data)
|
|
169
186
|
|
|
@@ -177,8 +194,8 @@ module ActionCable
|
|
|
177
194
|
end
|
|
178
195
|
end
|
|
179
196
|
|
|
180
|
-
# This method is called after subscription has been added to the connection
|
|
181
|
-
#
|
|
197
|
+
# This method is called after subscription has been added to the connection and
|
|
198
|
+
# confirms or rejects the subscription.
|
|
182
199
|
def subscribe_to_channel
|
|
183
200
|
run_callbacks :subscribe do
|
|
184
201
|
subscribed
|
|
@@ -188,29 +205,37 @@ module ActionCable
|
|
|
188
205
|
ensure_confirmation_sent
|
|
189
206
|
end
|
|
190
207
|
|
|
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
|
|
208
|
+
# Called by the cable connection when it's cut, so the channel has a chance to
|
|
209
|
+
# cleanup with callbacks. This method is not intended to be called directly by
|
|
210
|
+
# the user. Instead, override the #unsubscribed callback.
|
|
193
211
|
def unsubscribe_from_channel # :nodoc:
|
|
212
|
+
@unsubscribed = true
|
|
194
213
|
run_callbacks :unsubscribe do
|
|
195
214
|
unsubscribed
|
|
196
215
|
end
|
|
197
216
|
end
|
|
198
217
|
|
|
218
|
+
def unsubscribed? # :nodoc:
|
|
219
|
+
@unsubscribed
|
|
220
|
+
end
|
|
221
|
+
|
|
199
222
|
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
|
|
223
|
+
# Called once a consumer has become a subscriber of the channel. Usually the
|
|
224
|
+
# place to set up any streams you want this channel to be sending to the
|
|
225
|
+
# subscriber.
|
|
202
226
|
def subscribed # :doc:
|
|
203
227
|
# Override in subclasses
|
|
204
228
|
end
|
|
205
229
|
|
|
206
|
-
# Called once a consumer has cut its cable connection. Can be used for cleaning
|
|
207
|
-
# users as offline or the like.
|
|
230
|
+
# Called once a consumer has cut its cable connection. Can be used for cleaning
|
|
231
|
+
# up connections or marking users as offline or the like.
|
|
208
232
|
def unsubscribed # :doc:
|
|
209
233
|
# Override in subclasses
|
|
210
234
|
end
|
|
211
235
|
|
|
212
|
-
# Transmit a hash of data to the subscriber. The hash will automatically be
|
|
213
|
-
# the proper channel identifier marked as the
|
|
236
|
+
# Transmit a hash of data to the subscriber. The hash will automatically be
|
|
237
|
+
# wrapped in a JSON envelope with the proper channel identifier marked as the
|
|
238
|
+
# recipient.
|
|
214
239
|
def transmit(data, via: nil) # :doc:
|
|
215
240
|
logger.debug do
|
|
216
241
|
status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
|
|
@@ -297,7 +322,7 @@ module ActionCable
|
|
|
297
322
|
unless subscription_confirmation_sent?
|
|
298
323
|
logger.debug "#{self.class.name} is transmitting the subscription confirmation"
|
|
299
324
|
|
|
300
|
-
ActiveSupport::Notifications.instrument("transmit_subscription_confirmation.action_cable", channel_class: self.class.name) do
|
|
325
|
+
ActiveSupport::Notifications.instrument("transmit_subscription_confirmation.action_cable", channel_class: self.class.name, identifier: @identifier) do
|
|
301
326
|
connection.transmit identifier: @identifier, type: ActionCable::INTERNAL[:message_types][:confirmation]
|
|
302
327
|
@subscription_confirmation_sent = true
|
|
303
328
|
end
|
|
@@ -312,7 +337,7 @@ module ActionCable
|
|
|
312
337
|
def transmit_subscription_rejection
|
|
313
338
|
logger.debug "#{self.class.name} is transmitting the subscription rejection"
|
|
314
339
|
|
|
315
|
-
ActiveSupport::Notifications.instrument("transmit_subscription_rejection.action_cable", channel_class: self.class.name) do
|
|
340
|
+
ActiveSupport::Notifications.instrument("transmit_subscription_rejection.action_cable", channel_class: self.class.name, identifier: @identifier) do
|
|
316
341
|
connection.transmit identifier: @identifier, type: ActionCable::INTERNAL[:message_types][:rejection]
|
|
317
342
|
end
|
|
318
343
|
end
|