pry-exception_explorer 0.1.1pre5 → 0.1.1pre7

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.
data/Rakefile CHANGED
@@ -43,9 +43,14 @@ task :reinstall => :gems do
43
43
  sh "gem install #{direc}/pkg/pry-exception_explorer-#{PryExceptionExplorer::VERSION}.gem"
44
44
  end
45
45
 
46
- desc "Run example"
47
- task :example do
48
- sh "ruby -I#{direc}/lib/ #{direc}/examples/example.rb "
46
+ desc "Run example wrap"
47
+ task :example_wrap do
48
+ sh "ruby -I#{direc}/lib/ #{direc}/examples/example_wrap.rb "
49
+ end
50
+
51
+ desc "Run example inline"
52
+ task :example_inline do
53
+ sh "ruby -I#{direc}/lib/ #{direc}/examples/example_inline.rb "
49
54
  end
50
55
 
51
56
  task :default => :test
@@ -0,0 +1,27 @@
1
+ unless Object.const_defined? :PryExceptionExplorer
2
+ $:.unshift File.expand_path '../../lib', __FILE__
3
+ require 'pry'
4
+ end
5
+
6
+ require 'pry-exception_explorer'
7
+
8
+ PryExceptionExplorer.enabled = true
9
+ PryExceptionExplorer.intercept(RuntimeError)
10
+
11
+ def alpha
12
+ name = "john"
13
+ beta
14
+ puts name
15
+ end
16
+
17
+ def beta
18
+ x = 20
19
+ gamma
20
+ puts x
21
+ end
22
+
23
+ def gamma
24
+ raise "oh my, an exception"
25
+ end
26
+
27
+ alpha
@@ -4,7 +4,6 @@ unless Object.const_defined? :PryExceptionExplorer
4
4
  end
5
5
 
6
6
  require 'pry-exception_explorer'
7
- require 'pry-exception_explorer/exception_wrap'
8
7
 
9
8
  def alpha
10
9
  name = "john"
@@ -1,8 +1,6 @@
1
1
  Pry::CLI.add_options do
2
2
 
3
3
  on :w, :wrap, "Run the script wrapped by the exception explorer", true do |file|
4
- require 'pry-exception_explorer/exception_wrap'
5
-
6
4
  PryExceptionExplorer.wrap do
7
5
  require file
8
6
  end
@@ -1,72 +1,99 @@
1
1
  require 'pry-stack_explorer'
2
2
 
3
- PryExceptionExplorer::Commands = Pry::CommandSet.new do
4
-
5
- command "enter-exception", "Enter the context of the last exception" do
6
- ex = _pry_.last_exception
7
- if ex && ex.exception_call_stack
8
- _pry_.backtrace = ex.backtrace
9
- PryStackExplorer.create_and_push_frame_manager(ex.exception_call_stack, _pry_)
10
- PryStackExplorer.frame_manager(_pry_).user[:exception] = ex
11
- PryStackExplorer.frame_manager(_pry_).refresh_frame
12
- elsif ex
13
- output.puts "Current exception can't be entered! (perhaps a C exception)"
14
- else
15
- output.puts "No exception to enter!"
3
+ module PryExceptionExplorer
4
+ module ExceptionHelpers
5
+ include PryStackExplorer::FrameHelpers
6
+
7
+ private
8
+ def in_exception?
9
+ frame_manager && frame_manager.user[:exception]
10
+ end
11
+
12
+ def last_exception
13
+ _pry_.last_exception
14
+ end
15
+
16
+ def enterable_exception?
17
+ last_exception && last_exception.exception_call_stack
16
18
  end
17
19
  end
18
20
 
19
- command_class "exit-exception", "Leave the context of the current exception." do
20
- banner <<-BANNER
21
- Usage: exit-exception
22
- Exit active exception and return to containing context.
23
- BANNER
24
-
25
- def process
26
- if !in_exception?
27
- raise Pry::CommandError, "You are not in an exception!"
28
- elsif !prior_context_exists?
29
- run "exit-all"
30
- else
31
- popped_fm = PryStackExplorer.pop_frame_manager(_pry_)
32
- if frame_manager
33
- frame_manager.refresh_frame
21
+ Commands = Pry::CommandSet.new do
22
+ create_command "enter-exception", "Enter the context of the last exception" do
23
+ include PryExceptionExplorer::ExceptionHelpers
24
+
25
+ banner <<-BANNER
26
+ Usage: enter-exception
27
+ Enter the context of the last exception
28
+ BANNER
29
+
30
+ def process
31
+ if enterable_exception?
32
+ PryStackExplorer.create_and_push_frame_manager(last_exception.exception_call_stack, _pry_)
33
+ PryExceptionExplorer.setup_exception_context(last_exception, _pry_)
34
+
35
+ # have to use _pry_.run_command instead of 'run' here as
36
+ # 'run' works on the current target which hasnt been updated
37
+ # yet, whereas _pry_.run_command operates on the newly
38
+ # updated target (the context of the exception)
39
+ _pry_.run_command "whereami"
40
+ elsif last_exception
41
+ raise Pry::CommandError, "Current exception can't be entered! (perhaps a C exception)"
34
42
  else
35
- _pry_.binding_stack[-1] = popped_fm.prior_binding
43
+ raise Pry::CommandError, "No exception to enter!"
36
44
  end
37
45
  end
38
46
  end
39
47
 
40
- private
41
- def frame_manager
42
- PryStackExplorer.frame_manager(_pry_)
43
- end
48
+ create_command "exit-exception", "Leave the context of the current exception." do
49
+ include ExceptionHelpers
44
50
 
45
- def frame_managers
46
- PryStackExplorer.frame_managers(_pry_)
47
- end
51
+ banner <<-BANNER
52
+ Usage: exit-exception
53
+ Exit active exception and return to containing context.
54
+ BANNER
48
55
 
