actioncable 7.1.5 → 7.2.0.beta1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -161
  3. data/app/assets/javascripts/action_cable.js +3 -3
  4. data/app/assets/javascripts/actioncable.esm.js +3 -3
  5. data/app/assets/javascripts/actioncable.js +3 -3
  6. data/lib/action_cable/channel/base.rb +98 -86
  7. data/lib/action_cable/channel/broadcasting.rb +25 -18
  8. data/lib/action_cable/channel/callbacks.rb +27 -25
  9. data/lib/action_cable/channel/naming.rb +9 -8
  10. data/lib/action_cable/channel/periodic_timers.rb +7 -7
  11. data/lib/action_cable/channel/streams.rb +77 -64
  12. data/lib/action_cable/channel/test_case.rb +112 -86
  13. data/lib/action_cable/connection/authorization.rb +4 -1
  14. data/lib/action_cable/connection/base.rb +53 -38
  15. data/lib/action_cable/connection/callbacks.rb +20 -18
  16. data/lib/action_cable/connection/client_socket.rb +3 -1
  17. data/lib/action_cable/connection/identification.rb +9 -5
  18. data/lib/action_cable/connection/internal_channel.rb +5 -2
  19. data/lib/action_cable/connection/message_buffer.rb +4 -1
  20. data/lib/action_cable/connection/stream.rb +2 -0
  21. data/lib/action_cable/connection/stream_event_loop.rb +4 -3
  22. data/lib/action_cable/connection/subscriptions.rb +6 -3
  23. data/lib/action_cable/connection/tagged_logger_proxy.rb +7 -4
  24. data/lib/action_cable/connection/test_case.rb +66 -56
  25. data/lib/action_cable/connection/web_socket.rb +10 -8
  26. data/lib/action_cable/deprecator.rb +2 -0
  27. data/lib/action_cable/engine.rb +5 -3
  28. data/lib/action_cable/gem_version.rb +6 -4
  29. data/lib/action_cable/helpers/action_cable_helper.rb +21 -19
  30. data/lib/action_cable/remote_connections.rb +19 -16
  31. data/lib/action_cable/server/base.rb +27 -15
  32. data/lib/action_cable/server/broadcasting.rb +23 -17
  33. data/lib/action_cable/server/configuration.rb +17 -14
  34. data/lib/action_cable/server/connections.rb +11 -5
  35. data/lib/action_cable/server/worker/active_record_connection_management.rb +2 -0
  36. data/lib/action_cable/server/worker.rb +4 -2
  37. data/lib/action_cable/subscription_adapter/async.rb +2 -0
  38. data/lib/action_cable/subscription_adapter/base.rb +2 -0
  39. data/lib/action_cable/subscription_adapter/channel_prefix.rb +2 -0
  40. data/lib/action_cable/subscription_adapter/inline.rb +2 -0
  41. data/lib/action_cable/subscription_adapter/postgresql.rb +4 -2
  42. data/lib/action_cable/subscription_adapter/redis.rb +5 -2
  43. data/lib/action_cable/subscription_adapter/subscriber_map.rb +2 -0
  44. data/lib/action_cable/subscription_adapter/test.rb +8 -5
  45. data/lib/action_cable/test_case.rb +2 -0
  46. data/lib/action_cable/test_helper.rb +51 -48
  47. data/lib/action_cable/version.rb +3 -1
  48. data/lib/action_cable.rb +1 -0
  49. data/lib/rails/generators/channel/channel_generator.rb +4 -2
  50. data/lib/rails/generators/channel/templates/application_cable/channel.rb +2 -0
  51. data/lib/rails/generators/channel/templates/application_cable/connection.rb +2 -0
  52. data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
  53. metadata +14 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7dc88f1422b55b8e7644e94a2585d9709759827f7596f2badc872b3f0904ac31
