rage-rb 1.12.0 → 1.13.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 +13 -0
- data/Gemfile +1 -0
- data/lib/rage/all.rb +1 -0
- data/lib/rage/application.rb +3 -3
- data/lib/rage/cable/adapters/redis.rb +4 -3
- data/lib/rage/cable/cable.rb +14 -16
- data/lib/rage/cable/protocol/actioncable_v1_json.rb +5 -8
- data/lib/rage/cli.rb +1 -1
- data/lib/rage/controller/api.rb +9 -9
- data/lib/rage/fiber_scheduler.rb +2 -2
- data/lib/rage/middleware/request_id.rb +42 -0
- data/lib/rage/openapi/openapi.rb +1 -1
- data/lib/rage/openapi/parser.rb +18 -11
- data/lib/rage/request.rb +8 -0
- data/lib/rage/templates/Gemfile +1 -1
- data/lib/rage/version.rb +1 -1
- data/rage.gemspec +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d2f73c4770587ebabc7d8fa0c4793d53742196b928f21cc362799cb068849982
|
4
|
+
data.tar.gz: aecaa4ae2848bc1b15a38da29d3030d718d74697faaf8525e60e82d10b24396c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bb31ef275be5dc22322af8c9d563befc000ad2d15bb22454f2d3664736e6e7881ccdac365e6687fc123c23da1a06ee3cd4d63d08b8b5123c1fee67ebb97fbc57
|
7
|
+
data.tar.gz: 7debadefde85efa180f73e963b252cb1065a6b6f1bf4b8ca6e64e19d82a0ed838d3b321ddf9222006204ab59c3d90213e1a4eb6e1ff04d1d45572c1e166b3d77
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.13.0] - 2025-02-12
|
4
|
+
|
5
|
+
### Added
|
6
|
+
|
7
|
+
- [CLI] Support the PORT ENV variable by [@TheBlackArroVV](https://github.com/TheBlackArroVV) (#124).
|
8
|
+
- Add the `RequestId` middleware (#127).
|
9
|
+
|
10
|
+
### Fixed
|
11
|
+
|
12
|
+
- Correctly process persistent HTTP connections (#128).
|
13
|
+
- [OpenAPI] Ignore empty comments (#126).
|
14
|
+
- [Cable] Improve the time to connect (#129).
|
15
|
+
|
3
16
|
## [1.12.0] - 2025-01-21
|
4
17
|
|
5
18
|
### Added
|
data/Gemfile
CHANGED
data/lib/rage/all.rb
CHANGED
@@ -30,6 +30,7 @@ require_relative "middleware/origin_validator"
|
|
30
30
|
require_relative "middleware/fiber_wrapper"
|
31
31
|
require_relative "middleware/cors"
|
32
32
|
require_relative "middleware/reloader"
|
33
|
+
require_relative "middleware/request_id"
|
33
34
|
|
34
35
|
if defined?(Sidekiq)
|
35
36
|
require_relative "sidekiq_session"
|
data/lib/rage/application.rb
CHANGED
@@ -7,7 +7,7 @@ class Rage::Application
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def call(env)
|
10
|
-
init_logger
|
10
|
+
init_logger(env)
|
11
11
|
|
12
12
|
handler = @router.lookup(env)
|
13
13
|
|
@@ -33,9 +33,9 @@ class Rage::Application
|
|
33
33
|
DEFAULT_LOG_CONTEXT = {}.freeze
|
34
34
|
private_constant :DEFAULT_LOG_CONTEXT
|
35
35
|
|
36
|
-
def init_logger
|
36
|
+
def init_logger(env)
|
37
37
|
Thread.current[:rage_logger] = {
|
38
|
-
tags: [Iodine::Rack::Utils.gen_request_tag],
|
38
|
+
tags: [(env["rage.request_id"] ||= Iodine::Rack::Utils.gen_request_tag)],
|
39
39
|
context: DEFAULT_LOG_CONTEXT,
|
40
40
|
request_start: Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
41
41
|
}
|
@@ -14,6 +14,7 @@ end
|
|
14
14
|
class Rage::Cable::Adapters::Redis < Rage::Cable::Adapters::Base
|
15
15
|
REDIS_STREAM_NAME = "rage:cable:messages"
|
16
16
|
DEFAULT_REDIS_OPTIONS = { reconnect_attempts: [0.05, 0.1, 0.5] }
|
17
|
+
REDIS_MIN_VERSION_SUPPORTED = Gem::Version.create(6)
|
17
18
|
|
18
19
|
def initialize(config)
|
19
20
|
@redis_stream = if (prefix = config.delete(:channel_prefix))
|
@@ -26,8 +27,8 @@ class Rage::Cable::Adapters::Redis < Rage::Cable::Adapters::Base
|
|
26
27
|
@server_uuid = SecureRandom.uuid
|
27
28
|
|
28
29
|
redis_version = get_redis_version
|
29
|
-
if redis_version <
|
30
|
-
raise "Redis adapter only supports Redis
|
30
|
+
if redis_version < REDIS_MIN_VERSION_SUPPORTED
|
31
|
+
raise "Redis adapter only supports Redis 6+. Detected Redis version: #{redis_version}."
|
31
32
|
end
|
32
33
|
|
33
34
|
@trimming_strategy = redis_version < Gem::Version.create("6.2.0") ? :maxlen : :minid
|
@@ -73,7 +74,7 @@ class Rage::Cable::Adapters::Redis < Rage::Cable::Adapters::Base
|
|
73
74
|
rescue RedisClient::Error => e
|
74
75
|
puts "FATAL: Couldn't connect to Redis - all broadcasts will be limited to the current server."
|
75
76
|
puts e.backtrace.join("\n")
|
76
|
-
|
77
|
+
REDIS_MIN_VERSION_SUPPORTED
|
77
78
|
|
78
79
|
ensure
|
79
80
|
service_redis.close
|
data/lib/rage/cable/cable.rb
CHANGED
@@ -51,33 +51,22 @@ module Rage::Cable
|
|
51
51
|
end
|
52
52
|
|
53
53
|
@protocol = protocol
|
54
|
+
@default_log_context = {}.freeze
|
54
55
|
end
|
55
56
|
|
56
57
|
def on_open(connection)
|
57
|
-
|
58
|
-
|
59
|
-
rescue => e
|
60
|
-
log_error(e)
|
61
|
-
end
|
58
|
+
connection.env["rage.request_id"] ||= Iodine::Rack::Utils.gen_request_tag
|
59
|
+
schedule_fiber(connection) { @protocol.on_open(connection) }
|
62
60
|
end
|
63
61
|
|
64
62
|
def on_message(connection, data)
|
65
|
-
|
66
|
-
@protocol.on_message(connection, data)
|
67
|
-
rescue => e
|
68
|
-
log_error(e)
|
69
|
-
end
|
63
|
+
schedule_fiber(connection) { @protocol.on_message(connection, data) }
|
70
64
|
end
|
71
65
|
|
72
66
|
if protocol.respond_to?(:on_close)
|
73
67
|
def on_close(connection)
|
74
68
|
return unless ::Iodine.running?
|
75
|
-
|
76
|
-
Fiber.schedule do
|
77
|
-
@protocol.on_close(connection)
|
78
|
-
rescue => e
|
79
|
-
log_error(e)
|
80
|
-
end
|
69
|
+
schedule_fiber(connection) { @protocol.on_close(connection) }
|
81
70
|
end
|
82
71
|
end
|
83
72
|
|
@@ -91,6 +80,15 @@ module Rage::Cable
|
|
91
80
|
|
92
81
|
private
|
93
82
|
|
83
|
+
def schedule_fiber(connection)
|
84
|
+
Fiber.schedule do
|
85
|
+
Thread.current[:rage_logger] = { tags: [connection.env["rage.request_id"]], context: @default_log_context }
|
86
|
+
yield
|
87
|
+
rescue => e
|
88
|
+
log_error(e)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
94
92
|
def log_error(e)
|
95
93
|
Rage.logger.error("Unhandled exception has occured - #{e.class} (#{e.message}):\n#{e.backtrace.join("\n")}")
|
96
94
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "zlib"
|
4
|
+
require "set"
|
4
5
|
|
5
6
|
##
|
6
7
|
# A protocol defines the structure, rules and semantics for exchanging data between the client and the server.
|
@@ -69,8 +70,8 @@ class Rage::Cable::Protocol::ActioncableV1Json
|
|
69
70
|
end
|
70
71
|
end
|
71
72
|
|
72
|
-
# Hash<String(stream name) =>
|
73
|
-
@subscription_identifiers = Hash.new { |hash, key| hash[key] =
|
73
|
+
# Hash<String(stream name) => Set<Hash>(subscription params)>
|
74
|
+
@subscription_identifiers = Hash.new { |hash, key| hash[key] = Set.new }
|
74
75
|
|
75
76
|
# this is a fallback to synchronize subscription identifiers across different worker processes;
|
76
77
|
# we expect connections to be distributed among all workers, so this code will almost never be called;
|
@@ -78,7 +79,7 @@ class Rage::Cable::Protocol::ActioncableV1Json
|
|
78
79
|
# of the crashed ones also had access to the identifiers;
|
79
80
|
Iodine.subscribe("cable:synchronize") do |_, subscription_msg|
|
80
81
|
stream_name, params = Rage::ParamsParser.json_parse(subscription_msg)
|
81
|
-
@subscription_identifiers[stream_name] << params
|
82
|
+
@subscription_identifiers[stream_name] << params
|
82
83
|
end
|
83
84
|
|
84
85
|
Iodine.on_state(:on_finish) do
|
@@ -181,12 +182,8 @@ class Rage::Cable::Protocol::ActioncableV1Json
|
|
181
182
|
# @param name [String] the stream name
|
182
183
|
# @param data [Object] the data to send
|
183
184
|
def self.broadcast(name, data)
|
184
|
-
|
185
|
-
|
186
|
-
while i < identifiers.length
|
187
|
-
params = identifiers[i]
|
185
|
+
@subscription_identifiers[name].each do |params|
|
188
186
|
::Iodine.publish("cable:#{name}:#{Zlib.crc32(params.to_s)}", serialize(params, data))
|
189
|
-
i += 1
|
190
187
|
end
|
191
188
|
end
|
192
189
|
end
|
data/lib/rage/cli.rb
CHANGED
@@ -71,7 +71,7 @@ module Rage
|
|
71
71
|
|
72
72
|
server_options = { service: :http, handler: app }
|
73
73
|
|
74
|
-
server_options[:port] = options[:port] || Rage.config.server.port
|
74
|
+
server_options[:port] = options[:port] || ENV["PORT"] || Rage.config.server.port
|
75
75
|
server_options[:address] = options[:binding] || (Rage.env.production? ? "0.0.0.0" : "localhost")
|
76
76
|
server_options[:timeout] = Rage.config.server.timeout
|
77
77
|
server_options[:max_clients] = Rage.config.server.max_clients
|
data/lib/rage/controller/api.rb
CHANGED
@@ -585,27 +585,27 @@ class RageController::API
|
|
585
585
|
|
586
586
|
if !defined?(::ActionController::Parameters)
|
587
587
|
# Get the request data. The keys inside the hash are symbols, so `params.keys` returns an array of `Symbol`.<br>
|
588
|
-
# You can also load Strong
|
588
|
+
# You can also load Strong Parameters to have Rage automatically wrap `params` in an instance of `ActionController::Parameters`.<br>
|
589
589
|
# At the same time, if you are not implementing complex filtering rules or working with nested structures, consider using native `Hash#fetch` and `Hash#slice` instead.
|
590
590
|
#
|
591
591
|
# For multipart file uploads, the uploaded files are represented by an instance of {Rage::UploadedFile}.
|
592
592
|
#
|
593
593
|
# @return [Hash{Symbol=>String,Array,Hash,Numeric,NilClass,TrueClass,FalseClass}]
|
594
|
-
# @example
|
595
|
-
# #
|
596
|
-
# require "active_support/all"
|
597
|
-
# require "action_controller/metal/strong_parameters"
|
594
|
+
# @example With Strong Parameters
|
595
|
+
# # in the Gemfile:
|
596
|
+
# gem "activesupport", require: "active_support/all"
|
597
|
+
# gem "actionpack", require: "action_controller/metal/strong_parameters"
|
598
598
|
#
|
599
|
-
#
|
600
|
-
#
|
601
|
-
#
|
599
|
+
# # in the controller:
|
600
|
+
# params.require(:user).permit(:full_name, :dob)
|
601
|
+
# @example Without Strong Parameters
|
602
602
|
# params.fetch(:user).slice(:full_name, :dob)
|
603
603
|
def params
|
604
604
|
@__params
|
605
605
|
end
|
606
606
|
else
|
607
607
|
def params
|
608
|
-
@
|
608
|
+
@__params__ ||= ActionController::Parameters.new(@__params)
|
609
609
|
end
|
610
610
|
end
|
611
611
|
|
data/lib/rage/fiber_scheduler.rb
CHANGED
@@ -12,10 +12,10 @@ class Rage::FiberScheduler
|
|
12
12
|
|
13
13
|
def io_wait(io, events, timeout = nil)
|
14
14
|
f = Fiber.current
|
15
|
-
::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil
|
15
|
+
::Iodine::Scheduler.attach(io.fileno, events, timeout&.ceil) { |err| f.resume(err) }
|
16
16
|
|
17
17
|
err = Fiber.defer(io.fileno)
|
18
|
-
if err && err < 0
|
18
|
+
if err == false || (err && err < 0)
|
19
19
|
err
|
20
20
|
else
|
21
21
|
events
|
@@ -0,0 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
##
|
4
|
+
# The middleware establishes a connection between the `X-Request-Id` header (typically generated by a firewall, load balancer, or web server) and
|
5
|
+
# Rage's internal logging system. It ensures that:
|
6
|
+
#
|
7
|
+
# 1. All logs produced during the request are tagged with the value submitted in the `X-Request-Id` header.
|
8
|
+
# 2. The request ID is added back to the response in the `X-Request-Id` header. If no `X-Request-Id` header was provided in the request, the middleware adds an internally generated ID to the response.
|
9
|
+
#
|
10
|
+
# Additionally, the `X-Request-Id` header value is sanitized to a maximum of 255 characters, allowing only alphanumeric characters and dashes.
|
11
|
+
#
|
12
|
+
# @example
|
13
|
+
# Rage.configure do
|
14
|
+
# config.middleware.use Rage::RequestId
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
class Rage::RequestId
|
18
|
+
BLACKLISTED_CHARACTERS = /[^\w\-@]/
|
19
|
+
|
20
|
+
def initialize(app)
|
21
|
+
@app = app
|
22
|
+
end
|
23
|
+
|
24
|
+
def call(env)
|
25
|
+
env["rage.request_id"] = validate_external_request_id(env["HTTP_X_REQUEST_ID"])
|
26
|
+
response = @app.call(env)
|
27
|
+
response[1]["X-Request-Id"] = env["rage.request_id"]
|
28
|
+
|
29
|
+
response
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def validate_external_request_id(request_id)
|
35
|
+
if request_id && !request_id.empty?
|
36
|
+
request_id = request_id[0...255] if request_id.size > 255
|
37
|
+
request_id = request_id.gsub(BLACKLISTED_CHARACTERS, "") if request_id =~ BLACKLISTED_CHARACTERS
|
38
|
+
|
39
|
+
request_id
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
data/lib/rage/openapi/openapi.rb
CHANGED
data/lib/rage/openapi/parser.rb
CHANGED
@@ -16,7 +16,7 @@ class Rage::OpenAPI::Parser
|
|
16
16
|
else
|
17
17
|
node.deprecated = true
|
18
18
|
end
|
19
|
-
children = find_children(comments[i + 1..])
|
19
|
+
children = find_children(comments[i + 1..], node)
|
20
20
|
|
21
21
|
elsif expression =~ /@private\b/
|
22
22
|
if node.private
|
@@ -24,7 +24,7 @@ class Rage::OpenAPI::Parser
|
|
24
24
|
else
|
25
25
|
node.private = true
|
26
26
|
end
|
27
|
-
children = find_children(comments[i + 1..])
|
27
|
+
children = find_children(comments[i + 1..], node)
|
28
28
|
|
29
29
|
elsif expression =~ /@version\s/
|
30
30
|
if node.root.version
|
@@ -45,7 +45,7 @@ class Rage::OpenAPI::Parser
|
|
45
45
|
|
46
46
|
elsif expression =~ /@auth\s/
|
47
47
|
method, name, tail_name = expression[6..].split(" ", 3)
|
48
|
-
children = find_children(comments[i + 1..])
|
48
|
+
children = find_children(comments[i + 1..], node)
|
49
49
|
|
50
50
|
if tail_name
|
51
51
|
Rage::OpenAPI.__log_warn "incorrect `@auth` name detected at #{location_msg(comments[i])}; security scheme name cannot contain spaces"
|
@@ -83,7 +83,9 @@ class Rage::OpenAPI::Parser
|
|
83
83
|
children = nil
|
84
84
|
expression = comments[i].slice.delete_prefix("#").strip
|
85
85
|
|
86
|
-
if
|
86
|
+
if expression.empty?
|
87
|
+
# no-op
|
88
|
+
elsif !expression.start_with?("@")
|
87
89
|
if node.summary
|
88
90
|
Rage::OpenAPI.__log_warn "invalid summary entry detected at #{location_msg(comments[i])}; summary should only be one line"
|
89
91
|
else
|
@@ -96,7 +98,7 @@ class Rage::OpenAPI::Parser
|
|
96
98
|
else
|
97
99
|
node.deprecated = true
|
98
100
|
end
|
99
|
-
children = find_children(comments[i + 1..])
|
101
|
+
children = find_children(comments[i + 1..], node)
|
100
102
|
|
101
103
|
elsif expression =~ /@private\b/
|
102
104
|
if node.parents.any?(&:private)
|
@@ -104,10 +106,10 @@ class Rage::OpenAPI::Parser
|
|
104
106
|
else
|
105
107
|
node.private = true
|
106
108
|
end
|
107
|
-
children = find_children(comments[i + 1..])
|
109
|
+
children = find_children(comments[i + 1..], node)
|
108
110
|
|
109
111
|
elsif expression =~ /@description\s/
|
110
|
-
children = find_children(comments[i + 1..])
|
112
|
+
children = find_children(comments[i + 1..], node)
|
111
113
|
node.description = [expression[13..]] + children
|
112
114
|
|
113
115
|
elsif expression =~ /@response\s/
|
@@ -132,7 +134,7 @@ class Rage::OpenAPI::Parser
|
|
132
134
|
|
133
135
|
elsif expression =~ /@internal\b/
|
134
136
|
# no-op
|
135
|
-
children = find_children(comments[i + 1..])
|
137
|
+
children = find_children(comments[i + 1..], node)
|
136
138
|
|
137
139
|
else
|
138
140
|
Rage::OpenAPI.__log_warn "unrecognized `#{expression.split(" ")[0]}` tag detected at #{location_msg(comments[i])}"
|
@@ -148,16 +150,21 @@ class Rage::OpenAPI::Parser
|
|
148
150
|
|
149
151
|
private
|
150
152
|
|
151
|
-
def find_children(comments)
|
153
|
+
def find_children(comments, node)
|
152
154
|
children = []
|
153
155
|
|
154
156
|
comments.each do |comment|
|
155
|
-
expression = comment.slice.sub(/^#\s
|
157
|
+
expression = comment.slice.sub(/^#\s?/, "")
|
156
158
|
|
157
|
-
if expression.
|
159
|
+
if expression.empty?
|
160
|
+
# no-op
|
161
|
+
elsif expression.start_with?(/\s{2}/)
|
158
162
|
children << expression.strip
|
159
163
|
elsif expression.start_with?("@")
|
160
164
|
break
|
165
|
+
elsif !node.summary
|
166
|
+
# no-op - this is likely the summary entry
|
167
|
+
break
|
161
168
|
else
|
162
169
|
Rage::OpenAPI.__log_warn "unrecognized expression detected at #{location_msg(comment)}; use two spaces to mark multi-line expressions"
|
163
170
|
break
|
data/lib/rage/request.rb
CHANGED
@@ -76,6 +76,14 @@ class Rage::Request
|
|
76
76
|
@env["HTTP_USER_AGENT"]
|
77
77
|
end
|
78
78
|
|
79
|
+
# Returns the unique request ID. By default, this ID is internally generated, and all log entries created during the request
|
80
|
+
# are tagged with it. Alternatively, you can use the {Rage::RequestId} middleware to derive the ID from the `X-Request-Id` header.
|
81
|
+
def request_id
|
82
|
+
@env["rage.request_id"]
|
83
|
+
end
|
84
|
+
|
85
|
+
alias_method :uuid, :request_id
|
86
|
+
|
79
87
|
private
|
80
88
|
|
81
89
|
def if_none_match
|
data/lib/rage/templates/Gemfile
CHANGED
data/lib/rage/version.rb
CHANGED
data/rage.gemspec
CHANGED
@@ -29,7 +29,7 @@ Gem::Specification.new do |spec|
|
|
29
29
|
|
30
30
|
spec.add_dependency "thor", "~> 1.0"
|
31
31
|
spec.add_dependency "rack", "~> 2.0"
|
32
|
-
spec.add_dependency "rage-iodine", "~> 4.
|
32
|
+
spec.add_dependency "rage-iodine", "~> 4.1"
|
33
33
|
spec.add_dependency "zeitwerk", "~> 2.6"
|
34
34
|
spec.add_dependency "rack-test", "~> 2.1"
|
35
35
|
spec.add_dependency "rake", ">= 12.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rage-rb
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roman Samoilov
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-02-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|
@@ -44,14 +44,14 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '4.
|
47
|
+
version: '4.1'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '4.
|
54
|
+
version: '4.1'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: zeitwerk
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -141,6 +141,7 @@ files:
|
|
141
141
|
- lib/rage/middleware/fiber_wrapper.rb
|
142
142
|
- lib/rage/middleware/origin_validator.rb
|
143
143
|
- lib/rage/middleware/reloader.rb
|
144
|
+
- lib/rage/middleware/request_id.rb
|
144
145
|
- lib/rage/openapi/builder.rb
|
145
146
|
- lib/rage/openapi/collector.rb
|
146
147
|
- lib/rage/openapi/converter.rb
|