49
- def prior_context_exists?
50
- frame_managers.count > 1 || frame_manager.prior_binding
56
+ def process
57
+ if !in_exception?
58
+ raise Pry::CommandError, "You are not in an exception!"
59
+ elsif !prior_context_exists?
60
+ run "exit-all"
61
+ else
62
+ popped_fm = PryStackExplorer.pop_frame_manager(_pry_)
63
+ _pry_.last_exception = popped_fm.user[:exception]
64
+ end
65
+ end
51
66
  end
52
67
 
53
- def in_exception?
54
- frame_manager && frame_manager.user[:exception]
55
- end
56
- end
68
+ create_command "continue-exception", "Attempt to continue the current exception." do
69
+ include ExceptionHelpers
70
+
71
+ banner <<-BANNER
72
+ Usage: continue-exception
73
+ Attempt to continue the current exception.
74
+ BANNER
57
75
 
58
- command "continue-exception", "Attempt to continue the current exception." do
59
- fm = PryStackExplorer.frame_manager(_pry_)
60
-
61
- if fm && fm.user[:exception] && fm.user[:inline_exception]
62
- PryStackExplorer.pop_frame_manager(_pry_)
63
- _pry_.run_command "exit-all PryExceptionExplorer::CONTINUE_INLINE_EXCEPTION"
64
- elsif fm && fm.user[:exception] && fm.user[:exception].continuation
65
- PryStackExplorer.pop_frame_manager(_pry_)
66
- fm.user[:exception].continue
67
- else
68
- output.puts "No exception to continue!"
76
+ def process
77
+ if inline_exception?
78
+ PryStackExplorer.pop_frame_manager(_pry_)
79
+ run "exit-all PryExceptionExplorer::CONTINUE_INLINE_EXCEPTION"
80
+ elsif normal_exception?
81
+ popped_fm = PryStackExplorer.pop_frame_manager(_pry_)
82
+ popped_fm.user[:exception].continue
83
+ else
84
+ raise Pry::CommandError, "No exception to continue!"
85
+ end
86
+ end
87
+
88
+ private
89
+ def inline_exception?
90
+ frame_manager && frame_manager.user[:exception] && frame_manager.user[:inline_exception]
91
+ end
92
+
93
+ def normal_exception?
94
+ frame_manager && frame_manager.user[:exception] && frame_manager.user[:exception].continuation
95
+ end
69
96
  end
70
- end
71
97
 
98
+ end
72
99
  end
@@ -0,0 +1,54 @@
1
+ # `PryExceptionExplorer` monkey-patches to `Exception`
2
+ class Exception
3
+ NoContinuation = Class.new(StandardError)
4
+
5
+ attr_accessor :continuation
6
+ attr_accessor :exception_call_stack
7
+ attr_accessor :should_intercept
8
+
9
+ # This method enables us to continue an exception (using
10
+ # `callcc` internally)
11
+ def continue
12
+ raise NoContinuation unless continuation.respond_to?(:call)
13
+ continuation.call
14
+ end
15
+
16
+ alias_method :should_intercept?, :should_intercept
17
+ end
18
+
19
+ # `PryExceptionExplorer` monkey-patches to `Object`
20
+ class Object
21
+
22
+ # We monkey-patch the `raise` method so we can intercept exceptions
23
+ def raise(exception = RuntimeError, string = nil, array = caller)
24
+
25
+ if exception.is_a?(String)
26
+ string = exception
27
+ exception = RuntimeError
28
+ end
29
+
30
+ ex = exception.exception(string)
31
+ ex.set_backtrace(array)
32
+
33
+ # revert to normal exception behaviour if EE not enabled.
34
+ if !PryExceptionExplorer.enabled?
35
+ return super(ex)
36
+ end
37
+
38
+ if PryExceptionExplorer.should_intercept_exception?(binding.of_caller(1), ex)
39
+ ex.exception_call_stack = binding.callers.tap { |v| v.shift(1 + PryExceptionExplorer.intercept_object.skip) }
40
+ ex.should_intercept = true
41
+
42
+ if !PryExceptionExplorer.wrap_active?
43
+ retval = PryExceptionExplorer.enter_exception(ex, :inline => true)
44
+ end
45
+ end
46
+
47
+ if retval != PryExceptionExplorer::CONTINUE_INLINE_EXCEPTION
48
+ callcc do |cc|
49
+ ex.continuation = cc
50
+ super(ex)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,43 @@
1
+ module PryExceptionExplorer
2
+ class Intercept
3
+
4
+ # @return [Fixnum] Number of frames to skip when session starts.
5
+ attr_accessor :skip
6
+
7
+ # @return [Boolean] Whether to intercept exceptions raised inside the session.
8
+ attr_accessor :intercept_recurse
9
+ alias_method :intercept_recurse?, :intercept_recurse
10
+
11
+ # @return [Boolean] Whether this intercept object is active
12
+ # If it's inactive then calling it will always return `false`
13
+ # regardless of content inside block.
14
+ def active?() @active end
15
+
16
+ # Disable the intercept object.
17
+ def disable!() @active = false end
18
+
19
+ # Enable if the intercept object.
20
+ def enable!() @active = true end
21
+
22
+ # @return [Proc] The predicate block that determines if
23
+ # interception takes place.
24
+ attr_reader :block
25
+
26
+ def initialize(block, options={})
27
+ options = {
28
+ :skip => 0,
29
+ :intercept_recurse => false
30
+ }.merge!(options)
31
+
32
+ @block = block
33
+ @skip = options[:skip]
34
+ @intercept_recurse = options[:intercept_recurse]
35
+
36
+ @active = true
37
+ end
38
+
39
+ def call(*args)
40
+ active? && @block.call(*args)
41
+ end
42
+ end
43
+ end
@@ -2,29 +2,34 @@ module PryExceptionExplorer
2
2
  class LazyFrame
3
3
 
4
4
  # we need to jump over a few irrelevant frames to begin with
5
- START_FRAME_OFFSET = 5
6
-
5
+ START_FRAME_OFFSET = 6
6
+
7
7
  def initialize(frame, frame_counter = 0)
8
8
  @frame = frame
9
9
  @frame_counter = frame_counter
10
10
  end
11
11
 
12
+ # @return [Binding] The `Binding` object that represents the frame.
12
13
  def raw_frame