4
- data.tar.gz: c95aba4a0197bf449bc26eba95f4fd003367e4cdb2714e2cd780ca6253d9401b
3
+ metadata.gz: 0ef0b15a8ba035bb74d223a307b5582d7127717f3d63bc720832ed7f7e26232e
4
+ data.tar.gz: 6098f911679d8d5ecc08308c034c0034483178924920ece25c1ebb9b7ed6f9a7
5
5
  SHA512:
6
- metadata.gz: 76940e0bf0ede65620fe3481df80d8de2a78af6674c158a63ab6a7eb0132a014b433c93b61c933807bb92a4eda4e37519d08b2e17820738e1b04e4d2482f7dc1
7
- data.tar.gz: 824afbd29eae6f777c31d9b865515a3aacbd79892f87f9ae5a82283501a965402f5521ef314a63f312239819ed68f264e77aacde466b8fde8eb75d407d24915f
6
+ metadata.gz: ddef615b9413742c7c59ef63e04060f63df5c2509305ff8e8a69d26580145d52317b992fc0f3f0c47489e96a373f63fb0ab854356fb582fa9643112a5875d384
7
+ data.tar.gz: 7f14595ad0b02f979a36a287ebed53f4c52a9e162679f5ab748d687e87d66374bf8ebb43a1fddc30bdcf8876519d68f0a5539ab97c65d352db14abf30623c412
data/CHANGELOG.md CHANGED
@@ -1,171 +1,25 @@
1
- ## Rails 7.1.5 (October 30, 2024) ##
1
+ ## Rails 7.2.0.beta1 (May 29, 2024) ##
2
2
 
3
- * No changes.
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
- ## Rails 7.1.4.2 (October 23, 2024) ##
9
+ *yauhenininjia*
7
10
 
8
- * No changes.
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
- ## Rails 7.1.4.1 (October 15, 2024) ##
12
-
13
- * No changes.
14
-
15
-
16
- ## Rails 7.1.4 (August 22, 2024) ##
17
-
18
- * No changes.
19
-
20
-
21
- ## Rails 7.1.3.4 (June 04, 2024) ##
22
-
23
- * No changes.
24
-
25
-
26
- ## Rails 7.1.3.3 (May 16, 2024) ##
27
-
28
- * No changes.
29
-
30
-
31
- ## Rails 7.1.3.2 (February 21, 2024) ##
32
-
33
- * No changes.
34
-
35
-
36
- ## Rails 7.1.3.1 (February 21, 2024) ##
37
-
38
- * No changes.
39
-
40
-
41
- ## Rails 7.1.3 (January 16, 2024) ##
42
-
43
- * No changes.
44
-
45
-
46
- ## Rails 7.1.2 (November 10, 2023) ##
47
-
48
- * No changes.
49
-
50
-
51
- ## Rails 7.1.1 (October 11, 2023) ##
52
-
53
- * No changes.
54
-
55
-
56
- ## Rails 7.1.0 (October 05, 2023) ##
57
-
58
- * No changes.
59
-
60
-
61
- ## Rails 7.1.0.rc2 (October 01, 2023) ##
62
-
63
- * No changes.
64
-
65
-
66
- ## Rails 7.1.0.rc1 (September 27, 2023) ##
67
-
68
- * No changes.
69
-
70
-
71
- ## Rails 7.1.0.beta1 (September 13, 2023) ##
72
-
73
- * Add a `@server` instance variable referencing the `ActionCable.server`
74
- singleton to `ActionCable::Channel::ConnectionStub`
75
-
76
- This lets us delegate the `pubsub` and `config` method calls
77
- to the server. This fixes `NoMethodError` errors when testing
78
- channel logic that call `pubsub` (e.g. `stop_stream_for`).
79
-
80
- *Julian Foo*
81
-
82
- * Added `health_check_path` and `health_check_application` config to
83
- mount a given health check rack app on a given path.
84
- Useful when mounting Action Cable standalone.
85
-
86
- *Joé Dupuis*
87
-
88
- * Introduce the `capture_broadcasts` test helper.
89
-
90
- 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.
91
17
 
