pry-exception_explorer 0.1.1pre5 → 0.1.1pre7

Sign up to get free protection for your applications and to get access to all the features.
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
-