actioncable 7.1.3.4 → 7.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 +26 -129
- data/app/assets/javascripts/action_cable.js +3 -3
- data/app/assets/javascripts/actioncable.esm.js +3 -3
- data/app/assets/javascripts/actioncable.js +3 -3
- data/lib/action_cable/channel/base.rb +98 -86
- data/lib/action_cable/channel/broadcasting.rb +25 -18
- data/lib/action_cable/channel/callbacks.rb +27 -25
- data/lib/action_cable/channel/naming.rb +9 -8
- data/lib/action_cable/channel/periodic_timers.rb +7 -7
- data/lib/action_cable/channel/streams.rb +77 -64
- data/lib/action_cable/channel/test_case.rb +112 -86
- data/lib/action_cable/connection/authorization.rb +4 -1
- data/lib/action_cable/connection/base.rb +53 -38
- data/lib/action_cable/connection/callbacks.rb +20 -18
- data/lib/action_cable/connection/client_socket.rb +3 -1
- data/lib/action_cable/connection/identification.rb +9 -5
- data/lib/action_cable/connection/internal_channel.rb +5 -2
- data/lib/action_cable/connection/message_buffer.rb +4 -1
- data/lib/action_cable/connection/stream.rb +2 -0
- data/lib/action_cable/connection/stream_event_loop.rb +4 -3
- data/lib/action_cable/connection/subscriptions.rb +6 -3
- data/lib/action_cable/connection/tagged_logger_proxy.rb +7 -4
- data/lib/action_cable/connection/test_case.rb +66 -56
- data/lib/action_cable/connection/web_socket.rb +10 -8
- data/lib/action_cable/deprecator.rb +2 -0
- data/lib/action_cable/engine.rb +5 -3
- data/lib/action_cable/gem_version.rb +6 -4
- data/lib/action_cable/helpers/action_cable_helper.rb +21 -19
- data/lib/action_cable/remote_connections.rb +19 -16
- data/lib/action_cable/server/base.rb +27 -15
- data/lib/action_cable/server/broadcasting.rb +23 -17
- data/lib/action_cable/server/configuration.rb +17 -14
- data/lib/action_cable/server/connections.rb +11 -5
- data/lib/action_cable/server/worker/active_record_connection_management.rb +2 -0
- data/lib/action_cable/server/worker.rb +4 -2
- data/lib/action_cable/subscription_adapter/async.rb +2 -0
- data/lib/action_cable/subscription_adapter/base.rb +2 -0
- data/lib/action_cable/subscription_adapter/channel_prefix.rb +2 -0
- data/lib/action_cable/subscription_adapter/inline.rb +2 -0
- data/lib/action_cable/subscription_adapter/postgresql.rb +4 -2
- data/lib/action_cable/subscription_adapter/redis.rb +5 -2
- data/lib/action_cable/subscription_adapter/subscriber_map.rb +2 -0
- data/lib/action_cable/subscription_adapter/test.rb +8 -5
- data/lib/action_cable/test_case.rb +2 -0
- data/lib/action_cable/test_helper.rb +51 -52
- data/lib/action_cable/version.rb +3 -1
- data/lib/action_cable.rb +13 -7
- data/lib/rails/generators/channel/channel_generator.rb +4 -2
- data/lib/rails/generators/test_unit/channel_generator.rb +2 -0
- metadata +13 -13
- /data/lib/rails/generators/channel/templates/application_cable/{channel.rb → channel.rb.tt} +0 -0
- /data/lib/rails/generators/channel/templates/application_cable/{connection.rb → connection.rb.tt} +0 -0
@@ -1,36 +1,39 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
require "active_support/callbacks"
|
4
6
|
|
5
7
|
module ActionCable
|
6
8
|
module Channel
|
7
|
-
#
|
9
|
+
# # Action Cable Channel Callbacks
|
10
|
+
#
|
11
|
+
# Action Cable Channel provides callback hooks that are invoked during the life
|
12
|
+
# cycle of a channel:
|
8
13
|
#
|
9
|
-
#
|
10
|
-
#
|
14
|
+
# * [before_subscribe](rdoc-ref:ClassMethods#before_subscribe)
|
15
|
+
# * [after_subscribe](rdoc-ref:ClassMethods#after_subscribe) (aliased as
|
16
|
+
# [on_subscribe](rdoc-ref:ClassMethods#on_subscribe))
|
17
|
+
# * [before_unsubscribe](rdoc-ref:ClassMethods#before_unsubscribe)
|
18
|
+
# * [after_unsubscribe](rdoc-ref:ClassMethods#after_unsubscribe) (aliased as
|
19
|
+
# [on_unsubscribe](rdoc-ref:ClassMethods#on_unsubscribe))
|
11
20
|
#
|
12
|
-
# * {before_subscribe}[rdoc-ref:ClassMethods#before_subscribe]
|
13
|
-
# * {after_subscribe}[rdoc-ref:ClassMethods#after_subscribe] (aliased as
|
14
|
-
# {on_subscribe}[rdoc-ref:ClassMethods#on_subscribe])
|
15
|
-
# * {before_unsubscribe}[rdoc-ref:ClassMethods#before_unsubscribe]
|
16
|
-
# * {after_unsubscribe}[rdoc-ref:ClassMethods#after_unsubscribe] (aliased as
|
17
|
-
# {on_unsubscribe}[rdoc-ref:ClassMethods#on_unsubscribe])
|
18
21
|
#
|
19
|
-
#
|
22
|
+
# #### Example
|
20
23
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
+
# class ChatChannel < ApplicationCable::Channel
|
25
|
+
# after_subscribe :send_welcome_message, unless: :subscription_rejected?
|
26
|
+
# after_subscribe :track_subscription
|
24
27
|
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
28
|
-
#
|
28
|
+
# private
|
29
|
+
# def send_welcome_message
|
30
|
+
# broadcast_to(...)
|
31
|
+
# end
|
29
32
|
#
|
30
|
-
#
|
31
|
-
#
|
32
|
-
#
|
33
|
-
#
|
33
|
+
# def track_subscription
|
34
|
+
# # ...
|
35
|
+
# end
|
36
|
+
# end
|
34
37
|
#
|
35
38
|
module Callbacks
|
36
39
|
extend ActiveSupport::Concern
|
@@ -46,14 +49,13 @@ module ActionCable
|
|
46
49
|
set_callback(:subscribe, :before, *methods, &block)
|
47
50
|
end
|
48
51
|
|
49
|
-
# This callback will be triggered after the Base#subscribed method is
|
50
|
-
#
|
51
|
-
# method.
|
52
|
+
# This callback will be triggered after the Base#subscribed method is called,
|
53
|
+
# even if the subscription was rejected with the Base#reject method.
|
52
54
|
#
|
53
55
|
# To trigger the callback only on successful subscriptions, use the
|
54
56
|
# Base#subscription_rejected? method:
|
55
57
|
#
|
56
|
-
#
|
58
|
+
# after_subscribe :my_method, unless: :subscription_rejected?
|
57
59
|
#
|
58
60
|
def after_subscribe(*methods, &block)
|
59
61
|
set_callback(:subscribe, :after, *methods, &block)
|
@@ -1,26 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionCable
|
4
6
|
module Channel
|
5
7
|
module Naming
|
6
8
|
extend ActiveSupport::Concern
|
7
9
|
|
8
10
|
module ClassMethods
|
9
|
-
# Returns the name of the channel, underscored, without the
|
10
|
-
#
|
11
|
+
# Returns the name of the channel, underscored, without the `Channel` ending. If
|
12
|
+
# the channel is in a namespace, then the namespaces are represented by single
|
11
13
|
# colon separators in the channel name.
|
12
14
|
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
15
|
+
# ChatChannel.channel_name # => 'chat'
|
16
|
+
# Chats::AppearancesChannel.channel_name # => 'chats:appearances'
|
17
|
+
# FooChats::BarAppearancesChannel.channel_name # => 'foo_chats:bar_appearances'
|
16
18
|
def channel_name
|
17
19
|
@channel_name ||= name.delete_suffix("Channel").gsub("::", ":").underscore
|
18
20
|
end
|
19
21
|
end
|
20
22
|
|
21
|
-
|
22
|
-
|
23
|
-
delegate :channel_name, to: :class
|
23
|
+
def channel_name
|
24
|
+
self.class.channel_name
|
24
25
|
end
|
25
26
|
end
|
26
27
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
# :markup: markdown
|
4
|
+
|
3
5
|
module ActionCable
|
4
6
|
module Channel
|
5
7
|
module PeriodicTimers
|
@@ -13,14 +15,12 @@ module ActionCable
|
|
13
15
|
end
|
14
16
|
|
15
17
|
module ClassMethods
|
16
|
-
# Periodically performs a task on the channel, like updating an online
|
17
|
-
#
|
18
|
-
#
|
19
|
-
# progress updates.
|
18
|
+
# Periodically performs a task on the channel, like updating an online user
|
19
|
+
# counter, polling a backend for new status messages, sending regular
|
20
|
+
# "heartbeat" messages, or doing some internal work and giving progress updates.
|
20
21
|
#
|
21
|
-
# Pass a method name or lambda argument or provide a block to call.
|
22
|
-
#
|
23
|
-
# keyword argument.
|
22
|
+
# Pass a method name or lambda argument or provide a block to call. Specify the
|
23
|
+
# calling period in seconds using the `every:` keyword argument.
|
24
24
|
#
|
25
25
|
# periodically :transmit_progress, every: 5.seconds
|
26
26
|
#
|
@@ -1,67 +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
|
-
# Streams allow channels to route broadcastings to the subscriber. A
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
#
|
16
|
-
#
|
17
|
-
#
|
18
|
-
#
|
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
|
19
25
|
#
|
20
|
-
#
|
21
|
-
#
|
26
|
+
# def unfollow
|
27
|
+
# stop_all_streams
|
28
|
+
# end
|
22
29
|
# end
|
23
|
-
# end
|
24
30
|
#
|
25
|
-
# Based on the above example, the subscribers of this channel will get whatever
|
26
|
-
# 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.
|
27
34
|
#
|
28
35
|
# An example broadcasting for this channel looks like so:
|
29
36
|
#
|
30
|
-
#
|
37
|
+
# ActionCable.server.broadcast "comments_for_45", { author: 'DHH', content: 'Rails is just swell' }
|
31
38
|
#
|
32
|
-
# If you have a stream that is related to a model, then the broadcasting used
|
33
|
-
#
|
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`.
|
34
42
|
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
43
|
+
# class CommentsChannel < ApplicationCable::Channel
|
44
|
+
# def subscribed
|
45
|
+
# post = Post.find(params[:id])
|
46
|
+
# stream_for post
|
47
|
+
# end
|
39
48
|
# end
|
40
|
-
# end
|
41
49
|
#
|
42
50
|
# You can then broadcast to this channel using:
|
43
51
|
#
|
44
|
-
#
|
52
|
+
# CommentsChannel.broadcast_to(@post, @comment)
|
45
53
|
#
|
46
|
-
# If you don't just want to parlay the broadcast unfiltered to the subscriber,
|
47
|
-
#
|
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:
|
48
58
|
#
|
49
|
-
#
|
50
|
-
#
|
51
|
-
#
|
59
|
+
# class ChatChannel < ApplicationCable::Channel
|
60
|
+
# def subscribed
|
61
|
+
# @room = Chat::Room[params[:room_number]]
|
52
62
|
#
|
53
|
-
#
|
54
|
-
#
|
55
|
-
#
|
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)
|
56
66
|
#
|
57
|
-
#
|
58
|
-
#
|
59
|
-
#
|
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
|
60
70
|
#
|
61
|
-
#
|
71
|
+
# transmit message
|
72
|
+
# end
|
62
73
|
# end
|
63
74
|
# end
|
64
|
-
# end
|
65
75
|
#
|
66
76
|
# You can stop streaming from all broadcasts by calling #stop_all_streams.
|
67
77
|
module Streams
|
@@ -71,18 +81,20 @@ module ActionCable
|
|
71
81
|
on_unsubscribe :stop_all_streams
|
72
82
|
end
|
73
83
|
|
74
|
-
# Start streaming from the named
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
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.
|
78
90
|
def stream_from(broadcasting, callback = nil, coder: nil, &block)
|
79
91
|
broadcasting = String(broadcasting)
|
80
92
|
|
81
93
|
# Don't send the confirmation until pubsub#subscribe is successful
|
82
94
|
defer_subscription_confirmation!
|
83
95
|
|
84
|
-
# Build a stream handler by wrapping the user-provided callback with
|
85
|
-
#
|
96
|
+
# Build a stream handler by wrapping the user-provided callback with a decoder
|
97
|
+
# or defaulting to a JSON-decoding retransmitter.
|
86
98
|
handler = worker_pool_stream_handler(broadcasting, callback || block, coder: coder)
|
87
99
|
streams[broadcasting] = handler
|
88
100
|
|
@@ -94,17 +106,18 @@ module ActionCable
|
|
94
106
|
end
|
95
107
|
end
|
96
108
|
|
97
|
-
# Start streaming the pubsub queue for the
|
98
|
-
#
|
99
|
-
# 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.
|
100
112
|
#
|
101
|
-
# Pass
|
102
|
-
# 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.
|
103
116
|
def stream_for(model, callback = nil, coder: nil, &block)
|
104
117
|
stream_from(broadcasting_for(model), callback || block, coder: coder)
|
105
118
|
end
|
106
119
|
|
107
|
-
# Unsubscribes streams from the named
|
120
|
+
# Unsubscribes streams from the named `broadcasting`.
|
108
121
|
def stop_stream_from(broadcasting)
|
109
122
|
callback = streams.delete(broadcasting)
|
110
123
|
if callback
|
@@ -113,7 +126,7 @@ module ActionCable
|
|
113
126
|
end
|
114
127
|
end
|
115
128
|
|
116
|
-
# Unsubscribes streams for the
|
129
|
+
# Unsubscribes streams for the `model`.
|
117
130
|
def stop_stream_for(model)
|
118
131
|
stop_stream_from(broadcasting_for(model))
|
119
132
|
end
|
@@ -126,7 +139,7 @@ module ActionCable
|
|
126
139
|
end.clear
|
127
140
|
end
|
128
141
|
|
129
|
-
# Calls stream_for with the given
|
142
|
+
# Calls stream_for with the given `model` if it's present to start streaming,
|
130
143
|
# otherwise rejects the subscription.
|
131
144
|
def stream_or_reject_for(model)
|
132
145
|
if model
|
@@ -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
|