92
18
  ```ruby
93
- messages = capture_broadcasts("test") do
94
- ActionCable.server.broadcast "test", { message: "one" }
95
- ActionCable.server.broadcast "test", { message: "two" }
96
- end
97
- assert_equal 2, messages.length
98
- assert_equal({ "message" => "one" }, messages.first)
99
- assert_equal({ "message" => "two" }, messages.last)
100
- ```
101
-
102
- *Alex Ghiculescu*
103
-
104
- * Display broadcasted messages on error message when using `assert_broadcast_on`
105
-
106
- *Stéphane Robino*
107
-
108
- * The Action Cable client now supports subprotocols to allow passing arbitrary data
109
- to the server.
110
-
111
- ```js
112
- const consumer = ActionCable.createConsumer()
113
-
114
- consumer.addSubProtocol('custom-protocol')
115
-
116
- consumer.connect()
117
- ```
118
-
119
- See also:
120
-
121
- * https://developer.mozilla.org/en-US/docs/Web/API/WebSockets_API/Writing_WebSocket_servers#subprotocols
122
-
123
- *Guillaume Hain*
124
-
125
- * Redis pub/sub adapter now automatically reconnects when Redis connection is lost.
126
-
127
- *Vladimir Dementyev*
128
-
129
- * The `connected()` callback can now take a `{reconnected}` parameter to differentiate
130
- connections from reconnections.
131
-
132
- ```js
133
- import consumer from "./consumer"
134
-
135
- consumer.subscriptions.create("ExampleChannel", {
136
- connected({reconnected}) {
137
- if (reconnected) {
138
- ...
139
- } else {
140
- ...
141
- }
142
- }
143
- })
19
+ assert_has_no_stream "messages"
20
+ assert_has_no_stream_for User.find(42)
144
21
  ```
145
22
 
146
- *Mansa Keïta*
147
-
148
- * The Redis adapter is now compatible with redis-rb 5.0
149
-
150
- Compatibility with redis-rb 3.x was dropped.
151
-
152
- *Jean Boussier*
153
-
154
- * The Action Cable server is now mounted with `anchor: true`.
155
-
156
- This means that routes that also start with `/cable` will no longer clash with Action Cable.
157
-
158
- *Alex Ghiculescu*
159
-
160
- * `ActionCable.server.remote_connections.where(...).disconnect` now sends `disconnect` message
161
- before closing the connection with the reconnection strategy specified (defaults to `true`).
162
-
163
- *Vladimir Dementyev*
164
-
165
- * Added command callbacks to `ActionCable::Connection::Base`.
166
-
167
- 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.
168
-
169
- *Vladimir Dementyev*
23
+ *Sebastian Pöll*, *Junichi Sato*
170
24
 