13
14
  @frame
14
15
  end
15
16
 
17
+ # @return [Class] The class of the `self` of the frame.
16
18
  def klass
17
19
  @frame.eval("self.class")
18
20
  end
19
21
 
22
+ # @return [Object] The object context of the frame (the `self`).
20
23
  def self
21
24
  @frame.eval("self")
22
25
  end
23
26
 
27
+ # @return [Symbol, nil] The name of the active method in the frame (or `nil`)
24
28
  def method_name
25
29
  @frame.eval("__method__")
26
30
  end
27
31
 
32
+ # @return [LazyFrame] The caller frame.
28
33
  def prev
29
34
  LazyFrame.new(binding.of_caller(@frame_counter + START_FRAME_OFFSET), @frame_counter + 1)
30
35
  end
@@ -1,3 +1,3 @@
1
1
  module PryExceptionExplorer
2
- VERSION = "0.1.1pre5"
2
+ VERSION = "0.1.1pre7"
3
3
  end
@@ -1,138 +1,194 @@
1
1
  # pry-exception_explorer.rb
2
- # (C) John Mair (banisterfiend); MIT license
2
+ # (C) 2012 John Mair (banisterfiend); MIT license
3
3
 
4
4
  require 'pry-stack_explorer'
5
5
  require "pry-exception_explorer/version"
6
6
  require "pry-exception_explorer/lazy_frame"
7
7
  require "pry-exception_explorer/commands"
8
- require "pry"
8
+ require "pry-exception_explorer/core_ext"
9
+ require "pry-exception_explorer/intercept"
9
10
 
10
11
  if RUBY_VERSION =~ /1.9/
11
12
  require 'continuation'
12
13
  end
13
14
 
14
15
  module PryExceptionExplorer
16
+
17
+ # short-hand for `PryExceptionExplorer`
18
+ ::EE = self
19
+
20
+ # special constant
15
21
  CONTINUE_INLINE_EXCEPTION = Object.new
16
22
 
17
23
  class << self
18
24
 
19
- # @return [Boolean] Whether `PryStackExplorer` is enabled.
20
- attr_accessor :enabled
25
+ # @return [Hash] A thread-local hash.
26
+ def local_hash
27
+ Thread.current[:__pry_exception_explorer_hash__] ||= {}
28
+ end
29
+
30
+ # @param [Boolean] v Whether Exception Explorer is enabled.
31
+ def enabled=(v)
32
+ local_hash[:enabled] = v
33
+ end
21
34
 
35
+ # @return [Boolean] Whether Exception Explorer is enabled.
36
+ def enabled
37
+ !!local_hash[:enabled]
38
+ end
39
+
40
+ # @param [Boolean] v Whether to intercept only those exceptions that bubble out of
41
+ # `EE.wrap` block.
22
42
  def wrap_active=(v)
23
- Thread.current[:__pry_exception_explorer_wrap__] = v
43
+ local_hash[:wrap_active] = v
24
44
  end
25
45
 
46
+ # @return [Boolean] Whether to intercept only those exceptions that bubble out of
47
+ # `EE.wrap` block.
26
48
  def wrap_active
27
- !!Thread.current[:__pry_exception_explorer_wrap__]
49
+ !!local_hash[:wrap_active]
28
50
  end
29
51
 
30
52
  alias_method :wrap_active?, :wrap_active
31
53
  alias_method :enabled?, :enabled
32
- end
33
54
 
34
- self.wrap_active = false
35
55
 
36
- def self.should_capture_exception?(ex)
37
- true
38
- end
39
-
40
- def self.intercept(*exceptions, &block)
41
- if !exceptions.empty?
42
- block = proc { |_, ex| exceptions.any? { |v| v === ex } }
56
+ # Wrap the provided block - intercepting all exceptions
57
+ # that bubble out, provided they satisfy the
58
+ # assertion in `PryExceptionExplorer.intercept`.
59
+ # @yield The block to wrap.
60
+ def wrap
61
+ old_enabled, old_wrap_active = enabled, wrap_active
62
+ self.enabled = true
63
+ self.wrap_active = true
64
+ yield
65
+ rescue Exception => ex
66
+ if ex.should_intercept?
67
+ enter_exception(ex)
68
+ else
69
+ raise ex
70
+ end
71
+ ensure
72
+ self.enabled = old_enabled
73
+ self.wrap_active = old_wrap_active
43
74
  end
44
75
 
45
- Thread.current[:__pry_exception_explorer_intercept_block__] = block
46
- end
47
-
48
- def self.intercept_block
49
- Thread.current[:__pry_exception_explorer_intercept_block__]
50
- end
76
+ # This method allows the user to assert the situations where an
77
+ # exception interception occurs.
78
+ # This method can be invoked in two ways. The general form takes a
79
+ # block, the block is passed both the frame where the exception was
80
+ # raised, and the exception itself. The user then creates an
81
+ # assertion (a stack-assertion)
82
+ # based on these attributes. If the assertion is later satisfied by
83
+ # a raised exception, that exception will be intercepted.
84
+ # In the second form, the method simply takes an exception class, or
85
+ # a number of exception classes. If one of these exceptions is
86
+ # raised, it will be intercepted.
87
+ # @param [Array] exceptions The exception classes that will be intercepted.
88
+ # @yield [lazy_frame, exception] The block that determines whether an exception will be intercepted.
89
+ # @yieldparam [PryExceptionExplorer::Lazyframe] frame The frame
90
+ # where the exception was raised.
91
+ # @yieldparam [Exception] exception The exception that was raised.
92
+ # @yieldreturn [Boolean] The result of the stack assertion.
93
+ # @example First form: Assert method name is `toad` and exception is an `ArgumentError`
94
+ # PryExceptionExplorer.intercept do |frame, ex|
95
+ # frame.method_name == :toad && ex.is_a?(ArgumentError)
96
+ # end
97
+ # @example Second form: Assert exception is either `ArgumentError` or `RuntimeError`
98
+ # PryExceptionExplorer.intercept(ArgumentError, RuntimeError)
99
+ def intercept(*exceptions, &block)
100
+ return if exceptions.empty? && block.nil?
101
+
102
+ options = (exceptions.pop if exceptions.last.is_a?(Hash)) || {}
103
+
104
+ if !exceptions.empty?
105
+ block = proc { |_, ex| exceptions.any? { |v| v === ex } }
106
+ end
51
107
 
