actioncable 6.1.7.9 → 7.2.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -160
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +5 -5
  5. data/app/assets/javascripts/action_cable.js +239 -302
  6. data/app/assets/javascripts/actioncable.esm.js +512 -0
  7. data/app/assets/javascripts/actioncable.js +510 -0
  8. data/lib/action_cable/channel/base.rb +114 -90
  9. data/lib/action_cable/channel/broadcasting.rb +25 -16
  10. data/lib/action_cable/channel/callbacks.rb +39 -0
  11. data/lib/action_cable/channel/naming.rb +10 -7
  12. data/lib/action_cable/channel/periodic_timers.rb +7 -7
  13. data/lib/action_cable/channel/streams.rb +81 -68
  14. data/lib/action_cable/channel/test_case.rb +133 -87
  15. data/lib/action_cable/connection/authorization.rb +4 -1
  16. data/lib/action_cable/connection/base.rb +71 -43
  17. data/lib/action_cable/connection/callbacks.rb +57 -0
  18. data/lib/action_cable/connection/client_socket.rb +3 -1
  19. data/lib/action_cable/connection/identification.rb +10 -6
  20. data/lib/action_cable/connection/internal_channel.rb +7 -2
  21. data/lib/action_cable/connection/message_buffer.rb +4 -1
  22. data/lib/action_cable/connection/stream.rb +2 -2
  23. data/lib/action_cable/connection/stream_event_loop.rb +4 -4
  24. data/lib/action_cable/connection/subscriptions.rb +8 -3
  25. data/lib/action_cable/connection/tagged_logger_proxy.rb +14 -9
  26. data/lib/action_cable/connection/test_case.rb +67 -55
  27. data/lib/action_cable/connection/web_socket.rb +11 -7
  28. data/lib/action_cable/deprecator.rb +9 -0
  29. data/lib/action_cable/engine.rb +28 -9
  30. data/lib/action_cable/gem_version.rb +7 -5
  31. data/lib/action_cable/helpers/action_cable_helper.rb +21 -18
  32. data/lib/action_cable/remote_connections.rb +25 -13
  33. data/lib/action_cable/server/base.rb +29 -14
  34. data/lib/action_cable/server/broadcasting.rb +24 -16
  35. data/lib/action_cable/server/configuration.rb +28 -14
  36. data/lib/action_cable/server/connections.rb +13 -5
  37. data/lib/action_cable/server/worker/active_record_connection_management.rb +4 -2
  38. data/lib/action_cable/server/worker.rb +7 -7
  39. data/lib/action_cable/subscription_adapter/async.rb +1 -1
  40. data/lib/action_cable/subscription_adapter/base.rb +2 -0
  41. data/lib/action_cable/subscription_adapter/channel_prefix.rb +2 -0
  42. data/lib/action_cable/subscription_adapter/inline.rb +2 -0
  43. data/lib/action_cable/subscription_adapter/postgresql.rb +6 -5
  44. data/lib/action_cable/subscription_adapter/redis.rb +101 -25
  45. data/lib/action_cable/subscription_adapter/subscriber_map.rb +2 -0
  46. data/lib/action_cable/subscription_adapter/test.rb +7 -6
  47. data/lib/action_cable/test_case.rb +2 -0
  48. data/lib/action_cable/test_helper.rb +89 -59
  49. data/lib/action_cable/version.rb +3 -1
  50. data/lib/action_cable.rb +30 -12
  51. data/lib/rails/generators/channel/USAGE +14 -8
  52. data/lib/rails/generators/channel/channel_generator.rb +95 -20
  53. data/lib/rails/generators/channel/templates/javascript/index.js.tt +1 -5
  54. data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
  55. metadata +29 -15
  56. data/lib/action_cable/channel.rb +0 -17
  57. data/lib/action_cable/connection.rb +0 -22
  58. data/lib/action_cable/server.rb +0 -16
  59. data/lib/action_cable/subscription_adapter.rb +0 -12
