pry-moves 0.1.4 → 0.1.5

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23288b1d287b42591da2676415691400c9d6120b
4
- data.tar.gz: ad7ba33a0ec981169e22684620d0f0f9bc01442e
3
+ metadata.gz: c2844477b6ebf360fce390c440ac602b73dc287c
4
+ data.tar.gz: 102a4fee351cb6dcbc078b2d22fe6b6829ba314d
5
5
  SHA512:
6
- metadata.gz: 840e5a61835382777aa7635ab4ef786f5546d2f1efa735d2f744991380e7efea1d08276825850cbbf016083e7c373f92ac3f686e9538f2509cc3c86fb7a39ae0
7
- data.tar.gz: d0278d40a279fbcb7b18efa1f53fed7cf9509166159feac899ef4b457a8022e1eda6863d80f25c761c56f8fbbc03553afde1348293fc2fdf7207ad444b4dbab2
6
+ metadata.gz: aa98bf11ad2de420aa25795cc53cab6be2d8ff13401a2d0dad69c96932ea613002b04f0f64303c1b6e520ddabd897d3d095791a52dba021319745435e4ffa0af
7
+ data.tar.gz: fcabf21cb1742f44406cbd9d5391959d4a24eca3ff99e745b9298ba76dd7d98282992aaa9674f0963845c01b6059663e981797655b4cee203232d0658b0dd151
data/.gitignore CHANGED
@@ -3,7 +3,6 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
- Gemfile.lock
7
6
  InstalledFiles
8
7
  _yardoc
9
8
  coverage
data/Gemfile CHANGED
@@ -2,3 +2,8 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in pry-moves.gemspec
4
4
  gemspec
5
+
6
+ group :development, :test do
7
+ gem 'pry', path: '../pry'
8
+ gem 'rspec'
9
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,52 @@
1
+ PATH
2
+ remote: ../pry
3
+ specs:
4
+ pry (0.10.4)
5
+ coderay (~> 1.1.0)
6
+ method_source (~> 0.8.1)
7
+
8
+ PATH
9
+ remote: .
10
+ specs:
11
+ pry-moves (0.1.5)
12
+ binding_of_caller (~> 0.7)
13
+ pry (>= 0.9.10, < 0.11.0)
14
+
15
+ GEM
16
+ remote: https://rubygems.org/
17
+ specs:
18
+ binding_of_caller (0.7.2)
19
+ debug_inspector (>= 0.0.1)
20
+ coderay (1.1.1)
21
+ debug_inspector (0.0.3)
22
+ diff-lcs (1.3)
23
+ method_source (0.8.2)
24
+ pry-remote (0.1.8)
25
+ pry (~> 0.9)
26
+ slop (~> 3.0)
27
+ rspec (3.6.0)
28
+ rspec-core (~> 3.6.0)
29
+ rspec-expectations (~> 3.6.0)
30
+ rspec-mocks (~> 3.6.0)
31
+ rspec-core (3.6.0)
32
+ rspec-support (~> 3.6.0)
33
+ rspec-expectations (3.6.0)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.6.0)
36
+ rspec-mocks (3.6.0)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.6.0)
39
+ rspec-support (3.6.0)
40
+ slop (3.6.0)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ pry!
47
+ pry-moves!
48
+ pry-remote (~> 0.1.6)
49
+ rspec
50
+
51
+ BUNDLED WITH
52
+ 1.14.5
data/README.md CHANGED
@@ -1,5 +1,3 @@
1
- ### Using [**pry-byebug**][pry-byebug] and not happy with commands behavior? We recommend this project instead
2
-
3
1
  # pry-moves
4
2
 
5
3
  _An execution control add-on for [Pry][pry]._
@@ -17,11 +15,12 @@ _An execution control add-on for [Pry][pry]._
17
15
  * `bt` - show latest 5 lines from backtrace
18
16
  * `bt 10` - latest 10 lines
19
17
  * `bt all` - full backtrace
20
- * `bt >foo` - write backtrace to file `log/backtrace_foo.log`
18
+ * `bt > foo` - write backtrace to file `log/backtrace_foo.log`
21
19
  * `up`/`down`/`top`/`bottom` - move over call stack
22
20
  * `up +` - move up, including vapid frames (block callers, hidden frames)
23
21
  * `up pattern` - move up till first frame which method name or file position in format `folder/script.rb:12` matches regexp pattern