52
- def self.should_capture_exception?(ex, frame)
53
- if intercept_block
54
- intercept_block.call(LazyFrame.new(frame), ex)
55
- else
56
- false
108
+ local_hash[:intercept_object] = Intercept.new(block, options)
57
109
  end
58
- end
59
-
60
- def self.enter_exception_inline(ex)
61
- _pry_ = Pry.new
62
-
63
- Pry.initial_session_setup
64
- PryStackExplorer.create_and_push_frame_manager(ex.exception_call_stack, _pry_)
65
- PryStackExplorer.frame_manager(_pry_).user[:exception] = ex
66
- PryStackExplorer.frame_manager(_pry_).user[:inline_exception] = true
67
- _pry_.repl(ex.exception_call_stack.first)
68
-
69
- ensure
70
- PryStackExplorer.clear_frame_managers(_pry_)
71
- end
72
- end
73
110
 
74
- class Exception
75
- NoContinuation = Class.new(StandardError)
76
-
77
- attr_accessor :continuation
78
- attr_accessor :exception_call_stack
79
- attr_accessor :should_capture
80
-
81
- def continue
82
- raise NoContinuation unless continuation.respond_to?(:call)
83
- continuation.call
84
- end
111
+ # @return [PryExceptionExplorer::Intercept] The object defined earlier by a call to `PryExceptionExplorer.intercept`.
112
+ def intercept_object=(b)
113
+ local_hash[:intercept_object] = b
114
+ end
85
115
 
86
- alias_method :should_capture?, :should_capture
87
- end
116
+ # @return [PryExceptionExplorer::Intercept] The object defined earlier by a call to `PryExceptionExplorer.intercept`.
117
+ def intercept_object
118
+ local_hash[:intercept_object]
119
+ end
88
120
 
89
- class Object
90
- def raise(exception = RuntimeError, string = nil, array = caller)
121
+ # This method invokes the `PryExceptionExplorer.intercept_object`,
122
+ # passing in the exception's frame and the exception object itself.
123
+ # @param [Binding] frame The stack frame where the exception occurred.
124
+ # @param [Exception] ex The exception that was raised.
125
+ # @return [Boolean] Whether the exception should be intercepted.
126
+ def should_intercept_exception?(frame, ex)
127
+ if intercept_object
128
+ intercept_object.call(LazyFrame.new(frame), ex)
129
+ else
130
+ false
131
+ end
132
+ end
91
133
 
92
- if exception.is_a?(String)
93
- string = exception
94
- exception = RuntimeError
134
+ # Prepare the `Pry` instance and associated call-stack when entering
135
+ # into an exception context.
136
+ # @param [Exception] ex The exception.
137
+ # @param [Pry] _pry_ The relevant `Pry` instance.
138
+ # @param [Hash] options The optional configuration parameters.
139
+ # @option options [Boolean] :inline Whether the exception is being
140
+ # entered inline (i.e within the `raise` method itself)
141
+ def setup_exception_context(ex, _pry_, options={})
142
+ _pry_.last_exception = ex
143
+ _pry_.backtrace = ex.backtrace
144
+
145
+ PryStackExplorer.frame_manager(_pry_).user[:exception] = ex
146
+ PryStackExplorer.frame_manager(_pry_).user[:inline_exception] = !!options[:inline]
95
147
  end
96
148
 
97
- ex = exception.exception(string)
98
- ex.set_backtrace(array)
149
+ # Enter the exception context.
150
+ # @param [Exception] ex The exception.
151
+ # @param [Hash] options The optional configuration parameters.
152
+ # @option options [Boolean] :inline Whether the exception is being
153
+ # entered inline (i.e within the `raise` method itself)
154
+ def enter_exception(ex, options={})
155
+ hooks = Pry.config.hooks.dup.add_hook(:before_session, :set_exception_flag) do |_, _, _pry_|
156
+ setup_exception_context(ex, _pry_, options)
157
+ end.add_hook(:before_session, :manage_intercept_recurse) do
158
+ PryExceptionExplorer.intercept_object.disable! if !PryExceptionExplorer.intercept_object.intercept_recurse?
159
+ end.add_hook(:after_session, :manage_intercept_recurse) do
160
+ PryExceptionExplorer.intercept_object.enable! if !PryExceptionExplorer.intercept_object.active?
161
+ end
99
162
 
100
- # revert to normal exception behaviour if EE not enabled.
101
- if !PryExceptionExplorer.enabled?
102
- return super(ex)
163
+ Pry.start self, :call_stack => ex.exception_call_stack, :hooks => hooks
103
164
  end
104
165
 
105
- if PryExceptionExplorer.should_capture_exception?(ex, binding.of_caller(1))
106
- ex.exception_call_stack = binding.callers.tap(&:shift)
107
- ex.should_capture = true
166
+ # Set initial state
167
+ def init
168
+ # disable by default (intercept exceptions inline)
169
+ PryExceptionExplorer.wrap_active = false
108
170
 
109
- if !PryExceptionExplorer.wrap_active?
110
- retval = PryExceptionExplorer.enter_exception_inline(ex)
111
- end
112
- end
171
+ # default is to capture all exceptions
172
+ PryExceptionExplorer.intercept { true }
113
173
 
114
- if retval != PryExceptionExplorer::CONTINUE_INLINE_EXCEPTION
115
- callcc do |cc|
116
- ex.continuation = cc
117
- super(ex)
118
- end
174
+ # disable by default
175
+ PryExceptionExplorer.enabled = false
119
176
  end
120
177
  end
121
178
  end
122
179
 