@@ -1,65 +1,77 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module Channel
5
- # Streams allow channels to route broadcastings to the subscriber. A broadcasting is, as discussed elsewhere, a pubsub queue where any data
6
- # placed into it is automatically sent to the clients that are connected at that time. It's purely an online queue, though. If you're not
7
- # streaming a broadcasting at the very moment it sends out an update, you will not get that update, even if you connect after it has been sent.
8
- #
9
- # Most commonly, the streamed broadcast is sent straight to the subscriber on the client-side. The channel just acts as a connector between
10
- # the two parties (the broadcaster and the channel subscriber). Here's an example of a channel that allows subscribers to get all new
11
- # comments on a given page:
12
- #
13
- # class CommentsChannel < ApplicationCable::Channel
14
- # def follow(data)
15
- # stream_from "comments_for_#{data['recording_id']}"
16
- # end
7
+ # # Action Cable Channel Streams
8
+ #
9
+ # Streams allow channels to route broadcastings to the subscriber. A
10
+ # broadcasting is, as discussed elsewhere, a pubsub queue where any data placed
11
+ # into it is automatically sent to the clients that are connected at that time.
12
+ # It's purely an online queue, though. If you're not streaming a broadcasting at
13
+ # the very moment it sends out an update, you will not get that update, even if
14
+ # you connect after it has been sent.
15
+ #
16
+ # Most commonly, the streamed broadcast is sent straight to the subscriber on
17
+ # the client-side. The channel just acts as a connector between the two parties
18
+ # (the broadcaster and the channel subscriber). Here's an example of a channel
19
+ # that allows subscribers to get all new comments on a given page:
20
+ #
21
+ # class CommentsChannel < ApplicationCable::Channel
22
+ # def follow(data)
23
+ # stream_from "comments_for_#{data['recording_id']}"
24
+ # end
17
25
  #
18
- # def unfollow
19
- # stop_all_streams
26
+ # def unfollow
27
+ # stop_all_streams
28
+ # end
20
29
  # end
21
- # end
22
30
  #
23
- # Based on the above example, the subscribers of this channel will get whatever data is put into the,
24
- # let's say, <tt>comments_for_45</tt> broadcasting as soon as it's put there.
31
+ # Based on the above example, the subscribers of this channel will get whatever
32
+ # data is put into the, let's say, `comments_for_45` broadcasting as soon as
33
+ # it's put there.
25
34
  #
26
35
  # An example broadcasting for this channel looks like so:
27
36
  #
28
- # ActionCable.server.broadcast "comments_for_45", { author: 'DHH', content: 'Rails is just swell' }
37
+ # ActionCable.server.broadcast "comments_for_45", { author: 'DHH', content: 'Rails is just swell' }
29
38
  #
30
- # If you have a stream that is related to a model, then the broadcasting used can be generated from the model and channel.
31
- # The following example would subscribe to a broadcasting like <tt>comments:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE</tt>.
39
+ # If you have a stream that is related to a model, then the broadcasting used
40
+ # can be generated from the model and channel. The following example would
41
+ # subscribe to a broadcasting like `comments:Z2lkOi8vVGVzdEFwcC9Qb3N0LzE`.
32
42
  #
33
- # class CommentsChannel < ApplicationCable::Channel
34
- # def subscribed
35
- # post = Post.find(params[:id])
36
- # stream_for post
43
+ # class CommentsChannel < ApplicationCable::Channel
44
+ # def subscribed
45
+ # post = Post.find(params[:id])
46
+ # stream_for post
47
+ # end
37
48
  # end
38
- # end
39
49
  #
40
50
  # You can then broadcast to this channel using:
41
51
  #
42
- # CommentsChannel.broadcast_to(@post, @comment)
52
+ # CommentsChannel.broadcast_to(@post, @comment)
43
53
  #
44
- # If you don't just want to parlay the broadcast unfiltered to the subscriber, you can also supply a callback that lets you alter what is sent out.
45
- # The below example shows how you can use this to provide performance introspection in the process:
54
+ # If you don't just want to parlay the broadcast unfiltered to the subscriber,
55
+ # you can also supply a callback that lets you alter what is sent out. The below
56
+ # example shows how you can use this to provide performance introspection in the
57
+ # process:
46
58
  #
47
- # class ChatChannel < ApplicationCable::Channel
48
- # def subscribed
49
- # @room = Chat::Room[params[:room_number]]
59
+ # class ChatChannel < ApplicationCable::Channel
60
+ # def subscribed
61
+ # @room = Chat::Room[params[:room_number]]
50
62
  #
