rage-rb 1.28.2 → 1.29.0
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 +15 -0
- data/Gemfile +1 -1
- data/lib/rage/cable/cable.rb +84 -24
- data/lib/rage/cable/channel.rb +15 -9
- data/lib/rage/cable/connection.rb +62 -1
- data/lib/rage/cable/protocols/actioncable_v1_json.rb +36 -15
- data/lib/rage/cable/protocols/base.rb +18 -10
- data/lib/rage/cable/protocols/raw_web_socket_json.rb +22 -10
- data/lib/rage/cable/remote_disconnect.rb +69 -0
- data/lib/rage/cable/router.rb +42 -29
- data/lib/rage/code_loader.rb +1 -1
- data/lib/rage/configuration.rb +20 -19
- data/lib/rage/cookies.rb +11 -1
- data/lib/rage/daemon.rb +6 -5
- data/lib/rage/deferred/backends/disk.rb +215 -91
- data/lib/rage/deferred/backends/nil.rb +2 -2
- data/lib/rage/deferred/dead_task.rb +96 -0
- data/lib/rage/deferred/dead_tasks.rb +44 -0
- data/lib/rage/deferred/deferred.rb +28 -1
- data/lib/rage/extension.rb +8 -0
- data/lib/rage/fiber.rb +1 -1
- data/lib/rage/fiber_scheduler.rb +53 -39
- data/lib/rage/internal.rb +4 -0
- data/lib/rage/middleware/origin_validator.rb +35 -12
- data/lib/rage/pubsub/pubsub.rb +146 -1
- data/lib/rage/router/dsl.rb +4 -1
- data/lib/rage/rspec.rb +1 -6
- data/lib/rage/version.rb +1 -1
- data/lib/rage-rb.rb +6 -1
- metadata +6 -3
data/lib/rage/fiber_scheduler.rb
CHANGED
|
@@ -3,12 +3,20 @@
|
|
|
3
3
|
require "resolv"
|
|
4
4
|
|
|
5
5
|
class Rage::FiberScheduler
|
|
6
|
+
# @private
|
|
6
7
|
MAX_READ = 65536
|
|
7
8
|
|
|
9
|
+
# @private
|
|
10
|
+
VERSION = defined?(IO::Buffer::VERSION) && IO::Buffer::VERSION >= 3 ? 4 : 3
|
|
11
|
+
|
|
12
|
+
# @private
|
|
13
|
+
attr_reader :root_fiber
|
|
14
|
+
|
|
8
15
|
# Initialize the scheduler, storing the root fiber and an empty DNS cache.
|
|
9
16
|
def initialize
|
|
10
17
|
@root_fiber = Fiber.current
|
|
11
18
|
@dns_cache = {}
|
|
19
|
+
@blocked = {}
|
|
12
20
|
end
|
|
13
21
|
|
|
14
22
|
# Wait for I/O events on a file descriptor, yielding the fiber until ready or timeout.
|
|
@@ -29,43 +37,50 @@ class Rage::FiberScheduler
|
|
|
29
37
|
end
|
|
30
38
|
|
|
31
39
|
# Read data from an I/O object into a buffer, pausing the fiber between reads.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
else
|
|
36
|
-
length
|
|
40
|
+
if VERSION == 4
|
|
41
|
+
def io_read(io, buffer, offset, length)
|
|
42
|
+
::Iodine::Scheduler.read(io.fileno, buffer, length, offset)
|
|
37
43
|
end
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
+
else
|
|
45
|
+
def io_read(io, buffer, length, offset = 0)
|
|
46
|
+
length_to_read = if length == 0
|
|
47
|
+
buffer.size > MAX_READ ? MAX_READ : buffer.size
|
|
48
|
+
else
|
|
49
|
+
length
|
|
44
50
|
end
|
|
45
51
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
end
|
|
52
|
+
while true
|
|
53
|
+
result = ::Iodine::Scheduler.read(io.fileno, buffer, length_to_read, offset)
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
if result == 0
|
|
56
|
+
return offset
|
|
57
|
+
elsif result < 0
|
|
58
|
+
next if result == -Errno::EINTR::Errno
|
|
59
|
+
return result
|
|
60
|
+
end
|
|
51
61
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
return offset if size < length_to_read || size >= buffer.size
|
|
62
|
+
offset += result
|
|
63
|
+
return offset if result < length_to_read || result >= buffer.size
|
|
55
64
|
|
|
56
|
-
|
|
65
|
+
length_to_read = length_to_read.clamp(nil, buffer.size - offset)
|
|
66
|
+
Fiber.pause
|
|
67
|
+
end
|
|
57
68
|
end
|
|
58
69
|
end
|
|
59
70
|
|
|
60
71
|
unless ENV["RAGE_DISABLE_IO_WRITE"]
|
|
61
72
|
# Write data from a buffer to an I/O object.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
if VERSION == 4
|
|
74
|
+
def io_write(io, buffer, offset, length)
|
|
75
|
+
::Iodine::Scheduler.write(io.fileno, buffer, length, offset)
|
|
76
|
+
end
|
|
77
|
+
else
|
|
78
|
+
def io_write(io, buffer, length, offset = 0)
|
|
79
|
+
bytes_to_write = length
|
|
80
|
+
bytes_to_write = buffer.size if length == 0
|
|
67
81
|
|
|
68
|
-
|
|
82
|
+
::Iodine::Scheduler.write_async(io.fileno, buffer, bytes_to_write, offset)
|
|
83
|
+
end
|
|
69
84
|
end
|
|
70
85
|
end
|
|
71
86
|
|
|
@@ -106,28 +121,27 @@ class Rage::FiberScheduler
|
|
|
106
121
|
|
|
107
122
|
# Block the current fiber until unblocked or timeout.
|
|
108
123
|
def block(_blocker, timeout = nil)
|
|
109
|
-
f
|
|
110
|
-
|
|
124
|
+
f = Fiber.current
|
|
111
125
|
gen = (f.__wait_generation += 1)
|
|
112
|
-
|
|
126
|
+
@blocked[f] = gen
|
|
113
127
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
fulfilled = true
|
|
117
|
-
::Iodine.defer { ::Iodine.unsubscribe(channel) }
|
|
118
|
-
f.resume if f.alive? && gen == f.__wait_generation
|
|
119
|
-
end
|
|
128
|
+
if timeout
|
|
129
|
+
::Iodine.run_after((timeout * 1000).to_i) { unblock(nil, f) if gen == f.__wait_generation }
|
|
120
130
|
end
|
|
121
131
|
|
|
122
|
-
::Iodine.subscribe(channel) { resume_fiber_block.call }
|
|
123
|
-
::Iodine.run_after((timeout * 1000).to_i) { resume_fiber_block.call } if timeout
|
|
124
|
-
|
|
125
132
|
Fiber.yield
|
|
133
|
+
ensure
|
|
134
|
+
@blocked.delete(f)
|
|
126
135
|
end
|
|
127
136
|
|
|
128
|
-
# Unblock a fiber
|
|
137
|
+
# Unblock a fiber previously blocked by `#block`.
|
|
129
138
|
def unblock(_blocker, fiber)
|
|
130
|
-
|
|
139
|
+
gen = @blocked.delete(fiber)
|
|
140
|
+
return unless gen
|
|
141
|
+
|
|
142
|
+
::Iodine.defer do
|
|
143
|
+
fiber.resume if fiber.alive? && gen == fiber.__wait_generation
|
|
144
|
+
end
|
|
131
145
|
end
|
|
132
146
|
|
|
133
147
|
# Interrupt a fiber by incrementing its generation and raising an exception.
|
data/lib/rage/internal.rb
CHANGED
|
@@ -56,6 +56,10 @@ class Rage::Internal
|
|
|
56
56
|
"#{streamable.class.name}:#{streamable.id}"
|
|
57
57
|
elsif streamable.is_a?(String) || streamable.is_a?(Symbol) || streamable.is_a?(Numeric)
|
|
58
58
|
streamable
|
|
59
|
+
elsif streamable.is_a?(Array)
|
|
60
|
+
stream_name_for(streamable)
|
|
61
|
+
elsif streamable.is_a?(Hash)
|
|
62
|
+
stream_name_for(streamable.sort)
|
|
59
63
|
else
|
|
60
64
|
raise ArgumentError, "Unable to generate stream name. Expected an object that responds to `id`, got: #{streamable.class}"
|
|
61
65
|
end
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Rage::OriginValidator
|
|
4
|
-
def initialize(app, *allowed_origins)
|
|
4
|
+
def initialize(app, *allowed_origins, allow_same_origin_as_host: true)
|
|
5
5
|
@app = app
|
|
6
|
-
@validator = build_validator(allowed_origins)
|
|
6
|
+
@validator = build_validator(allowed_origins, allow_same_origin_as_host)
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def call(env)
|
|
@@ -17,20 +17,43 @@ class Rage::OriginValidator
|
|
|
17
17
|
|
|
18
18
|
private
|
|
19
19
|
|
|
20
|
-
def build_validator(allowed_origins)
|
|
21
|
-
if allowed_origins.empty?
|
|
20
|
+
def build_validator(allowed_origins, allow_same_origin_as_host)
|
|
21
|
+
if allowed_origins.empty? && !allow_same_origin_as_host
|
|
22
22
|
->(env) { false }
|
|
23
23
|
else
|
|
24
|
-
origins_eval =
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
24
|
+
origins_eval = []
|
|
25
|
+
|
|
26
|
+
same_origin_eval = if allow_same_origin_as_host
|
|
27
|
+
origins_eval << "is_same_origin"
|
|
28
|
+
|
|
29
|
+
<<~RUBY
|
|
30
|
+
host = env["HTTP_HOST"]
|
|
31
|
+
|
|
32
|
+
is_same_origin = if host.nil?
|
|
33
|
+
false
|
|
34
|
+
elsif origin.start_with?("https://")
|
|
35
|
+
origin.size == host.size + 8 && origin.end_with?(host)
|
|
36
|
+
elsif origin.start_with?("http://")
|
|
37
|
+
origin.size == host.size + 7 && origin.end_with?(host)
|
|
38
|
+
end
|
|
39
|
+
RUBY
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
origins_eval += allowed_origins.map do |configured_origin|
|
|
43
|
+
configured_origin.is_a?(Regexp) ?
|
|
44
|
+
"origin =~ /#{configured_origin.source}/" :
|
|
45
|
+
"origin == '#{configured_origin}'"
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
eval <<~RUBY
|
|
49
|
+
# frozen_string_literal: true
|
|
29
50
|
|
|
30
|
-
eval <<-RUBY
|
|
31
51
|
->(env) do
|
|
32
|
-
origin = env["HTTP_ORIGIN"
|
|
33
|
-
|
|
52
|
+
origin = env["HTTP_ORIGIN"]
|
|
53
|
+
return false unless origin
|
|
54
|
+
|
|
55
|
+
#{same_origin_eval}
|
|
56
|
+
#{origins_eval.join(" || ")}
|
|
34
57
|
end
|
|
35
58
|
RUBY
|
|
36
59
|
end
|
data/lib/rage/pubsub/pubsub.rb
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
##
|
|
2
|
-
# The module
|
|
4
|
+
# The module enables communication between Rage instances across worker processes and nodes.
|
|
5
|
+
#
|
|
6
|
+
# It provides the infrastructure for multi-server setups for `Rage::Cable` and `Rage::SSE`, allowing broadcasting
|
|
7
|
+
# messages across multiple servers or from different runtimes, e.g. Sidekiq. It also exposes a low-level API via
|
|
8
|
+
# the {subscribe}, {unsubscribe}, and {publish} methods for direct inter-process communication.
|
|
9
|
+
#
|
|
10
|
+
# @note The low-level API is primarily intended for integrations and gems. Most applications should use
|
|
11
|
+
# higher-level abstractions like {Rage::Cable Rage::Cable} or {Rage::SSE Rage::SSE} instead.
|
|
12
|
+
#
|
|
13
|
+
# ## Configuration
|
|
3
14
|
#
|
|
4
15
|
# To use the module, add the `redis-client` gem to your Gemfile and create the environment-specific configuration in `config/pubsub.yml`:
|
|
5
16
|
#
|
|
@@ -19,7 +30,141 @@
|
|
|
19
30
|
# The rest of the options are passed directly to `redis-client`.
|
|
20
31
|
#
|
|
21
32
|
module Rage::PubSub
|
|
33
|
+
BROADCASTER_ID = "pubsub"
|
|
34
|
+
private_constant :BROADCASTER_ID
|
|
35
|
+
|
|
36
|
+
module Relay
|
|
37
|
+
def self.broadcast(channel, data)
|
|
38
|
+
Iodine.publish(channel, data)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
private_constant :Relay
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
# @private
|
|
45
|
+
def __initialize
|
|
46
|
+
if (adapter = Rage.config.pubsub.adapter)
|
|
47
|
+
adapter.add_broadcaster(BROADCASTER_ID, Relay)
|
|
48
|
+
@__adapter = adapter
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Subscribe to messages on a topic.
|
|
53
|
+
#
|
|
54
|
+
# @param topic [String, Array, #id] The channel name to subscribe to. Can be a string, an array,
|
|
55
|
+
# an Active Record model, or any object responding to `id`.
|
|
56
|
+
# @param subscription_id [Object] A unique identifier for this subscription within the topic.
|
|
57
|
+
# Used to manage multiple subscriptions to the same topic and for cleanup via {unsubscribe}.
|
|
58
|
+
# @yieldparam message [String] The raw message string.
|
|
59
|
+
# @raise [ArgumentError] If a subscription with the same `topic` and `subscription_id` already exists.
|
|
60
|
+
#
|
|
61
|
+
# @example Subscribe to order updates
|
|
62
|
+
# Rage::PubSub.subscribe(topic: "orders", subscription_id: :order_listener) do |message|
|
|
63
|
+
# data = JSON.parse(message)
|
|
64
|
+
# puts "Received order update: #{data}"
|
|
65
|
+
# end
|
|
66
|
+
def subscribe(topic:, subscription_id:, &block)
|
|
67
|
+
serialized_topic = Rage::Internal.stream_name_for(topic)
|
|
68
|
+
topic_subscriptions = (subscriptions[serialized_topic] ||= {})
|
|
69
|
+
|
|
70
|
+
if topic_subscriptions.has_key?(subscription_id)
|
|
71
|
+
raise ArgumentError, "A subscription with topic #{topic.inspect} and subscription_id #{subscription_id.inspect} already exists"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
topic_subscriptions[subscription_id] = block
|
|
75
|
+
subscribe_to_topic(serialized_topic) if topic_subscriptions.size == 1
|
|
76
|
+
|
|
77
|
+
true
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Remove a subscription from a topic.
|
|
81
|
+
#
|
|
82
|
+
# @param topic [String, Array, #id] The channel name to unsubscribe from. Must match the value
|
|
83
|
+
# used when calling {subscribe}.
|
|
84
|
+
# @param subscription_id [Object, nil] The identifier of the subscription to remove. Omit the identifier to remove all subscriptions for the topic.
|
|
85
|
+
#
|
|
86
|
+
# @example Unsubscribe from order updates
|
|
87
|
+
# Rage::PubSub.unsubscribe(topic: "orders", subscription_id: :order_listener)
|
|
88
|
+
def unsubscribe(topic:, subscription_id: nil)
|
|
89
|
+
serialized_topic = Rage::Internal.stream_name_for(topic)
|
|
90
|
+
topic_subscriptions = subscriptions[serialized_topic]
|
|
91
|
+
|
|
92
|
+
return false if topic_subscriptions.nil? || (subscription_id && !topic_subscriptions.has_key?(subscription_id))
|
|
93
|
+
|
|
94
|
+
if subscription_id
|
|
95
|
+
topic_subscriptions.delete(subscription_id)
|
|
96
|
+
unsubscribe_from_topic(serialized_topic) if topic_subscriptions.empty?
|
|
97
|
+
else
|
|
98
|
+
unsubscribe_from_topic(serialized_topic)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
true
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Publish a message to all subscribers of a topic.
|
|
105
|
+
#
|
|
106
|
+
# Messages are delivered to all subscribers across worker processes and nodes (when a PubSub
|
|
107
|
+
# adapter is configured).
|
|
108
|
+
#
|
|
109
|
+
# @param message [String] The message to publish.
|
|
110
|
+
# @param topic [String, Array, #id] The channel name to publish to. Can be a string, an array,
|
|
111
|
+
# an Active Record model, or any object responding to `id`.
|
|
112
|
+
# @raise [ArgumentError] If message is not a String.
|
|
113
|
+
#
|
|
114
|
+
# @example Publish an order update
|
|
115
|
+
# Rage::PubSub.publish(JSON.generate({ id: 123, status: "shipped" }), topic: "orders")
|
|
116
|
+
#
|
|
117
|
+
# @example Publish to a model-based topic
|
|
118
|
+
# Rage::PubSub.publish("refresh", topic: current_user)
|
|
119
|
+
def publish(message, topic:)
|
|
120
|
+
raise ArgumentError, "Message must be a String, got #{message.class}" unless message.is_a?(String)
|
|
121
|
+
|
|
122
|
+
serialized_topic = Rage::Internal.stream_name_for(topic)
|
|
123
|
+
channel = "pubsub:#{serialized_topic}"
|
|
124
|
+
|
|
125
|
+
Iodine.publish(channel, message) if Iodine.running?
|
|
126
|
+
@__adapter&.publish(BROADCASTER_ID, channel, message)
|
|
127
|
+
|
|
128
|
+
true
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
private
|
|
132
|
+
|
|
133
|
+
def subscriptions
|
|
134
|
+
@subscriptions ||= {}
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def subscribe_to_topic(serialized_topic)
|
|
138
|
+
Iodine.subscribe("pubsub:#{serialized_topic}") do |_, message|
|
|
139
|
+
topic_subscriptions = subscriptions[serialized_topic]
|
|
140
|
+
|
|
141
|
+
topic_subscriptions&.each do |subscription_id, callback|
|
|
142
|
+
callback.call(message)
|
|
143
|
+
rescue => e
|
|
144
|
+
Rage.logger.error(
|
|
145
|
+
"PubSub callback failed with exception: #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}",
|
|
146
|
+
subscription_id:
|
|
147
|
+
)
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def unsubscribe_from_topic(serialized_topic)
|
|
153
|
+
subscriptions.delete(serialized_topic)
|
|
154
|
+
|
|
155
|
+
Iodine.defer do
|
|
156
|
+
Iodine.unsubscribe("pubsub:#{serialized_topic}") unless subscriptions.has_key?(serialized_topic)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end # class << self
|
|
160
|
+
|
|
22
161
|
module Adapters
|
|
23
162
|
autoload :Redis, "rage/pubsub/adapters/redis"
|
|
24
163
|
end
|
|
25
164
|
end
|
|
165
|
+
|
|
166
|
+
if Rage.config.internal.initialized?
|
|
167
|
+
Rage::PubSub.__initialize
|
|
168
|
+
else
|
|
169
|
+
Rage.config.after_initialize { Rage::PubSub.__initialize }
|
|
170
|
+
end
|
data/lib/rage/router/dsl.rb
CHANGED
|
@@ -11,7 +11,10 @@ class Rage::Router::DSL
|
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def draw(&block)
|
|
14
|
-
Handler.new(@router)
|
|
14
|
+
handler = Handler.new(@router)
|
|
15
|
+
Rage::Extension.__routes.each { |block| handler.instance_eval(&block) }
|
|
16
|
+
handler.instance_eval(&block)
|
|
17
|
+
|
|
15
18
|
# propagate route definitions to Rails for `rails routes` to work
|
|
16
19
|
Rails.application.routes.draw(&block) if Rage.config.internal.rails_mode
|
|
17
20
|
end
|
data/lib/rage/rspec.rb
CHANGED
|
@@ -256,11 +256,10 @@ module RageCableHelpers
|
|
|
256
256
|
end
|
|
257
257
|
|
|
258
258
|
def subscribe(params = {})
|
|
259
|
-
__stub_cable_protocol!
|
|
260
259
|
__stub_iodine_defer!
|
|
261
260
|
|
|
262
261
|
channel_class = __channel_class
|
|
263
|
-
channel_class.__register_actions
|
|
262
|
+
channel_class.__register_actions(__cable_test_protocol)
|
|
264
263
|
|
|
265
264
|
params = (params || {}).each_with_object({}) { |(k, v), memo| memo[k.to_sym] = v }
|
|
266
265
|
params[:channel] = channel_class.name
|
|
@@ -355,10 +354,6 @@ module RageCableHelpers
|
|
|
355
354
|
URI.parse("http://#{default_host}/")
|
|
356
355
|
end
|
|
357
356
|
|
|
358
|
-
def __stub_cable_protocol!
|
|
359
|
-
allow(Rage.cable).to receive(:__protocol).and_return(__cable_test_protocol)
|
|
360
|
-
end
|
|
361
|
-
|
|
362
357
|
def __stub_iodine_defer!
|
|
363
358
|
allow(Iodine).to receive(:defer) do |&block|
|
|
364
359
|
block&.call
|
data/lib/rage/version.rb
CHANGED
data/lib/rage-rb.rb
CHANGED
|
@@ -8,7 +8,12 @@ require "pathname"
|
|
|
8
8
|
module Rage
|
|
9
9
|
# Builds the Rage application with the configured middlewares.
|
|
10
10
|
def self.application
|
|
11
|
-
|
|
11
|
+
middleware = config.middleware.middlewares
|
|
12
|
+
if Fiber.scheduler && Fiber.current != Fiber.scheduler.root_fiber
|
|
13
|
+
middleware = middleware.reject { |m, _, _| m == Rage::FiberWrapper }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
with_middlewares(Application.new(__router), middleware)
|
|
12
17
|
end
|
|
13
18
|
|
|
14
19
|
# Builds the Rage application which delegates Rails requests to `Rails.application`.
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rage-rb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.29.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Roman Samoilov
|
|
@@ -43,14 +43,14 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '
|
|
46
|
+
version: '6.3'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
53
|
+
version: '6.3'
|
|
54
54
|
- !ruby/object:Gem::Dependency
|
|
55
55
|
name: zeitwerk
|
|
56
56
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -150,6 +150,7 @@ files:
|
|
|
150
150
|
- lib/rage/cable/protocols/actioncable_v1_json.rb
|
|
151
151
|
- lib/rage/cable/protocols/base.rb
|
|
152
152
|
- lib/rage/cable/protocols/raw_web_socket_json.rb
|
|
153
|
+
- lib/rage/cable/remote_disconnect.rb
|
|
153
154
|
- lib/rage/cable/router.rb
|
|
154
155
|
- lib/rage/cli.rb
|
|
155
156
|
- lib/rage/cli/base.rb
|
|
@@ -166,6 +167,8 @@ files:
|
|
|
166
167
|
- lib/rage/deferred/backends/disk.rb
|
|
167
168
|
- lib/rage/deferred/backends/nil.rb
|
|
168
169
|
- lib/rage/deferred/context.rb
|
|
170
|
+
- lib/rage/deferred/dead_task.rb
|
|
171
|
+
- lib/rage/deferred/dead_tasks.rb
|
|
169
172
|
- lib/rage/deferred/deferred.rb
|
|
170
173
|
- lib/rage/deferred/metadata.rb
|
|
171
174
|
- lib/rage/deferred/middleware_chain.rb
|