actioncable 7.1.3.3 → 7.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -136
- 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 +6 -4
- 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 -52
- data/lib/action_cable/version.rb +3 -1
- data/lib/action_cable.rb +2 -1
- data/lib/rails/generators/channel/channel_generator.rb +4 -2
- data/lib/rails/generators/channel/templates/application_cable/channel.rb +2 -0
- data/lib/rails/generators/channel/templates/application_cable/connection.rb +2 -0
- data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
- metadata +10 -10
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ef0b15a8ba035bb74d223a307b5582d7127717f3d63bc720832ed7f7e26232e
|
4
|
+
data.tar.gz: 6098f911679d8d5ecc08308c034c0034483178924920ece25c1ebb9b7ed6f9a7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ddef615b9413742c7c59ef63e04060f63df5c2509305ff8e8a69d26580145d52317b992fc0f3f0c47489e96a373f63fb0ab854356fb582fa9643112a5875d384
|
7
|
+
data.tar.gz: 7f14595ad0b02f979a36a287ebed53f4c52a9e162679f5ab748d687e87d66374bf8ebb43a1fddc30bdcf8876519d68f0a5539ab97c65d352db14abf30623c412
|
data/CHANGELOG.md
CHANGED
@@ -1,146 +1,25 @@
|
|
1
|
-
## Rails 7.
|
1
|
+
## Rails 7.2.0.beta1 (May 29, 2024) ##
|
2
2
|
|
3
|
-
*
|
3
|
+
* Record ping on every Action Cable message.
|
4
4
|
|
5
|
+
Previously only `ping` and `welcome` message types were keeping the connection active.
|
6
|
+
Now every Action Cable message updates the `pingedAt` value, preventing the connection
|
7
|
+
from being marked as stale.
|
5
8
|
|
6
|
-
|
9
|
+
*yauhenininjia*
|
7
10
|
|
8
|
-
*
|
11
|
+
* Add two new assertion methods for Action Cable test cases: `assert_has_no_stream`
|
12
|
+
and `assert_has_no_stream_for`.
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
* No changes.
|
14
|
-
|
15
|
-
|
16
|
-
## Rails 7.1.3 (January 16, 2024) ##
|
17
|
-
|
18
|
-
* No changes.
|
19
|
-
|
20
|
-
|
21
|
-
## Rails 7.1.2 (November 10, 2023) ##
|
22
|
-
|
23
|
-
* No changes.
|
24
|
-
|
25
|
-
|
26
|
-
## Rails 7.1.1 (October 11, 2023) ##
|
27
|
-
|
28
|
-
* No changes.
|
29
|
-
|
30
|
-
|
31
|
-
## Rails 7.1.0 (October 05, 2023) ##
|
32
|
-
|
33
|
-
* No changes.
|
34
|
-
|
35
|
-
|
36
|
-
## Rails 7.1.0.rc2 (October 01, 2023) ##
|
37
|
-
|
38
|
-
* No changes.
|
39
|
-
|
40
|
-
|
41
|
-
## Rails 7.1.0.rc1 (September 27, 2023) ##
|
42
|
-
|
43
|
-
* No changes.
|
44
|
-
|
45
|
-
|
46
|
-
## Rails 7.1.0.beta1 (September 13, 2023) ##
|
47
|
-
|
48
|
-
* Add a `@server` instance variable referencing the `ActionCable.server`
|
49
|
-
singleton to `ActionCable::Channel::ConnectionStub`
|
50
|
-
|
51
|
-
This lets us delegate the `pubsub` and `config` method calls
|
52
|
-
to the server. This fixes `NoMethodError` errors when testing
|
53
|
-
channel logic that call `pubsub` (e.g. `stop_stream_for`).
|
54
|
-
|
55
|
-
*Julian Foo*
|
56
|
-
|
57
|
-
* Added `health_check_path` and `health_check_application` config to
|
58
|
-
mount a given health check rack app on a given path.
|
59
|
-
Useful when mounting Action Cable standalone.
|
60
|
-
|
61
|
-
*Joé Dupuis*
|
62
|
-
|
63
|
-
* Introduce the `capture_broadcasts` test helper.
|
64
|
-
|
65
|
-
Returns all messages broadcast in a block.
|
14
|
+
These methods can be used to assert that a stream has been stopped, e.g. via
|
15
|
+
`stop_stream` or `stop_stream_for`. They complement the already existing
|
16
|
+
`assert_has_stream` and `assert_has_stream_for` methods.
|
66
17
|
|
67
18
|
```ruby
|
68
|
-
|
69
|
-
|
70
|
-
ActionCable.server.broadcast "test", { message: "two" }
|
71
|
-
end
|
72
|
-
assert_equal 2, messages.length
|
73
|
-
assert_equal({ "message" => "one" }, messages.first)
|
74
|
-
assert_equal({ "message" => "two" }, messages.last)
|
19
|
+
assert_has_no_stream "messages"
|
20
|
+
assert_has_no_stream_for User.find(42)
|
75
21
|
```
|
76
22
|
|
77
|
-
*
|
78
|
-
|
79
|
-
* Display broadcasted messages on error message when using `assert_broadcast_on`
|
80
|
-
|
81
|
-
*Stéphane Robino*
|
82
|
-
|
83
|
-
* The Action Cable client now supports subprotocols to allow passing arbitrary data
|
84
|
-
to the server.
|
85
|
-
|
86
|
-
```js
|
87
|
-
const consumer = ActionCable.createConsumer()
|
88
|
-
|
89
|
-
consumer.addSubProtocol('custom-protocol')
|
90
|
-
|
91
|
-
consumer.connect()
|
92
|
-
```
|
93
|
-
|
94
|
-
See also:
|
95
|
-
|
96
|
-
* https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols
|
97
|
-
|
98
|
-
*Guillaume Hain*
|
99
|
-
|
100
|
-
* Redis pub/sub adapter now automatically reconnects when Redis connection is lost.
|
101
|
-
|
102
|
-
*Vladimir Dementyev*
|
103
|
-
|
104
|
-
* The `connected()` callback can now take a `{reconnected}` parameter to differentiate
|
105
|
-
connections from reconnections.
|
106
|
-
|
107
|
-
```js
|
108
|
-
import consumer from "./consumer"
|
109
|
-
|
110
|
-
consumer.subscriptions.create("ExampleChannel", {
|
111
|
-
connected({reconnected}) {
|
112
|
-
if (reconnected) {
|
113
|
-
...
|
114
|
-
} else {
|
115
|
-
...
|
116
|
-
}
|
117
|
-
}
|
118
|
-
})
|
119
|
-
```
|
120
|
-
|
121
|
-
*Mansa Keïta*
|
122
|
-
|
123
|
-
* The Redis adapter is now compatible with redis-rb 5.0
|
124
|
-
|
125
|
-
Compatibility with redis-rb 3.x was dropped.
|
126
|
-
|
127
|
-
*Jean Boussier*
|
128
|
-
|
129
|
-
* The Action Cable server is now mounted with `anchor: true`.
|
130
|
-
|
131
|
-
This means that routes that also start with `/cable` will no longer clash with Action Cable.
|
132
|
-
|
133
|
-
*Alex Ghiculescu*
|
134
|
-
|
135
|
-
* `ActionCable.server.remote_connections.where(...).disconnect` now sends `disconnect` message
|
136
|
-
before closing the connection with the reconnection strategy specified (defaults to `true`).
|
137
|
-
|
138
|
-
*Vladimir Dementyev*
|
139
|
-
|
140
|
-
* Added command callbacks to `ActionCable::Connection::Base`.
|
141
|
-
|
142
|
-
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.
|
143
|
-
|
144
|
-
*Vladimir Dementyev*
|
23
|
+
*Sebastian Pöll*, *Junichi Sato*
|
145
24
|
|
146
|
-
Please check [7-
|
25
|
+
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
|