180
+ # Add a hook to enable EE when invoked via `pry` executable
181
+ Pry.config.hooks.add_hook(:when_started, :try_enable_exception_explorer) do
182
+ if Pry.cli
183
+ PryExceptionExplorer.wrap_active = true
184
+ PryExceptionExplorer.enabled = true
185
+ end
186
+ end
123
187
 
124
- # Let exceptions get caught by Pry REPL loop (i.e dont catch
125
- # immediately at point of 'raise')
126
- PryExceptionExplorer.wrap_active = true
127
-
128
- # default is to capture all exceptions that bubble to the top
129
- PryExceptionExplorer.intercept { true }
130
-
188
+ # Bring in commands
131
189
  Pry.config.commands.import PryExceptionExplorer::Commands
132
190
 
133
- # disable by default
134
- PryExceptionExplorer.enabled = false
191
+ # Set initial state
192
+ PryExceptionExplorer.init
193
+
135
194
 
136
- Pry.config.hooks.add_hook(:when_started, :try_enable_exception_explorer) do
137
- PryExceptionExplorer.enabled = true if Pry.cli
138
- end
@@ -2,14 +2,14 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = "pry-exception_explorer"
5
- s.version = "0.1.1pre5"
5
+ s.version = "0.1.1pre6"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new("> 1.3.1") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["John Mair (banisterfiend)"]
9
- s.date = "2012-01-12"
9
+ s.date = "2012-01-28"
10
10
  s.description = "Enter the context of exceptions"
11
11
  s.email = "jrmair@gmail.com"
12
- s.files = [".gemtest", ".gitignore", ".travis.yml", ".yardopts", "CHANGELOG", "Gemfile", "LICENSE", "README.md", "Rakefile", "examples/example.rb", "lib/pry-exception_explorer.rb", "lib/pry-exception_explorer/cli.rb", "lib/pry-exception_explorer/commands.rb", "lib/pry-exception_explorer/exception_wrap.rb", "lib/pry-exception_explorer/lazy_frame.rb", "lib/pry-exception_explorer/shim_builder.rb", "lib/pry-exception_explorer/version.rb", "pry-exception_explorer.gemspec", "test/helper.rb", "test/test_exceptions_in_pry.rb", "test/test_inline_exceptions.rb", "test/test_wrapped_exceptions.rb"]
12
+ s.files = [".gemtest", ".gitignore", ".travis.yml", ".yardopts", "CHANGELOG", "Gemfile", "LICENSE", "README.md", "Rakefile", "examples/example_inline.rb", "examples/example_wrap.rb", "lib/pry-exception_explorer.rb", "lib/pry-exception_explorer/cli.rb", "lib/pry-exception_explorer/commands.rb", "lib/pry-exception_explorer/core_ext.rb", "lib/pry-exception_explorer/intercept.rb", "lib/pry-exception_explorer/lazy_frame.rb", "lib/pry-exception_explorer/shim_builder.rb", "lib/pry-exception_explorer/version.rb", "pry-exception_explorer.gemspec", "test/helper.rb", "test/test_exceptions_in_pry.rb", "test/test_inline_exceptions.rb", "test/test_wrapped_exceptions.rb"]
13
13
  s.homepage = "https://github.com/banister/pry-exception_explorer"
14
14
  s.require_paths = ["lib"]
15
15
  s.rubygems_version = "1.8.11"
data/test/helper.rb CHANGED
@@ -17,8 +17,6 @@ class OpenStruct
17
17
  end
18
18
  end
19
19
 
20
- EE = PryExceptionExplorer
21
-
22
20
  class Ratty
23
21
  def ratty
24
22
  Weasel.new.weasel
@@ -45,7 +43,7 @@ class << Pry
45
43
  Pry.color = false
46
44
  Pry.pager = false
47
45
  Pry.config.should_load_rc = false
48
- Pry.config.plugins.enabled = false
46
+ Pry.config.should_load_plugins = false
49
47
  Pry.config.history.should_load = false
50
48
  Pry.config.history.should_save = false
51
49
  Pry.config.auto_indent = false
@@ -44,6 +44,36 @@ describe PryExceptionExplorer do
44
44
  mock_pry("Ratty.new.ratty", "enter-exception", "show-stack", "exit").should =~ /toad.*?weasel.*?ratty/m
45
45
  end
46
46
 
47
+ describe "enabled = false" do
48
+ it 'should prevent moving into an exception' do
49
+ old_e = PryExceptionExplorer.enabled
50
+ PryExceptionExplorer.enabled = false
51
+
52
+ mock_pry("Ratty.new.ratty", "enter-exception", "exit-all").should =~ /can't be entered/
53
+
54
+ PryExceptionExplorer.enabled = old_e
55
+ end
56
+ end
57
+
58
+ describe "continue-exception" do
59
+ it 'should continue the exception' do
60
+ o = OpenStruct.new
61
+ def o.test_method
62
+ raise "baby likes to raise an exception"
63
+ self.value = 10
64
+ end
65
+
66
+ redirect_pry_io(InputTester.new("test_method",
67
+ "enter-exception",
68
+ "continue-exception",
69
+ "exit-all")) do
70
+ Pry.start(o)
71
+ end
72
+
73
+ o.value.should == 10
74
+ end
75
+ end
76
+
47
77
  describe "exit-exception" do
48
78
  it 'should display error message when exit-exception used outside of exception context' do
49
79
  mock_pry("exit-exception").should =~ /You are not in an exception!/
@@ -88,6 +118,115 @@ describe PryExceptionExplorer do
88
118
  # Ratty -> Weasel -> Toad (raise is here)
89
119
  O.exception_self.is_a?(Toad).should == true
90
120
  end