171
- Please check [7-0-stable](https://github.com/rails/rails/blob/7-0-stable/actioncable/CHANGELOG.md) for previous changes.
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
- recordPing() {
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 this.monitor.recordPing();
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
- recordPing() {
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 this.monitor.recordPing();
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
- recordPing() {
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 this.monitor.recordPing();
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
- # = Action Cable \Channel \Base
11
+ # # Action Cable Channel Base
10
12
  #
11
- # The channel provides the basic structure of grouping behavior into logical units when communicating over the WebSocket connection.
12
- # You can think of a channel like a form of controller, but one that's capable of pushing content to the subscriber in addition to simply
13
- # responding to the subscriber's direct requests.
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 the cable consumer becomes a subscriber, and then
16
- # lives until the consumer disconnects. This may be seconds, minutes, hours, or even days. That means you have to take special care
17
- # not to do anything silly in a channel that would balloon its memory footprint or whatever. The references are forever, so they won't be released
18
- # as is normally the case with a controller instance that gets thrown away after every request.
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 ensuring that the data is fresh. If you hold a reference to a user
21
- # record, but the name is changed while that reference is held, you may be sending stale data if you don't take precautions to avoid it.
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 variables to keep reference to objects that future subscriber requests
24
- # can interact with. Here's a quick example:
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
- # class ChatChannel < ApplicationCable::Channel
27
- # def subscribed
28
- # @room = Chat::Room[params[:room_number]]
29
- # end
36
+ # class ChatChannel < ApplicationCable::Channel
37
+ # def subscribed
38
+ # @room = Chat::Room[params[:room_number]]
39
+ # end
30
40
  #
31
- # def speak(data)
32
- # @room.speak data, user: current_user
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 channel was first subscribed to by the consumer when that
37
- # subscriber wants to say something in the room.
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
- # == Action processing
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
- # channel (optionally taking a <tt>data</tt> argument), and this method is
45
- # automatically exposed as callable to the client.
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
- # class AppearanceChannel < ApplicationCable::Channel
50
- # def subscribed
51
- # @connection_token = generate_connection_token
52
- # end
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
- # def appear(data)
59
- # current_user.appear @connection_token, on: data['appearing_on']
60
- # end
65
+ # def unsubscribed
66
+ # current_user.disappear @connection_token
67
+ # end
61
68
  #
62
- # def away
63
- # current_user.away @connection_token
64
- # end
69
+ # def appear(data)
70
+ # current_user.appear @connection_token, on: data['appearing_on']
71
+ # end
65
72
  #
66
- # private
67
- # def generate_connection_token
68
- # SecureRandom.hex(36)
73
+ # def away
74
+ # current_user.away @connection_token
69
75
  # end
70
- # end
71
76
  #
72
- # In this example, the subscribed and unsubscribed methods are not callable methods, as they
73
- # were already declared in ActionCable::Channel::Base, but <tt>#appear</tt>
74
- # and <tt>#away</tt> are. <tt>#generate_connection_token</tt> is also not
75
- # callable, since it's a private method. You'll see that appear accepts a data
76
- # parameter, which it then uses as part of its model call. <tt>#away</tt>
77
- # does not, since it's simply a trigger action.
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, <tt>current_user</tt> is available because
80
- # it was marked as an identifying attribute on the connection. All such
81
- # identifiers will automatically create a delegation method of the same name
82
- # on the channel instance.
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
- # == Rejecting subscription requests
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
- # class ChatChannel < ApplicationCable::Channel
90
- # def subscribed
91
- # @room = Chat::Room[params[:room_number]]
92
- # reject unless current_user.can_access?(@room)
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
- # <tt>current_user</tt> does not have access to the chat room. On the
98
- # client-side, the <tt>Channel#rejected</tt> callback will get invoked when
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
- # includes all public instance methods on a channel, less
114
- # any internal methods (defined on Base), adding back in
115
- # any methods that are internal, but still exist on the class
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
- # ==== Returns
119
- # * <tt>Set</tt> - A set of all methods that should be considered actions.
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
- # them. ::clear_action_methods! allows you to do that, so next time
135
- # you run action_methods, they will be recalculated.
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. The process will ensure
165
- # that the action requested is a public method on the channel declared by the user (so not one of the callbacks
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
- # and confirms or rejects the subscription.
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 cleanup with callbacks.
192
- # This method is not intended to be called directly by the user. Instead, override the #unsubscribed callback.
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 place to set up any streams
201
- # you want this channel to be sending to the subscriber.
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 up connections or marking
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 wrapped in a JSON envelope with
213
- # the proper channel identifier marked as the recipient.
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 <tt>model</tt> in this channel.
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 <tt>model</tt> in this channel:
18
+ # Returns a unique broadcasting identifier for this `model` in this channel:
21
19
  #
22
- # CommentsChannel.broadcasting_for("all") # => "comments:all"
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
- # would be serialized into a string under the hood.
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
- def serialize_broadcasting(object) # :nodoc:
31
- case
32
- when object.is_a?(Array)
33
- object.map { |m| serialize_broadcasting(m) }.join(":")
34
- when object.respond_to?(:to_gid_param)
35
- object.to_gid_param
36
- else
37
- object.to_param
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
- end
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