51
- # stream_for @room, coder: ActiveSupport::JSON do |message|
52
- # if message['originated_at'].present?
53
- # elapsed_time = (Time.now.to_f - message['originated_at']).round(2)
63
+ # stream_for @room, coder: ActiveSupport::JSON do |message|
64
+ # if message['originated_at'].present?
65
+ # elapsed_time = (Time.now.to_f - message['originated_at']).round(2)
54
66
  #
55
- # ActiveSupport::Notifications.instrument :performance, measurement: 'Chat.message_delay', value: elapsed_time, action: :timing
56
- # logger.info "Message took #{elapsed_time}s to arrive"
57
- # end
67
+ # ActiveSupport::Notifications.instrument :performance, measurement: 'Chat.message_delay', value: elapsed_time, action: :timing
68
+ # logger.info "Message took #{elapsed_time}s to arrive"
69
+ # end
58
70
  #
59
- # transmit message
71
+ # transmit message
72
+ # end
60
73
  # end
61
74
  # end
62
- # end
63
75
  #
64
76
  # You can stop streaming from all broadcasts by calling #stop_all_streams.
65
77
  module Streams
@@ -69,18 +81,20 @@ module ActionCable
69
81
  on_unsubscribe :stop_all_streams
70
82
  end
71
83
 
72
- # Start streaming from the named <tt>broadcasting</tt> pubsub queue. Optionally, you can pass a <tt>callback</tt> that'll be used
73
- # instead of the default of just transmitting the updates straight to the subscriber.
74
- # Pass <tt>coder: ActiveSupport::JSON</tt> to decode messages as JSON before passing to the callback.
75
- # Defaults to <tt>coder: nil</tt> which does no decoding, passes raw messages.
84
+ # Start streaming from the named `broadcasting` pubsub queue. Optionally, you
85
+ # can pass a `callback` that'll be used instead of the default of just
86
+ # transmitting the updates straight to the subscriber. Pass `coder:
87
+ # ActiveSupport::JSON` to decode messages as JSON before passing to the
88
+ # callback. Defaults to `coder: nil` which does no decoding, passes raw
89
+ # messages.
76
90
  def stream_from(broadcasting, callback = nil, coder: nil, &block)
77
91
  broadcasting = String(broadcasting)
78
92
 
79
93
  # Don't send the confirmation until pubsub#subscribe is successful
80
94
  defer_subscription_confirmation!
81
95
 
82
- # Build a stream handler by wrapping the user-provided callback with
83
- # a decoder or defaulting to a JSON-decoding retransmitter.
96
+ # Build a stream handler by wrapping the user-provided callback with a decoder
97
+ # or defaulting to a JSON-decoding retransmitter.
84
98
  handler = worker_pool_stream_handler(broadcasting, callback || block, coder: coder)
85
99
  streams[broadcasting] = handler
86
100
 
@@ -92,17 +106,18 @@ module ActionCable
92
106
  end
93
107
  end
94
108
 
95
- # Start streaming the pubsub queue for the <tt>model</tt> in this channel. Optionally, you can pass a
96
- # <tt>callback</tt> that'll be used instead of the default of just transmitting the updates straight
97
- # to the subscriber.
109
+ # Start streaming the pubsub queue for the `model` in this channel. Optionally,
110
+ # you can pass a `callback` that'll be used instead of the default of just
111
+ # transmitting the updates straight to the subscriber.
98
112
  #
99
- # Pass <tt>coder: ActiveSupport::JSON</tt> to decode messages as JSON before passing to the callback.
100
- # Defaults to <tt>coder: nil</tt> which does no decoding, passes raw messages.
113
+ # Pass `coder: ActiveSupport::JSON` to decode messages as JSON before passing to
114
+ # the callback. Defaults to `coder: nil` which does no decoding, passes raw
115
+ # messages.
101
116
  def stream_for(model, callback = nil, coder: nil, &block)
102
117
  stream_from(broadcasting_for(model), callback || block, coder: coder)
103
118
  end
104
119
 
105
- # Unsubscribes streams from the named <tt>broadcasting</tt>.
120
+ # Unsubscribes streams from the named `broadcasting`.
106
121
  def stop_stream_from(broadcasting)