121
+
122
+
123
+ describe "_ex_" do
124
+ it "should correctly update _ex_ to reflect exception context" do
125
+ o = Object.new
126
+ class << o
127
+ attr_accessor :first_backtrace, :actual_first_backtrace
128
+ attr_accessor :second_backtrace, :actual_second_backtrace
129
+ end
130
+
131
+
132
+ redirect_pry_io(InputTester.new("raise ArgumentError, 'yo yo'",
133
+ "self.first_backtrace = _ex_.backtrace",
134
+ "enter-exception",
135
+ "self.actual_first_backtrace = _ex_.backtrace",
136
+ "raise RuntimeError, 'bing bong'",
137
+ "self.second_backtrace = _ex_.backtrace",
138
+ "enter-exception",
139
+ "self.actual_second_backtrace = _ex_.backtrace",
140
+ "exit-all", StringIO.new)) do
141
+ Pry.start(o)
142
+ end
143
+
144
+ o.first_backtrace.should == o.actual_first_backtrace
145
+ o.second_backtrace.should == o.actual_second_backtrace
146
+
147
+ # ensure nothing weird going on
148
+ o.first_backtrace.should.not == o.second_backtrace
149
+ end
150
+
151
+ it "should correctly restore _ex_ when exiting out of exceptions" do
152
+ o = Object.new
153
+ class << o
154
+ attr_accessor :first_backtrace, :restored_first_backtrace
155
+ attr_accessor :second_backtrace, :restored_second_backtrace
156
+ end
157
+
158
+ redirect_pry_io(InputTester.new("raise ArgumentError, 'yo yo'",
159
+ "enter-exception",
160
+ "self.first_backtrace = _ex_.backtrace",
161
+ "raise RuntimeError, 'bing bong'",
162
+ "enter-exception",
163
+ "self.second_backtrace = _ex_.backtrace",
164
+ "exit-exception",
165
+ "exit-exception",
166
+ "self.restored_first_backtrace = _ex_.backtrace",
167
+ "exit-all", StringIO.new)) do
168
+ Pry.start(o)
169
+ end
170
+
171
+ # just ensure nothing weird is happening (probably unnecessary)
172
+ o.first_backtrace.should.not == o.second_backtrace
173
+
174
+ o.first_backtrace.should == o.restored_first_backtrace
175
+ end
176
+ end
177
+
178
+ describe "_pry_.backtrace" do
179
+ it "should correctly update _pry_.backtrace to reflect exception context" do
180
+ o = Object.new
181
+ class << o
182
+ attr_accessor :first_backtrace, :ex_first_backtrace
183
+ attr_accessor :second_backtrace, :ex_second_backtrace
184
+ end
185
+
186
+ redirect_pry_io(InputTester.new("raise ArgumentError, 'yo yo'",
187
+ "enter-exception",
188
+ "self.first_backtrace = _pry_.backtrace",
189
+ "self.ex_first_backtrace = _ex_.backtrace",
190
+ "raise RuntimeError, 'bing bong'",
191
+ "enter-exception",
192
+ "self.second_backtrace = _pry_.backtrace",
193
+ "self.ex_second_backtrace = _ex_.backtrace",
194
+ "exit-all", StringIO.new)) do
195
+ Pry.start(o)
196
+ end
197
+
198
+ o.first_backtrace.should == o.ex_first_backtrace
199
+ o.second_backtrace.should == o.ex_second_backtrace
200
+
201
+ o.first_backtrace.should.not == o.second_backtrace
202
+ end
203
+
204
+ it "should correctly restore _pry_.backtrace when exiting out of exceptions" do
205
+ o = Object.new
206
+ class << o
207
+ attr_accessor :first_backtrace, :restored_first_backtrace
208
+ attr_accessor :second_backtrace, :restored_second_backtrace
209
+ end
210
+
211
+ redirect_pry_io(InputTester.new("raise ArgumentError, 'yo yo'",
212
+ "enter-exception",
213
+ "self.first_backtrace = _pry_.backtrace",
214
+ "raise RuntimeError, 'bing bong'",
215
+ "enter-exception",
216
+ "self.second_backtrace = _pry_.backtrace",
217
+ "exit-exception",
218
+ "self.restored_first_backtrace = _pry_.backtrace",
219
+ "exit-all", StringIO.new)) do
220
+
221
+ Pry.start(o)
222
+ end
223
+
224
+ o.first_backtrace.should.not == o.second_backtrace
225
+ o.first_backtrace.should == o.restored_first_backtrace
226
+ end
227
+ end
228
+
229
+
91
230
  end
92
231
  end
93
232
  end
@@ -7,7 +7,7 @@ O = OpenStruct.new
7
7
  prev_wrap_state = PryExceptionExplorer.wrap_active
8
8
  PryExceptionExplorer.wrap_active = false
9
9
 
10
- prev_intercept_state = PryExceptionExplorer.intercept_block
10
+ prev_intercept_state = PryExceptionExplorer.intercept_object
11
11
 
12
12
  describe PryExceptionExplorer do
13
13
 
@@ -19,14 +19,102 @@ describe PryExceptionExplorer do
19
19
  # started) that this is registered by setting state on `O`
20
20
  Pry.config.input = StringIO.new("O.exception_intercepted = true\ncontinue-exception")
21
21
  Pry.config.output = StringIO.new
22
+ Pry.config.hooks.add_hook(:when_started, :save_caller_bindings, WhenStartedHook)
23
+ Pry.config.hooks.add_hook(:after_session, :delete_frame_manager, AfterSessionHook)
22
24
  end
23
25
 
24
26
  after do
25
27
  Pry.config.input.rewind
28
+ Pry.config.hooks.delete_hook(:when_started, :save_caller_bindings)
29
+ Pry.config.hooks.delete_hook(:after_session, :delete_frame_manager)
26
30
  O.clear
27
31
  end
28
32
 
33
+ describe "enabled = false" do
34
+ it 'should prevent interception of an exception' do
35
+ old_e = PryExceptionExplorer.enabled
36
+ PryExceptionExplorer.enabled = false
37
+
38
+ my_error = Class.new(StandardError)
39
+ EE.intercept(my_error)
40
+
41
+ begin
42
+ raise my_error
43
+ rescue => ex
44
+ exception = ex
45
+ end
46
+
47
+ exception.is_a?(my_error).should == true
48
+
49
+ PryExceptionExplorer.enabled = old_e
50
+ end
51
+ end
52
+
29
53
  describe "PryExceptionExplorer.intercept" do