24
- * `debug some_method(param, param2)` - call `some_method(param, param2)` and interactively step into it
22
+ * `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
23
+ * `watch variable` - display variable's value on each step
25
24
  * `!` - exit
26
25
 
27
26
 
@@ -96,7 +95,16 @@ gem 'pry-moves'
96
95
  ## Performance
97
96
 
98
97
  Please note that debugging functionality is implemented through
99
- [`set_trace_func`][set_trace_func], which imposes certain performance penalty.
98
+ [`set_trace_func`][set_trace_func], which imposes heavy performance penalty while tracing
99
+ (while running code within `next`/`step`/`finish` commands).
100
+
101
+ # Development
102
+
103
+ ## Testing
104
+
105
+ ```
106
+ bundle exec rspec
107
+ ```
100
108
 
101
109
  ## Contributors
102
110
 
@@ -7,7 +7,7 @@ class PryMoves::Backtrace
7
7
  def lines_count=(f); @lines_count = f; end
8
8
 
9
9
  def filter
10
- @filter || /(\/gems\/|\/rubygems\/|\/bin\/|\/lib\/ruby\/|\/pry-moves\/)/
10
+ @filter || /(\/gems\/|\/rubygems\/|\/bin\/|\/lib\/ruby\/)/
11
11
  end
12
12
  def filter=(f); @filter = f; end
13
13
 
@@ -26,43 +26,44 @@ class PryMoves::Backtrace
26
26
  @binding, @pry = binding, pry
27
27
  end
28
28
 
29
- def build(lines_count = nil)
29
+ def run_command(param, param2)
30
+ if param.is_a?(String) and (match = param.match /^>(.*)/)
31
+ suffix = match[1].size > 0 ? match[1] : param2
32
+ write_to_file build, suffix
33
+ else
34
+ @colorize = true
35
+ if param.is_a? String and param.match /\d+/
36
+ param = param.to_i
37
+ end
38
+ @lines_count = param || PryMoves::Backtrace::lines_count
39
+ @pry.output.puts build
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ def build
30
46
  result = []
31
- show_vapid = lines_count == 'all'
47
+ show_vapid = @lines_count == 'all'
32
48
  stack = stack_bindings(show_vapid)
33
49
  .reverse.reject do |binding|
34
50
  binding.eval('__FILE__').match self.class::filter
35
51
  end
36
52
 
37
- if lines_count.is_a? String and lines_count.match /\d+/
38
- lines_count = lines_count.to_i
39
- end
40
- if lines_count.is_a?(Numeric) and stack.count > lines_count
41
- result << "Latest #{lines_count} lines: (`bt all` for full tracing)"
42
- stack = stack.last(lines_count)
53
+ if @lines_count.is_a?(Numeric) and stack.count > @lines_count
54
+ result << "Latest #{@lines_count} lines: (`bt all` for full tracing)"
55
+ stack = stack.last(@lines_count)
43
56
  end
44
57
 
45
58
  build_result stack, result
46
59
  end
47
60
 
48
- def run_command(param)
49
- if param.is_a?(String) and (match = param.match /^>(.*)/)
50
- write_to_file build, match[1]
51
- else
52
- puts build(param || PryMoves::Backtrace::lines_count)
53
- end
54
- end
55
-
56
- private
57
-
58
61
  def build_result(stack, result)
59
62
  current_object = nil
60
63
  stack.each do |binding|
61
64
  obj = binding.eval 'self'
62
65
  if current_object != obj
63
- colored_obj = ""
64
- Pry::ColorPrinter.pp obj, colored_obj
65
- result << "#{colored_obj.chomp}:"
66
+ result << "#{format_obj(obj)}:"
66
67
  current_object = obj
67
68
  end
68
69
 
@@ -71,17 +72,26 @@ class PryMoves::Backtrace
71
72
  result
72
73
  end
73
74
 
75
+ def format_obj(obj)
76
+ if @colorize
77
+ PryMoves::Painter.colorize obj
78
+ else
79
+ obj.inspect
80
+ end
81
+ end
82
+
74
83
  def build_line(binding)
75
84
  file = "#{binding.eval('__FILE__')}"
76
85
  file.gsub!( /^#{Rails.root.to_s}/, '') if defined? Rails
77
86
 
78
- signature = PryMoves::Helpers.method_signature_with_owner binding
87
+ signature = PryMoves::Helpers.method_signature binding
88
+ signature = signature.presence || ":#{binding.frame_type}"
79
89
 
80
90
  indent = frame_manager.current_frame == binding ?
81
91
  ' => ': ' '
82
92
 
83
- "#{indent}#{file}:#{binding.eval('__LINE__')} "+
84
- " #{signature} :#{binding.frame_type}"
93
+ line = binding.eval('__LINE__')
94
+ "#{indent}#{file}:#{line} #{signature}"
85
95
  end
86
96
 
87
97
  def frame_manager
@@ -94,9 +104,7 @@ class PryMoves::Backtrace
94
104
 
95
105
  def write_to_file(lines, file_suffix)
96
106
  log_path = log_path file_suffix
97
- File.open(log_path, "w") do |f|
98
- f.puts lines
99
- end
107
+ File.write log_path, lines
100
108
  puts "Backtrace logged to #{log_path}"
101
109
  end
102
110
 
@@ -31,8 +31,8 @@ module PryMoves
31
31
  PryMoves::Watch.instance.process_cmd param, target
32
32
  end
33
33
 
34
- block_command 'bt', 'Backtrace' do |param|
35
- PryMoves::Backtrace.new(target, _pry_).run_command param
34
+ block_command 'bt', 'Backtrace' do |param, param2|
35
+ PryMoves::Backtrace.new(target, _pry_).run_command param, param2
36
36
  end
37
37
 
38
38
  block_command 'debug', '' do
@@ -53,9 +53,10 @@ module PryMoves
53
53
  def breakout_navigation(action, param)
54
54
  _pry_.binding_stack.clear # Clear the binding stack.
55
55
  throw :breakout_nav, { # Break out of the REPL loop and
56
- :action => action, # signal the tracer.
57
- :param => param,
58
- :binding => target
56
+ action: action, # signal the tracer.
57
+ param: param,
58
+ binding: target,
59
+ pry: _pry_
59
60
  }
60
61
  end
61
62
 
@@ -25,4 +25,26 @@ module PryMoves::Helpers
25
25
  end
26
26
  end
27
27
 
28
+ def method_signature(binding)
29
+ meth = binding.eval('__method__')
30
+ meth_obj = meth ? Pry::Method.from_binding(binding) : nil
31
+ if !meth_obj
32
+ ""
33
+ elsif meth_obj.undefined?
34
+ "#{meth_obj.name}(UNKNOWN) (undefined method)"
35
+ else
36
+ args = meth_obj.parameters.inject([]) do |arr, (type, name)|
37
+ name ||= (type == :block ? 'block' : "arg#{arr.size + 1}")
38
+ arr << case type
39
+ when :req then name.to_s
40
+ when :opt then "#{name}=?"
41
+ when :rest then "*#{name}"
42
+ when :block then "&#{name}"
43
+ else '?'
44
+ end
45
+ end
46
+ "#{meth_obj.name}(#{args.join(', ')})"
47
+ end
48
+ end
49
+
28
50
  end
@@ -0,0 +1,23 @@
1
+ module PryMoves::Painter
2
+
3
+ class Canvas < String
4
+
5
+ def <<(text)
6
+ if length > 2000
7
+ super("... (cut)")
8
+ throw :cut
9
+ end
10
+ super
11
+ end
12
+
13
+ end
14
+
15
+ def self.colorize(obj)
16
+ colored_str = Canvas.new
17
+ catch (:cut) do
18
+ Pry::ColorPrinter.pp obj, colored_str
19
+ end
20
+ colored_str.chomp
21
+ end
22
+
23
+ end
@@ -69,12 +69,19 @@ Pry::Command::Whereami.class_eval do
69
69
 
70
70
  def location
71
71
  me = target.eval 'self' rescue nil
72
- if me
73
- colored_str = ''
74
- Pry::ColorPrinter.pp me, colored_str
75
- me = colored_str.chomp
76
- end
72
+ me = PryMoves::Painter.colorize me if me
77
73
  file = defined?(Rails) ? @file.gsub(Rails.root.to_s, '') : @file
78
74
  "#{file}:#{@line} #{me}"
79
75
  end
80
76
  end
77
+
78
+ Pry.config.marker = "=>"
79
+ Pry::Code::LOC.class_eval do
80
+
81
+ def add_marker(marker_lineno)
82
+ marker = lineno == marker_lineno ?
83
+ Pry.config.marker : " "
84
+ tuple[0] = " #{marker} #{ line }"
85
+ end
86
+
87
+ end
@@ -10,6 +10,8 @@ class PryWrapper
10
10
  def run(&block)
11
11
  PryMoves.lock
12
12
 
13
+ Pry.config.marker = "⛔️ " if @pry_start_options[:exit_from_method]
14
+
13
15
  return_value = nil
14
16
  PryMoves.is_open = true
15
17
  @command = catch(:breakout_nav) do # Coordinates with PryMoves::Commands
@@ -17,6 +19,7 @@ class PryWrapper
17
19
  nil # Nothing thrown == no navigational command
18
20
  end
19
21
  PryMoves.is_open = false
22
+ Pry.config.marker = "=>"
20
23
 
21
24
  if @command
22
25
  trace_command
@@ -44,7 +47,16 @@ class PryWrapper
44
47
  #puts "##wrap debug"
45
48
  #puts "CALLER:\n#{caller.join "\n"}\n"
46
49
  # Thread.abort_on_exception=true
50
+ parent_thread = Thread.current
47
51
  Thread.new do
52
+
53
+ # copy non-pry thread's properties
54
+ parent_thread.keys.select do |k|
55
+ !k.to_s.include?('pry')
56
+ end.each do |k|
57
+ Thread.current[k] = parent_thread[k]
58
+ end
59
+
48
60
  Thread.current[:pry_moves_debug] = true
49
61
  #@command[:binding].eval 'puts "###########"'
50
62
  start_tracing
@@ -10,15 +10,19 @@ class Tracer
10
10
 
11
11
  def trace
12
12
  @action = @command[:action]
13
+ #puts "COMMAND: #{@action}"
13
14
  binding_ = @command[:binding]
14
15
  set_traced_method binding_
15
16
 
17
+ @recursion_level -= 1 if @pry_start_options.delete :exit_from_method
18
+
16
19
  case @action
17
20
  when :step
18
21
  @step_info_funcs = nil
19
22
  if (func = @command[:param])
20
23
  @step_info_funcs = [func]
21
24
  @step_info_funcs << 'initialize' if func == 'new'
25
+ @caller_digest = frame_digest(binding_)
22
26
  end
23
27
  when :finish
24
28
  @method_to_finish = @method
@@ -62,6 +66,7 @@ class Tracer
62
66
  set_method({
63
67
  file: source[0],
64
68
  start: source[1],
69
+ name: method.name,
65
70
  end: (source[1] + method.source.count("\n") - 1)
66
71
  })
67
72
  else
@@ -91,8 +96,11 @@ class Tracer
91
96
  Pry.start(binding_, @pry_start_options)
92
97
 
93
98
  # for cases when currently traced method called more times recursively
94
- elsif %w(call return).include?(event) and within_current_method?(file, line)
95
- @recursion_level += event == 'call' ? 1 : -1
99
+ elsif %w(call return).include?(event) and within_current_method?(file, line) and
100
+ @method[:name] == id # fix for bug in traced_method: return for dynamic methods has line number inside of caller
101
+ delta = event == 'call' ? 1 : -1
102
+ #puts "recursion #{event}: #{delta}; changed: #{@recursion_level} => #{@recursion_level + delta}"
103
+ @recursion_level += delta
96
104
  end
97
105
  end
98
106
  end
@@ -100,8 +108,13 @@ class Tracer
100
108
  def trace_step(event, file, line, binding_)
101
109
  return unless event == 'line'
102
110
  if @step_info_funcs
111
+ if @recursion_level < 0
112
+ pry_puts "⚠️ Unable to find function with name #{@step_info_funcs.join(',')}"
113
+ return true
114
+ end
103
115
  method = binding_.eval('__callee__').to_s
104
- @step_info_funcs.any? {|pattern| method.include? pattern}
116
+ @step_info_funcs.any? {|pattern| method.include? pattern} and
117
+ @caller_digest == frame_digest(binding_.of_caller(3 + 1))
105
118
  else
106
119
  true
107
120
  end
@@ -115,9 +128,16 @@ class Tracer
115
128
  throw :skip if event == 'call'
116
129
  end
117
130
 
118
- event == 'line' and
119
- @recursion_level == 0 and
131
+ if @recursion_level == 0 and
120
132
  within_current_method?(file, line)
133
+
134
+ return true if event == 'line'
135
+
136
+ if event == 'return' and before_end?(line)
137
+ @pry_start_options[:exit_from_method] = true
138
+ true
139
+ end
140
+ end
121
141
  end
122
142
 
123
143
  def trace_finish(event, file, line, binding_)
@@ -126,8 +146,9 @@ class Tracer
126
146
 
127
147
  # for finishing blocks inside current method
128
148
  if @block_to_finish
129
- within_current_method?(file, line) and
130
- @block_to_finish != frame_digest(binding_.of_caller(3))
149
+ @recursion_level == 0 and
150
+ within_current_method?(file, line) and
151
+ @block_to_finish != frame_digest(binding_.of_caller(3))
131
152
  end
132
153
  end
133
154
 
@@ -153,5 +174,13 @@ class Tracer
153
174
  )
154
175
  end
155
176
 
177
+ def before_end?(line)
178
+ @method[:end] and line < @method[:end]
179
+ end
180
+
181
+ def pry_puts(text)
182
+ @command[:pry].output.puts text
183
+ end
184
+
156
185
  end
157
186
  end
@@ -1,3 +1,3 @@
1
1
  module PryMoves
2
- VERSION = '0.1.4'
2
+ VERSION = '0.1.5'
3
3
  end
data/lib/pry-moves.rb CHANGED
@@ -6,6 +6,7 @@ require 'pry-moves/tracer'
6
6
  require 'pry-moves/backtrace'
7
7
  require 'pry-moves/watch'
8
8
  require 'pry-moves/helpers'
9
+ require 'pry-moves/painter'
9
10
 
10
11
  require 'pry-stack_explorer/pry-stack_explorer'
11
12
 
@@ -43,7 +44,13 @@ module PryMoves
43
44
  end
44
45
 
45
46
  def synchronize_threads
46
- semaphore.synchronize {} unless Thread.current[:pry_moves_debug]
47
+ return if Thread.current[:pry_moves_debug]
48
+
49
+ semaphore.synchronize {}
50
+ rescue ThreadError => e
51
+ puts e.backtrace.reverse
52
+ puts e
53
+ raise e
47
54
  end
48
55
 
49
56
  # Reference to currently running pry-remote server. Used by the tracer.
@@ -4,13 +4,10 @@ module PryStackExplorer
4
4
 
5
5
  def caller_bindings(target)
6
6
  bindings = binding.callers
7
-
7
+ pre_callers = Thread.current[:pre_callers]
8
+ bindings = bindings + pre_callers if pre_callers
8
9
  bindings = remove_internal_frames(bindings)
9
- bindings = remove_debugger_frames(bindings)
10
- bindings = bindings.drop(1) if pry_method_frame?(bindings.first)
11
-
12
10
  mark_vapid_frames(bindings)
13
-
14
11
  bindings
15
12
  end
16
13
 
@@ -63,44 +60,24 @@ module PryStackExplorer
63
60
 
64
61
  # remove internal frames related to setting up the session
65
62
  def remove_internal_frames(bindings)
66
- start_frames = internal_frames_with_indices(bindings)
67
- start_frame_index = start_frames.first.last
68
-
69
- if start_frames.size >= 2
70
- # god knows what's going on in here
71
- idx1, idx2 = start_frames.take(2).map(&:last)
72
- start_frame_index = idx2 if !nested_session?(bindings[idx1..idx2])
73
- end
74
-
75
- bindings.drop(start_frame_index + 1)
76
- end
77
-
78
- # remove pry-nav / pry-debugger / pry-byebug frames
79
- def remove_debugger_frames(bindings)
80
- bindings.drop_while { |b| b.eval("__FILE__") =~ /\/pry-/ }
81
- end
82
-
83
- # binding.pry frame
84
- # @return [Boolean]
85
- def pry_method_frame?(binding)
86
- safe_send(binding.eval("__method__"), :==, :pry)
63
+ i = top_internal_frame_index(bindings)
64
+ # DEBUG:
65
+ #bindings.each_with_index do |b, index|
66
+ # puts "#{index}: #{b.eval("self.class")} #{b.eval("__method__")}"
67
+ #end
68
+ #puts "FOUND top internal frame: #{bindings.size} => #{i}"
69
+
70
+ bindings.drop i+1
87
71
  end
88
72
 
89
- # When a pry session is started within a pry session
90
- # @return [Boolean]
91
- def nested_session?(bindings)
92
- bindings.detect do |b|
93
- safe_send(b.eval("__method__"), :==, :re) &&
94
- safe_send(b.eval("self.class"), :equal?, Pry)
95
- end
96
- end
97
-
98
- # @return [Array<Array<Binding, Fixnum>>]
99
- def internal_frames_with_indices(bindings)
100
- bindings.each_with_index.select do |b, i|
101
- b.frame_type == :method &&
102
- safe_send(b.eval("self"), :equal?, Pry) &&
103
- safe_send(b.eval("__method__"), :==, :start)
73
+ def top_internal_frame_index(bindings)
74
+ bindings.rindex do |b|
75
+ if b.frame_type == :method
76
+ self_, method = b.eval("self"), b.eval("__method__")
77
+ self_.equal?(Pry) && method == :start ||
78
+ self_.class == Binding && method == :pry ||
79
+ self_.class == PryMoves::Tracer && method == :tracing_func
80
+ end
104
81
  end
105
82
  end
106
83
 
@@ -0,0 +1,29 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ pry-moves (0.1.5)
5
+ binding_of_caller (>= 0.7)
6
+ pry (>= 0.9.10, < 0.11.0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ binding_of_caller (0.7.2)
12
+ debug_inspector (>= 0.0.1)
13
+ coderay (1.1.1)
14
+ debug_inspector (0.0.3)
15
+ method_source (0.8.2)
16
+ pry (0.10.4)
17
+ coderay (~> 1.1.0)
18
+ method_source (~> 0.8.1)
19
+ slop (~> 3.4)
20
+ slop (3.6.0)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ pry-moves!
27
+
28
+ BUNDLED WITH
29
+ 1.14.5
@@ -0,0 +1,90 @@
1
+ # Rails has such type of definitions
2
+ Fixnum.send(:alias_method, :to_default_s, :to_s)
3
+ Fixnum.send(:define_method, :to_s) do |*args|
4
+ to_default_s(*args)
5
+ end
6
+
7
+ class Playground
8
+
9
+ def basic_next
10
+ dummy = :basic_1
11
+ binding.pry # basic next stop
12
+ :basic_2 # next step
13
+ end
14
+
15
+ def step_into
16
+ binding.pry # step_into stop
17
+ something_inside # point to step inside
18
+ end
19
+
20
+ def continue
21
+ binding.pry # first stop
22
+ dummy = :something
23
+ binding.pry # second stop
24
+ end
25
+
26
+ def recursion(depth = 0)
27
+ str = 3.to_s # here fired event "return" if "to_s" patched
28
+ binding.pry if depth == 0
29
+ recursion depth + 1 if depth < 2 # next step
30
+ :ok # should stop here after 2 next-s
31
+ end
32
+
33
+ def step_by_name
34
+ binding.pry # stop in step_by_name
35
+ level_a.level_c(:target)
36
+ end
37
+
38
+ def step_by_name_wrap
39
+ step_by_name
40
+ :after_step_by_name # after_step_by_name
41
+ end
42
+
43
+ def level_a
44
+ level_b # inside of level_a
45
+ end
46
+
47
+ def level_b
48
+ hide_from_stack = true
49
+ level_c
50
+ end
51
+
52
+ def level_c(param = nil)
53
+ binding.pry # stop in level_c
54
+ self
55
+ end
56
+
57
+ def with_simple_block
58
+ binding.pry # stop in with_simple_block
59
+ iterator do |i|
60
+ dummy = 1 # inside block
61
+ dummy = 2
62
+ end
63
+ :after_block # after block
64
+ end
65
+
66
+ def zaloop(pass = :root)
67
+ binding.pry if pass == :root # stop in zaloop
68
+ iterator do |i|
69
+ dummy = 1 # inside block
70
+ zaloop i if pass == :root
71
+ end
72
+ :after_block # after block
73
+ end
74
+
75
+ private
76
+
77
+ def iterator
78
+ 2.times do |i|
79
+ dummy = :pre_yield # pre_yield
80
+ yield i
81
+ :post_yield # post_yield
82
+ end
83
+ end
84
+
85
+ def something_inside
86
+ :something # some internal line
87
+ end
88
+
89
+ end
90
+
data/playground/sand.rb CHANGED
@@ -1,11 +1,6 @@
1
1
  require 'pry-moves'
2
- #require 'pry-nav'
3
2
  require './tracer.rb'
4
3
 
5
- def debucher?
6
- binding.pry
7
- true
8
- end
9
4
 
10
5
  def fi(param)
11
6
  a = 2 + 1
@@ -30,19 +25,20 @@ class A
30
25
  end
31
26
 
32
27
  def cc
28
+ koko = :love
33
29
  binding.pry
30
+ return if true
31
+ puts :two
34
32
  end
33
+ alias cc_al cc
35
34
 
36
35
  end
37
36
 
38
- #trace_events
39
-
40
- a = 1123
41
- b = binding
42
-
43
37
  puts :prepare
44
38
 
45
- #binding.pry
39
+ A.new.cc_al
40
+ A.new.cc_al
41
+ A.new.cc_al
46
42
 
47
43
  a = A.new.aa.bb.cc
48
44
 
@@ -2,25 +2,28 @@ require 'pry-moves'
2
2
 
3
3
  Thread.current[:name] = 'main'
4
4
 
5
- a = Thread.new do
6
- Thread.current[:name] = 'a'
7
- sleep 0.2
8
- puts 'a'
5
+
6
+ def inside_thread
9
7
  binding.pry
10
- puts 'aaaa'
11
- sleep 1
12
8
  puts 'aaa'
13
9
  end
14
10
 
15
- b = Thread.new do
16
- Thread.current[:name] = 'b'
17
- 20223000.times do
18
- 432 * 3232
11
+ def aaa
12
+ pre_callers = binding.callers +
13
+ (Thread.current[:pre_callers] || [])
14
+ a = Thread.new do
15
+ # что можно сделать - записать в текущий тред ссылку на биндинг
16
+ # предыдущего треда. А если цепочка тредов - то как? Можно прямо там собирать каллеров предыдущего треда... ??
17
+ Thread.current[:pre_callers] = pre_callers
18
+
19
+ Thread.current[:name] = 'a'
20
+ inside_thread
19
21
  end
20
- puts '2'
21
- binding.pry
22
- puts '22'
22
+ a.join
23
+ end
24
+
25
+ def bbb
26
+ aaa
23
27
  end
24
28
 
25
- a.join
26
- b.join
29
+ bbb
data/pry-moves.gemspec CHANGED
@@ -20,6 +20,6 @@ Gem::Specification.new do |gem|
20
20
  # Dependencies
21
21
  gem.required_ruby_version = '>= 1.8.7'
22
22
  gem.add_runtime_dependency 'pry', '>= 0.9.10', '< 0.11.0'
23
- gem.add_runtime_dependency 'binding_of_caller', '>= 0.7'
23
+ gem.add_runtime_dependency 'binding_of_caller', '~> 0.7'
24
24
  gem.add_development_dependency 'pry-remote', '~> 0.1.6'
25
25
  end
@@ -0,0 +1,34 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe 'PryMoves Commands' do
4
+
5
+ it 'should backtrace' do
6
+ breakpoints [
7
+ [nil, 'stop in level_c'],
8
+ ['bt', lambda{|b, out|
9
+ lines = out.split("\n").reverse
10
+ expect(lines[0]).to end_with 'Playground#level_c(param=?) :method'
11
+ expect(lines[1]).to end_with 'Playground#level_a() :method'
12
+ expect(lines[2]).to include 'Playground:'
13
+ expect(lines[3]).to end_with ':block'
14
+ expect(lines[4]).to include 'RSpec::ExampleGroups'
15
+ expect(lines.count).to be 5
16
+ }],
17
+ ['bt all', lambda{|b, out|
18
+ lines = out.split("\n").reverse
19
+ # show hidden frame
20
+ expect(lines[1]).to end_with 'Playground#level_b() :method'
21
+ expect(lines.count).to be 6
22
+ }],
23
+ ['bt 2', lambda{|b, out|
24
+ lines = out.split("\n").reverse
25
+ expect(lines[0]).to end_with 'Playground#level_c(param=?) :method'
26
+ expect(lines[1]).to end_with 'Playground#level_a() :method'
27
+ expect(lines[3]).to start_with 'Latest 2 lines'
28
+ expect(lines.count).to be 4
29
+ }],
30
+ ]
31
+ Playground.new.level_a
32
+ end
33
+
34
+ end
@@ -0,0 +1,52 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe 'PryMoves Commands' do
4
+
5
+
6
+ it 'should go next over blocks' do
7
+ breakpoints [
8
+ [nil, 'stop in zaloop'],
9
+ ['n', ''],
10
+ # repeat commands
11
+ ['', 'inside block'],
12
+ ['', nil],
13
+
14
+ ['s', 'stop in zaloop'],
15
+ ['n', nil],
16
+ ['', 'inside block'],
17
+ ['pass', {out: '=> 0'}],
18
+
19
+ ['f', 'after block'],
20
+ ['pass', {out: '=> 0'}],
21
+
22
+ ['f', 'post_yield'], # Тут хорошо бы, чтобы сразу шёл на "after block",
23
+ # но пока и не понятно, как это угадать
24
+ ['f', 'after block'],
25
+ ['pass', {out: '=> :root'}],
26
+ ]
27
+ Playground.new.zaloop
28
+ end
29
+
30
+ it 'should finish simple block' do
31
+ breakpoints [
32
+ [nil, 'stop in with_simple_block'],
33
+ ['n', ''],
34
+ ['', 'inside block'],
35
+ ['f', 'after block']
36
+ ]
37
+ Playground.new.with_simple_block
38
+ end
39
+
40
+ it 'should finish block with sub-calls' do
41
+ breakpoints [
42
+ [nil, 'stop in zaloop'],
43
+ ['n', ''],
44
+ ['', 'inside block'],
45
+ ['f', 'after block'],
46
+ ['pass', {out: '=> :root'}],
47
+ ]
48
+ Playground.new.zaloop
49
+ end
50
+
51
+
52
+ end
@@ -0,0 +1,73 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe 'PryMoves Commands' do
4
+
5
+ it 'should make one step next' do
6
+ breakpoints [
7
+ [nil, 'basic next stop'],
8
+ ['n', 'next step'],
9
+ ]
10
+ Playground.new.basic_next
11
+ end
12
+
13
+ it 'should stop on second breakpoint' do
14
+ breakpoints [
15
+ [nil, 'first stop'],
16
+ ['c', 'second stop'],
17
+ ]
18
+ Playground.new.continue
19
+ end
20
+
21
+ it 'should step into func and walk over stack' do
22
+ breakpoints [
23
+ [nil, 'step_into stop'],
24
+ ['s', 'point to step inside'],
25
+ ['s', 'some internal line'],
26
+ ['up', 'point to step inside'],
27
+ ['up', nil ],
28
+ ['up', {out_includes: 'top of stack'} ],
29
+ ['down', nil ],
30
+ ['down', 'some internal line'],
31
+ ['down', {out_includes: 'bottom of stack'} ],
32
+ ]
33
+ Playground.new.step_into
34
+ end
35
+
36
+ it 'should step into func by name' do
37
+ breakpoints [
38
+ [nil, 'stop in step_by_name'],
39
+ ['s level_c', 'stop in level_c'],
40
+ ['param', {out: '=> :target'}],
41
+ ['n', nil],
42
+ ]
43
+ Playground.new.step_by_name
44
+ end
45
+
46
+ it 'should stop after inability to step into func by name' do
47
+ breakpoints [
48
+ [nil, 'stop in step_by_name'],
49
+ ['s absent_function', 'after_step_by_name'],
50
+ ]
51
+ Playground.new.step_by_name_wrap
52
+ end
53
+
54
+ it 'should go next over recursion calls' do
55
+ breakpoints [
56
+ [nil, nil],
57
+ ['n', 'next step'],
58
+ ['n', 'should stop here after 2 next-s'],
59
+ ['depth', {out: '=> 0'}],
60
+ ]
61
+ Playground.new.recursion
62
+ end
63
+
64
+ it 'should debug' do
65
+ breakpoints [
66
+ [nil, 'basic next stop'],
67
+ ['debug level_a', 'inside of level_a'],
68
+ ['n', 'basic next stop'],
69
+ ]
70
+ Playground.new.basic_next
71
+ end
72
+
73
+ end
@@ -0,0 +1,89 @@
1
+ module PryDebugger
2
+
3
+ module Breakpoints
4
+ def breakpoints(breakpoints)
5
+ breakpoints.each_with_index do |b, index|
6
+ next_b = breakpoints[index+1]
7
+ b[0] = next_b ? next_b[0] : nil
8
+ end
9
+
10
+ PryDebugger.breakpoints =
11
+ breakpoints.map do |b|
12
+ Proc.new do |label, binding_, output|
13
+ compare(b[1], label, binding_, output)
14
+ b[0]
15
+ end
16
+ end
17
+ end
18
+
19
+ def compare(subj, label, binding_, output)
20
+ if subj.is_a? Proc
21
+ subj.call binding_, output
22
+ elsif subj.is_a? Hash
23
+ if subj[:out_includes]
24
+ expect(output).to include subj[:out_includes]
25
+ else
26
+ expect(output).to eq subj[:out]
27
+ end
28
+ elsif not subj.nil?
29
+ expect(label).to eq subj
30
+ end
31
+ end
32
+ end
33
+
34
+ class InputPipe
35
+
36
+ def readline(*args)
37
+ repl_binding = binding.callers[1]
38
+ pry_ = repl_binding.eval('@pry')
39
+ binding_ = pry_.current_binding
40
+
41
+ next_cmd = PryDebugger.enter_breakpoint binding_
42
+ next_cmd || 'c'
43
+ end
44
+
45
+ end
46
+
47
+ class OutputPipe < StringIO
48
+
49
+ def print(*args)
50
+ #STDOUT.print *args
51
+ super
52
+ end
53
+
54
+ def take_away
55
+ result = string.clone
56
+ truncate(0)
57
+ rewind
58
+ result.gsub(/\e\[([;\d]+)?m/, '')
59
+ end
60
+
61
+ end
62
+
63
+ extend self
64
+
65
+ def inject
66
+ Pry.config.input = InputPipe.new
67
+ @output = OutputPipe.new
68
+ Pry.config.output = @output
69
+ end
70
+
71
+ def breakpoints
72
+ @breakpoints_procs
73
+ end
74
+
75
+ def breakpoints=(breakpoints)
76
+ @breakpoints_procs = breakpoints
77
+ @breakpoint_call = 0
78
+ end
79
+
80
+ def enter_breakpoint(binding_)
81
+ raise 'Next breakpoint handler missing' if @breakpoints_procs.size == 0
82
+ #puts (@breakpoint_call += 1)
83
+ output = @output.take_away
84
+ output.match(/^ => .*#(.*)/)
85
+ label = ($1 || '').strip
86
+ @breakpoints_procs.shift.call label, binding_, output.strip
87
+ end
88
+
89
+ end
@@ -0,0 +1,54 @@
1
+ require 'pry'
2
+ require_relative 'pry_debugger'
3
+ require_relative '../playground/playground.rb'
4
+
5
+ PryDebugger.inject unless ENV['DEBUG']
6
+
7
+ RSpec.configure do |config|
8
+
9
+ config.include PryDebugger::Breakpoints
10
+
11
+ config.before(:example) do
12
+ PryMoves.unlock if PryMoves.semaphore.locked?
13
+ end
14
+
15
+ config.after(:example) do |example|
16
+ unless example.exception
17
+ expect(PryDebugger.breakpoints.count).to be(0), "not all breakpoints launched"
18
+ end
19
+ end
20
+
21
+ end
22
+
23
+ RSpec::Core::BacktraceFormatter.class_eval do
24
+
25
+ alias :native_backtrace_line :backtrace_line
26
+
27
+ def format_backtrace(backtrace, options={})
28
+ return [] unless backtrace
29
+ return backtrace if options[:full_backtrace] || backtrace.empty?
30
+
31
+ @lines = 0
32
+ backtrace.map { |l| backtrace_line(l) }.compact
33
+ end
34
+
35
+ FILTER = /(\/gems\/|\/lib\/pry\/|spec\/pry_debugger\.rb)/
36
+ def backtrace_line(line)
37
+ return if @lines == 3 and not ENV['TRACE']
38
+ #return if line.match FILTER
39
+ return unless line.include? '/playground.rb'
40
+
41
+ result = native_backtrace_line(line)
42
+ if result
43
+ @lines += 1 if @lines
44
+ result[0 .. 1] == './' ? result[2 .. -1] : result
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ Pry::REPL.class_eval do
51
+ def handle_read_errors
52
+ yield
53
+ end
54
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pry-moves
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - garmoshka-mo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-14 00:00:00.000000000 Z
11
+ date: 2018-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -34,14 +34,14 @@ dependencies:
34
34
  name: binding_of_caller
35
35
  requirement: !ruby/object:Gem::Requirement
36
36
  requirements:
37
- - - ">="
37
+ - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0.7'
40
40
  type: :runtime
41
41
  prerelease: false
42
42
  version_requirements: !ruby/object:Gem::Requirement
43
43
  requirements:
44
- - - ">="
44
+ - - "~>"
45
45
  - !ruby/object:Gem::Version
46
46
  version: '0.7'
47
47
  - !ruby/object:Gem::Dependency
@@ -67,6 +67,7 @@ extra_rdoc_files: []
67
67
  files:
68
68
  - ".gitignore"
69
69
  - Gemfile
70
+ - Gemfile.lock
70
71
  - LICENSE
71
72
  - README.md
72
73
  - Rakefile
@@ -76,6 +77,7 @@ files:
76
77
  - lib/pry-moves/cli.rb
77
78
  - lib/pry-moves/commands.rb
78
79
  - lib/pry-moves/helpers.rb
80
+ - lib/pry-moves/painter.rb
79
81
  - lib/pry-moves/pry_ext.rb
80
82
  - lib/pry-moves/pry_remote_ext.rb
81
83
  - lib/pry-moves/pry_wrapper.rb
@@ -87,13 +89,20 @@ files:
87
89
  - lib/pry-stack_explorer/pry-stack_explorer.rb
88
90
  - lib/pry-stack_explorer/when_started_hook.rb
89
91
  - playground/Gemfile
92
+ - playground/Gemfile.lock
90
93
  - playground/README.md
91
94
  - playground/demo.rb
95
+ - playground/playground.rb
92
96
  - playground/recursions.rb
93
97
  - playground/sand.rb
94
98
  - playground/threads.rb
95
99
  - playground/tracer.rb
96
100
  - pry-moves.gemspec
101
+ - spec/backtrace_spec.rb
102
+ - spec/blocks_spec.rb
103
+ - spec/commands_spec.rb
104
+ - spec/pry_debugger.rb
105
+ - spec/spec_helper.rb
97
106
  homepage: https://github.com/garmoshka-mo/pry-moves
98
107
  licenses:
99
108
  - MIT
@@ -118,4 +127,9 @@ rubygems_version: 2.6.8
118
127
  signing_key:
119
128
  specification_version: 4
120
129
  summary: Debugger for ruby
121
- test_files: []
130
+ test_files:
131
+ - spec/backtrace_spec.rb
132
+ - spec/blocks_spec.rb
133
+ - spec/commands_spec.rb
134
+ - spec/pry_debugger.rb
135
+ - spec/spec_helper.rb