107
122
  callback = streams.delete(broadcasting)
108
123
  if callback
@@ -111,7 +126,7 @@ module ActionCable
111
126
  end
112
127
  end
113
128
 
114
- # Unsubscribes streams for the <tt>model</tt>.
129
+ # Unsubscribes streams for the `model`.
115
130
  def stop_stream_for(model)
116
131
  stop_stream_from(broadcasting_for(model))
117
132
  end
@@ -124,13 +139,11 @@ module ActionCable
124
139
  end.clear
125
140
  end
126
141
 
127
- # Calls stream_for if record is present, otherwise calls reject.
128
- # This method is intended to be called when you're looking
129
- # for a record based on a parameter, if its found it will start
130
- # streaming. If the record is nil then it will reject the connection.
131
- def stream_or_reject_for(record)
132
- if record
133
- stream_for record
142
+ # Calls stream_for with the given `model` if it's present to start streaming,
143
+ # otherwise rejects the subscription.
144
+ def stream_or_reject_for(model)
145
+ if model
146
+ stream_for model
134
147
  else
135
148
  reject
136
149
  end
@@ -143,8 +156,8 @@ module ActionCable
143
156
  @_streams ||= {}
144
157
  end
145
158
 
146
- # Always wrap the outermost handler to invoke the user handler on the
147
- # worker pool rather than blocking the event loop.
159
+ # Always wrap the outermost handler to invoke the user handler on the worker
160
+ # pool rather than blocking the event loop.
148
161
  def worker_pool_stream_handler(broadcasting, user_handler, coder: nil)
149
162
  handler = stream_handler(broadcasting, user_handler, coder: coder)
150
163
 
@@ -153,8 +166,8 @@ module ActionCable
153
166
  end
154
167
  end
155
168
 
156
- # May be overridden to add instrumentation, logging, specialized error
157
- # handling, or other forms of handler decoration.
169
+ # May be overridden to add instrumentation, logging, specialized error handling,
170
+ # or other forms of handler decoration.
158
171
  #
159
172
  # TODO: Tests demonstrating this.
160
173
  def stream_handler(broadcasting, user_handler, coder: nil)
@@ -165,14 +178,14 @@ module ActionCable
165
178
  end
166
179
  end
167
180
 
168
- # May be overridden to change the default stream handling behavior
169
- # which decodes JSON and transmits to the client.
181
+ # May be overridden to change the default stream handling behavior which decodes
182
+ # JSON and transmits to the client.
170
183
  #
171
184
  # TODO: Tests demonstrating this.
172
185
  #
173
- # TODO: Room for optimization. Update transmit API to be coder-aware
174
- # so we can no-op when pubsub and connection are both JSON-encoded.
175
- # Then we can skip decode+encode if we're just proxying messages.
186
+ # TODO: Room for optimization. Update transmit API to be coder-aware so we can
187
+ # no-op when pubsub and connection are both JSON-encoded. Then we can skip
188
+ # decode+encode if we're just proxying messages.
176
189
  def default_stream_handler(broadcasting, coder:)
177
190
  coder ||= ActiveSupport::JSON
178
191
  stream_transmitter stream_decoder(coder: coder), broadcasting: broadcasting
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  require "active_support"
4
6
  require "active_support/test_case"
5
7
  require "active_support/core_ext/hash/indifferent_access"
@@ -15,9 +17,10 @@ module ActionCable
15
17
  end
16
18
  end
17
19
 
18
- # Stub +stream_from+ to track streams for the channel.
19
- # Add public aliases for +subscription_confirmation_sent?+ and
20
- # +subscription_rejected?+.
20
+ # # Action Cable Channel Stub
21
+ #
22
+ # Stub `stream_from` to track streams for the channel. Add public aliases for
23
+ # `subscription_confirmation_sent?` and `subscription_rejected?`.
21
24
  module ChannelStub
22
25
  def confirmed?
23
26
  subscription_confirmation_sent?
@@ -45,9 +48,12 @@ module ActionCable
45
48
  end
46
49
 
47
50
  class ConnectionStub
48
- attr_reader :transmissions, :identifiers, :subscriptions, :logger
51
+ attr_reader :server, :transmissions, :identifiers, :subscriptions, :logger
52
+
53
+ delegate :pubsub, :config, to: :server
49
54
 