54
+ it 'should be a no-op when intercept called with no parameters' do
55
+ b = proc {}
56
+ old = EE.intercept_object
57
+ EE.intercept &b
58
+ EE.intercept
59
+ EE.intercept_object.block.should == b
60
+ EE.intercept_object = old
61
+ end
62
+
63
+ describe "intercept_recurse" do
64
+ it 'should NOT allow recursive (in-session) interceptions by default' do
65
+ EE.intercept { |frame, ex| frame.klass == Toad }
66
+
67
+ redirect_pry_io(InputTester.new("O.before_self = self",
68
+ "Ratty.new.ratty",
69
+ "O.after_self = self",
70
+ "continue-exception",
71
+ "continue-exception")) do
72
+ Ratty.new.ratty
73
+ end
74
+
75
+ O.before_self.should == O.after_self
76
+ end
77
+
78
+ it 'should allow recursive (in-session) interceptions when :intercept_recurse => true' do
79
+ EE.intercept(:intercept_recurse => true) { |frame, ex| frame.klass == Toad }
80
+
81
+ redirect_pry_io(InputTester.new("O.before_self = self",
82
+ "Ratty.new.ratty",
83
+ "O.after_self = self",
84
+ "continue-exception",
85
+ "continue-exception")) do
86
+ Ratty.new.ratty
87
+ end
88
+
89
+ O.before_self.should.not == O.after_self
90
+ end
91
+
92
+ end
93
+
94
+ describe "skip" do
95
+ it 'should skip first frame with :skip => 1' do
96
+ EE.intercept(:skip => 1) { |frame, ex| frame.klass == Toad }
97
+
98
+ redirect_pry_io(InputTester.new("O.method_name = __method__",
99
+ "continue-exception")) do
100
+ Ratty.new.ratty
101
+ end
102
+
103
+ O.method_name.should == :weasel
104
+ end
105
+
106
+ it 'should skip first two framed with :skip => 2' do
107
+ EE.intercept(:skip => 2) { |frame, ex| frame.klass == Toad }
108
+
109
+ redirect_pry_io(InputTester.new("O.method_name = __method__",
110
+ "continue-exception")) do
111
+ Ratty.new.ratty
112
+ end
113
+
114
+ O.method_name.should == :ratty
115
+ end
116
+ end
117
+
30
118
  describe "special case exception-only syntax" do
31
119
 
32
120
  describe "single exception" do
@@ -101,7 +189,12 @@ describe PryExceptionExplorer do
101
189
  describe "second frame" do
102
190
  it "should intercept exception based on second frame's method name" do
103
191
  EE.intercept { |frame, ex| frame.prev.klass == Weasel }
104
- Ratty.new.ratty
192
+ begin
193
+ Ratty.new.ratty
194
+ rescue => ex
195
+ Pry.new(:input => Readline, :output =>
196
+ $stdout).repl(binding)
197
+ end
105
198
  O.exception_intercepted.should == true
106
199
  end
107
200
 
@@ -251,12 +344,29 @@ describe PryExceptionExplorer do
251
344
 
252
345
  end
253
346
 
347
+ describe "exit-exception" do
348
+ it 'should exit session and raise exception' do
349
+ my_error = Class.new(StandardError)
350
+ EE.intercept(my_error)
351
+
352
+ begin
353
+ redirect_pry_io(InputTester.new("exit-exception")) do
354
+ raise my_error
355
+ end
356
+ rescue => ex
357
+ exception = ex
358
+ end
359
+
360
+ exception.is_a?(my_error).should == true
361
+ end
362
+ end
363
+
254
364
  end
255
365
 
256
366
  end
257
367
 
258
368
  # restore to default
259
369
  PryExceptionExplorer.wrap_active = prev_wrap_state
260
- PryExceptionExplorer.intercept &prev_intercept_state
370
+ PryExceptionExplorer.intercept_object = prev_intercept_state
261
371
 
262
372
  Object.send(:remove_const, :O)
@@ -1,19 +1,15 @@
1
1
  require 'helper'
2
2
 
3
- require 'pry-exception_explorer/exception_wrap'
4
-
5
3
  CaughtException = Class.new(StandardError)
6
4
  UncaughtException = Class.new(StandardError)
7
5
 
8
-
9
6
  describe PryExceptionExplorer do
10
7
 
11
-
12
8
  before do
13
9
  Pry.config.input = StringIO.new("exit :caught\n")
14
10
  Pry.config.output = StringIO.new
15
- Pry.config.hooks.add_hook(:when_started, :save_caller_bindings, &WhenStartedHook)
16
- Pry.config.hooks.add_hook(:after_session, :delete_frame_manager, &AfterSessionHook)
11
+ Pry.config.hooks.add_hook(:when_started, :save_caller_bindings, WhenStartedHook)
12
+ Pry.config.hooks.add_hook(:after_session, :delete_frame_manager, AfterSessionHook)
17
13
  end
18
14
 
19
15
  after do
@@ -23,6 +19,56 @@ describe PryExceptionExplorer do
23
19
 
24
20
  describe "PryExceptionExplorer.wrap" do
25
21
 
22
+ describe "_ex_" do
23
+ it 'should correctly set _ex_ inside session (set to raised exception)' do
24
+ ex = Class.new(StandardError)
25
+ o = Object.new
26
+ class << o; attr_accessor :ex; self; end.class_eval { define_method(:raze) { raise ex } }
27
+
28
+ PryExceptionExplorer.intercept { true }
29
+
30
+ redirect_pry_io(InputTester.new("@ex = _ex_", "exit-all")) do
31
+ PryExceptionExplorer.wrap do
32
+ o.raze
33
+ end
34
+ end
35
+
36
+ o.ex.is_a?(ex).should == true
37
+ end
38
+ end
39
+
40
+ describe "_pry_.backtrace" do
41
+ it 'should correctly set _pry_ inside session to backtrace of raised exception' do
42
+ ex = Class.new(StandardError)
43
+ o = Object.new
44
+ class << o; attr_accessor :ex, :pry_bt; self; end.class_eval { define_method(:raze) { raise ex } }
45
+
46
+ PryExceptionExplorer.intercept { true }
47
+
48
+ redirect_pry_io(InputTester.new("@ex = _ex_", "@pry_bt = _pry_.backtrace", "exit-all")) do
49
+ PryExceptionExplorer.wrap do
50
+ o.raze
51
+ end
52
+ end
53
+
54
+ o.pry_bt.should == o.ex.backtrace
55
+ end
56
+ end
57
+
58
+ describe "enabled = false" do
59
+ it 'should have no effect for wrap block (which sets enabled=true internally)' do
60
+ old_e = PryExceptionExplorer.enabled
61
+ PryExceptionExplorer.enabled = false
62
+
63
+ PryExceptionExplorer.wrap do
64
+ raise CaughtException, "catch me if u can"
65
+ end.should == :caught
66
+
67
+ PryExceptionExplorer.enabled.should == false
68
+ PryExceptionExplorer.enabled = old_e
69
+ end
70
+ end
71
+
26
72
  # use of exit-exception inside a wrapped exception is weird
