actioncable 7.1.3.2 → 8.0.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 +13 -117
- 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 +100 -89
- 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 +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 +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 +7 -5
- 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 +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 -52
- data/lib/action_cable/version.rb +3 -1
- data/lib/action_cable.rb +13 -7
- data/lib/rails/generators/channel/channel_generator.rb +4 -2
- data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
- metadata +16 -16
- /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: e9b858d5609ad95c84f7cdc6684952e5b3bfa01adc6c8e5037a5f239d8c53543
|
4
|
+
data.tar.gz: ad87c01cbd1dac29aff96279a0acbc8b07ccebe3674637fae7959fa59e8c8f46
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d73ae632820d77d1fb5619e0a554117a106fa6e3fb1a5dbc0925c98f50ce2465e6fc16762e4765f31153d08e3b897c15aafa9aa0eb50585010db20e7ac48bf17
|
7
|
+
data.tar.gz: 241eff0e18d7de8c5032aed0d85149582bb244f4466e40f30528b56fbe94a906d58a8305c15e175dd09b892bdc58870806cc529bc781ebedc90451091acce89d
|
data/CHANGELOG.md
CHANGED
@@ -1,141 +1,37 @@
|
|
1
|
-
## Rails
|
2
|
-
|
3
|
-
* No changes.
|
4
|
-
|
5
|
-
|
6
|
-
## Rails 7.1.3.1 (February 21, 2024) ##
|
7
|
-
|
8
|
-
* No changes.
|
1
|
+
## Rails 8.0.1 (December 13, 2024) ##
|
9
2
|
|
3
|
+
* Ensure the Postgresql adapter always use a dedicated connection even during system tests.
|
10
4
|
|
11
|
-
|
5
|
+
Fix an issue with the Action Cable Postgresql adapter causing deadlock or various weird
|
6
|
+
pg client error during system tests.
|
12
7
|
|
13
|
-
*
|
14
|
-
|
15
|
-
|
16
|
-
## Rails 7.1.2 (November 10, 2023) ##
|
17
|
-
|
18
|
-
* No changes.
|
8
|
+
*Jean Boussier*
|
19
9
|
|
20
10
|
|
21
|
-
## Rails
|
11
|
+
## Rails 8.0.0.1 (December 10, 2024) ##
|
22
12
|
|
23
13
|
* No changes.
|
24
14
|
|
25
15
|
|
26
|
-
## Rails
|
16
|
+
## Rails 8.0.0 (November 07, 2024) ##
|
27
17
|
|
28
18
|
* No changes.
|
29
19
|
|
30
20
|
|
31
|
-
## Rails
|
21
|
+
## Rails 8.0.0.rc2 (October 30, 2024) ##
|
32
22
|
|
33
23
|
* No changes.
|
34
24
|
|
35
25
|
|
36
|
-
## Rails
|
26
|
+
## Rails 8.0.0.rc1 (October 19, 2024) ##
|
37
27
|
|
38
28
|
* No changes.
|
39
29
|
|
40
30
|
|
41
|
-
## Rails
|
42
|
-
|
43
|
-
* Add a `@server` instance variable referencing the `ActionCable.server`
|
44
|
-
singleton to `ActionCable::Channel::ConnectionStub`
|
45
|
-
|
46
|
-
This lets us delegate the `pubsub` and `config` method calls
|
47
|
-
to the server. This fixes `NoMethodError` errors when testing
|
48
|
-
channel logic that call `pubsub` (e.g. `stop_stream_for`).
|
49
|
-
|
50
|
-
*Julian Foo*
|
51
|
-
|
52
|
-
* Added `health_check_path` and `health_check_application` config to
|
53
|
-
mount a given health check rack app on a given path.
|
54
|
-
Useful when mounting Action Cable standalone.
|
55
|
-
|
56
|
-
*Joé Dupuis*
|
57
|
-
|
58
|
-
* Introduce the `capture_broadcasts` test helper.
|
59
|
-
|
60
|
-
Returns all messages broadcast in a block.
|
61
|
-
|
62
|
-
```ruby
|
63
|
-
messages = capture_broadcasts("test") do
|
64
|
-
ActionCable.server.broadcast "test", { message: "one" }
|
65
|
-
ActionCable.server.broadcast "test", { message: "two" }
|
66
|
-
end
|
67
|
-
assert_equal 2, messages.length
|
68
|
-
assert_equal({ "message" => "one" }, messages.first)
|
69
|
-
assert_equal({ "message" => "two" }, messages.last)
|
70
|
-
```
|
71
|
-
|
72
|
-
*Alex Ghiculescu*
|
73
|
-
|
74
|
-
* Display broadcasted messages on error message when using `assert_broadcast_on`
|
75
|
-
|
76
|
-
*Stéphane Robino*
|
77
|
-
|
78
|
-
* The Action Cable client now supports subprotocols to allow passing arbitrary data
|
79
|
-
to the server.
|
80
|
-
|
81
|
-
```js
|
82
|
-
const consumer = ActionCable.createConsumer()
|
83
|
-
|
84
|
-
consumer.addSubProtocol('custom-protocol')
|
85
|
-
|
86
|
-
consumer.connect()
|
87
|
-
```
|
88
|
-
|
89
|
-
See also:
|
90
|
-
|
91
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols
|
92
|
-
|
93
|
-
*Guillaume Hain*
|
94
|
-
|
95
|
-
* Redis pub/sub adapter now automatically reconnects when Redis connection is lost.
|
96
|
-
|
97
|
-
*Vladimir Dementyev*
|
98
|
-
|
99
|
-
* The `connected()` callback can now take a `{reconnected}` parameter to differentiate
|
100
|
-
connections from reconnections.
|
101
|
-
|
102
|
-
```js
|
103
|
-
import consumer from "./consumer"
|
104
|
-
|
105
|
-
consumer.subscriptions.create("ExampleChannel", {
|
106
|
-
connected({reconnected}) {
|
107
|
-
if (reconnected) {
|
108
|
-
...
|
109
|
-
} else {
|
110
|
-
...
|
111
|
-
}
|
112
|
-
}
|
113
|
-
})
|
114
|
-
```
|
115
|
-
|
116
|
-
*Mansa Keïta*
|
117
|
-
|
118
|
-
* The Redis adapter is now compatible with redis-rb 5.0
|
119
|
-
|
120
|
-
Compatibility with redis-rb 3.x was dropped.
|
121
|
-
|
122
|
-
*Jean Boussier*
|
123
|
-
|
124
|
-
* The Action Cable server is now mounted with `anchor: true`.
|
125
|
-
|
126
|
-
This means that routes that also start with `/cable` will no longer clash with Action Cable.
|
127
|
-
|
128
|
-
*Alex Ghiculescu*
|
129
|
-
|
130
|
-
* `ActionCable.server.remote_connections.where(...).disconnect` now sends `disconnect` message
|
131
|
-
before closing the connection with the reconnection strategy specified (defaults to `true`).
|
132
|
-
|
133
|
-
*Vladimir Dementyev*
|
134
|
-
|
135
|
-
* Added command callbacks to `ActionCable::Connection::Base`.
|
31
|
+
## Rails 8.0.0.beta1 (September 26, 2024) ##
|
136
32
|
|
137
|
-
|
33
|
+
* Add an `identifier` to the event payload for the ActiveSupport::Notification `transmit_subscription_confirmation.action_cable` and `transmit_subscription_rejection.action_cable`.
|
138
34
|
|
139
|
-
*
|
35
|
+
*Keith Schacht*
|
140
36
|
|
141
|
-
Please check [7-
|
37
|
+
Please check [7-2-stable](https://github.com/rails/rails/blob/7-2-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,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
|
@@ -130,9 +138,9 @@ module ActionCable
|
|
130
138
|
end
|
131
139
|
|
132
140
|
private
|
133
|
-
# action_methods are cached and there is sometimes need to refresh
|
134
|
-
#
|
135
|
-
#
|
141
|
+
# action_methods are cached and there is sometimes need to refresh them.
|
142
|
+
# ::clear_action_methods! allows you to do that, so next time you run
|
143
|
+
# action_methods, they will be recalculated.
|
136
144
|
def clear_action_methods! # :doc:
|
137
145
|
@action_methods = nil
|
138
146
|
end
|
@@ -161,9 +169,9 @@ module ActionCable
|
|
161
169
|
delegate_connection_identifiers
|
162
170
|
end
|
163
171
|
|
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).
|
172
|
+
# Extract the action name from the passed data and process it via the channel.
|
173
|
+
# The process will ensure that the action requested is a public method on the
|
174
|
+
# channel declared by the user (so not one of the callbacks like #subscribed).
|
167
175
|
def perform_action(data)
|
168
176
|
action = extract_action(data)
|
169
177
|
|
@@ -177,8 +185,8 @@ module ActionCable
|
|
177
185
|
end
|
178
186
|
end
|
179
187
|
|
180
|
-
# This method is called after subscription has been added to the connection
|
181
|
-
#
|
188
|
+
# This method is called after subscription has been added to the connection and
|
189
|
+
# confirms or rejects the subscription.
|
182
190
|
def subscribe_to_channel
|
183
191
|
run_callbacks :subscribe do
|
184
192
|
subscribed
|
@@ -188,8 +196,9 @@ module ActionCable
|
|
188
196
|
ensure_confirmation_sent
|
189
197
|
end
|
190
198
|
|
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
|
199
|
+
# Called by the cable connection when it's cut, so the channel has a chance to
|
200
|
+
# cleanup with callbacks. This method is not intended to be called directly by
|
201
|
+
# the user. Instead, override the #unsubscribed callback.
|
193
202
|
def unsubscribe_from_channel # :nodoc:
|
194
203
|
run_callbacks :unsubscribe do
|
195
204
|
unsubscribed
|
@@ -197,20 +206,22 @@ module ActionCable
|
|
197
206
|
end
|
198
207
|
|
199
208
|
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
|
209
|
+
# Called once a consumer has become a subscriber of the channel. Usually the
|
210
|
+
# place to set up any streams you want this channel to be sending to the
|
211
|
+
# subscriber.
|
202
212
|
def subscribed # :doc:
|
203
213
|
# Override in subclasses
|
204
214
|
end
|
205
215
|
|
206
|
-
# Called once a consumer has cut its cable connection. Can be used for cleaning
|
207
|
-
# users as offline or the like.
|
216
|
+
# Called once a consumer has cut its cable connection. Can be used for cleaning
|
217
|
+
# up connections or marking users as offline or the like.
|
208
218
|
def unsubscribed # :doc:
|
209
219
|
# Override in subclasses
|
210
220
|
end
|
211
221
|
|
212
|
-
# Transmit a hash of data to the subscriber. The hash will automatically be
|
213
|
-
# the proper channel identifier marked as the
|
222
|
+
# Transmit a hash of data to the subscriber. The hash will automatically be
|
223
|
+
# wrapped in a JSON envelope with the proper channel identifier marked as the
|
224
|
+
# recipient.
|
214
225
|
def transmit(data, via: nil) # :doc:
|
215
226
|
logger.debug do
|
216
227
|
status = "#{self.class.name} transmitting #{data.inspect.truncate(300)}"
|
@@ -297,7 +308,7 @@ module ActionCable
|
|
297
308
|
unless subscription_confirmation_sent?
|
298
309
|
logger.debug "#{self.class.name} is transmitting the subscription confirmation"
|
299
310
|
|
300
|
-
ActiveSupport::Notifications.instrument("transmit_subscription_confirmation.action_cable", channel_class: self.class.name) do
|
311
|
+
ActiveSupport::Notifications.instrument("transmit_subscription_confirmation.action_cable", channel_class: self.class.name, identifier: @identifier) do
|
301
312
|
connection.transmit identifier: @identifier, type: ActionCable::INTERNAL[:message_types][:confirmation]
|
302
313
|
@subscription_confirmation_sent = true
|
303
314
|
end
|
@@ -312,7 +323,7 @@ module ActionCable
|
|
312
323
|
def transmit_subscription_rejection
|
313
324
|
logger.debug "#{self.class.name} is transmitting the subscription rejection"
|
314
325
|
|
315
|
-
ActiveSupport::Notifications.instrument("transmit_subscription_rejection.action_cable", channel_class: self.class.name) do
|
326
|
+
ActiveSupport::Notifications.instrument("transmit_subscription_rejection.action_cable", channel_class: self.class.name, identifier: @identifier) do
|
316
327
|
connection.transmit identifier: @identifier, type: ActionCable::INTERNAL[:message_types][:rejection]
|
317
328
|
end
|
318
329
|
end
|
@@ -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
|