50
55
  def initialize(identifiers = {})
56
+ @server = ActionCable.server
51
57
  @transmissions = []
52
58
 
53
59
  identifiers.each do |identifier, val|
@@ -62,107 +68,125 @@ module ActionCable
62
68
  def transmit(cable_message)
63
69
  transmissions << cable_message.with_indifferent_access
64
70
  end
71
+
72
+ def connection_identifier
73
+ @connection_identifier ||= connection_gid(identifiers.filter_map { |id| send(id.to_sym) if id })
74
+ end
75
+
76
+ private
77
+ def connection_gid(ids)
78
+ ids.map do |o|
79
+ if o.respond_to?(:to_gid_param)
80
+ o.to_gid_param
81
+ else
82
+ o.to_s
83
+ end
84
+ end.sort.join(":")
85
+ end
65
86
  end
66
87
 
67
88
  # Superclass for Action Cable channel functional tests.
68
89
  #
69
- # == Basic example
90
+ # ## Basic example
70
91
  #
71
92
  # Functional tests are written as follows:
72
- # 1. First, one uses the +subscribe+ method to simulate subscription creation.
73
- # 2. Then, one asserts whether the current state is as expected. "State" can be anything:
74
- # transmitted messages, subscribed streams, etc.
93
+ # 1. First, one uses the `subscribe` method to simulate subscription creation.
94
+ # 2. Then, one asserts whether the current state is as expected. "State" can be
95
+ # anything: transmitted messages, subscribed streams, etc.
96
+ #
75
97
  #
76
98
  # For example:
77
99
  #
78
- # class ChatChannelTest < ActionCable::Channel::TestCase
79
- # def test_subscribed_with_room_number
80
- # # Simulate a subscription creation
81
- # subscribe room_number: 1
100
+ # class ChatChannelTest < ActionCable::Channel::TestCase
101
+ # def test_subscribed_with_room_number
102
+ # # Simulate a subscription creation
103
+ # subscribe room_number: 1
82
104
  #
83
- # # Asserts that the subscription was successfully created
84
- # assert subscription.confirmed?
105
+ # # Asserts that the subscription was successfully created
106
+ # assert subscription.confirmed?
85
107
  #
86
- # # Asserts that the channel subscribes connection to a stream
87
- # assert_has_stream "chat_1"
108
+ # # Asserts that the channel subscribes connection to a stream
109
+ # assert_has_stream "chat_1"
88
110
  #
89
- # # Asserts that the channel subscribes connection to a specific
90
- # # stream created for a model
91
- # assert_has_stream_for Room.find(1)
92
- # end
111
+ # # Asserts that the channel subscribes connection to a specific
112
+ # # stream created for a model
113
+ # assert_has_stream_for Room.find(1)
114
+ # end
93
115
  #
94
- # def test_does_not_stream_with_incorrect_room_number
95
- # subscribe room_number: -1
116
+ # def test_does_not_stream_with_incorrect_room_number
117
+ # subscribe room_number: -1
96
118
  #
97
- # # Asserts that not streams was started
98
- # assert_no_streams
99
- # end
119
+ # # Asserts that not streams was started
120
+ # assert_no_streams
121
+ # end
100
122
  #
101
- # def test_does_not_subscribe_without_room_number
102
- # subscribe
123
+ # def test_does_not_subscribe_without_room_number
124
+ # subscribe
103
125
  #
104
- # # Asserts that the subscription was rejected
105
- # assert subscription.rejected?
126
+ # # Asserts that the subscription was rejected
127
+ # assert subscription.rejected?
128
+ # end
106
129
  # end
107
- # end
108
130
  #
109
131
  # You can also perform actions:
110
- # def test_perform_speak
111
- # subscribe room_number: 1
132
+ # def test_perform_speak
133
+ # subscribe room_number: 1
112
134
  #
113
- # perform :speak, message: "Hello, Rails!"
135
+ # perform :speak, message: "Hello, Rails!"
114
136
  #
115
- # assert_equal "Hello, Rails!", transmissions.last["text"]
116
- # end
137
+ # assert_equal "Hello, Rails!", transmissions.last["text"]
138
+ # end
117
139
  #