27
73
  # (because exit-exception is really designed for pry exceptions)
28
74
  # but when we do receive one, we should exit out of pry
metadata CHANGED
@@ -1,59 +1,55 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: pry-exception_explorer
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1pre7
4
5
  prerelease: 5
5
- version: 0.1.1pre5
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - John Mair (banisterfiend)
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2012-01-12 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2012-01-28 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: pry-stack_explorer
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &70111600095300 !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
24
22
  type: :runtime
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: bacon
28
23
  prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *70111600095300
25
+ - !ruby/object:Gem::Dependency
26
+ name: bacon
27
+ requirement: &70111600094500 !ruby/object:Gem::Requirement
30
28
  none: false
31
- requirements:
29
+ requirements:
32
30
  - - ~>
33
- - !ruby/object:Gem::Version
31
+ - !ruby/object:Gem::Version
34
32
  version: 1.1.0
35
33
  type: :development
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
38
- name: rake
39
34
  prerelease: false
40
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *70111600094500
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: &70111600093540 !ruby/object:Gem::Requirement
41
39
  none: false
42
- requirements:
40
+ requirements:
43
41
  - - ~>
44
- - !ruby/object:Gem::Version
45
- version: "0.9"
42
+ - !ruby/object:Gem::Version
43
+ version: '0.9'
46
44
  type: :development
47
- version_requirements: *id003
45
+ prerelease: false
46
+ version_requirements: *70111600093540
48
47
  description: Enter the context of exceptions
49
48
  email: jrmair@gmail.com
50
49
  executables: []
51
-
52
50
  extensions: []
53
-
54
51
  extra_rdoc_files: []
55
-
56
- files:
52
+ files:
57
53
  - .gemtest
58
54
  - .gitignore
59
55
  - .travis.yml
@@ -63,11 +59,13 @@ files:
63
59
  - LICENSE
64
60
  - README.md
65
61
  - Rakefile
66
- - examples/example.rb
62
+ - examples/example_inline.rb
63
+ - examples/example_wrap.rb
67
64
  - lib/pry-exception_explorer.rb
68
65
  - lib/pry-exception_explorer/cli.rb
69
66
  - lib/pry-exception_explorer/commands.rb
70
- - lib/pry-exception_explorer/exception_wrap.rb
67
+ - lib/pry-exception_explorer/core_ext.rb
68
+ - lib/pry-exception_explorer/intercept.rb
71
69
  - lib/pry-exception_explorer/lazy_frame.rb
72
70
  - lib/pry-exception_explorer/shim_builder.rb
73
71
  - lib/pry-exception_explorer/version.rb
@@ -78,32 +76,29 @@ files:
78
76
  - test/test_wrapped_exceptions.rb
79
77
  homepage: https://github.com/banister/pry-exception_explorer
80
78
  licenses: []
81
-
82
79
  post_install_message:
83
80
  rdoc_options: []
84
-
85
- require_paths:
81
+ require_paths:
86
82
  - lib
87
- required_ruby_version: !ruby/object:Gem::Requirement
83
+ required_ruby_version: !ruby/object:Gem::Requirement
88
84
  none: false
89
- requirements:
90
- - - ">="
91
- - !ruby/object:Gem::Version
92
- version: "0"
93
- required_rubygems_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ! '>='
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
90
  none: false
95
- requirements:
96
- - - ">"
97
- - !ruby/object:Gem::Version
91
+ requirements:
92
+ - - ! '>'
93
+ - !ruby/object:Gem::Version
98
94
  version: 1.3.1
99
95
  requirements: []
100
-
101
96
  rubyforge_project:
102
97
  rubygems_version: 1.8.11
103
98
  signing_key:
104
99
  specification_version: 3
105
100
  summary: Enter the context of exceptions
106
- test_files:
101
+ test_files:
107
102
  - test/helper.rb
108
103
  - test/test_exceptions_in_pry.rb
109
104
  - test/test_inline_exceptions.rb
@@ -1,33 +0,0 @@
1
- require 'pry-exception_explorer'
2
-
3
- #Pry.config.hooks.delete_hook(:when_started, :save_caller_bindings)
4
-
5
- # default is to capture all exceptions that bubble to the top
6
- PryExceptionExplorer.intercept { true }
7
-
8
- module PryExceptionExplorer
9
-
10
- def self.wrap
11
- old_enabled, old_wrap_active = enabled, wrap_active
12
- self.enabled = true
13
- self.wrap_active = true
14
- yield
15
- rescue Exception => ex
16
- if ex.should_capture?
17
- hooks = Pry.config.hooks.dup.add_hook(:before_session, :set_exception_flag) do |_, _, _pry_|
18
- PryStackExplorer.frame_manager(_pry_).user[:exception] = ex
19
-
20
- _pry_.last_exception = ex
21
- _pry_.backtrace = ex.backtrace
22
- end
23
-
24
- Pry.start self, :call_stack => ex.exception_call_stack, :hooks => hooks
25
- else
26
- raise ex
27
- end
28
- ensure
29
- self.enabled = old_enabled
30
- self.wrap_active = old_wrap_active
31
- end
32
- end
33
-