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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -160
- data/MIT-LICENSE +1 -1
- data/README.md +5 -5
- data/app/assets/javascripts/action_cable.js +239 -302
- data/app/assets/javascripts/actioncable.esm.js +512 -0
- data/app/assets/javascripts/actioncable.js +510 -0
- data/lib/action_cable/channel/base.rb +114 -90
- data/lib/action_cable/channel/broadcasting.rb +25 -16
- data/lib/action_cable/channel/callbacks.rb +39 -0
- data/lib/action_cable/channel/naming.rb +10 -7
- data/lib/action_cable/channel/periodic_timers.rb +7 -7
- data/lib/action_cable/channel/streams.rb +81 -68
- data/lib/action_cable/channel/test_case.rb +133 -87
- data/lib/action_cable/connection/authorization.rb +4 -1
- data/lib/action_cable/connection/base.rb +71 -43
- data/lib/action_cable/connection/callbacks.rb +57 -0
- data/lib/action_cable/connection/client_socket.rb +3 -1
- data/lib/action_cable/connection/identification.rb +10 -6
- data/lib/action_cable/connection/internal_channel.rb +7 -2
- data/lib/action_cable/connection/message_buffer.rb +4 -1
- data/lib/action_cable/connection/stream.rb +2 -2
- data/lib/action_cable/connection/stream_event_loop.rb +4 -4
- data/lib/action_cable/connection/subscriptions.rb +8 -3
- data/lib/action_cable/connection/tagged_logger_proxy.rb +14 -9
- data/lib/action_cable/connection/test_case.rb +67 -55
- data/lib/action_cable/connection/web_socket.rb +11 -7
- data/lib/action_cable/deprecator.rb +9 -0
- data/lib/action_cable/engine.rb +28 -9
- data/lib/action_cable/gem_version.rb +7 -5
- data/lib/action_cable/helpers/action_cable_helper.rb +21 -18
- data/lib/action_cable/remote_connections.rb +25 -13
- data/lib/action_cable/server/base.rb +29 -14
- data/lib/action_cable/server/broadcasting.rb +24 -16
- data/lib/action_cable/server/configuration.rb +28 -14
- data/lib/action_cable/server/connections.rb +13 -5
- data/lib/action_cable/server/worker/active_record_connection_management.rb +4 -2
- data/lib/action_cable/server/worker.rb +7 -7
- data/lib/action_cable/subscription_adapter/async.rb +1 -1
- 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 +6 -5
- data/lib/action_cable/subscription_adapter/redis.rb +101 -25
- data/lib/action_cable/subscription_adapter/subscriber_map.rb +2 -0
- data/lib/action_cable/subscription_adapter/test.rb +7 -6
- data/lib/action_cable/test_case.rb +2 -0
- data/lib/action_cable/test_helper.rb +89 -59
- data/lib/action_cable/version.rb +3 -1
- data/lib/action_cable.rb +30 -12
- data/lib/rails/generators/channel/USAGE +14 -8
- data/lib/rails/generators/channel/channel_generator.rb +95 -20
- data/lib/rails/generators/channel/templates/javascript/index.js.tt +1 -5
- data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
- metadata +29 -15
- data/lib/action_cable/channel.rb +0 -17
- data/lib/action_cable/connection.rb +0 -22
- data/lib/action_cable/server.rb +0 -16
- 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
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
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
|
-
#
|
19
|
-
#
|
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
|
24
|
-
# let's say,
|
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
|
-
#
|
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
|
31
|
-
#
|
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
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
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
|
-
#
|
52
|
+
# CommentsChannel.broadcast_to(@post, @comment)
|
43
53
|
#
|
44
|
-
# If you don't just want to parlay the broadcast unfiltered to the subscriber,
|
45
|
-
#
|
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
|
-
#
|
48
|
-
#
|
49
|
-
#
|
59
|
+
# class ChatChannel < ApplicationCable::Channel
|
60
|
+
# def subscribed
|
61
|
+
# @room = Chat::Room[params[:room_number]]
|
50
62
|
#
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
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
|
-
#
|
56
|
-
#
|
57
|
-
#
|
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
|
-
#
|
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
|
73
|
-
#
|
74
|
-
#
|
75
|
-
#
|
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
|
-
#
|
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
|
96
|
-
#
|
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
|
100
|
-
# Defaults to
|
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
|
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
|
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
|
128
|
-
#
|
129
|
-
|
130
|
-
|
131
|
-
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
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
|
-
#
|
175
|
-
#
|
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
|
-
#
|
19
|
-
#
|
20
|
-
#
|
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
|
-
#
|
90
|
+
# ## Basic example
|
70
91
|
#
|
71
92
|
# Functional tests are written as follows:
|
72
|
-
# 1.
|
73
|
-
# 2.
|
74
|
-
#
|
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
|
-
#
|
79
|
-
#
|
80
|
-
#
|
81
|
-
#
|
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
|
-
#
|
84
|
-
#
|
105
|
+
# # Asserts that the subscription was successfully created
|
106
|
+
# assert subscription.confirmed?
|
85
107
|
#
|
86
|
-
#
|
87
|
-
#
|
108
|
+
# # Asserts that the channel subscribes connection to a stream
|
109
|
+
# assert_has_stream "chat_1"
|
88
110
|
#
|
89
|
-
#
|
90
|
-
#
|
91
|
-
#
|
92
|
-
#
|
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
|
-
#
|
95
|
-
#
|
116
|
+
# def test_does_not_stream_with_incorrect_room_number
|
117
|
+
# subscribe room_number: -1
|
96
118
|
#
|
97
|
-
#
|
98
|
-
#
|
99
|
-
#
|
119
|
+
# # Asserts that not streams was started
|
120
|
+
# assert_no_streams
|
121
|
+
# end
|
100
122
|
#
|
101
|
-
#
|
102
|
-
#
|
123
|
+
# def test_does_not_subscribe_without_room_number
|
124
|
+
# subscribe
|
103
125
|
#
|
104
|
-
#
|
105
|
-
#
|
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
|
-
#
|
111
|
-
#
|
132
|
+
# def test_perform_speak
|
133
|
+
# subscribe room_number: 1
|
112
134
|
#
|
113
|
-
#
|
135
|
+
# perform :speak, message: "Hello, Rails!"
|
114
136
|
#
|
115
|
-
#
|
116
|
-
#
|
137
|
+
# assert_equal "Hello, Rails!", transmissions.last["text"]
|
138
|
+
# end
|
117
139
|
#
|
118
|
-
#
|
140
|
+
# ## Special methods
|
119
141
|
#
|
120
|
-
# ActionCable::Channel::TestCase will also automatically provide the following
|
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
|
-
#
|
124
|
-
#
|
125
|
-
#
|
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
|
-
#
|
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
|
160
|
+
# class name, you can explicitly set it with `tests`.
|
136
161
|
#
|
137
|
-
#
|
138
|
-
#
|
139
|
-
#
|
140
|
-
#
|
141
|
-
# == Specifying connection identifiers
|
162
|
+
# class SpecialEdgeCaseChannelTest < ActionCable::Channel::TestCase
|
163
|
+
# tests SpecialChannel
|
164
|
+
# end
|
142
165
|
#
|
143
|
-
#
|
144
|
-
# To do this just use:
|
166
|
+
# ## Specifying connection identifiers
|
145
167
|
#
|
146
|
-
#
|
168
|
+
# You need to set up your connection manually to provide values for the
|
169
|
+
# identifiers. To do this just use:
|
147
170
|
#
|
148
|
-
#
|
171
|
+
# stub_connection(user: users(:john))
|
149
172
|
#
|
150
|
-
#
|
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
|
-
#
|
155
|
-
#
|
156
|
-
#
|
157
|
-
#
|
178
|
+
# # in your channel
|
179
|
+
# def speak(data)
|
180
|
+
# broadcast_to room, text: data["message"]
|
181
|
+
# end
|
158
182
|
#
|
159
|
-
#
|
160
|
-
#
|
183
|
+
# def test_speak
|
184
|
+
# subscribe room_id: rooms(:chat).id
|
161
185
|
#
|
162
|
-
#
|
163
|
-
#
|
164
|
-
#
|
165
|
-
#
|
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
|
-
#
|
215
|
-
#
|
216
|
-
#
|
238
|
+
# class ApplicationCable < ActionCable::Connection::Base
|
239
|
+
# identified_by :user, :token
|
240
|
+
# end
|
217
241
|
#
|
218
|
-
#
|
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
|
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.
|
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
|
-
#
|
265
|
-
#
|
266
|
-
#
|
267
|
-
#
|
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
|
-
#
|
276
|
-
#
|
277
|
-
#
|
278
|
-
#
|
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
|
-
#
|
287
|
-
#
|
288
|
-
#
|
289
|
-
#
|
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
|
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
|