gubed 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/CHANGELOG.md +5 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +72 -0
- data/Rakefile +10 -0
- data/examples/calculator.rb +22 -0
- data/examples/sample_app.rb +28 -0
- data/exe/gubed +69 -0
- data/lib/gubed/breakpoint_manager.rb +196 -0
- data/lib/gubed/breakpoint_scanner.rb +93 -0
- data/lib/gubed/version.rb +5 -0
- data/lib/gubed.rb +9 -0
- metadata +59 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 208a0c9f02fb1d518500ba12917be9aa63454f8a1f270a25385b22479a70f29f
|
|
4
|
+
data.tar.gz: 501f3302b575817e1273decf824d6a640974ddbe43a5471256595f5f27138fd5
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1ec8528a5c4795934aff69083d308eb8e906b9d69009250e26f96c3892092043d4ff5dbee1231040c0085e542b0eca2f2f678bdae8e9ee9e407ce949c239953b
|
|
7
|
+
data.tar.gz: fbe63215e825481e7471a26353034991a4ca9c78a8b8a83f187dfd1bc479505522124fb50eff71839b0f44b258893afee7480c85d75c7d0297738d80d87f451b
|
data/CHANGELOG.md
ADDED
data/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thomas Countz
|
|
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/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Thomas Countz
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Gubed
|
|
2
|
+
|
|
3
|
+
A command-line tool for managing debugger breakpoints in Ruby projects. Find, comment, uncomment, and delete debugging statements like `binding.pry`, `debugger`, `binding.irb`, and more.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
gem install gubed
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
Run `gubed` in any Ruby project directory to scan for debugging breakpoints:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
$ gubed --help
|
|
17
|
+
|
|
18
|
+
gubed # Scan current directory
|
|
19
|
+
gubed /path/to/project # Scan specific directory
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### Interactive Mode
|
|
23
|
+
|
|
24
|
+
Once Gubed finds breakpoints, you can:
|
|
25
|
+
|
|
26
|
+
- Navigate with `j` (down) and `k` (up)
|
|
27
|
+
- Press `c` to comment/uncomment breakpoints
|
|
28
|
+
- Press `d` to delete breakpoints
|
|
29
|
+
- Press `v` to view surrounding code context
|
|
30
|
+
- Press `q` to quit
|
|
31
|
+
- Press `h` for help
|
|
32
|
+
|
|
33
|
+
### Supported Breakpoint Types
|
|
34
|
+
|
|
35
|
+
These are found based on regex.
|
|
36
|
+
|
|
37
|
+
- `binding.pry`
|
|
38
|
+
- `binding.irb`
|
|
39
|
+
- `binding.break`
|
|
40
|
+
- `debugger`
|
|
41
|
+
- `byebug`
|
|
42
|
+
|
|
43
|
+
## Example
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
$ gubed
|
|
47
|
+
Gubed - Ruby Breakpoint Manager
|
|
48
|
+
========================================
|
|
49
|
+
|
|
50
|
+
> 1. [#] binding.irb /Users/thomas.countz/Code/experiments/gubed/examples/calculator.rb:14
|
|
51
|
+
2. [ ] binding.break /Users/thomas.countz/Code/experiments/gubed/examples/calculator.rb:19
|
|
52
|
+
3. [ ] binding.pry /Users/thomas.countz/Code/experiments/gubed/examples/calculator.rb:3
|
|
53
|
+
4. [#] debugger /Users/thomas.countz/Code/experiments/gubed/examples/calculator.rb:8
|
|
54
|
+
5. [#] debugger /Users/thomas.countz/Code/experiments/gubed/examples/sample_app.rb:11
|
|
55
|
+
6. [ ] debugger /Users/thomas.countz/Code/experiments/gubed/examples/sample_app.rb:15
|
|
56
|
+
7. [#] binding.break /Users/thomas.countz/Code/experiments/gubed/examples/sample_app.rb:17
|
|
57
|
+
8. [ ] binding.break /Users/thomas.countz/Code/experiments/gubed/examples/sample_app.rb:22
|
|
58
|
+
9. [ ] byebug /Users/thomas.countz/Code/experiments/gubed/examples/sample_app.rb:25
|
|
59
|
+
10. [ ] binding.pry /Users/thomas.countz/Code/experiments/gubed/examples/sample_app.rb:4
|
|
60
|
+
11. [#] binding.irb /Users/thomas.countz/Code/experiments/gubed/examples/sample_app.rb:9
|
|
61
|
+
|
|
62
|
+
Commands: [j]down [k]up [g]oto [v]iew [t]oggle [d]elete [r]efresh [q]uit [h]elp
|
|
63
|
+
Selected: 1 of 11
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Development
|
|
67
|
+
|
|
68
|
+
After checking out the repo, run `bin/setup` to install dependencies. Run `rake test` to run the tests.
|
|
69
|
+
|
|
70
|
+
## License
|
|
71
|
+
|
|
72
|
+
MIT License Copyright (c) 2025 Thomas Countz
|
data/Rakefile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class Calculator
|
|
2
|
+
def add(a, b)
|
|
3
|
+
binding.pry
|
|
4
|
+
a + b
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def subtract(a, b)
|
|
8
|
+
# debugger
|
|
9
|
+
a - b
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def multiply(a, b)
|
|
13
|
+
a * b
|
|
14
|
+
# binding.irb
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def divide(a, b)
|
|
18
|
+
return 0 if b == 0
|
|
19
|
+
binding.break
|
|
20
|
+
a / b
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
class SampleApp
|
|
2
|
+
def initialize
|
|
3
|
+
@users = []
|
|
4
|
+
binding.pry # Debug initialization
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def add_user(name, email)
|
|
8
|
+
user = {name: name, email: email}
|
|
9
|
+
# binding.irb # Commented out debug point
|
|
10
|
+
@users << user
|
|
11
|
+
# debugger # Check user addition
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def find_user(email)
|
|
15
|
+
debugger
|
|
16
|
+
@users.find { |user| user[:email] == email }
|
|
17
|
+
# binding.break if result.nil?
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def process_users
|
|
21
|
+
require "debug"
|
|
22
|
+
binding.break
|
|
23
|
+
@users.each do |user|
|
|
24
|
+
puts "Processing #{user[:name]}"
|
|
25
|
+
byebug # Debug each user processing
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
data/exe/gubed
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require "optparse"
|
|
5
|
+
require "gubed"
|
|
6
|
+
|
|
7
|
+
class GubedCLI
|
|
8
|
+
def initialize
|
|
9
|
+
@options = {}
|
|
10
|
+
@parser = create_option_parser
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run(args = ARGV)
|
|
14
|
+
@parser.parse!(args)
|
|
15
|
+
|
|
16
|
+
root_path = args.first || "."
|
|
17
|
+
|
|
18
|
+
unless File.directory?(root_path)
|
|
19
|
+
puts "Error: '#{root_path}' is not a valid directory"
|
|
20
|
+
exit 1
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
manager = Gubed::BreakpointManager.new(root_path)
|
|
24
|
+
manager.run
|
|
25
|
+
rescue Interrupt
|
|
26
|
+
puts "\n\nExiting..."
|
|
27
|
+
exit 0
|
|
28
|
+
rescue => e
|
|
29
|
+
puts "Error: #{e.message}"
|
|
30
|
+
exit 1
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def create_option_parser
|
|
36
|
+
OptionParser.new do |opts|
|
|
37
|
+
opts.banner = "Usage: gubed [options] [directory]"
|
|
38
|
+
opts.separator ""
|
|
39
|
+
opts.separator "Gubed is a Ruby breakpoint manager that helps you find, manage, and remove"
|
|
40
|
+
opts.separator "debugging breakpoints (binding.pry, debugger, etc.) in your Ruby projects."
|
|
41
|
+
opts.separator ""
|
|
42
|
+
opts.separator "Options:"
|
|
43
|
+
|
|
44
|
+
opts.on("-h", "--help", "Show this help message") do
|
|
45
|
+
puts opts
|
|
46
|
+
exit
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
opts.on("-v", "--version", "Show version") do
|
|
50
|
+
puts "Gubed version #{Gubed::VERSION}"
|
|
51
|
+
exit
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
opts.separator ""
|
|
55
|
+
opts.separator "Examples:"
|
|
56
|
+
opts.separator " gubed # Scan current directory"
|
|
57
|
+
opts.separator " gubed /path/to/project # Scan specific directory"
|
|
58
|
+
opts.separator ""
|
|
59
|
+
opts.separator "Supported breakpoint types:"
|
|
60
|
+
opts.separator " - binding.pry"
|
|
61
|
+
opts.separator " - binding.irb"
|
|
62
|
+
opts.separator " - binding.break"
|
|
63
|
+
opts.separator " - debugger"
|
|
64
|
+
opts.separator " - byebug"
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
GubedCLI.new.run if $0 == __FILE__ || File.basename($0) == "gubed"
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "io/console"
|
|
4
|
+
|
|
5
|
+
module Gubed
|
|
6
|
+
class BreakpointManager
|
|
7
|
+
def initialize(root_path = ".")
|
|
8
|
+
@scanner = BreakpointScanner.new(root_path)
|
|
9
|
+
@breakpoints = []
|
|
10
|
+
@current_index = 0
|
|
11
|
+
@message = nil
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
refresh_breakpoints
|
|
16
|
+
return puts "No breakpoints found." if @breakpoints.empty?
|
|
17
|
+
|
|
18
|
+
loop do
|
|
19
|
+
show_list
|
|
20
|
+
puts @message if @message
|
|
21
|
+
input = get_input
|
|
22
|
+
|
|
23
|
+
case input
|
|
24
|
+
when "q"
|
|
25
|
+
break if exit_program?(input)
|
|
26
|
+
when "r"
|
|
27
|
+
refresh_breakpoints
|
|
28
|
+
@current_index = 0
|
|
29
|
+
when "t"
|
|
30
|
+
toggle_comment
|
|
31
|
+
when "d"
|
|
32
|
+
delete_breakpoint
|
|
33
|
+
when "v"
|
|
34
|
+
show_context
|
|
35
|
+
when "g"
|
|
36
|
+
prompt_for_breakpoint
|
|
37
|
+
when "j"
|
|
38
|
+
move_down
|
|
39
|
+
when "k"
|
|
40
|
+
move_up
|
|
41
|
+
when "h", "?"
|
|
42
|
+
show_help
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def refresh_breakpoints
|
|
50
|
+
print "Scanning..."
|
|
51
|
+
@breakpoints = @scanner.scan
|
|
52
|
+
puts " found #{@breakpoints.length} breakpoints"
|
|
53
|
+
@current_index = [@current_index, @breakpoints.length - 1].min if @breakpoints.any?
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def show_list
|
|
57
|
+
system("clear") || system("cls")
|
|
58
|
+
puts "Gubed - Ruby Breakpoint Manager"
|
|
59
|
+
puts "=" * 40
|
|
60
|
+
puts
|
|
61
|
+
|
|
62
|
+
if @breakpoints.empty?
|
|
63
|
+
puts "No breakpoints found."
|
|
64
|
+
return
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
width = @breakpoints.length.to_s.length
|
|
68
|
+
@breakpoints.each_with_index do |bp, index|
|
|
69
|
+
marker = (index == @current_index) ? ">" : " "
|
|
70
|
+
status = bp.commented? ? "[#]" : "[ ]"
|
|
71
|
+
number = (index + 1).to_s.rjust(width)
|
|
72
|
+
|
|
73
|
+
puts "#{marker} #{number}. #{status} #{bp.type} #{bp.location}"
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
puts
|
|
77
|
+
puts "Commands: [j]down [k]up [g]oto [v]iew [t]oggle [d]elete [r]efresh [q]uit [h]elp"
|
|
78
|
+
puts "Selected: #{@current_index + 1} of #{@breakpoints.length}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def get_input
|
|
82
|
+
$stdin.getch
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def prompt_for_breakpoint
|
|
86
|
+
print "Go to breakpoint: "
|
|
87
|
+
input = gets.chomp
|
|
88
|
+
return if input.empty?
|
|
89
|
+
|
|
90
|
+
num = input.to_i
|
|
91
|
+
if num.between?(1, @breakpoints.length)
|
|
92
|
+
@current_index = num - 1
|
|
93
|
+
else
|
|
94
|
+
@message = "Invalid selection: #{num}."
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def move_down
|
|
99
|
+
@current_index = (@current_index + 1) % @breakpoints.length
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def move_up
|
|
103
|
+
@current_index = (@current_index - 1) % @breakpoints.length
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def show_context
|
|
107
|
+
return if @breakpoints.empty?
|
|
108
|
+
|
|
109
|
+
bp = @breakpoints[@current_index]
|
|
110
|
+
lines = File.readlines(bp.file)
|
|
111
|
+
|
|
112
|
+
puts
|
|
113
|
+
puts "Context for #{bp.location}:"
|
|
114
|
+
puts "-" * 40
|
|
115
|
+
|
|
116
|
+
start_line = [bp.line_number - 6, 1].max
|
|
117
|
+
end_line = [bp.line_number + 4, lines.length].min
|
|
118
|
+
|
|
119
|
+
(start_line..end_line).each do |line_num|
|
|
120
|
+
line_index = line_num - 1
|
|
121
|
+
content = lines[line_index].rstrip
|
|
122
|
+
marker = (line_num == bp.line_number) ? ">>> " : " "
|
|
123
|
+
puts "#{marker}#{line_num}: #{content}"
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
puts
|
|
127
|
+
print "Press Enter to continue..."
|
|
128
|
+
gets
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
def toggle_comment
|
|
132
|
+
return if @breakpoints.empty?
|
|
133
|
+
|
|
134
|
+
bp = @breakpoints[@current_index]
|
|
135
|
+
|
|
136
|
+
modify_line(bp) do |line|
|
|
137
|
+
if bp.commented?
|
|
138
|
+
line.sub(/^(\s*)#\s*/, '\1')
|
|
139
|
+
else
|
|
140
|
+
line.sub(/^(\s*)/, '\1# ')
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
refresh_breakpoints
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def delete_breakpoint
|
|
148
|
+
return if @breakpoints.empty?
|
|
149
|
+
|
|
150
|
+
bp = @breakpoints[@current_index]
|
|
151
|
+
|
|
152
|
+
print "Delete breakpoint at #{bp.location}? (y/N): "
|
|
153
|
+
response = gets.chomp.downcase
|
|
154
|
+
|
|
155
|
+
return unless response == "y" || response == "yes"
|
|
156
|
+
|
|
157
|
+
lines = File.readlines(bp.file)
|
|
158
|
+
lines.delete_at(bp.line_number - 1)
|
|
159
|
+
File.write(bp.file, lines.join)
|
|
160
|
+
|
|
161
|
+
refresh_breakpoints
|
|
162
|
+
@current_index = [@current_index, @breakpoints.length - 1].min if @breakpoints.any?
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def exit_program?(input)
|
|
166
|
+
print "Press #{(input == "\e") ? "esc" : input} again to exit or any other key to continue: "
|
|
167
|
+
response = $stdin.getch
|
|
168
|
+
true if response == input
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
def modify_line(breakpoint)
|
|
172
|
+
lines = File.readlines(breakpoint.file)
|
|
173
|
+
lines[breakpoint.line_number - 1] = yield(lines[breakpoint.line_number - 1])
|
|
174
|
+
File.write(breakpoint.file, lines.join)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def show_help
|
|
178
|
+
puts
|
|
179
|
+
puts "Commands:"
|
|
180
|
+
puts " j - Move selection down"
|
|
181
|
+
puts " k - Move selection up"
|
|
182
|
+
puts " g - Go to a specific breakpoint by number"
|
|
183
|
+
puts " v - Show context around breakpoint"
|
|
184
|
+
puts " t - Toggle line comment on selected breakpoint"
|
|
185
|
+
puts " d - Delete selected breakpoint"
|
|
186
|
+
puts " r - Rescan for breakpoints"
|
|
187
|
+
puts " q - Exit program"
|
|
188
|
+
puts " h, ? - Show this help"
|
|
189
|
+
puts
|
|
190
|
+
puts "Legend: [ ] = Active, [#] = Commented"
|
|
191
|
+
puts
|
|
192
|
+
print "Press Enter to continue..."
|
|
193
|
+
gets
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "find"
|
|
4
|
+
|
|
5
|
+
module Gubed
|
|
6
|
+
Breakpoint = Data.define(:file, :line_number, :content, :type) do
|
|
7
|
+
def initialize(file:, line_number:, content:, type:)
|
|
8
|
+
super(file: file, line_number: line_number, content: content.strip, type: type)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def commented?
|
|
12
|
+
content.start_with?("#")
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def location
|
|
16
|
+
"#{file}:#{line_number}"
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class BreakpointScanner
|
|
21
|
+
BREAKPOINT_PATTERNS = {
|
|
22
|
+
"binding.pry" => /^\s*(?:#\s*)?binding\.pry\b/,
|
|
23
|
+
"binding.irb" => /^\s*(?:#\s*)?binding\.irb\b/,
|
|
24
|
+
"binding.break" => /^\s*(?:#\s*)?binding\.break\b/,
|
|
25
|
+
"debugger" => /^\s*(?:#\s*)?debugger\b/,
|
|
26
|
+
"byebug" => /^\s*(?:#\s*)?byebug\b/,
|
|
27
|
+
"debug" => /^\s*(?:#\s*)?(?:require\s+['"]debug['"];\s*)?binding\.break\b/
|
|
28
|
+
}.freeze
|
|
29
|
+
|
|
30
|
+
RUBY_FILE_EXTENSIONS = %w[.rb .rake .gemspec].freeze
|
|
31
|
+
DIR_TO_SKIP = %w[.git vendor node_modules tmp].freeze
|
|
32
|
+
|
|
33
|
+
def initialize(root_path = ".")
|
|
34
|
+
@root_path = File.expand_path(root_path)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def scan
|
|
38
|
+
breakpoints = []
|
|
39
|
+
|
|
40
|
+
Find.find(@root_path) do |path|
|
|
41
|
+
if File.directory?(path)
|
|
42
|
+
Find.prune if DIR_TO_SKIP.any? { |dir| path.include?(dir) }
|
|
43
|
+
next
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
next unless ruby_file?(path)
|
|
47
|
+
|
|
48
|
+
breakpoints.concat(scan_file(path))
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
breakpoints.sort_by(&:location)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
def ruby_file?(path)
|
|
57
|
+
RUBY_FILE_EXTENSIONS.any? { |ext| path.end_with?(ext) } ||
|
|
58
|
+
(File.executable?(path) && ruby_shebang?(path))
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def ruby_shebang?(path)
|
|
62
|
+
first_line = begin
|
|
63
|
+
File.open(path, &:readline).strip
|
|
64
|
+
rescue
|
|
65
|
+
""
|
|
66
|
+
end
|
|
67
|
+
first_line.include?("ruby")
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def scan_file(file_path)
|
|
71
|
+
breakpoints = []
|
|
72
|
+
|
|
73
|
+
File.foreach(file_path).each_with_index do |line, index|
|
|
74
|
+
BREAKPOINT_PATTERNS.each do |type, pattern|
|
|
75
|
+
if line.match(pattern)
|
|
76
|
+
breakpoints << Breakpoint.new(
|
|
77
|
+
file: file_path,
|
|
78
|
+
line_number: index + 1,
|
|
79
|
+
content: line.chomp,
|
|
80
|
+
type: type
|
|
81
|
+
)
|
|
82
|
+
break
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
breakpoints
|
|
88
|
+
rescue => e
|
|
89
|
+
puts "Warning: Could not scan #{file_path}: #{e.message}"
|
|
90
|
+
[]
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
data/lib/gubed.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gubed
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Thomas Countz
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Gubed is a standalone command-line tool for finding and managing debugger
|
|
13
|
+
breakpoints (binding.pry, debugger, etc.) in Ruby codebases. Install globally with
|
|
14
|
+
'gem install gubed' and use in any project.
|
|
15
|
+
email:
|
|
16
|
+
- thomascountz@gmail.com
|
|
17
|
+
executables:
|
|
18
|
+
- gubed
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- CHANGELOG.md
|
|
23
|
+
- LICENSE
|
|
24
|
+
- LICENSE.txt
|
|
25
|
+
- README.md
|
|
26
|
+
- Rakefile
|
|
27
|
+
- examples/calculator.rb
|
|
28
|
+
- examples/sample_app.rb
|
|
29
|
+
- exe/gubed
|
|
30
|
+
- lib/gubed.rb
|
|
31
|
+
- lib/gubed/breakpoint_manager.rb
|
|
32
|
+
- lib/gubed/breakpoint_scanner.rb
|
|
33
|
+
- lib/gubed/version.rb
|
|
34
|
+
homepage: https://github.com/thomascountz/gubed
|
|
35
|
+
licenses:
|
|
36
|
+
- MIT
|
|
37
|
+
metadata:
|
|
38
|
+
allowed_push_host: https://rubygems.org
|
|
39
|
+
homepage_uri: https://github.com/thomascountz/gubed
|
|
40
|
+
source_code_uri: https://github.com/thomascountz/gubed
|
|
41
|
+
changelog_uri: https://github.com/thomascountz/gubed/blob/main/CHANGELOG.md
|
|
42
|
+
rdoc_options: []
|
|
43
|
+
require_paths:
|
|
44
|
+
- lib
|
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
46
|
+
requirements:
|
|
47
|
+
- - ">="
|
|
48
|
+
- !ruby/object:Gem::Version
|
|
49
|
+
version: 3.2.0
|
|
50
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
requirements: []
|
|
56
|
+
rubygems_version: 3.6.9
|
|
57
|
+
specification_version: 4
|
|
58
|
+
summary: Ruby CLI for managing and editing debugger breakpoints
|
|
59
|
+
test_files: []
|