actioncable 7.1.5.1 → 7.2.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 +30 -138
- 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 +98 -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 +77 -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 -56
- 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 +4 -2
- 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: 720238323d1411f0913bcc9b65ac89d955c4fc4e708bbab5b5dd1c0c47c4cf00
|
4
|
+
data.tar.gz: 10a5cd05102adeda05b151a39c40183bc55c2c55c2c63dca5a13d642fcf49f58
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: '0639a73b1efc8078fe167814911d5694e76781f618ac7fe5bb34ae8448ab495b60f598a76c6a0e8d7c9f45d9674d19564ad14eaae0ccaaf1f75b441d1318d7c8'
|
7
|
+
data.tar.gz: 4988a92b2c683bc86c28d60be1d2f92d1efe36bc5d33f814da55ac7328af60597fb0313321aa663b6d5632ecd565661725d12bf1238fa48a153b18d538e6e08e
|
data/CHANGELOG.md
CHANGED
@@ -1,176 +1,68 @@
|
|
1
|
-
## Rails 7.
|
1
|
+
## Rails 7.2.2.1 (December 10, 2024) ##
|
2
2
|
|
3
3
|
* No changes.
|
4
4
|
|
5
5
|
|
6
|
-
## Rails 7.
|
6
|
+
## Rails 7.2.2 (October 30, 2024) ##
|
7
7
|
|
8
8
|
* No changes.
|
9
9
|
|
10
10
|
|
11
|
-
## Rails 7.1.
|
11
|
+
## Rails 7.2.1.2 (October 23, 2024) ##
|
12
12
|
|
13
13
|
* No changes.
|
14
14
|
|
15
15
|
|
16
|
-
## Rails 7.1.
|
16
|
+
## Rails 7.2.1.1 (October 15, 2024) ##
|
17
17
|
|
18
18
|
* No changes.
|
19
19
|
|
20
20
|
|
21
|
-
## Rails 7.1
|
21
|
+
## Rails 7.2.1 (August 22, 2024) ##
|
22
22
|
|
23
23
|
* No changes.
|
24
24
|
|
25
25
|
|
26
|
-
## Rails 7.
|
26
|
+
## Rails 7.2.0 (August 09, 2024) ##
|
27
27
|
|
28
|
-
*
|
29
|
-
|
30
|
-
|
31
|
-
## Rails 7.1.3.3 (May 16, 2024) ##
|
32
|
-
|
33
|
-
* No changes.
|
34
|
-
|
35
|
-
|
36
|
-
## Rails 7.1.3.2 (February 21, 2024) ##
|
37
|
-
|
38
|
-
* No changes.
|
39
|
-
|
40
|
-
|
41
|
-
## Rails 7.1.3.1 (February 21, 2024) ##
|
42
|
-
|
43
|
-
* No changes.
|
44
|
-
|
45
|
-
|
46
|
-
## Rails 7.1.3 (January 16, 2024) ##
|
47
|
-
|
48
|
-
* No changes.
|
49
|
-
|
50
|
-
|
51
|
-
## Rails 7.1.2 (November 10, 2023) ##
|
52
|
-
|
53
|
-
* No changes.
|
54
|
-
|
55
|
-
|
56
|
-
## Rails 7.1.1 (October 11, 2023) ##
|
57
|
-
|
58
|
-
* No changes.
|
59
|
-
|
60
|
-
|
61
|
-
## Rails 7.1.0 (October 05, 2023) ##
|
62
|
-
|
63
|
-
* No changes.
|
64
|
-
|
65
|
-
|
66
|
-
## Rails 7.1.0.rc2 (October 01, 2023) ##
|
67
|
-
|
68
|
-
* No changes.
|
69
|
-
|
70
|
-
|
71
|
-
## Rails 7.1.0.rc1 (September 27, 2023) ##
|
72
|
-
|
73
|
-
* No changes.
|
74
|
-
|
75
|
-
|
76
|
-
## Rails 7.1.0.beta1 (September 13, 2023) ##
|
77
|
-
|
78
|
-
* Add a `@server` instance variable referencing the `ActionCable.server`
|
79
|
-
singleton to `ActionCable::Channel::ConnectionStub`
|
28
|
+
* Bring `ActionCable::Connection::TestCookieJar` in alignment with `ActionDispatch::Cookies::CookieJar` in regards to setting the cookie value.
|
80
29
|
|
81
|
-
|
82
|
-
to the server. This fixes `NoMethodError` errors when testing
|
83
|
-
channel logic that call `pubsub` (e.g. `stop_stream_for`).
|
84
|
-
|
85
|
-
*Julian Foo*
|
86
|
-
|
87
|
-
* Added `health_check_path` and `health_check_application` config to
|
88
|
-
mount a given health check rack app on a given path.
|
89
|
-
Useful when mounting Action Cable standalone.
|
90
|
-
|
91
|
-
*Joé Dupuis*
|
92
|
-
|
93
|
-
* Introduce the `capture_broadcasts` test helper.
|
94
|
-
|
95
|
-
Returns all messages broadcast in a block.
|
30
|
+
Before:
|
96
31
|
|
97
32
|
```ruby
|
98
|
-
|
99
|
-
|
100
|
-
ActionCable.server.broadcast "test", { message: "two" }
|
101
|
-
end
|
102
|
-
assert_equal 2, messages.length
|
103
|
-
assert_equal({ "message" => "one" }, messages.first)
|
104
|
-
assert_equal({ "message" => "two" }, messages.last)
|
33
|
+
cookies[:foo] = { value: "bar" }
|
34
|
+
puts cookies[:foo] # => { value: "bar" }
|
105
35
|
```
|
106
36
|
|
107
|
-
|
108
|
-
|
109
|
-
* Display broadcasted messages on error message when using `assert_broadcast_on`
|
110
|
-
|
111
|
-
*Stéphane Robino*
|
112
|
-
|
113
|
-
* The Action Cable client now supports subprotocols to allow passing arbitrary data
|
114
|
-
to the server.
|
37
|
+
After:
|
115
38
|
|
116
|
-
```
|
117
|
-
|
118
|
-
|
119
|
-
consumer.addSubProtocol('custom-protocol')
|
120
|
-
|
121
|
-
consumer.connect()
|
39
|
+
```ruby
|
40
|
+
cookies[:foo] = { value: "bar" }
|
41
|
+
puts cookies[:foo] # => "bar"
|
122
42
|
```
|
123
43
|
|
124
|
-
|
125
|
-
|
126
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols
|
44
|
+
*Justin Ko*
|
127
45
|
|
128
|
-
|
46
|
+
* Record ping on every Action Cable message.
|
129
47
|
|
130
|
-
|
48
|
+
Previously only `ping` and `welcome` message types were keeping the connection active.
|
49
|
+
Now every Action Cable message updates the `pingedAt` value, preventing the connection
|
50
|
+
from being marked as stale.
|
131
51
|
|
132
|
-
*
|
52
|
+
*yauhenininjia*
|
133
53
|
|
134
|
-
*
|
135
|
-
|
54
|
+
* Add two new assertion methods for Action Cable test cases: `assert_has_no_stream`
|
55
|
+
and `assert_has_no_stream_for`.
|
136
56
|
|
137
|
-
|
138
|
-
|
57
|
+
These methods can be used to assert that a stream has been stopped, e.g. via
|
58
|
+
`stop_stream` or `stop_stream_for`. They complement the already existing
|
59
|
+
`assert_has_stream` and `assert_has_stream_for` methods.
|
139
60
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
...
|
144
|
-
} else {
|
145
|
-
...
|
146
|
-
}
|
147
|
-
}
|
148
|
-
})
|
61
|
+
```ruby
|
62
|
+
assert_has_no_stream "messages"
|
63
|
+
assert_has_no_stream_for User.find(42)
|
149
64
|
```
|
150
65
|
|
151
|
-
*
|
152
|
-
|
153
|
-
* The Redis adapter is now compatible with redis-rb 5.0
|
154
|
-
|
155
|
-
Compatibility with redis-rb 3.x was dropped.
|
156
|
-
|
157
|
-
*Jean Boussier*
|
158
|
-
|
159
|
-
* The Action Cable server is now mounted with `anchor: true`.
|
160
|
-
|
161
|
-
This means that routes that also start with `/cable` will no longer clash with Action Cable.
|
162
|
-
|
163
|
-
*Alex Ghiculescu*
|
164
|
-
|
165
|
-
* `ActionCable.server.remote_connections.where(...).disconnect` now sends `disconnect` message
|
166
|
-
before closing the connection with the reconnection strategy specified (defaults to `true`).
|
167
|
-
|
168
|
-
*Vladimir Dementyev*
|
169
|
-
|
170
|
-
* Added command callbacks to `ActionCable::Connection::Base`.
|
171
|
-
|
172
|
-
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.
|
173
|
-
|
174
|
-
*Vladimir Dementyev*
|
66
|
+
*Sebastian Pöll*, *Junichi Sato*
|
175
67
|
|
176
|
-
Please check [7-
|
68
|
+
Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/actioncable/CHANGELOG.md) for previous changes.
|
@@ -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
|
@@ -161,9 +170,9 @@ module ActionCable
|
|
161
170
|
delegate_connection_identifiers
|
162
171
|
end
|
163
172
|
|
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).
|
173
|
+
# Extract the action name from the passed data and process it via the channel.
|
174
|
+
# The process will ensure that the action requested is a public method on the
|
175
|
+
# channel declared by the user (so not one of the callbacks like #subscribed).
|
167
176
|
def perform_action(data)
|
168
177
|
action = extract_action(data)
|
169
178
|
|
@@ -177,8 +186,8 @@ module ActionCable
|
|
177
186
|
end
|
178
187
|
end
|
179
188
|
|
180
|
-
# This method is called after subscription has been added to the connection
|
181
|
-
#
|
189
|
+
# This method is called after subscription has been added to the connection and
|
190
|
+
# confirms or rejects the subscription.
|
182
191
|
def subscribe_to_channel
|
183
192
|
run_callbacks :subscribe do
|
184
193
|
subscribed
|
@@ -188,8 +197,9 @@ module ActionCable
|
|
188
197
|
ensure_confirmation_sent
|
189
198
|
end
|
190
199
|
|
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
|
200
|
+
# Called by the cable connection when it's cut, so the channel has a chance to
|
201
|
+
# cleanup with callbacks. This method is not intended to be called directly by
|
202
|
+
# the user. Instead, override the #unsubscribed callback.
|
193
203
|
def unsubscribe_from_channel # :nodoc:
|
194
204
|
run_callbacks :unsubscribe do
|
195
205
|
unsubscribed
|
@@ -197,20 +207,22 @@ module ActionCable
|
|
197
207
|
end
|
198
208
|
|
199
209
|
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
|
210
|
+
# Called once a consumer has become a subscriber of the channel. Usually the
|
211
|
+
# place to set up any streams you want this channel to be sending to the
|
212
|
+
# subscriber.
|
202
213
|
def subscribed # :doc:
|
203
214
|
# Override in subclasses
|
204
215
|
end
|
205
216
|
|
206
|
-
# Called once a consumer has cut its cable connection. Can be used for cleaning
|
207
|
-
# users as offline or the like.
|
217
|
+
# Called once a consumer has cut its cable connection. Can be used for cleaning
|
218
|
+
# up connections or marking users as offline or the like.
|
208
219
|
def unsubscribed # :doc:
|
209
220
|
# Override in subclasses
|
210
221
|
end
|
211
222
|
|
212
|
-
# Transmit a hash of data to the subscriber. The hash will automatically be
|
213
|
-
# the proper channel identifier marked as the
|
223
|
+
# Transmit a hash of data to the subscriber. The hash will automatically be
|
224
|
+
# wrapped in a JSON envelope with the proper channel identifier marked as the
|
225
|
+
# recipient.
|
214
226
|
def transmit(data, via: nil) # :doc:
|
215
227
|
logger.debug do
|
216
228
|
status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/core_ext/object/to_param"
|
4
6
|
|
5
7
|
module ActionCable
|
@@ -7,36 +9,41 @@ module ActionCable
|
|
7
9
|
module Broadcasting
|
8
10
|
extend ActiveSupport::Concern
|
9
11
|
|
10
|
-
included do
|
11
|
-
delegate :broadcasting_for, :broadcast_to, to: :class
|
12
|
-
end
|
13
|
-
|
14
12
|
module ClassMethods
|
15
|
-
# Broadcast a hash to a unique broadcasting for this
|
13
|
+
# Broadcast a hash to a unique broadcasting for this `model` in this channel.
|
16
14
|
def broadcast_to(model, message)
|
17
15
|
ActionCable.server.broadcast(broadcasting_for(model), message)
|
18
16
|
end
|
19
17
|
|
20
|
-
# Returns a unique broadcasting identifier for this
|
18
|
+
# Returns a unique broadcasting identifier for this `model` in this channel:
|
21
19
|
#
|
22
|
-
#
|
20
|
+
# CommentsChannel.broadcasting_for("all") # => "comments:all"
|
23
21
|
#
|
24
|
-
# You can pass any object as a target (e.g. Active Record model), and it
|
25
|
-
#
|
22
|
+
# You can pass any object as a target (e.g. Active Record model), and it would
|
23
|
+
# be serialized into a string under the hood.
|
26
24
|
def broadcasting_for(model)
|
27
25
|
serialize_broadcasting([ channel_name, model ])
|
28
26
|
end
|
29
27
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
object.
|
34
|
-
|
35
|
-
object.to_gid_param
|
36
|
-
|
37
|
-
|
28
|
+
private
|
29
|
+
def serialize_broadcasting(object) # :nodoc:
|
30
|
+
case
|
31
|
+
when object.is_a?(Array)
|
32
|
+
object.map { |m| serialize_broadcasting(m) }.join(":")
|
33
|
+
when object.respond_to?(:to_gid_param)
|
34
|
+
object.to_gid_param
|
35
|
+
else
|
36
|
+
object.to_param
|
37
|
+
end
|
38
38
|
end
|
39
|
-
|
39
|
+
end
|
40
|
+
|
41
|
+
def broadcasting_for(model)
|
42
|
+
self.class.broadcasting_for(model)
|
43
|
+
end
|
44
|
+
|
45
|
+
def broadcast_to(model, message)
|
46
|
+
self.class.broadcast_to(model, message)
|
40
47
|
end
|
41
48
|
end
|
42
49
|
end
|