actioncable 7.1.3.4 → 7.2.1

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