pry-moves 0.1.10 → 1.0.1
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/Gemfile +1 -1
- data/Gemfile.lock +7 -5
- data/README.md +18 -8
- data/lib/commands/debug.rb +17 -0
- data/lib/commands/finish.rb +26 -0
- data/lib/commands/goto.rb +19 -0
- data/lib/commands/iterate.rb +22 -0
- data/lib/commands/next.rb +37 -0
- data/lib/commands/next_breakpoint.rb +22 -0
- data/lib/commands/step.rb +83 -0
- data/lib/commands/trace_command.rb +87 -0
- data/lib/commands/trace_helpers.rb +49 -0
- data/lib/commands/traced_method.rb +71 -0
- data/lib/debug_sugar.rb +72 -0
- data/lib/pry-moves/add_suffix.rb +87 -0
- data/lib/pry-moves/backtrace.rb +81 -50
- data/lib/pry-moves/bindings_stack.rb +97 -0
- data/lib/pry-moves/commands.rb +50 -7
- data/lib/pry-moves/formatter.rb +75 -0
- data/lib/pry-moves/painter.rb +5 -1
- data/lib/pry-moves/pry_ext.rb +64 -16
- data/lib/pry-moves/pry_wrapper.rb +30 -17
- data/lib/pry-moves/recursion_tracker.rb +94 -0
- data/lib/pry-moves/restartable.rb +38 -0
- data/lib/pry-moves/version.rb +1 -1
- data/lib/pry-moves/watch.rb +3 -0
- data/lib/pry-moves.rb +66 -4
- data/lib/pry-stack_explorer/VERSION +2 -0
- data/lib/pry-stack_explorer/frame_manager.rb +6 -9
- data/lib/pry-stack_explorer/pry-stack_explorer.rb +3 -17
- data/lib/pry-stack_explorer/{commands.rb → stack_commands.rb} +10 -6
- data/lib/pry-stack_explorer/when_started_hook.rb +17 -63
- data/playground/Gemfile.lock +9 -9
- data/playground/README.md +1 -0
- data/playground/playground.rb +94 -9
- data/playground/sand.rb +45 -12
- data/playground/test.rb +5 -1
- data/playground/test.sh +1 -0
- data/pry-moves.gemspec +3 -2
- data/publish.sh +3 -0
- data/spec/backtrace_spec.rb +11 -13
- data/spec/blocks_spec.rb +41 -8
- data/spec/commands_spec.rb +26 -25
- data/spec/pry_debugger.rb +5 -1
- data/spec/redirection_spec.rb +7 -0
- data/spec/spec_helper.rb +9 -4
- data/spec/step_spec.rb +51 -0
- metadata +44 -10
- data/lib/pry-moves/helpers.rb +0 -50
- data/lib/pry-moves/trace_commands.rb +0 -105
- data/lib/pry-moves/tracer.rb +0 -169
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 47c7a7529bee28ca664536458891b00f6a1bee574869106686cd2abef9b57adb
|
4
|
+
data.tar.gz: 13d671c5f8ed4005304da6d4201908c0857d19e8ee3fe85631d18cea5c109580
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: addaf61aa995184e49f3b655f1d4fa863fa0554d1fb00f022fce7a9e1d8671595c03056e37754fd394bf9a204e32c91b3005a00fac3287f2803bafb66273e2d6
|
7
|
+
data.tar.gz: 9ba407049732e75c27e8cc87936e25967a30798212098fce844e948e7986912874137088fc87ffc8d9ef48ca09a3c2445da545132109a58ed47d5c4577775db4
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
pry-moves (0.1
|
4
|
+
pry-moves (1.0.1)
|
5
5
|
binding_of_caller (~> 0.7)
|
6
|
-
|
6
|
+
colorize (~> 0.8)
|
7
|
+
pry (>= 0.10.4, < 0.13)
|
7
8
|
|
8
9
|
GEM
|
9
10
|
remote: https://rubygems.org/
|
@@ -11,7 +12,8 @@ GEM
|
|
11
12
|
binding_of_caller (0.8.0)
|
12
13
|
debug_inspector (>= 0.0.1)
|
13
14
|
coderay (1.1.2)
|
14
|
-
|
15
|
+
colorize (0.8.1)
|
16
|
+
debug_inspector (1.1.0)
|
15
17
|
diff-lcs (1.3)
|
16
18
|
method_source (0.9.0)
|
17
19
|
pry (0.11.3)
|
@@ -39,10 +41,10 @@ PLATFORMS
|
|
39
41
|
ruby
|
40
42
|
|
41
43
|
DEPENDENCIES
|
42
|
-
pry
|
44
|
+
pry (= 0.11.3)
|
43
45
|
pry-moves!
|
44
46
|
pry-remote (~> 0.1.6)
|
45
47
|
rspec
|
46
48
|
|
47
49
|
BUNDLED WITH
|
48
|
-
1.
|
50
|
+
1.17.3
|
data/README.md
CHANGED
@@ -9,26 +9,35 @@ _An execution control add-on for [Pry][pry]._
|
|
9
9
|
|
10
10
|
## Commands:
|
11
11
|
|
12
|
+
Documentation for latest version. For [v0.1.12 see documentation here](https://github.com/garmoshka-mo/pry-moves/tree/v0.1.12#commands)
|
13
|
+
|
12
14
|
* `n` - **next** line in current frame, including block lines (moving to next line goes as naturally expected)
|
13
15
|
* `nn` - **next** line in current frame, skipping block lines
|
14
16
|
* `s` - **step** into function execution
|
15
17
|
* `s method_name` - step into method `method_name` (For example from `User.new.method_name`). Partial name match supported.
|
16
18
|
* `s +` - step into function, including hidden frames
|
17
19
|
* `f` - **finish** execution of current frame (block or method) and stop at next line on higher level
|
18
|
-
* `iterate` - go to next iteration of current block
|
19
20
|
* `c` - **continue**
|
20
|
-
* `
|
21
|
-
|
22
|
-
|
23
|
-
|
21
|
+
* `b` - go to next breakpoint (breakpoints currently are methods which contain "debug" in their name)
|
22
|
+
* `iterate` - go to next iteration of current block
|
23
|
+
* `g 10` - **goto** line 10
|
24
|
+
* `bt` - show backtrace, excluding hidden frames
|
25
|
+
* `bt +` `bt hidden` - show backtrace including hidden frames
|
26
|
+
* `bt a` `bt all` - full backtrace with system and hidden frames
|
27
|
+
* `bt 10` - go to backtrace line 10
|
24
28
|
* `bt > foo` - write backtrace to file `log/backtrace_foo.log`
|
25
29
|
* `up`/`down`/`top`/`bottom` - move over call stack
|
26
30
|
* `up +` - move up, including vapid frames (block callers, hidden frames)
|
27
31
|
* `up pattern` - move up till first frame which method name or file position in format `folder/script.rb:12` matches regexp pattern
|
28
32
|
* `debug some_method(some_param)` - call `some_method(some_param)` and interactively step into it. This way you can virtually "step back" by executing previous pieces of code from current method
|
33
|
+
* `.method` or `123` or `:hash_key` - Continue traversing of last object in history. E.g. `orders` will list array, then `3` will enter `orders[3]`, then `.price` will enter `orders[3].price`
|
29
34
|
* `watch variable` - display variable's value on each step
|
35
|
+
* `@` - restart. Set config `PryMoves.reload_rake_tasks = true` to automatically reload rake tasks
|
36
|
+
* `#` - exit with code 3, can be wrapped in bash script to fully reload ruby scripts
|
30
37
|
* `!` - exit
|
31
38
|
|
39
|
+
Variable & methods names takes precedence over commands.
|
40
|
+
So if you have variable named `step`, to execute command `step` type `cmd step` or command's alias, e.g. `s`
|
32
41
|
|
33
42
|
## Examples
|
34
43
|
|
@@ -61,10 +70,9 @@ end
|
|
61
70
|
|
62
71
|
## Configuration
|
63
72
|
|
64
|
-
Here is default configuration, you can
|
73
|
+
Here is default configuration, you can reassign it:
|
65
74
|
|
66
75
|
```ruby
|
67
|
-
PryMoves::Backtrace::lines_count = 5
|
68
76
|
PryMoves::Backtrace::filter =
|
69
77
|
/(\/gems\/|\/rubygems\/|\/bin\/|\/lib\/ruby\/|\/pry-moves\/)/
|
70
78
|
```
|
@@ -120,6 +128,8 @@ Please note that debugging functionality is implemented through
|
|
120
128
|
|
121
129
|
```
|
122
130
|
bundle exec rspec
|
131
|
+
|
132
|
+
DEBUG=true bundle exec rspec -e 'backtrace should backtrace'
|
123
133
|
```
|
124
134
|
|
125
135
|
## ToDo
|
@@ -136,7 +146,7 @@ bundle exec rspec
|
|
136
146
|
* Ivo Anjo ([@ivoanjo](https://github.com/ivoanjo))
|
137
147
|
|
138
148
|
Patches and bug reports are welcome. Just send a [pull request][pullrequests] or
|
139
|
-
file an [issue][issues].
|
149
|
+
file an [issue][issues].
|
140
150
|
|
141
151
|
## Acknowledgments
|
142
152
|
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class PryMoves::Debug < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
#
|
5
|
+
end
|
6
|
+
|
7
|
+
def trace(event, file, line, method, binding_)
|
8
|
+
return unless event == 'line'
|
9
|
+
if @first_line_skipped
|
10
|
+
true
|
11
|
+
else
|
12
|
+
@first_line_skipped = true
|
13
|
+
false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class PryMoves::Finish < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@block_to_finish = frame_digest(binding_) if binding_.frame_type == :block
|
5
|
+
end
|
6
|
+
|
7
|
+
def trace(event, file, line, method, binding_)
|
8
|
+
return true if @on_exit_from_method
|
9
|
+
return if @call_depth > 0 or event == 'c-return'
|
10
|
+
|
11
|
+
return true if @call_depth < 0
|
12
|
+
|
13
|
+
# early return:
|
14
|
+
return true if event == 'return' and
|
15
|
+
@call_depth == 0 and @method.within?(file, line) and
|
16
|
+
method == @method[:name] and @method.before_end?(line)
|
17
|
+
|
18
|
+
# for finishing blocks inside current method
|
19
|
+
if @block_to_finish
|
20
|
+
((@call_depth == 0) ^ (event == 'return')) and
|
21
|
+
@method.within?(file, line) and
|
22
|
+
@block_to_finish != current_frame_digest
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class PryMoves::Goto < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@goto_line = @command[:param].to_i
|
5
|
+
end
|
6
|
+
|
7
|
+
def trace(event, file, line, method, binding_)
|
8
|
+
if @call_depth < 0 or
|
9
|
+
@call_depth == 0 and event == 'return' and @method.within?(file, line)
|
10
|
+
PryMoves.messages << "⚠️ Unable to reach line #{@goto_line} in current frame"
|
11
|
+
return true
|
12
|
+
end
|
13
|
+
|
14
|
+
event == 'line' && @goto_line == line and
|
15
|
+
@method[:file] == file and
|
16
|
+
@call_depth == 0
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class PryMoves::Iterate < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@iteration_start_line = binding_.eval('__LINE__')
|
5
|
+
@caller_digest = frame_digest(binding_)
|
6
|
+
end
|
7
|
+
|
8
|
+
def trace(event, file, line, method, binding_)
|
9
|
+
return true if event == 'return' and
|
10
|
+
@method.within?(file, line)
|
11
|
+
|
12
|
+
# промотка итерации -
|
13
|
+
# попасть на ту же или предыдущую строку или выйти из дайджеста
|
14
|
+
# будучи в том же методе
|
15
|
+
event == 'line' and @call_depth == 0 and
|
16
|
+
@method.within?(file, line) and
|
17
|
+
(line <= @iteration_start_line or
|
18
|
+
@caller_digest != current_frame_digest
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class PryMoves::Next < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@start_line = binding_.eval('__LINE__')
|
5
|
+
@start_digest = frame_digest(binding_)
|
6
|
+
if @command[:param] == 'blockless'
|
7
|
+
@stay_at_frame = @start_digest
|
8
|
+
end
|
9
|
+
@events_traced = 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def trace(event, file, line, method, binding_)
|
13
|
+
@events_traced += 1
|
14
|
+
|
15
|
+
return true if @call_depth < 0
|
16
|
+
|
17
|
+
return unless @call_depth == 0 and @method.within?(file, line)
|
18
|
+
|
19
|
+
if event == 'line'
|
20
|
+
if @stay_at_frame
|
21
|
+
return (
|
22
|
+
@stay_at_frame == current_frame_digest or
|
23
|
+
@c_stack_level < 0
|
24
|
+
)
|
25
|
+
elsif @start_line != line or (
|
26
|
+
@events_traced > 1 and # чтобы не застревало на while (vx = shift)
|
27
|
+
@start_digest == current_frame_digest # for correct iterating over one_line_in_block
|
28
|
+
)
|
29
|
+
return true
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
true if event == 'return' and
|
34
|
+
method == @method[:name] and @method.before_end?(line)
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class PryMoves::NextBreakpoint < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@reach_digest = frame_digest(binding_)
|
5
|
+
end
|
6
|
+
|
7
|
+
def trace(event, file, line, method, binding_)
|
8
|
+
if @reach_digest
|
9
|
+
if @reach_digest == current_frame_digest
|
10
|
+
@reach_digest = nil
|
11
|
+
else
|
12
|
+
return
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
if method.to_s.include? "debug"
|
17
|
+
@pry_start_options[:initial_frame] = 1
|
18
|
+
true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
class PryMoves::Step < PryMoves::TraceCommand
|
2
|
+
|
3
|
+
def init(binding_)
|
4
|
+
@step_into_funcs = nil
|
5
|
+
@start_line = binding_.eval('__LINE__')
|
6
|
+
@caller_digest = frame_digest(binding_)
|
7
|
+
func = @command[:param]
|
8
|
+
redirect_step? binding_ # set @step_into_funcs from initial binding
|
9
|
+
if func == '+'
|
10
|
+
@step_in_everywhere = true
|
11
|
+
elsif func
|
12
|
+
@find_straight_descendant = true
|
13
|
+
@step_into_funcs = [func]
|
14
|
+
@step_into_funcs << '=initialize' if ['new', '=new'].include? func
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def trace(event, file, line, method, binding_)
|
19
|
+
if binding_.local_variable_defined? :pry_moves_skip
|
20
|
+
finish_cmd = {binding: binding_}
|
21
|
+
PryMoves::Finish.new finish_cmd, @pry_start_options do |binding|
|
22
|
+
start_tracing
|
23
|
+
end
|
24
|
+
return
|
25
|
+
end
|
26
|
+
|
27
|
+
@const_missing_level ||= 0
|
28
|
+
if method == :const_missing
|
29
|
+
if event == 'call'
|
30
|
+
@const_missing_level += 1
|
31
|
+
elsif event == 'return'
|
32
|
+
@const_missing_level -= 1
|
33
|
+
end
|
34
|
+
end
|
35
|
+
return if @const_missing_level > 0
|
36
|
+
|
37
|
+
return unless event == 'line'
|
38
|
+
|
39
|
+
if @step_in_everywhere
|
40
|
+
return true
|
41
|
+
elsif @step_into_funcs
|
42
|
+
if @call_depth < 0
|
43
|
+
PryMoves.messages << "⚠️ Unable to find function with name #{@step_into_funcs.join(',')}"
|
44
|
+
return true
|
45
|
+
end
|
46
|
+
|
47
|
+
return false if keep_search_method? binding_
|
48
|
+
elsif redirect_step? binding_
|
49
|
+
return false
|
50
|
+
elsif binding_.local_variable_defined? :hide_from_stack and
|
51
|
+
not @method.within?(file, line, method)
|
52
|
+
return false
|
53
|
+
end
|
54
|
+
|
55
|
+
true
|
56
|
+
end
|
57
|
+
|
58
|
+
def keep_search_method? binding_
|
59
|
+
return true unless method_matches? binding_.eval('__callee__')
|
60
|
+
|
61
|
+
return true if @find_straight_descendant &&
|
62
|
+
# if we want to step-in only into straight descendant
|
63
|
+
@caller_digest != current_frame_digest(upward: 1 + 1) # 1 for getting parent and 1 for 'def keep_search_method?'
|
64
|
+
@find_straight_descendant = false
|
65
|
+
|
66
|
+
return true if redirect_step? binding_
|
67
|
+
end
|
68
|
+
|
69
|
+
def method_matches?(method)
|
70
|
+
@step_into_funcs.any? do |pattern|
|
71
|
+
if pattern.is_a? Symbol
|
72
|
+
method == pattern
|
73
|
+
elsif pattern.is_a? Regexp
|
74
|
+
method.to_s.match pattern
|
75
|
+
elsif pattern.start_with? '='
|
76
|
+
"=#{method}" == pattern
|
77
|
+
else
|
78
|
+
method.to_s.include? pattern
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'digest'
|
2
|
+
require 'pry' unless defined? Pry
|
3
|
+
|
4
|
+
module PryMoves
|
5
|
+
class TraceCommand
|
6
|
+
|
7
|
+
include PryMoves::TraceHelpers
|
8
|
+
|
9
|
+
def self.trace(command, pry_start_options, &callback)
|
10
|
+
cls = command[:action].to_s.split('_').collect(&:capitalize).join
|
11
|
+
cls = Object.const_get "PryMoves::#{cls}"
|
12
|
+
cls.new command, pry_start_options, &callback
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(command, pry_start_options, &callback)
|
16
|
+
@command = command
|
17
|
+
@pry_start_options = pry_start_options
|
18
|
+
@pry_start_options[:pry_moves_loop] = true
|
19
|
+
@callback = callback
|
20
|
+
@call_depth = 0
|
21
|
+
@c_stack_level = 0
|
22
|
+
|
23
|
+
binding_ = @command[:binding] # =Command.target - more rich, contains required @iseq
|
24
|
+
unless binding_.instance_variable_get('@iseq')
|
25
|
+
binding_ = PryMoves::BindingsStack.new.initial_frame
|
26
|
+
end
|
27
|
+
@method = PryMoves::TracedMethod.new binding_
|
28
|
+
|
29
|
+
if @pry_start_options.delete :exit_from_method
|
30
|
+
@on_exit_from_method = true
|
31
|
+
@call_depth -= 1
|
32
|
+
end
|
33
|
+
@pry_start_options.delete :initial_frame
|
34
|
+
|
35
|
+
init binding_
|
36
|
+
start_tracing
|
37
|
+
end
|
38
|
+
|
39
|
+
def start_tracing
|
40
|
+
#puts "##trace_obj #{trace_obj}"
|
41
|
+
Pry.config.disable_breakpoints = true
|
42
|
+
trace_obj.set_trace_func method(:tracing_func).to_proc
|
43
|
+
end
|
44
|
+
|
45
|
+
def stop_tracing
|
46
|
+
trace_obj.set_trace_func nil
|
47
|
+
Pry.config.disable_breakpoints = false
|
48
|
+
end
|
49
|
+
|
50
|
+
# You can't call set_trace_func or Thread.current.set_trace_func recursively
|
51
|
+
# even in different threads 😪
|
52
|
+
# But! 💡
|
53
|
+
# The hack is - you can call Thread.current.set_trace_func
|
54
|
+
# from inside of set_trace_func! 🤗
|
55
|
+
def trace_obj
|
56
|
+
Thread.current[:pry_moves_debug] ?
|
57
|
+
Thread.current : Kernel
|
58
|
+
end
|
59
|
+
|
60
|
+
def tracing_func(event, file, line, id, binding_, klass)
|
61
|
+
|
62
|
+
# Ignore traces inside pry-moves code
|
63
|
+
return if file && TRACE_IGNORE_FILES.include?(File.expand_path(file))
|
64
|
+
return unless binding_ # ignore strange cases
|
65
|
+
|
66
|
+
# for cases when currently traced method called more times recursively
|
67
|
+
if event == "call" and @method.within?(file, line, id)
|
68
|
+
@call_depth += 1
|
69
|
+
elsif %w(c-call c-return).include?(event)
|
70
|
+
# todo: может быть, c-return тоже правильнее делать после trace
|
71
|
+
delta = event == 'c-call' ? 1 : -1
|
72
|
+
@c_stack_level += delta
|
73
|
+
end
|
74
|
+
|
75
|
+
printf "👟 %8s %s:%-2d %10s %8s dep:#{@call_depth} c_st:#{@c_stack_level}\n", event, file, line, id, klass if PryMoves.trace # TRACE_MOVES=1
|
76
|
+
|
77
|
+
if trace event, file, line, id, binding_
|
78
|
+
@pry_start_options[:exit_from_method] = true if event == 'return'
|
79
|
+
stop_tracing
|
80
|
+
@callback.call binding_
|
81
|
+
elsif event == "return" and @method.within?(file, line, id)
|
82
|
+
@call_depth -= 1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module PryMoves::TraceHelpers
|
2
|
+
|
3
|
+
def redirect_step?(binding_)
|
4
|
+
return false unless binding_.local_variable_defined? :debug_redirect
|
5
|
+
|
6
|
+
debug_redirect = binding_.local_variable_get(:debug_redirect)
|
7
|
+
@step_into_funcs = [debug_redirect] if debug_redirect
|
8
|
+
true
|
9
|
+
end
|
10
|
+
|
11
|
+
def debug_info(file, line, id)
|
12
|
+
puts "📽 call_depth:#{@call_depth} #{@method[:file]}:#{file}"
|
13
|
+
puts "#{id} #{@method[:start]} > #{line} > #{@method[:end]}"
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_frame_digest(upward: 0)
|
17
|
+
# binding_ from tracing_func doesn't have @iseq,
|
18
|
+
# therefore binding should be re-retrieved using 'binding_of_caller' lib
|
19
|
+
frame_digest(binding.of_caller(3 + upward))
|
20
|
+
end
|
21
|
+
|
22
|
+
def frame_digest(binding_)
|
23
|
+
#puts "frame_digest for: #{binding_.eval '__callee__'}"
|
24
|
+
iseq = binding_.instance_variable_get('@iseq')
|
25
|
+
Digest::MD5.hexdigest iseq.disasm
|
26
|
+
end
|
27
|
+
|
28
|
+
def current_frame_type(upward: 0)
|
29
|
+
# binding_ from tracing_func doesn't have @iseq,
|
30
|
+
# therefore binding should be re-retrieved using 'binding_of_caller' lib
|
31
|
+
frame_type(binding.of_caller(3 + upward))
|
32
|
+
end
|
33
|
+
|
34
|
+
def frame_type(binding_)
|
35
|
+
line = binding_.instance_variable_get('@iseq').disasm.split("\n").first
|
36
|
+
m = line.match /\== disasm: #<ISeq:([\w ]+)@/
|
37
|
+
if m
|
38
|
+
str = m[1]
|
39
|
+
if str.start_with? 'block in '
|
40
|
+
:block
|
41
|
+
else
|
42
|
+
:method
|
43
|
+
end
|
44
|
+
else
|
45
|
+
:unknown
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
class PryMoves::TracedMethod < Hash
|
2
|
+
|
3
|
+
@@last = nil
|
4
|
+
def self.last
|
5
|
+
@@last
|
6
|
+
end
|
7
|
+
|
8
|
+
def initialize(binding_)
|
9
|
+
super()
|
10
|
+
|
11
|
+
method = find_method_definition binding_
|
12
|
+
if method
|
13
|
+
source = method.source_location
|
14
|
+
set_method({
|
15
|
+
file: source[0],
|
16
|
+
start: source[1],
|
17
|
+
name: method.name,
|
18
|
+
end: (source[1] + method.source.count("\n") - 1)
|
19
|
+
})
|
20
|
+
else
|
21
|
+
set_method({file: binding_.eval('__FILE__')})
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def within?(file, line, id = nil)
|
26
|
+
return unless self[:file] == file
|
27
|
+
return unless self[:start].nil? or
|
28
|
+
line.between?(self[:start], self[:end])
|
29
|
+
return unless id.nil? or self[:name] == id # fix for bug in traced_method: return for dynamic methods has line number inside of caller
|
30
|
+
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
def binding_inside?(binding)
|
35
|
+
within? *binding.eval('[__FILE__, __LINE__, __method__]')
|
36
|
+
end
|
37
|
+
|
38
|
+
def before_end?(line)
|
39
|
+
self[:end] and line < self[:end]
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def find_method_definition(binding)
|
45
|
+
method_name, obj, file =
|
46
|
+
binding.eval '[__method__, self, __FILE__]'
|
47
|
+
return unless method_name
|
48
|
+
|
49
|
+
method = obj.method(method_name)
|
50
|
+
return method if method.source_location[0] == file
|
51
|
+
|
52
|
+
# If found file was different - search definition at superclasses:
|
53
|
+
obj.class.ancestors.each do |cls|
|
54
|
+
if cls.instance_methods(false).include? method_name
|
55
|
+
method = cls.instance_method method_name
|
56
|
+
return method if method.source_location[0] == file
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
PryMoves.messages << "⚠️ Unable to find definition for method #{method_name} in #{obj}"
|
61
|
+
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
|
65
|
+
def set_method(method)
|
66
|
+
#puts "set_traced_method #{method}"
|
67
|
+
merge! method
|
68
|
+
@@last = self
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/lib/debug_sugar.rb
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
def debug *args
|
2
|
+
pry_moves_stack_root = true
|
3
|
+
PryMoves.debug *args
|
4
|
+
end
|
5
|
+
|
6
|
+
def error(msg, debug_object = nil)
|
7
|
+
pry_moves_stack_root = true
|
8
|
+
err = "😱 #{msg}"
|
9
|
+
unless PryMoves.open?
|
10
|
+
if PryMoves.stop_on_breakpoints
|
11
|
+
lines = [err.red]
|
12
|
+
lines.prepend debug_object.ai if debug_object
|
13
|
+
PryMoves.debug lines.join("\n")
|
14
|
+
else
|
15
|
+
STDERR.puts debug_object.ai if debug_object
|
16
|
+
STDERR.puts err.ljust(80, ' ').red
|
17
|
+
end
|
18
|
+
end
|
19
|
+
raise msg
|
20
|
+
end
|
21
|
+
|
22
|
+
def shit!(err = 'Oh, shit!', debug_object = nil)
|
23
|
+
pry_moves_stack_root = true
|
24
|
+
message = "💩 #{err.is_a?(String) ? err : err.message}"
|
25
|
+
raise err unless PryMoves.stop_on_breakpoints
|
26
|
+
lines = [message.red]
|
27
|
+
lines.prepend debug_object.ai if debug_object
|
28
|
+
PryMoves.debug lines.join("\n")
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
def required(var)
|
33
|
+
pry_moves_stack_root = true
|
34
|
+
error("required parameter is missing") if var.nil?
|
35
|
+
var
|
36
|
+
end
|
37
|
+
|
38
|
+
RSpec.configure do |config|
|
39
|
+
|
40
|
+
config.before(:each) do
|
41
|
+
PryMoves.launched_specs_examples += 1
|
42
|
+
PryMoves.stop_on_breakpoints =
|
43
|
+
PryMoves.launched_specs_examples < 2
|
44
|
+
end
|
45
|
+
|
46
|
+
config.around(:each) do |example|
|
47
|
+
PryMoves.restartable do
|
48
|
+
example.run
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
end if defined? RSpec
|
53
|
+
|
54
|
+
Rake::Task.class_eval do
|
55
|
+
|
56
|
+
alias execute_origin_for_pry_moves execute
|
57
|
+
|
58
|
+
def execute(args=nil)
|
59
|
+
args ||= EMPTY_TASK_ARGS
|
60
|
+
PryMoves.restartable do
|
61
|
+
reload_actions if PryMoves.reload_rake_tasks
|
62
|
+
execute_origin_for_pry_moves args
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def reload_actions
|
67
|
+
rake_task_path = actions[0].source_location[0]
|
68
|
+
actions.clear
|
69
|
+
load rake_task_path
|
70
|
+
end
|
71
|
+
|
72
|
+
end if defined? Rake and defined? Rake::Task
|