debugger_replay 0.1.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 +7 -0
- data/LICENSE +21 -0
- data/README.md +41 -0
- data/lib/debugger_replay/core/capture.rb +53 -0
- data/lib/debugger_replay/core/replay.rb +80 -0
- data/lib/debugger_replay/helpers/env_builder.rb +17 -0
- data/lib/debugger_replay/helpers/formatter.rb +11 -0
- data/lib/debugger_replay/helpers/request_parser.rb +19 -0
- data/lib/debugger_replay/setup/callbacks.rb +21 -0
- data/lib/debugger_replay/setup/middleware.rb +27 -0
- data/lib/debugger_replay/setup/railtie.rb +32 -0
- data/lib/debugger_replay/setup/subscriptions.rb +27 -0
- data/lib/debugger_replay/version.rb +3 -0
- data/lib/debugger_replay.rb +23 -0
- metadata +74 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 05e538193555af9b38c46655099ed40a6cf70a789acd8a13267b69018d8e8b24
|
|
4
|
+
data.tar.gz: dd054040cf7f6f70c2c598e0ecd18823a18f6f05366cd662132d72be8122b97f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: e517f19aa71fc87d26f8012b74f0bc54d8f737aba7c6daad00e9b29f61f39fc72a917cf4b54f62ed2872d253e3496dcb4dc6e2a3af28e02c586e75e4de1a6b16
|
|
7
|
+
data.tar.gz: d4bfd7c7f913cd30e440b7f84c5dcb451c05069d8b310048c79e4525dd87aa401feaf486ed68b59a818c4e704bd348b13672fa634ad3063db36866c1bfdc0496
|
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Ted Nguyen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# debugger_replay
|
|
2
|
+
|
|
3
|
+
Hot-reload and replay requests during debugging. Edit code, type `r`, see changes instantly.
|
|
4
|
+
|
|
5
|
+
https://github.com/user-attachments/assets/8908892d-ed77-48eb-9fd7-d6b4f0743185
|
|
6
|
+
|
|
7
|
+
## Compare
|
|
8
|
+
|
|
9
|
+
| Steps | Without debugger_replay | With debugger_replay |
|
|
10
|
+
|-------|----------------------------|----------------------|
|
|
11
|
+
| 1 | Hit breakpoint | Hit breakpoint |
|
|
12
|
+
| 2 | Edit code | Edit code |
|
|
13
|
+
| 3 | Type `c` to continue | Type `r` |
|
|
14
|
+
| 4 | Switch to browser, refresh | Breakpoint hit again |
|
|
15
|
+
| 5 | Breakpoint hit again | |
|
|
16
|
+
|
|
17
|
+
## Use cases
|
|
18
|
+
|
|
19
|
+
Replays HTTP requests, ActiveJob jobs, ActionMailer deliveries, Rake tasks
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
group :development do
|
|
25
|
+
gem "debugger_replay"
|
|
26
|
+
end
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Commands
|
|
30
|
+
|
|
31
|
+
| Command | Description |
|
|
32
|
+
|---------|--------------------------------------|
|
|
33
|
+
| `r` | Reload code + auto-continue + replay |
|
|
34
|
+
|
|
35
|
+
## How it works
|
|
36
|
+
|
|
37
|
+
Middleware captures request details. `r` triggers `Rails.application.reloader.reload!`, queues replay, auto-continues. After current request completes, replays with fresh code.
|
|
38
|
+
|
|
39
|
+
## License
|
|
40
|
+
|
|
41
|
+
MIT
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DebuggerReplay
|
|
4
|
+
class << self
|
|
5
|
+
attr_accessor :last_request, :last_job, :last_mailer, :last_rake
|
|
6
|
+
attr_accessor :current_context
|
|
7
|
+
|
|
8
|
+
def capture_request!(env)
|
|
9
|
+
@current_context = :request
|
|
10
|
+
@last_request = {
|
|
11
|
+
method: env["REQUEST_METHOD"],
|
|
12
|
+
path: env["PATH_INFO"],
|
|
13
|
+
query_string: env["QUERY_STRING"],
|
|
14
|
+
content_type: env["CONTENT_TYPE"],
|
|
15
|
+
body: read_body(env),
|
|
16
|
+
headers: extract_headers(env),
|
|
17
|
+
cookies: env["HTTP_COOKIE"],
|
|
18
|
+
host: env["HTTP_HOST"],
|
|
19
|
+
timestamp: defined?(Time.current) ? Time.current : Time.now
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def capture_job!(job)
|
|
24
|
+
@current_context = :job
|
|
25
|
+
@last_job = {
|
|
26
|
+
class: job.class,
|
|
27
|
+
arguments: job.arguments,
|
|
28
|
+
job_id: job.job_id,
|
|
29
|
+
queue_name: job.queue_name,
|
|
30
|
+
timestamp: defined?(Time.current) ? Time.current : Time.now
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def capture_mailer!(mailer_class, action, args)
|
|
35
|
+
@current_context = :mailer
|
|
36
|
+
@last_mailer = {
|
|
37
|
+
class: mailer_class.to_s,
|
|
38
|
+
action: action.to_s,
|
|
39
|
+
arguments: args,
|
|
40
|
+
timestamp: defined?(Time.current) ? Time.current : Time.now
|
|
41
|
+
}
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def capture_rake!(task_name, args)
|
|
45
|
+
@current_context = :rake
|
|
46
|
+
@last_rake = {
|
|
47
|
+
task_name: task_name,
|
|
48
|
+
arguments: args,
|
|
49
|
+
timestamp: defined?(Time.current) ? Time.current : Time.now
|
|
50
|
+
}
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DebuggerReplay
|
|
4
|
+
class << self
|
|
5
|
+
attr_accessor :pending_replay, :pending_job, :pending_mailer, :pending_rake
|
|
6
|
+
|
|
7
|
+
def replay!
|
|
8
|
+
case current_context
|
|
9
|
+
when :job then last_job ? replay_job! : puts("No job captured.")
|
|
10
|
+
when :mailer then last_mailer ? replay_mailer! : puts("No mailer captured.")
|
|
11
|
+
when :rake then last_rake ? replay_rake! : puts("No rake task captured.")
|
|
12
|
+
when :request then last_request ? replay_request! : puts("No request captured.")
|
|
13
|
+
else puts "No request, job, mailer, or rake task captured."
|
|
14
|
+
end
|
|
15
|
+
nil
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def replay_request!
|
|
19
|
+
@pending_replay = last_request.dup
|
|
20
|
+
reload_and_queue!("request")
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def replay_job!
|
|
24
|
+
@pending_job = last_job.dup
|
|
25
|
+
reload_and_queue!("job")
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def replay_mailer!
|
|
29
|
+
@pending_mailer = last_mailer.dup
|
|
30
|
+
reload_and_queue!("mailer")
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def replay_rake!
|
|
34
|
+
@pending_rake = last_rake.dup
|
|
35
|
+
reload_and_queue!("rake")
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def execute_pending_request!
|
|
39
|
+
return unless @pending_replay
|
|
40
|
+
req = @pending_replay
|
|
41
|
+
@pending_replay = nil
|
|
42
|
+
puts "\n=> Replaying: #{req[:method]} #{req[:path]}"
|
|
43
|
+
Rails.application.call(build_env(req))
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def execute_pending_job!
|
|
47
|
+
return unless @pending_job
|
|
48
|
+
job = @pending_job
|
|
49
|
+
@pending_job = nil
|
|
50
|
+
puts "\n=> Replaying: #{job[:class]}#perform(#{format_args(job[:arguments])})"
|
|
51
|
+
job[:class].perform_now(*job[:arguments])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def execute_pending_mailer!
|
|
55
|
+
return unless @pending_mailer
|
|
56
|
+
mailer = @pending_mailer
|
|
57
|
+
@pending_mailer = nil
|
|
58
|
+
puts "\n=> Replaying: #{mailer[:class]}##{mailer[:action]}(#{format_args(mailer[:arguments])})"
|
|
59
|
+
mailer[:class].constantize.send(mailer[:action], *mailer[:arguments]).deliver_now
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def execute_pending_rake!
|
|
63
|
+
return unless @pending_rake
|
|
64
|
+
rake = @pending_rake
|
|
65
|
+
@pending_rake = nil
|
|
66
|
+
puts "\n=> Replaying: rake #{rake[:task_name]}[#{rake[:arguments].join(',')}]"
|
|
67
|
+
task = Rake::Task[rake[:task_name]]
|
|
68
|
+
task.reenable
|
|
69
|
+
task.invoke(*rake[:arguments])
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
def reload_and_queue!(type)
|
|
75
|
+
puts "\n=> Reloading code..."
|
|
76
|
+
Rails.application.reloader.reload!
|
|
77
|
+
puts "=> #{type.capitalize} replay queued."
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DebuggerReplay
|
|
4
|
+
class << self
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def build_env(req)
|
|
8
|
+
url = req[:path]
|
|
9
|
+
url += "?#{req[:query_string]}" if req[:query_string]&.length&.positive?
|
|
10
|
+
env = Rack::MockRequest.env_for(url, method: req[:method], input: req[:body],
|
|
11
|
+
"CONTENT_TYPE" => req[:content_type], "HTTP_HOST" => req[:host] || "localhost")
|
|
12
|
+
req[:headers].each { |k, v| env["HTTP_#{k.upcase.tr('-', '_')}"] = v }
|
|
13
|
+
env["HTTP_COOKIE"] = req[:cookies] if req[:cookies]&.length&.positive?
|
|
14
|
+
env
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DebuggerReplay
|
|
4
|
+
class << self
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def read_body(env)
|
|
8
|
+
return nil unless env["rack.input"]
|
|
9
|
+
body = env["rack.input"].read
|
|
10
|
+
env["rack.input"].rewind
|
|
11
|
+
body&.length&.positive? ? body : nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def extract_headers(env)
|
|
15
|
+
env.select { |k, _| k.start_with?("HTTP_") && !%w[HTTP_COOKIE HTTP_HOST].include?(k) }
|
|
16
|
+
.transform_keys { |k| k.delete_prefix("HTTP_").split("_").map(&:capitalize).join("-") }
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DebuggerReplay
|
|
4
|
+
class MailerSubscriber
|
|
5
|
+
def call(name, started, finished, unique_id, payload)
|
|
6
|
+
mailer_class = payload[:mailer].to_s.split("#").first
|
|
7
|
+
action = payload[:action].to_s
|
|
8
|
+
DebuggerReplay.capture_mailer!(mailer_class, action, payload[:args] || [])
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
module JobReplayCallback
|
|
13
|
+
def self.included(base)
|
|
14
|
+
base.class_eval do
|
|
15
|
+
after_perform do
|
|
16
|
+
DebuggerReplay.execute_pending_job!
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DebuggerReplay
|
|
4
|
+
class RequestCapture
|
|
5
|
+
def initialize(app)
|
|
6
|
+
@app = app
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def call(env)
|
|
10
|
+
path = env["PATH_INFO"]
|
|
11
|
+
DebuggerReplay.capture_request!(env) unless IGNORED_PATHS.any? { |p| path.start_with?(p) }
|
|
12
|
+
@app.call(env)
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class ReplayExecutor
|
|
17
|
+
def initialize(app)
|
|
18
|
+
@app = app
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def call(env)
|
|
22
|
+
result = @app.call(env)
|
|
23
|
+
DebuggerReplay.execute_pending_request!
|
|
24
|
+
result
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module DebuggerReplay
|
|
2
|
+
class Railtie < Rails::Railtie
|
|
3
|
+
initializer "debugger_replay.setup", after: :load_config_initializers do
|
|
4
|
+
next unless Rails.env.development?
|
|
5
|
+
|
|
6
|
+
# Request middleware
|
|
7
|
+
Rails.application.config.middleware.insert_before(0, DebuggerReplay::RequestCapture)
|
|
8
|
+
Rails.application.config.middleware.use(DebuggerReplay::ReplayExecutor)
|
|
9
|
+
|
|
10
|
+
# Job support
|
|
11
|
+
DebuggerReplay.subscribe_to_jobs!
|
|
12
|
+
ActiveSupport.on_load(:active_job) do
|
|
13
|
+
include DebuggerReplay::JobReplayCallback
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Mailer support
|
|
17
|
+
DebuggerReplay.subscribe_to_mailers!
|
|
18
|
+
|
|
19
|
+
# Rake support
|
|
20
|
+
DebuggerReplay.instrument_rake_tasks!
|
|
21
|
+
|
|
22
|
+
# Define `r` shortcut at top level
|
|
23
|
+
Object.send(:define_method, :r) do
|
|
24
|
+
DebuggerReplay.replay!
|
|
25
|
+
if defined?(DEBUGGER__::SESSION)
|
|
26
|
+
DEBUGGER__::SESSION.add_preset_commands("replay", ["c"], kick: false, continue: true)
|
|
27
|
+
end
|
|
28
|
+
nil
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module DebuggerReplay
|
|
4
|
+
class << self
|
|
5
|
+
def subscribe_to_jobs!
|
|
6
|
+
return unless defined?(ActiveSupport::Notifications)
|
|
7
|
+
ActiveSupport::Notifications.subscribe("perform_start.active_job") { |*, payload| capture_job!(payload[:job]) }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def subscribe_to_mailers!
|
|
11
|
+
return unless defined?(ActiveSupport::Notifications)
|
|
12
|
+
ActiveSupport::Notifications.subscribe("process.action_mailer", MailerSubscriber.new)
|
|
13
|
+
ActiveSupport::Notifications.subscribe("deliver.action_mailer") { |*, _| execute_pending_mailer! }
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def instrument_rake_tasks!
|
|
17
|
+
return unless defined?(Rake::Task)
|
|
18
|
+
Rake::Task.class_eval do
|
|
19
|
+
alias_method :original_execute, :execute
|
|
20
|
+
def execute(args = nil)
|
|
21
|
+
DebuggerReplay.capture_rake!(name, args&.to_a || [])
|
|
22
|
+
original_execute(args).tap { DebuggerReplay.execute_pending_rake! }
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "debugger_replay/version"
|
|
4
|
+
|
|
5
|
+
# helpers/ - Private utility methods
|
|
6
|
+
require_relative "debugger_replay/helpers/env_builder"
|
|
7
|
+
require_relative "debugger_replay/helpers/request_parser"
|
|
8
|
+
require_relative "debugger_replay/helpers/formatter"
|
|
9
|
+
|
|
10
|
+
# core/ - Main logic
|
|
11
|
+
require_relative "debugger_replay/core/capture"
|
|
12
|
+
require_relative "debugger_replay/core/replay"
|
|
13
|
+
|
|
14
|
+
# setup/ - Rails integration
|
|
15
|
+
require_relative "debugger_replay/setup/subscriptions"
|
|
16
|
+
require_relative "debugger_replay/setup/middleware"
|
|
17
|
+
require_relative "debugger_replay/setup/callbacks"
|
|
18
|
+
|
|
19
|
+
module DebuggerReplay
|
|
20
|
+
IGNORED_PATHS = %w[/cable /assets /rails/ /logo /avatar /banner].freeze
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
require_relative "debugger_replay/setup/railtie" if defined?(Rails::Railtie)
|
metadata
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: debugger_replay
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Nguyen Vu
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2026-01-18 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: rack
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ">="
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
description: Replay HTTP requests, ActiveJob jobs, ActionMailer deliveries, and Rake
|
|
28
|
+
tasks with hot-reloaded code at debugger breakpoints.
|
|
29
|
+
email:
|
|
30
|
+
- ''
|
|
31
|
+
executables: []
|
|
32
|
+
extensions: []
|
|
33
|
+
extra_rdoc_files: []
|
|
34
|
+
files:
|
|
35
|
+
- LICENSE
|
|
36
|
+
- README.md
|
|
37
|
+
- lib/debugger_replay.rb
|
|
38
|
+
- lib/debugger_replay/core/capture.rb
|
|
39
|
+
- lib/debugger_replay/core/replay.rb
|
|
40
|
+
- lib/debugger_replay/helpers/env_builder.rb
|
|
41
|
+
- lib/debugger_replay/helpers/formatter.rb
|
|
42
|
+
- lib/debugger_replay/helpers/request_parser.rb
|
|
43
|
+
- lib/debugger_replay/setup/callbacks.rb
|
|
44
|
+
- lib/debugger_replay/setup/middleware.rb
|
|
45
|
+
- lib/debugger_replay/setup/railtie.rb
|
|
46
|
+
- lib/debugger_replay/setup/subscriptions.rb
|
|
47
|
+
- lib/debugger_replay/version.rb
|
|
48
|
+
homepage: https://github.com/tednguyendev/debugger_replay
|
|
49
|
+
licenses:
|
|
50
|
+
- MIT
|
|
51
|
+
metadata:
|
|
52
|
+
homepage_uri: https://github.com/tednguyendev/debugger_replay
|
|
53
|
+
source_code_uri: https://github.com/tednguyendev/debugger_replay
|
|
54
|
+
changelog_uri: https://github.com/tednguyendev/debugger_replay/blob/main/CHANGELOG.md
|
|
55
|
+
post_install_message:
|
|
56
|
+
rdoc_options: []
|
|
57
|
+
require_paths:
|
|
58
|
+
- lib
|
|
59
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
60
|
+
requirements:
|
|
61
|
+
- - ">="
|
|
62
|
+
- !ruby/object:Gem::Version
|
|
63
|
+
version: 3.0.0
|
|
64
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
requirements: []
|
|
70
|
+
rubygems_version: 3.5.22
|
|
71
|
+
signing_key:
|
|
72
|
+
specification_version: 4
|
|
73
|
+
summary: Hot-reload and replay requests during debugging
|
|
74
|
+
test_files: []
|