pry-stack 0.5.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/.gemspec +30 -0
- data/.gemtest +0 -0
- data/.gitignore +7 -0
- data/.travis.yml +15 -0
- data/.yardopts +1 -0
- data/CHANGELOG +0 -0
- data/Gemfile +2 -0
- data/LICENSE +25 -0
- data/README.md +39 -0
- data/Rakefile +30 -0
- data/VERSION +1 -0
- data/examples/example.rb +45 -0
- data/examples/example2.rb +18 -0
- data/examples/example3.rb +46 -0
- data/lib/pry-stack.rb +133 -0
- data/lib/pry-stack/commands.rb +316 -0
- data/lib/pry-stack/frame_manager.rb +85 -0
- data/lib/pry-stack/when_started_hook.rb +94 -0
- data/test/helper.rb +92 -0
- data/test/test_commands.rb +358 -0
- data/test/test_frame_manager.rb +65 -0
- data/test/test_stack.rb +393 -0
- data/tester.rb +27 -0
- metadata +140 -0
@@ -0,0 +1,85 @@
|
|
1
|
+
module PryStack
|
2
|
+
|
3
|
+
# This class represents a call-stack. It stores the
|
4
|
+
# frames that make up the stack and is responsible for updating the
|
5
|
+
# associated Pry instance to reflect the active frame. It is fully Enumerable.
|
6
|
+
class FrameManager
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# @return [Array<Binding>] The array of bindings that constitute
|
10
|
+
# the call-stack.
|
11
|
+
attr_accessor :bindings
|
12
|
+
|
13
|
+
# @return [Fixnum] The index of the active frame (binding) in the call-stack.
|
14
|
+
attr_accessor :binding_index
|
15
|
+
|
16
|
+
# @return [Hash] A hash for user defined data
|
17
|
+
attr_reader :user
|
18
|
+
|
19
|
+
# @return [Binding] The binding of the Pry instance before the
|
20
|
+
# FrameManager took over.
|
21
|
+
attr_reader :prior_binding
|
22
|
+
|
23
|
+
# @return [Array] The backtrace of the Pry instance before the
|
24
|
+
# FrameManager took over.
|
25
|
+
attr_reader :prior_backtrace
|
26
|
+
|
27
|
+
def initialize(bindings, _pry_)
|
28
|
+
self.bindings = bindings
|
29
|
+
self.binding_index = 0
|
30
|
+
@pry = _pry_
|
31
|
+
@user = {}
|
32
|
+
@prior_binding = _pry_.binding_stack.last
|
33
|
+
@prior_backtrace = _pry_.backtrace
|
34
|
+
end
|
35
|
+
|
36
|
+
# Iterate over all frames
|
37
|
+
def each(&block)
|
38
|
+
bindings.each(&block)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Ensure the Pry instance's active binding is the frame manager's
|
42
|
+
# active binding.
|
43
|
+
def refresh_frame(run_whereami=true)
|
44
|
+
change_frame_to binding_index, run_whereami
|
45
|
+
end
|
46
|
+
|
47
|
+
# @return [Binding] The currently active frame
|
48
|
+
def current_frame
|
49
|
+
bindings[binding_index]
|
50
|
+
end
|
51
|
+
|
52
|
+
# Set the binding index (aka frame index), but raising an Exception when invalid
|
53
|
+
# index received. Also converts negative indices to their positive counterparts.
|
54
|
+
# @param [Fixnum] index The index.
|
55
|
+
def set_binding_index_safely(index)
|
56
|
+
if index > bindings.size - 1
|
57
|
+
raise Pry::CommandError, "At top of stack, cannot go further!"
|
58
|
+
elsif index < -bindings.size
|
59
|
+
raise Pry::CommandError, "At bottom of stack, cannot go further!"
|
60
|
+
else
|
61
|
+
# wrap around negative indices
|
62
|
+
index = (bindings.size - 1) + index + 1 if index < 0
|
63
|
+
|
64
|
+
self.binding_index = index
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Change active frame to the one indexed by `index`.
|
69
|
+
# Note that indexing base is `0`
|
70
|
+
# @param [Fixnum] index The index of the frame.
|
71
|
+
def change_frame_to(index, run_whereami=true)
|
72
|
+
|
73
|
+
set_binding_index_safely(index)
|
74
|
+
|
75
|
+
if @pry.binding_stack.empty?
|
76
|
+
@pry.binding_stack.replace [bindings[binding_index]]
|
77
|
+
else
|
78
|
+
@pry.binding_stack[-1] = bindings[binding_index]
|
79
|
+
end
|
80
|
+
|
81
|
+
@pry.run_command "whereami" if run_whereami
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module PryStack
|
2
|
+
class WhenStartedHook
|
3
|
+
include Pry::Helpers::BaseHelpers
|
4
|
+
|
5
|
+
def caller_bindings(target)
|
6
|
+
bindings = binding.callers
|
7
|
+
|
8
|
+
bindings = remove_internal_frames(bindings)
|
9
|
+
bindings = remove_debugger_frames(bindings)
|
10
|
+
bindings = bindings.drop(1) if pry_method_frame?(bindings.first)
|
11
|
+
|
12
|
+
# Use the binding returned by #of_caller if possible (as we get
|
13
|
+
# access to frame_type).
|
14
|
+
# Otherwise stick to the given binding (target).
|
15
|
+
if !PryStack.bindings_equal?(target, bindings.first)
|
16
|
+
bindings.shift
|
17
|
+
bindings.unshift(target)
|
18
|
+
end
|
19
|
+
|
20
|
+
bindings
|
21
|
+
end
|
22
|
+
|
23
|
+
def call(target, options, _pry_)
|
24
|
+
target ||= _pry_.binding_stack.first if _pry_
|
25
|
+
options = {
|
26
|
+
:call_stack => true,
|
27
|
+
:initial_frame => 0
|
28
|
+
}.merge!(options)
|
29
|
+
|
30
|
+
return if !options[:call_stack]
|
31
|
+
|
32
|
+
if options[:call_stack].is_a?(Array)
|
33
|
+
bindings = options[:call_stack]
|
34
|
+
|
35
|
+
if !valid_call_stack?(bindings)
|
36
|
+
raise ArgumentError, ":call_stack must be an array of bindings"
|
37
|
+
end
|
38
|
+
else
|
39
|
+
bindings = caller_bindings(target)
|
40
|
+
end
|
41
|
+
|
42
|
+
PryStack.create_and_push_frame_manager bindings, _pry_, :initial_frame => options[:initial_frame]
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
# remove internal frames related to setting up the session
|
48
|
+
def remove_internal_frames(bindings)
|
49
|
+
start_frames = internal_frames_with_indices(bindings)
|
50
|
+
start_frame_index = start_frames.first.last
|
51
|
+
|
52
|
+
if start_frames.size >= 2
|
53
|
+
# god knows what's going on in here
|
54
|
+
idx1, idx2 = start_frames.take(2).map(&:last)
|
55
|
+
start_frame_index = idx2 if !nested_session?(bindings[idx1..idx2])
|
56
|
+
end
|
57
|
+
|
58
|
+
bindings.drop(start_frame_index + 1)
|
59
|
+
end
|
60
|
+
|
61
|
+
# remove pry-nav / pry-debugger frames
|
62
|
+
def remove_debugger_frames(bindings)
|
63
|
+
bindings.drop_while { |b| b.eval("__FILE__") =~ /pry-(?:nav|debugger)/ }
|
64
|
+
end
|
65
|
+
|
66
|
+
# binding.pry frame
|
67
|
+
# @return [Boolean]
|
68
|
+
def pry_method_frame?(binding)
|
69
|
+
safe_send(binding.eval("__method__"), :==, :pry)
|
70
|
+
end
|
71
|
+
|
72
|
+
# When a pry session is started within a pry session
|
73
|
+
# @return [Boolean]
|
74
|
+
def nested_session?(bindings)
|
75
|
+
bindings.detect do |b|
|
76
|
+
safe_send(b.eval("__method__"), :==, :re) &&
|
77
|
+
safe_send(b.eval("self.class"), :equal?, Pry)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [Array<Array<Binding, Fixnum>>]
|
82
|
+
def internal_frames_with_indices(bindings)
|
83
|
+
bindings.each_with_index.select do |b, i|
|
84
|
+
b.frame_type == :method &&
|
85
|
+
safe_send(b.eval("self"), :equal?, Pry) &&
|
86
|
+
safe_send(b.eval("__method__"), :==, :start)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def valid_call_stack?(bindings)
|
91
|
+
bindings.any? && bindings.all? { |v| v.is_a?(Binding) }
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/test/helper.rb
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'pry'
|
4
|
+
|
5
|
+
PRY_STACK_VERSION = File.read(File.expand_path '../../VERSION', __FILE__)
|
6
|
+
|
7
|
+
unless Object.const_defined? 'PryStack'
|
8
|
+
$:.unshift File.expand_path '../../lib', __FILE__
|
9
|
+
require 'pry-stack'
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'bacon'
|
13
|
+
|
14
|
+
puts "---------------------------------------------------------"
|
15
|
+
puts "Testing"
|
16
|
+
puts "---------------------------------------------------------"
|
17
|
+
puts "pry-stack version: #{PRY_STACK_VERSION}"
|
18
|
+
puts "Ruby version: #{RUBY_VERSION}"
|
19
|
+
|
20
|
+
PE = PryStack
|
21
|
+
|
22
|
+
class << Pry
|
23
|
+
alias_method :orig_reset_defaults, :reset_defaults
|
24
|
+
def reset_defaults
|
25
|
+
orig_reset_defaults
|
26
|
+
|
27
|
+
Pry.color = false
|
28
|
+
Pry.pager = false
|
29
|
+
Pry.config.should_load_rc = false
|
30
|
+
Pry.config.should_load_plugins = false
|
31
|
+
Pry.config.history.should_load = false
|
32
|
+
Pry.config.history.should_save = false
|
33
|
+
Pry.config.auto_indent = false
|
34
|
+
Pry.config.hooks = Pry::Hooks.new
|
35
|
+
Pry.config.collision_warning = false
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
AfterSessionHook = Pry.config.hooks.get_hook(:after_session, :delete_frame_manager)
|
40
|
+
WhenStartedHook = Pry.config.hooks.get_hook(:when_started, :save_caller_bindings)
|
41
|
+
|
42
|
+
Pry.reset_defaults
|
43
|
+
|
44
|
+
class InputTester
|
45
|
+
def initialize(*actions)
|
46
|
+
if actions.last.is_a?(Hash) && actions.last.keys == [:history]
|
47
|
+
@hist = actions.pop[:history]
|
48
|
+
end
|
49
|
+
@orig_actions = actions.dup
|
50
|
+
@actions = actions
|
51
|
+
end
|
52
|
+
|
53
|
+
def readline(*)
|
54
|
+
@actions.shift.tap{ |line| @hist << line if @hist }
|
55
|
+
end
|
56
|
+
|
57
|
+
def rewind
|
58
|
+
@actions = @orig_actions.dup
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Set I/O streams.
|
63
|
+
#
|
64
|
+
# Out defaults to an anonymous StringIO.
|
65
|
+
#
|
66
|
+
def redirect_pry_io(new_in, new_out = StringIO.new)
|
67
|
+
old_in = Pry.input
|
68
|
+
old_out = Pry.output
|
69
|
+
|
70
|
+
Pry.input = new_in
|
71
|
+
Pry.output = new_out
|
72
|
+
begin
|
73
|
+
yield
|
74
|
+
ensure
|
75
|
+
Pry.input = old_in
|
76
|
+
Pry.output = old_out
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def mock_pry(*args)
|
81
|
+
|
82
|
+
binding = args.first.is_a?(Binding) ? args.shift : binding()
|
83
|
+
|
84
|
+
input = InputTester.new(*args)
|
85
|
+
output = StringIO.new
|
86
|
+
|
87
|
+
redirect_pry_io(input, output) do
|
88
|
+
binding.pry
|
89
|
+
end
|
90
|
+
|
91
|
+
output.string
|
92
|
+
end
|
@@ -0,0 +1,358 @@
|
|
1
|
+
require 'helper'
|
2
|
+
|
3
|
+
|
4
|
+
class Top
|
5
|
+
attr_accessor :method_list, :middle
|
6
|
+
def initialize method_list
|
7
|
+
@method_list = method_list
|
8
|
+
end
|
9
|
+
def bing
|
10
|
+
@middle = Middle.new method_list
|
11
|
+
@middle.bong
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Middle
|
16
|
+
attr_accessor :method_list, :bottom
|
17
|
+
def initialize method_list
|
18
|
+
@method_list = method_list
|
19
|
+
end
|
20
|
+
def bong
|
21
|
+
@bottom = Bottom.new method_list
|
22
|
+
@bottom.bang
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Bottom
|
27
|
+
attr_accessor :method_list
|
28
|
+
def initialize method_list
|
29
|
+
@method_list = method_list
|
30
|
+
end
|
31
|
+
def bang
|
32
|
+
Pry.start(binding)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
describe PryStack::Commands do
|
38
|
+
|
39
|
+
before do
|
40
|
+
Pry.config.hooks.add_hook(:when_started, :save_caller_bindings, WhenStartedHook)
|
41
|
+
Pry.config.hooks.add_hook(:after_session, :delete_frame_manager, AfterSessionHook)
|
42
|
+
|
43
|
+
@o = Object.new
|
44
|
+
class << @o; attr_accessor :first_method, :second_method, :third_method; end
|
45
|
+
def @o.bing() bong end
|
46
|
+
def @o.bong() bang end
|
47
|
+
def @o.bang() Pry.start(binding) end
|
48
|
+
|
49
|
+
method_list = []
|
50
|
+
@top = Top.new method_list
|
51
|
+
end
|
52
|
+
|
53
|
+
after do
|
54
|
+
Pry.config.hooks.delete_hook(:when_started, :save_caller_bindings)
|
55
|
+
Pry.config.hooks.delete_hook(:after_session, :delete_frame_manager)
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "up" do
|
59
|
+
it 'should move up the call stack one frame at a time' do
|
60
|
+
redirect_pry_io(InputTester.new("@first_method = __method__",
|
61
|
+
"up",
|
62
|
+
"@second_method = __method__",
|
63
|
+
"up",
|
64
|
+
"@third_method = __method__",
|
65
|
+
"exit-all"), out=StringIO.new) do
|
66
|
+
@o.bing
|
67
|
+
end
|
68
|
+
|
69
|
+
@o.first_method.should == :bang
|
70
|
+
@o.second_method.should == :bong
|
71
|
+
@o.third_method.should == :bing
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should move up the call stack two frames at a time' do
|
75
|
+
redirect_pry_io(InputTester.new("@first_method = __method__",
|
76
|
+
"up 2",
|
77
|
+
"@second_method = __method__",
|
78
|
+
"exit-all"), out=StringIO.new) do
|
79
|
+
@o.bing
|
80
|
+
end
|
81
|
+
|
82
|
+
@o.first_method.should == :bang
|
83
|
+
@o.second_method.should == :bing
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "by method name regex" do
|
87
|
+
it 'should move to the method name that matches the regex' do
|
88
|
+
redirect_pry_io(InputTester.new("@first_method = __method__",
|
89
|
+
"up bi",
|
90
|
+
"@second_method = __method__",
|
91
|
+
"exit-all"), out=StringIO.new) do
|
92
|
+
@o.bing
|
93
|
+
end
|
94
|
+
|
95
|
+
@o.first_method.should == :bang
|
96
|
+
@o.second_method.should == :bing
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should move through all methods that match regex in order' do
|
100
|
+
redirect_pry_io(InputTester.new("@first_method = __method__",
|
101
|
+
"up b",
|
102
|
+
"@second_method = __method__",
|
103
|
+
"up b",
|
104
|
+
"@third_method = __method__",
|
105
|
+
"exit-all"), out=StringIO.new) do
|
106
|
+
@o.bing
|
107
|
+
end
|
108
|
+
|
109
|
+
@o.first_method.should == :bang
|
110
|
+
@o.second_method.should == :bong
|
111
|
+
@o.third_method.should == :bing
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'should error if it cant find frame to match regex' do
|
115
|
+
redirect_pry_io(InputTester.new("up conrad_irwin",
|
116
|
+
"exit-all"), out=StringIO.new) do
|
117
|
+
@o.bing
|
118
|
+
end
|
119
|
+
|
120
|
+
out.string.should =~ /Error: No frame that matches/
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
describe 'by Class#method name regex' do
|
126
|
+
it 'should move to the method and class that matches the regex' do
|
127
|
+
redirect_pry_io(InputTester.new("@method_list << self.class.to_s + '#' + __method__.to_s",
|
128
|
+
'up Middle#bong',
|
129
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
130
|
+
"exit-all"), out=StringIO.new) do
|
131
|
+
@top.bing
|
132
|
+
end
|
133
|
+
|
134
|
+
@top.method_list.should == ['Bottom#bang', 'Middle#bong']
|
135
|
+
end
|
136
|
+
|
137
|
+
### ????? ###
|
138
|
+
# it 'should be case sensitive' do
|
139
|
+
# end
|
140
|
+
### ????? ###
|
141
|
+
|
142
|
+
it 'should allow partial class names' do
|
143
|
+
redirect_pry_io(InputTester.new("@method_list << self.class.to_s + '#' + __method__.to_s",
|
144
|
+
'up Mid#bong',
|
145
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
146
|
+
"exit-all"), out=StringIO.new) do
|
147
|
+
@top.bing
|
148
|
+
end
|
149
|
+
|
150
|
+
@top.method_list.should == ['Bottom#bang', 'Middle#bong']
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
it 'should allow partial method names' do
|
155
|
+
redirect_pry_io(InputTester.new("@method_list << self.class.to_s + '#' + __method__.to_s",
|
156
|
+
'up Middle#bo',
|
157
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
158
|
+
"exit-all"), out=StringIO.new) do
|
159
|
+
@top.bing
|
160
|
+
end
|
161
|
+
|
162
|
+
@top.method_list.should == ['Bottom#bang', 'Middle#bong']
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should error if it cant find frame to match regex' do
|
167
|
+
redirect_pry_io(InputTester.new('up Conrad#irwin',
|
168
|
+
"exit-all"), out=StringIO.new) do
|
169
|
+
@top.bing
|
170
|
+
end
|
171
|
+
|
172
|
+
out.string.should =~ /Error: No frame that matches/
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe "down" do
|
178
|
+
it 'should move down the call stack one frame at a time' do
|
179
|
+
def @o.bang() Pry.start(binding, :initial_frame => 1) end
|
180
|
+
|
181
|
+
redirect_pry_io(InputTester.new("@first_method = __method__",
|
182
|
+
"down",
|
183
|
+
"@second_method = __method__",
|
184
|
+
"exit-all"), out=StringIO.new) do
|
185
|
+
@o.bing
|
186
|
+
end
|
187
|
+
|
188
|
+
@o.first_method.should == :bong
|
189
|
+
@o.second_method.should == :bang
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'should move down the call stack two frames at a time' do
|
193
|
+
def @o.bang() Pry.start(binding, :initial_frame => 2) end
|
194
|
+
|
195
|
+
redirect_pry_io(InputTester.new("@first_method = __method__",
|
196
|
+
"down 2",
|
197
|
+
"@second_method = __method__",
|
198
|
+
"exit-all"), out=StringIO.new) do
|
199
|
+
@o.bing
|
200
|
+
end
|
201
|
+
|
202
|
+
@o.first_method.should == :bing
|
203
|
+
@o.second_method.should == :bang
|
204
|
+
end
|
205
|
+
|
206
|
+
describe "by method name regex" do
|
207
|
+
it 'should move to the method name that matches the regex' do
|
208
|
+
redirect_pry_io(InputTester.new("frame -1",
|
209
|
+
"down bo",
|
210
|
+
"@first_method = __method__",
|
211
|
+
"exit-all"), out=StringIO.new) do
|
212
|
+
@o.bing
|
213
|
+
end
|
214
|
+
|
215
|
+
@o.first_method.should == :bong
|
216
|
+
end
|
217
|
+
|
218
|
+
it 'should move through all methods that match regex in order' do
|
219
|
+
redirect_pry_io(InputTester.new("frame bing",
|
220
|
+
"@first_method = __method__",
|
221
|
+
"down b",
|
222
|
+
"@second_method = __method__",
|
223
|
+
"down b",
|
224
|
+
"@third_method = __method__",
|
225
|
+
"exit-all"), out=StringIO.new) do
|
226
|
+
@o.bing
|
227
|
+
end
|
228
|
+
|
229
|
+
@o.first_method.should == :bing
|
230
|
+
@o.second_method.should == :bong
|
231
|
+
@o.third_method.should == :bang
|
232
|
+
end
|
233
|
+
|
234
|
+
it 'should error if it cant find frame to match regex' do
|
235
|
+
redirect_pry_io(InputTester.new("frame -1",
|
236
|
+
"down conrad_irwin",
|
237
|
+
"exit-all"), out=StringIO.new) do
|
238
|
+
@o.bing
|
239
|
+
end
|
240
|
+
|
241
|
+
out.string.should =~ /Error: No frame that matches/
|
242
|
+
end
|
243
|
+
end
|
244
|
+
|
245
|
+
describe 'by Class#method name regex' do
|
246
|
+
it 'should move to the method and class that matches the regex' do
|
247
|
+
redirect_pry_io(InputTester.new('frame Top#bing',
|
248
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
249
|
+
'down Middle#bong',
|
250
|
+
"@method_list << self.class.to_s + '#' + __method__.to_s",
|
251
|
+
"exit-all"), out=StringIO.new) do
|
252
|
+
@top.bing
|
253
|
+
end
|
254
|
+
|
255
|
+
@top.method_list.should == ['Top#bing', 'Middle#bong']
|
256
|
+
end
|
257
|
+
|
258
|
+
### ????? ###
|
259
|
+
# it 'should be case sensitive' do
|
260
|
+
# end
|
261
|
+
### ????? ###
|
262
|
+
|
263
|
+
it 'should error if it cant find frame to match regex' do
|
264
|
+
redirect_pry_io(InputTester.new('down Conrad#irwin',
|
265
|
+
"exit-all"), out=StringIO.new) do
|
266
|
+
@top.bing
|
267
|
+
end
|
268
|
+
|
269
|
+
out.string.should =~ /Error: No frame that matches/
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
describe "frame" do
|
276
|
+
describe "by method name regex" do
|
277
|
+
it 'should jump to correct stack frame when given method name' do
|
278
|
+
redirect_pry_io(InputTester.new("frame bi",
|
279
|
+
"@first_method = __method__",
|
280
|
+
"exit-all"), out=StringIO.new) do
|
281
|
+
@o.bing
|
282
|
+
end
|
283
|
+
|
284
|
+
@o.first_method.should == :bing
|
285
|
+
end
|
286
|
+
|
287
|
+
it 'should NOT jump to frames lower down stack when given method name' do
|
288
|
+
redirect_pry_io(InputTester.new("frame -1",
|
289
|
+
"frame bang",
|
290
|
+
"exit-all"), out=StringIO.new) do
|
291
|
+
@o.bing
|
292
|
+
end
|
293
|
+
|
294
|
+
out.string.should =~ /Error: No frame that matches/
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
|
299
|
+
it 'should move to the given frame in the call stack' do
|
300
|
+
redirect_pry_io(InputTester.new("frame 2",
|
301
|
+
"@first_method = __method__",
|
302
|
+
"exit-all"), out=StringIO.new) do
|
303
|
+
@o.bing
|
304
|
+
end
|
305
|
+
|
306
|
+
@o.first_method.should == :bing
|
307
|
+
end
|
308
|
+
|
309
|
+
it 'should return info on current frame when given no parameters' do
|
310
|
+
redirect_pry_io(InputTester.new("frame",
|
311
|
+
"exit-all"), out=StringIO.new) do
|
312
|
+
@o.bing
|
313
|
+
end
|
314
|
+
|
315
|
+
out.string.should =~ /\#0.*?bang/
|
316
|
+
out.string.should.not =~ /\#1/
|
317
|
+
end
|
318
|
+
|
319
|
+
describe "negative indices" do
|
320
|
+
it 'should work with negative frame numbers' do
|
321
|
+
o = Object.new
|
322
|
+
class << o; attr_accessor :frame; end
|
323
|
+
def o.alpha() binding end
|
324
|
+
def o.beta() binding end
|
325
|
+
def o.gamma() binding end
|
326
|
+
|
327
|
+
call_stack = [o.alpha, o.beta, o.gamma]
|
328
|
+
method_names = call_stack.map { |v| v.eval('__method__') }.reverse
|
329
|
+
(1..3).each_with_index do |v, idx|
|
330
|
+
redirect_pry_io(InputTester.new("frame -#{v}",
|
331
|
+
"@frame = __method__",
|
332
|
+
"exit-all"), out=StringIO.new) do
|
333
|
+
Pry.start(o, :call_stack => call_stack)
|
334
|
+
end
|
335
|
+
o.frame.should == method_names[idx]
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
it 'should convert negative indices to their positive counterparts' do
|
340
|
+
o = Object.new
|
341
|
+
class << o; attr_accessor :frame_number; end
|
342
|
+
def o.alpha() binding end
|
343
|
+
def o.beta() binding end
|
344
|
+
def o.gamma() binding end
|
345
|
+
|
346
|
+
call_stack = [o.alpha, o.beta, o.gamma]
|
347
|
+
(1..3).each_with_index do |v, idx|
|
348
|
+
redirect_pry_io(InputTester.new("frame -#{v}",
|
349
|
+
"@frame_number = PryStack.frame_manager(_pry_).binding_index",
|
350
|
+
"exit-all"), out=StringIO.new) do
|
351
|
+
Pry.start(o, :call_stack => call_stack)
|
352
|
+
end
|
353
|
+
o.frame_number.should == call_stack.size - v
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
357
|
+
end
|
358
|
+
end
|