118
- # == Special methods
140
+ # ## Special methods
119
141
  #
120
- # ActionCable::Channel::TestCase will also automatically provide the following instance
121
- # methods for use in the tests:
142
+ # ActionCable::Channel::TestCase will also automatically provide the following
143
+ # instance methods for use in the tests:
122
144
  #
123
- # <b>connection</b>::
124
- # An ActionCable::Channel::ConnectionStub, representing the current HTTP connection.
125
- # <b>subscription</b>::
126
- # An instance of the current channel, created when you call +subscribe+.
127
- # <b>transmissions</b>::
128
- # A list of all messages that have been transmitted into the channel.
145
+ # connection
146
+ # : An ActionCable::Channel::ConnectionStub, representing the current HTTP
147
+ # connection.
129
148
  #
149
+ # subscription
150
+ # : An instance of the current channel, created when you call `subscribe`.
130
151
  #
131
- # == Channel is automatically inferred
152
+ # transmissions
153
+ # : A list of all messages that have been transmitted into the channel.
154
+ #
155
+ #
156
+ # ## Channel is automatically inferred
132
157
  #
133
158
  # ActionCable::Channel::TestCase will automatically infer the channel under test
134
159
  # from the test class name. If the channel cannot be inferred from the test
135
- # class name, you can explicitly set it with +tests+.
160
+ # class name, you can explicitly set it with `tests`.
136
161
  #
137
- # class SpecialEdgeCaseChannelTest < ActionCable::Channel::TestCase
138
- # tests SpecialChannel
139
- # end
140
- #
141
- # == Specifying connection identifiers
162
+ # class SpecialEdgeCaseChannelTest < ActionCable::Channel::TestCase
163
+ # tests SpecialChannel
164
+ # end
142
165
  #
143
- # You need to set up your connection manually to provide values for the identifiers.
144
- # To do this just use:
166
+ # ## Specifying connection identifiers
145
167
  #
146
- # stub_connection(user: users(:john))
168
+ # You need to set up your connection manually to provide values for the
169
+ # identifiers. To do this just use:
147
170
  #
148
- # == Testing broadcasting
171
+ # stub_connection(user: users(:john))
149
172
  #
150
- # ActionCable::Channel::TestCase enhances ActionCable::TestHelper assertions (e.g.
151
- # +assert_broadcasts+) to handle broadcasting to models:
173
+ # ## Testing broadcasting
152
174
  #
175
+ # ActionCable::Channel::TestCase enhances ActionCable::TestHelper assertions
176
+ # (e.g. `assert_broadcasts`) to handle broadcasting to models:
153
177
  #
154
- # # in your channel
155
- # def speak(data)
156
- # broadcast_to room, text: data["message"]
157
- # end
178
+ # # in your channel
179
+ # def speak(data)
180
+ # broadcast_to room, text: data["message"]
181
+ # end
158
182
  #
159
- # def test_speak
160
- # subscribe room_id: rooms(:chat).id
183
+ # def test_speak
184
+ # subscribe room_id: rooms(:chat).id
161
185
  #
162
- # assert_broadcast_on(rooms(:chat), text: "Hello, Rails!") do
163
- # perform :speak, message: "Hello, Rails!"
164
- # end
165
- # end
186
+ # assert_broadcast_on(rooms(:chat), text: "Hello, Rails!") do
187
+ # perform :speak, message: "Hello, Rails!"
188
+ # end
189
+ # end
166
190
  class TestCase < ActiveSupport::TestCase
167
191
  module Behavior
168
192
  extend ActiveSupport::Concern
@@ -211,16 +235,17 @@ module ActionCable
211
235
 
212
236
  # Set up test connection with the specified identifiers:
213
237
  #
214
- # class ApplicationCable < ActionCable::Connection::Base
215
- # identified_by :user, :token
216
- # end
238
+ # class ApplicationCable < ActionCable::Connection::Base
239
+ # identified_by :user, :token
240
+ # end
217
241
  #
218
- # stub_connection(user: users[:john], token: 'my-secret-token')
242
+ # stub_connection(user: users[:john], token: 'my-secret-token')
219
243
  def stub_connection(identifiers = {})
