rage-rb 1.19.1 → 1.20.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/.rspec +1 -0
- data/Appraisals +19 -0
- data/CHANGELOG.md +20 -0
- data/CODE_OF_CONDUCT.md +13 -17
- data/Gemfile +3 -0
- data/README.md +60 -63
- data/Rakefile +14 -0
- data/lib/rage/all.rb +3 -0
- data/lib/rage/cable/cable.rb +11 -7
- data/lib/rage/cable/channel.rb +6 -1
- data/lib/rage/cable/connection.rb +4 -0
- data/lib/rage/cable/router.rb +14 -9
- data/lib/rage/configuration.rb +235 -21
- data/lib/rage/controller/api.rb +50 -45
- data/lib/rage/cookies.rb +7 -23
- data/lib/rage/deferred/context.rb +30 -2
- data/lib/rage/deferred/deferred.rb +18 -6
- data/lib/rage/deferred/metadata.rb +39 -0
- data/lib/rage/deferred/middleware_chain.rb +67 -0
- data/lib/rage/deferred/task.rb +45 -17
- data/lib/rage/events/events.rb +3 -3
- data/lib/rage/events/subscriber.rb +36 -25
- data/lib/rage/fiber.rb +33 -31
- data/lib/rage/fiber_scheduler.rb +6 -2
- data/lib/rage/logger/logger.rb +7 -1
- data/lib/rage/middleware/body_finalizer.rb +14 -0
- data/lib/rage/middleware/cors.rb +12 -12
- data/lib/rage/middleware/request_id.rb +1 -1
- data/lib/rage/openapi/openapi.rb +2 -2
- data/lib/rage/response.rb +12 -7
- data/lib/rage/rspec.rb +17 -17
- data/lib/rage/setup.rb +2 -2
- data/lib/rage/telemetry/handler.rb +131 -0
- data/lib/rage/telemetry/spans/await_fiber.rb +50 -0
- data/lib/rage/telemetry/spans/broadcast_cable_stream.rb +50 -0
- data/lib/rage/telemetry/spans/create_websocket_connection.rb +50 -0
- data/lib/rage/telemetry/spans/dispatch_fiber.rb +48 -0
- data/lib/rage/telemetry/spans/enqueue_deferred_task.rb +52 -0
- data/lib/rage/telemetry/spans/process_cable_action.rb +56 -0
- data/lib/rage/telemetry/spans/process_cable_connection.rb +56 -0
- data/lib/rage/telemetry/spans/process_controller_action.rb +56 -0
- data/lib/rage/telemetry/spans/process_deferred_task.rb +54 -0
- data/lib/rage/telemetry/spans/process_event_subscriber.rb +54 -0
- data/lib/rage/telemetry/spans/publish_event.rb +54 -0
- data/lib/rage/telemetry/spans/spawn_fiber.rb +50 -0
- data/lib/rage/telemetry/telemetry.rb +121 -0
- data/lib/rage/telemetry/tracer.rb +97 -0
- data/lib/rage/version.rb +1 -1
- data/rage.gemspec +5 -4
- metadata +42 -9
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class Rage::Deferred::MiddlewareChain
|
|
4
|
+
def initialize(enqueue_middleware:, perform_middleware:)
|
|
5
|
+
@enqueue_middleware = enqueue_middleware
|
|
6
|
+
@perform_middleware = perform_middleware
|
|
7
|
+
|
|
8
|
+
build_enqueue_chain!
|
|
9
|
+
build_perform_chain!
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def build_enqueue_chain!
|
|
15
|
+
raw_arguments = {
|
|
16
|
+
phase: ":enqueue",
|
|
17
|
+
args: "Rage::Deferred::Context.get_or_create_args(context)",
|
|
18
|
+
kwargs: "Rage::Deferred::Context.get_or_create_kwargs(context)",
|
|
19
|
+
context: "Rage::Deferred::Context.get_or_create_user_context(context)",
|
|
20
|
+
task_class: "Rage::Deferred::Context.get_task(context)",
|
|
21
|
+
delay: "delay",
|
|
22
|
+
delay_until: "delay_until"
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
self.class.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
26
|
+
def with_enqueue_middleware(context, delay:, delay_until:)
|
|
27
|
+
#{build_middleware_chain(:@enqueue_middleware, raw_arguments)}
|
|
28
|
+
end
|
|
29
|
+
RUBY
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def build_perform_chain!
|
|
33
|
+
raw_arguments = {
|
|
34
|
+
phase: ":perform",
|
|
35
|
+
args: "Rage::Deferred::Context.get_or_create_args(context)",
|
|
36
|
+
kwargs: "Rage::Deferred::Context.get_or_create_kwargs(context)",
|
|
37
|
+
context: "Rage::Deferred::Context.get_or_create_user_context(context)",
|
|
38
|
+
task_class: "task.class",
|
|
39
|
+
task: "task"
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
self.class.class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
43
|
+
def with_perform_middleware(context, task:)
|
|
44
|
+
#{build_middleware_chain(:@perform_middleware, raw_arguments)}
|
|
45
|
+
end
|
|
46
|
+
RUBY
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def build_middleware_chain(middlewares_var, raw_arguments)
|
|
50
|
+
middlewares = instance_variable_get(middlewares_var)
|
|
51
|
+
i = middlewares.length
|
|
52
|
+
|
|
53
|
+
middlewares.reverse.inject("yield") do |memo, middleware_with_args|
|
|
54
|
+
middleware, _, _ = middleware_with_args
|
|
55
|
+
arguments = Rage::Internal.build_arguments(middleware.instance_method(:call), raw_arguments)
|
|
56
|
+
i -= 1
|
|
57
|
+
|
|
58
|
+
<<~RUBY
|
|
59
|
+
middleware, args, block = #{middlewares_var}[#{i}]
|
|
60
|
+
|
|
61
|
+
middleware.new(*args, &block).call(#{arguments}) do
|
|
62
|
+
#{memo}
|
|
63
|
+
end
|
|
64
|
+
RUBY
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
data/lib/rage/deferred/task.rb
CHANGED
|
@@ -37,29 +37,55 @@ module Rage::Deferred::Task
|
|
|
37
37
|
BACKOFF_INTERVAL = 5
|
|
38
38
|
private_constant :BACKOFF_INTERVAL
|
|
39
39
|
|
|
40
|
+
# @private
|
|
41
|
+
CONTEXT_KEY = :__rage_deferred_execution_context
|
|
42
|
+
|
|
40
43
|
def perform
|
|
41
44
|
end
|
|
42
45
|
|
|
46
|
+
# Access metadata for the current task execution.
|
|
47
|
+
# @return [Rage::Deferred::Metadata] the metadata object for the current task execution
|
|
48
|
+
# @example
|
|
49
|
+
# class MyTask
|
|
50
|
+
# include Rage::Deferred::Task
|
|
51
|
+
#
|
|
52
|
+
# def perform
|
|
53
|
+
# puts meta.retries
|
|
54
|
+
# end
|
|
55
|
+
# end
|
|
56
|
+
def meta
|
|
57
|
+
Rage::Deferred::Metadata
|
|
58
|
+
end
|
|
59
|
+
|
|
43
60
|
# @private
|
|
44
61
|
def __perform(context)
|
|
45
|
-
args = Rage::Deferred::Context.get_args(context)
|
|
46
|
-
kwargs = Rage::Deferred::Context.get_kwargs(context)
|
|
47
|
-
attempts = Rage::Deferred::Context.get_attempts(context)
|
|
48
|
-
|
|
49
62
|
restore_log_info(context)
|
|
50
63
|
|
|
64
|
+
attempts = Rage::Deferred::Context.get_attempts(context)
|
|
51
65
|
task_log_context = { task: self.class.name }
|
|
52
66
|
task_log_context[:attempt] = attempts + 1 if attempts
|
|
53
67
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
Fiber[CONTEXT_KEY] = context
|
|
69
|
+
|
|
70
|
+
Rage::Telemetry.tracer.span_deferred_task_process(task: self, context:) do
|
|
71
|
+
Rage::Deferred.__middleware_chain.with_perform_middleware(context, task: self) do
|
|
72
|
+
Rage.logger.with_context(task_log_context) do
|
|
73
|
+
args = Rage::Deferred::Context.get_args(context)
|
|
74
|
+
kwargs = Rage::Deferred::Context.get_kwargs(context)
|
|
75
|
+
|
|
76
|
+
perform(*args, **kwargs)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
62
79
|
end
|
|
80
|
+
|
|
81
|
+
true
|
|
82
|
+
rescue Exception => e
|
|
83
|
+
unless respond_to?(:__deferred_suppress_exception_logging?, true) && __deferred_suppress_exception_logging?
|
|
84
|
+
Rage.logger.with_context(task_log_context) do
|
|
85
|
+
Rage.logger.error("Deferred task failed with exception: #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
false
|
|
63
89
|
end
|
|
64
90
|
|
|
65
91
|
private def restore_log_info(context)
|
|
@@ -80,11 +106,13 @@ module Rage::Deferred::Task
|
|
|
80
106
|
|
|
81
107
|
module ClassMethods
|
|
82
108
|
def enqueue(*args, delay: nil, delay_until: nil, **kwargs)
|
|
83
|
-
Rage::Deferred.
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
delay_until:
|
|
87
|
-
|
|
109
|
+
context = Rage::Deferred::Context.build(self, args, kwargs)
|
|
110
|
+
|
|
111
|
+
Rage::Telemetry.tracer.span_deferred_task_enqueue(task_class: self, context:) do
|
|
112
|
+
Rage::Deferred.__middleware_chain.with_enqueue_middleware(context, delay:, delay_until:) do
|
|
113
|
+
Rage::Deferred.__queue.enqueue(context, delay:, delay_until:)
|
|
114
|
+
end
|
|
115
|
+
end
|
|
88
116
|
|
|
89
117
|
nil
|
|
90
118
|
end
|
data/lib/rage/events/events.rb
CHANGED
|
@@ -39,7 +39,9 @@ module Rage::Events
|
|
|
39
39
|
# Rage::Events.publish(MyEvent.new, context: { published_at: Time.now })
|
|
40
40
|
def self.publish(event, context: nil)
|
|
41
41
|
handler = __event_handlers[event.class] || __build_event_handler(event.class)
|
|
42
|
-
|
|
42
|
+
Rage::Telemetry.tracer.span_events_event_publish(event:, context:) do
|
|
43
|
+
handler.call(event, context)
|
|
44
|
+
end
|
|
43
45
|
|
|
44
46
|
nil
|
|
45
47
|
end
|
|
@@ -71,8 +73,6 @@ module Rage::Events
|
|
|
71
73
|
# @private
|
|
72
74
|
def self.__build_event_handler(event_class)
|
|
73
75
|
subscriber_calls = __get_subscribers(event_class).map do |subscriber_class|
|
|
74
|
-
subscriber_class.__register_rescue_handlers
|
|
75
|
-
|
|
76
76
|
arguments = "event"
|
|
77
77
|
|
|
78
78
|
context_type, _ = subscriber_class.instance_method(:call).parameters.find do |param_type, param_name|
|
|
@@ -79,16 +79,28 @@ module Rage::Events::Subscriber
|
|
|
79
79
|
|
|
80
80
|
# @private
|
|
81
81
|
def __call(event, context: nil)
|
|
82
|
-
Rage.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if e
|
|
88
|
-
Rage.logger.error("Subscriber failed with exception: #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
|
|
89
|
-
raise Rage::Deferred::TaskFailed if self.class.__is_deferred
|
|
82
|
+
Rage::Telemetry.tracer.span_events_subscriber_process(event:, context:, subscriber: self) do
|
|
83
|
+
Rage.logger.with_context(self.class.__log_context) do
|
|
84
|
+
with_exception_handler do
|
|
85
|
+
context.nil? ? call(event) : call(event, context: context.freeze)
|
|
86
|
+
end
|
|
90
87
|
end
|
|
91
88
|
end
|
|
89
|
+
rescue Exception => e
|
|
90
|
+
Rage.logger.with_context(self.class.__log_context) do
|
|
91
|
+
Rage.logger.error("Subscriber failed with exception: #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
|
|
92
|
+
end
|
|
93
|
+
raise e if self.class.__is_deferred
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
private
|
|
97
|
+
|
|
98
|
+
def __deferred_suppress_exception_logging?
|
|
99
|
+
true
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def with_exception_handler
|
|
103
|
+
yield
|
|
92
104
|
end
|
|
93
105
|
|
|
94
106
|
module ClassMethods
|
|
@@ -136,6 +148,14 @@ module Rage::Events::Subscriber
|
|
|
136
148
|
end
|
|
137
149
|
|
|
138
150
|
@__rescue_handlers.unshift([klasses, with])
|
|
151
|
+
|
|
152
|
+
rebuild_exception_handler!
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Check if the subscriber is executed in the background.
|
|
156
|
+
# @return [Boolean] `true` if the subscriber is deferred, `false` otherwise
|
|
157
|
+
def deferred?
|
|
158
|
+
@__is_deferred
|
|
139
159
|
end
|
|
140
160
|
|
|
141
161
|
# @private
|
|
@@ -144,29 +164,20 @@ module Rage::Events::Subscriber
|
|
|
144
164
|
klass.subscribe_to(*@__event_classes, deferred: @__is_deferred) if @__event_classes
|
|
145
165
|
end
|
|
146
166
|
|
|
147
|
-
|
|
148
|
-
def __register_rescue_handlers
|
|
149
|
-
return if method_defined?(:__run_rescue_handlers, false) || @__rescue_handlers.nil?
|
|
150
|
-
|
|
151
|
-
matcher_calls = @__rescue_handlers.map do |klasses, handler|
|
|
152
|
-
handler_call = instance_method(handler).arity == 0 ? handler : "#{handler}(exception)"
|
|
167
|
+
private
|
|
153
168
|
|
|
169
|
+
def rebuild_exception_handler!
|
|
170
|
+
rescue_calls = @__rescue_handlers.map do |klasses, handler|
|
|
154
171
|
<<~RUBY
|
|
155
|
-
|
|
156
|
-
#{
|
|
157
|
-
nil
|
|
172
|
+
rescue #{klasses.join(", ")} => e
|
|
173
|
+
method(:#{handler}).arity == 0 ? #{handler} : #{handler}(e)
|
|
158
174
|
RUBY
|
|
159
175
|
end
|
|
160
176
|
|
|
161
177
|
class_eval <<~RUBY, __FILE__, __LINE__ + 1
|
|
162
|
-
def
|
|
163
|
-
|
|
164
|
-
#{
|
|
165
|
-
else
|
|
166
|
-
exception
|
|
167
|
-
end
|
|
168
|
-
rescue Exception => e
|
|
169
|
-
e
|
|
178
|
+
private def with_exception_handler
|
|
179
|
+
yield
|
|
180
|
+
#{rescue_calls.join("\n")}
|
|
170
181
|
end
|
|
171
182
|
RUBY
|
|
172
183
|
end
|
data/lib/rage/fiber.rb
CHANGED
|
@@ -158,43 +158,45 @@ class Fiber
|
|
|
158
158
|
f, fibers = Fiber.current, Array(fibers)
|
|
159
159
|
await_channel = f.__await_channel(true)
|
|
160
160
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
161
|
+
Rage::Telemetry.tracer.span_core_fiber_await(fibers:) do
|
|
162
|
+
# check which fibers are alive (i.e. have yielded) and which have errored out
|
|
163
|
+
i, err, num_wait_for = 0, nil, 0
|
|
164
|
+
while i < fibers.length
|
|
165
|
+
if fibers[i].alive?
|
|
166
|
+
num_wait_for += 1
|
|
167
|
+
else
|
|
168
|
+
err = fibers[i].__get_err
|
|
169
|
+
break if err
|
|
170
|
+
end
|
|
171
|
+
i += 1
|
|
169
172
|
end
|
|
170
|
-
i += 1
|
|
171
|
-
end
|
|
172
173
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
174
|
+
# raise if one of the fibers has errored out or return the result if none have yielded
|
|
175
|
+
if err
|
|
176
|
+
raise err
|
|
177
|
+
elsif num_wait_for == 0
|
|
178
|
+
return fibers.map!(&:__get_result)
|
|
179
|
+
end
|
|
179
180
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
181
|
+
# wait on async fibers; resume right away if one of the fibers errors out
|
|
182
|
+
Iodine.subscribe(await_channel) do |_, err|
|
|
183
|
+
if err == AWAIT_ERROR_MESSAGE
|
|
184
|
+
f.resume
|
|
185
|
+
else
|
|
186
|
+
num_wait_for -= 1
|
|
187
|
+
f.resume if num_wait_for == 0
|
|
188
|
+
end
|
|
187
189
|
end
|
|
188
|
-
end
|
|
189
190
|
|
|
190
|
-
|
|
191
|
-
|
|
191
|
+
Fiber.defer(-1)
|
|
192
|
+
Iodine.defer { Iodine.unsubscribe(await_channel) }
|
|
192
193
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
194
|
+
# if num_wait_for is not 0 means we exited prematurely because of an error
|
|
195
|
+
if num_wait_for > 0
|
|
196
|
+
raise fibers.find(&:__get_err).__get_err
|
|
197
|
+
else
|
|
198
|
+
fibers.map!(&:__get_result)
|
|
199
|
+
end
|
|
198
200
|
end
|
|
199
201
|
end
|
|
200
202
|
|
data/lib/rage/fiber_scheduler.rb
CHANGED
|
@@ -120,7 +120,9 @@ class Rage::FiberScheduler
|
|
|
120
120
|
# the fiber to wrap a request in
|
|
121
121
|
Fiber.new(blocking: false) do
|
|
122
122
|
Fiber.current.__set_id
|
|
123
|
-
|
|
123
|
+
Rage::Telemetry.tracer.span_core_fiber_dispatch do
|
|
124
|
+
Fiber.current.__set_result(block.call)
|
|
125
|
+
end
|
|
124
126
|
end
|
|
125
127
|
else
|
|
126
128
|
# the fiber was created in the user code
|
|
@@ -128,7 +130,9 @@ class Rage::FiberScheduler
|
|
|
128
130
|
|
|
129
131
|
Fiber.new(blocking: false) do
|
|
130
132
|
Thread.current[:rage_logger] = logger
|
|
131
|
-
|
|
133
|
+
Rage::Telemetry.tracer.span_core_fiber_spawn(parent:) do
|
|
134
|
+
Fiber.current.__set_result(block.call)
|
|
135
|
+
end
|
|
132
136
|
# send a message for `Fiber.await` to work
|
|
133
137
|
Iodine.publish(parent.__await_channel, "", Iodine::PubSub::PROCESS) if parent.alive?
|
|
134
138
|
rescue Exception => e
|
data/lib/rage/logger/logger.rb
CHANGED
|
@@ -209,7 +209,13 @@ class Rage::Logger
|
|
|
209
209
|
RUBY
|
|
210
210
|
elsif @external_logger.is_a?(External::Dynamic)
|
|
211
211
|
# a callable object is used as a logger
|
|
212
|
-
|
|
212
|
+
call_method = if @external_logger.wrapped.is_a?(Proc)
|
|
213
|
+
@external_logger.wrapped
|
|
214
|
+
else
|
|
215
|
+
@external_logger.wrapped.method(:call)
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
parameters = Rage::Internal.build_arguments(call_method, {
|
|
213
219
|
severity: ":#{level_name}",
|
|
214
220
|
tags: "logger[:tags].freeze",
|
|
215
221
|
context: "logger[:context].freeze",
|
data/lib/rage/middleware/cors.rb
CHANGED
|
@@ -14,20 +14,20 @@ class Rage::Cors
|
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
response = @app.call(env)
|
|
17
|
-
response[1]["
|
|
18
|
-
response[1]["
|
|
17
|
+
response[1]["access-control-allow-credentials"] = @allow_credentials if @allow_credentials
|
|
18
|
+
response[1]["access-control-expose-headers"] = @expose_headers if @expose_headers
|
|
19
19
|
|
|
20
20
|
response
|
|
21
21
|
ensure
|
|
22
22
|
if !$! && (origin = @cors_check.call(env))
|
|
23
23
|
headers = response[1]
|
|
24
|
-
headers["
|
|
24
|
+
headers["access-control-allow-origin"] = origin
|
|
25
25
|
if @origins != "*"
|
|
26
|
-
vary = headers["
|
|
26
|
+
vary = headers["vary"]
|
|
27
27
|
if vary.nil?
|
|
28
|
-
headers["
|
|
28
|
+
headers["vary"] = "Origin"
|
|
29
29
|
elsif vary != "Origin"
|
|
30
|
-
headers["
|
|
30
|
+
headers["vary"] += ", Origin"
|
|
31
31
|
end
|
|
32
32
|
end
|
|
33
33
|
end
|
|
@@ -98,21 +98,21 @@ class Rage::Cors
|
|
|
98
98
|
|
|
99
99
|
def create_headers
|
|
100
100
|
headers = {
|
|
101
|
-
"
|
|
102
|
-
"
|
|
101
|
+
"access-control-allow-origin" => "",
|
|
102
|
+
"access-control-allow-methods" => @methods
|
|
103
103
|
}
|
|
104
104
|
|
|
105
105
|
if @allow_headers
|
|
106
|
-
headers["
|
|
106
|
+
headers["access-control-allow-headers"] = @allow_headers
|
|
107
107
|
end
|
|
108
108
|
if @expose_headers
|
|
109
|
-
headers["
|
|
109
|
+
headers["access-control-expose-headers"] = @expose_headers
|
|
110
110
|
end
|
|
111
111
|
if @max_age
|
|
112
|
-
headers["
|
|
112
|
+
headers["access-control-max-age"] = @max_age
|
|
113
113
|
end
|
|
114
114
|
if @allow_credentials
|
|
115
|
-
headers["
|
|
115
|
+
headers["access-control-allow-credentials"] = @allow_credentials
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
headers
|
|
@@ -24,7 +24,7 @@ class Rage::RequestId
|
|
|
24
24
|
def call(env)
|
|
25
25
|
env["rage.request_id"] = validate_external_request_id(env["HTTP_X_REQUEST_ID"])
|
|
26
26
|
response = @app.call(env)
|
|
27
|
-
response[1]["
|
|
27
|
+
response[1]["x-request-id"] = env["rage.request_id"]
|
|
28
28
|
|
|
29
29
|
response
|
|
30
30
|
end
|
data/lib/rage/openapi/openapi.rb
CHANGED
|
@@ -48,13 +48,13 @@ module Rage::OpenAPI
|
|
|
48
48
|
spec_url = "#{scheme}://#{host}#{path}/json"
|
|
49
49
|
page = ERB.new(File.read("#{__dir__}/index.html.erb")).result(binding)
|
|
50
50
|
|
|
51
|
-
[200, { "
|
|
51
|
+
[200, { "content-type" => "text/html; charset=UTF-8" }, [page]]
|
|
52
52
|
end
|
|
53
53
|
end
|
|
54
54
|
|
|
55
55
|
json_app = ->(env) do
|
|
56
56
|
spec = (__data_cache[[:spec, namespace]] ||= build(namespace:).to_json)
|
|
57
|
-
[200, { "
|
|
57
|
+
[200, { "content-type" => "application/json" }, [spec]]
|
|
58
58
|
end
|
|
59
59
|
|
|
60
60
|
app = ->(env) do
|
data/lib/rage/response.rb
CHANGED
|
@@ -4,25 +4,30 @@ require "digest"
|
|
|
4
4
|
require "time"
|
|
5
5
|
|
|
6
6
|
class Rage::Response
|
|
7
|
-
ETAG_HEADER = "
|
|
8
|
-
LAST_MODIFIED_HEADER = "
|
|
7
|
+
ETAG_HEADER = "etag"
|
|
8
|
+
LAST_MODIFIED_HEADER = "last-modified"
|
|
9
9
|
|
|
10
10
|
# @private
|
|
11
|
-
def initialize(
|
|
12
|
-
@
|
|
13
|
-
|
|
11
|
+
def initialize(controller)
|
|
12
|
+
@controller = controller
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Returns the HTTP status code of the response.
|
|
16
|
+
# @return [Integer]
|
|
17
|
+
def status
|
|
18
|
+
@controller.__status
|
|
14
19
|
end
|
|
15
20
|
|
|
16
21
|
# Returns the content of the response as a string. This contains the contents of any calls to `render`.
|
|
17
22
|
# @return [String]
|
|
18
23
|
def body
|
|
19
|
-
@
|
|
24
|
+
@controller.__body[0] || ""
|
|
20
25
|
end
|
|
21
26
|
|
|
22
27
|
# Returns the headers for the response.
|
|
23
28
|
# @return [Hash]
|
|
24
29
|
def headers
|
|
25
|
-
@
|
|
30
|
+
@controller.__headers
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
# Returns ETag response header or +nil+ if it's empty.
|
data/lib/rage/rspec.rb
CHANGED
|
@@ -14,23 +14,6 @@ require_relative "#{Rage.root}/config/application"
|
|
|
14
14
|
# verify the environment
|
|
15
15
|
abort("The test suite is running in #{Rage.env} mode instead of 'test'!") unless Rage.env.test?
|
|
16
16
|
|
|
17
|
-
# mock fiber methods as RSpec tests don't run concurrently
|
|
18
|
-
class Fiber
|
|
19
|
-
def self.schedule(&block)
|
|
20
|
-
fiber = Fiber.new(blocking: true) do
|
|
21
|
-
Fiber.current.__set_id
|
|
22
|
-
Fiber.current.__set_result(block.call)
|
|
23
|
-
end
|
|
24
|
-
fiber.resume
|
|
25
|
-
|
|
26
|
-
fiber
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def self.await(fibers)
|
|
30
|
-
Array(fibers).map(&:__get_result)
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
|
|
34
17
|
# define request helpers
|
|
35
18
|
module RageRequestHelpers
|
|
36
19
|
include Rack::Test::Methods
|
|
@@ -96,6 +79,23 @@ end
|
|
|
96
79
|
# include request helpers
|
|
97
80
|
RSpec.configure do |config|
|
|
98
81
|
config.include(RageRequestHelpers, type: :request)
|
|
82
|
+
|
|
83
|
+
# mock fiber methods as RSpec tests don't run concurrently
|
|
84
|
+
config.before do
|
|
85
|
+
allow(Fiber).to receive(:schedule) do |&block|
|
|
86
|
+
fiber = Fiber.new(blocking: true) do
|
|
87
|
+
Fiber.current.__set_id
|
|
88
|
+
Fiber.current.__set_result(block.call)
|
|
89
|
+
end
|
|
90
|
+
fiber.resume
|
|
91
|
+
|
|
92
|
+
fiber
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
allow(Fiber).to receive(:await) do |fibers|
|
|
96
|
+
Array(fibers).map(&:__get_result)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
99
|
end
|
|
100
100
|
|
|
101
101
|
# patch MockResponse class
|
data/lib/rage/setup.rb
CHANGED
|
@@ -14,9 +14,9 @@ require "rage/ext/setup"
|
|
|
14
14
|
# Load application classes
|
|
15
15
|
Rage.code_loader.setup
|
|
16
16
|
|
|
17
|
+
require_relative "#{Rage.root}/config/routes"
|
|
18
|
+
|
|
17
19
|
# Run after_initialize hooks
|
|
18
20
|
Rage.config.run_after_initialize!
|
|
19
21
|
|
|
20
|
-
require_relative "#{Rage.root}/config/routes"
|
|
21
|
-
|
|
22
22
|
Rage.config.internal.initialized!
|