220
244
  @connection = ConnectionStub.new(identifiers)
221
245
  end
222
246
 
223
- # Subscribe to the channel under test. Optionally pass subscription parameters as a Hash.
247
+ # Subscribe to the channel under test. Optionally pass subscription parameters
248
+ # as a Hash.
224
249
  def subscribe(params = {})
225
250
  @connection ||= stub_connection
226
251
  @subscription = self.class.channel_class.new(connection, CHANNEL_IDENTIFIER, params.with_indifferent_access)
@@ -246,11 +271,10 @@ module ActionCable
246
271
  # Returns messages transmitted into channel
247
272
  def transmissions
248
273
  # Return only directly sent message (via #transmit)
249
- connection.transmissions.map { |data| data["message"] }.compact
274
+ connection.transmissions.filter_map { |data| data["message"] }
250
275
  end
251
276
 
252
- # Enhance TestHelper assertions to handle non-String
253
- # broadcastings
277
+ # Enhance TestHelper assertions to handle non-String broadcastings
254
278
  def assert_broadcasts(stream_or_object, *args)
255
279
  super(broadcasting_for(stream_or_object), *args)
256
280
  end
@@ -261,10 +285,10 @@ module ActionCable
261
285
 
262
286
  # Asserts that no streams have been started.
263
287
  #
264
- # def test_assert_no_started_stream
265
- # subscribe
266
- # assert_no_streams
267
- # end
288
+ # def test_assert_no_started_stream
289
+ # subscribe
290
+ # assert_no_streams
291
+ # end
268
292
  #
269
293
  def assert_no_streams
270
294
  assert subscription.streams.empty?, "No streams started was expected, but #{subscription.streams.count} found"
@@ -272,10 +296,10 @@ module ActionCable
272
296
 
273
297
  # Asserts that the specified stream has been started.
274
298
  #
275
- # def test_assert_started_stream
276
- # subscribe
277
- # assert_has_stream 'messages'
278
- # end
299
+ # def test_assert_started_stream
300
+ # subscribe
301
+ # assert_has_stream 'messages'
302
+ # end
279
303
  #
280
304
  def assert_has_stream(stream)
281
305
  assert subscription.streams.include?(stream), "Stream #{stream} has not been started"
@@ -283,15 +307,37 @@ module ActionCable
283
307
 
284
308
  # Asserts that the specified stream for a model has started.
285
309
  #
286
- # def test_assert_started_stream_for
287
- # subscribe id: 42
288
- # assert_has_stream_for User.find(42)
289
- # end
310
+ # def test_assert_started_stream_for
311
+ # subscribe id: 42
312
+ # assert_has_stream_for User.find(42)
313
+ # end
290
314
  #
291
315
  def assert_has_stream_for(object)
292
316
  assert_has_stream(broadcasting_for(object))
293
317
  end
294
318
 
319
+ # Asserts that the specified stream has not been started.
320
+ #
321
+ # def test_assert_no_started_stream
322
+ # subscribe
323
+ # assert_has_no_stream 'messages'
324
+ # end
325
+ #
326
+ def assert_has_no_stream(stream)
327
+ assert subscription.streams.exclude?(stream), "Stream #{stream} has been started"
328
+ end
329
+
330
+ # Asserts that the specified stream for a model has not started.
331
+ #
332
+ # def test_assert_no_started_stream_for
333
+ # subscribe id: 41
334
+ # assert_has_no_stream_for User.find(42)
335
+ # end
336
+ #
337
+ def assert_has_no_stream_for(object)
338
+ assert_has_no_stream(broadcasting_for(object))
339
+ end
340
+
295
341
  private
296
342
  def check_subscribed!
297
343
  raise "Must be subscribed!" if subscription.nil? || subscription.rejected?
@@ -1,11 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionCable
4
6
  module Connection
5
7
  module Authorization
6
8
  class UnauthorizedError < StandardError; end
7
9
 
8
- # Closes the WebSocket connection if it is open and returns a 404 "File not Found" response.
10
+ # Closes the WebSocket connection if it is open and returns an "unauthorized"
11
+ # reason.
9
12
  def reject_unauthorized_connection
10
13
  logger.error "An unauthorized connection attempt was rejected"
11
14
  raise UnauthorizedError