Module: PryExceptionExplorer
- Defined in:
- lib/pry-exception_explorer/core_ext.rb,
lib/pry-exception_explorer.rb,
lib/pry-exception_explorer/version.rb,
lib/pry-exception_explorer/commands.rb,
lib/pry-exception_explorer/intercept.rb,
lib/pry-exception_explorer/lazy_frame.rb
Overview
PryExceptionExplorer
monkey-patches to Object
Defined Under Namespace
Modules: CoreExtensions, ExceptionHelpers Classes: Intercept, LazyFrame
Constant Summary collapse
- CONTINUE_INLINE_EXCEPTION =
special constant
Object.new
- VERSION =
"0.2.3"
- Commands =
Pry::CommandSet.new do create_command "enter-exception", "Enter the context of the last exception" do include PryExceptionExplorer::ExceptionHelpers <<-BANNER Usage: enter-exception Enter the context of the last exception BANNER def process ex = extract_exception if enterable_exception?(ex) PryStackExplorer.create_and_push_frame_manager(ex.exception_call_stack, _pry_) PryExceptionExplorer.setup_exception_context(ex, _pry_) # have to use _pry_.run_command instead of 'run' here as # 'run' works on the current target which hasnt been updated # yet, whereas _pry_.run_command operates on the newly # updated target (the context of the exception) _pry_.run_command "cat --ex 0" elsif ex raise Pry::CommandError, "Exception can't be entered! (perhaps an internal exception)" else raise Pry::CommandError, "No exception to enter!" end end def extract_exception if !arg_string.empty? ex = target.eval(arg_string) raise if !ex.is_a?(Exception) ex else last_exception end rescue raise Pry::CommandError, "Parameter must be a valid exception object." end end create_command "exit-exception", "Leave the context of the current exception." do include ExceptionHelpers <<-BANNER Usage: exit-exception Exit active exception and return to containing context. BANNER def process if !in_exception? raise Pry::CommandError, "You are not in an exception!" elsif !prior_context_exists? run "exit-all" else popped_fm = PryStackExplorer.pop_frame_manager(_pry_) _pry_.last_exception = popped_fm.user[:exception] end end end create_command "continue-exception", "Attempt to continue the current exception." do include ExceptionHelpers <<-BANNER Usage: continue-exception Attempt to continue the current exception. BANNER def process if internal_exception? raise Pry::CommandError, "Internal exceptions (C-level exceptions) cannot be continued!" elsif inline_exception? PryStackExplorer.pop_frame_manager(_pry_) run "exit-all PryExceptionExplorer::CONTINUE_INLINE_EXCEPTION" elsif normal_exception? popped_fm = PryStackExplorer.pop_frame_manager(_pry_) popped_fm.user[:exception].continue else raise Pry::CommandError, "No exception to continue!" end end end end
Class Attribute Summary collapse
-
.inline ⇒ Boolean
(also: inline?)
Whether exceptions are to be intercepted inline (at the raise site).
-
.old_inline_state ⇒ Boolean
Holds the previous value of
EE.inline
. -
.post_mortem ⇒ Boolean
(also: post_mortem?)
Whether exceptions are to be auto-rescued if they would terminate the program.
Class Method Summary collapse
-
.amend_exception_call_stack!(ex) ⇒ Object
Amends (destructively) an exception call stack according to the info in
PryExceptionExplorer.intercept_object
, specificallyPryExceptionExplorer::Intercept#skip_until_block
andPryExceptionExplorer::Intercept#skip_while_block
. -
.disable! ⇒ Boolean
Disable Exception Explorer.
-
.enable! ⇒ Boolean
Enable Exception Explorer.
-
.enabled ⇒ Boolean
(also: enabled?)
Whether Exception Explorer is enabled.
- .enabled=(v) ⇒ Object
-
.enter_exception(ex, options = {}) ⇒ Object
Enter the exception context.
-
.init ⇒ Object
Set initial state.
-
.inline! ⇒ Object
Ensure exceptions are intercepted at the raise site, and enable EE.
-
.intercept(*exceptions) {|lazy_frame, exception| ... } ⇒ Object
This method allows the user to assert the situations where an exception interception occurs.
-
.intercept_object ⇒ PryExceptionExplorer::Intercept
The object defined earlier by a call to
PryExceptionExplorer.intercept
. -
.intercept_object=(b) ⇒ PryExceptionExplorer::Intercept
The object defined earlier by a call to
PryExceptionExplorer.intercept
. -
.local_hash ⇒ Hash
A local hash.
-
.post_mortem! ⇒ Object
Ensure exceptions are intercepted if they would terminate the program, and enable EE.
-
.setup_exception_context(ex, _pry_, options = {}) ⇒ Object
Prepare the
Pry
instance and associated call-stack when entering into an exception context. -
.should_intercept_exception?(frame, ex) ⇒ Boolean
This method invokes the
PryExceptionExplorer.intercept_object
, passing in the exception's frame and the exception object itself. -
.wrap { ... } ⇒ Object
Wrap the provided block - intercepting all exceptions that bubble out, provided they satisfy the assertion in
PryExceptionExplorer.intercept
.
Class Attribute Details
.inline ⇒ Boolean Also known as: inline?
Returns Whether exceptions are to be intercepted inline (at the raise site).
28 29 30 |
# File 'lib/pry-exception_explorer.rb', line 28 def inline @inline end |
.old_inline_state ⇒ Boolean
Returns Holds the previous value of EE.inline
.
35 36 37 |
# File 'lib/pry-exception_explorer.rb', line 35 def old_inline_state @old_inline_state end |
.post_mortem ⇒ Boolean Also known as: post_mortem?
Returns Whether exceptions are to be auto-rescued if they would terminate the program.
32 33 34 |
# File 'lib/pry-exception_explorer.rb', line 32 def post_mortem @post_mortem end |
Class Method Details
.amend_exception_call_stack!(ex) ⇒ Object
Amends (destructively) an exception call stack according to the info in
PryExceptionExplorer.intercept_object
, specifically
PryExceptionExplorer::Intercept#skip_until_block
and PryExceptionExplorer::Intercept#skip_while_block
.
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 |
# File 'lib/pry-exception_explorer.rb', line 167 def amend_exception_call_stack!(ex) call_stack = ex.exception_call_stack # skip_until if intercept_object.skip_until_block idx = call_stack.each_with_index.find_index do |frame, idx| intercept_object.skip_until_block.call(LazyFrame.new(frame, idx, call_stack)) end call_stack = call_stack.drop(idx) if idx # skip_while elsif intercept_object.skip_while_block idx = call_stack.each_with_index.find_index do |frame, idx| intercept_object.skip_while_block.call(LazyFrame.new(frame, idx, call_stack)) == false end call_stack = call_stack.drop(idx) if idx end ex.exception_call_stack = call_stack end |
.disable! ⇒ Boolean
Disable Exception Explorer.
65 66 67 |
# File 'lib/pry-exception_explorer.rb', line 65 def disable! self.enabled = false end |
.enable! ⇒ Boolean
Enable Exception Explorer.
59 60 61 |
# File 'lib/pry-exception_explorer.rb', line 59 def enable! self.enabled = true end |
.enabled ⇒ Boolean Also known as: enabled?
Returns Whether Exception Explorer is enabled.
75 76 77 |
# File 'lib/pry-exception_explorer.rb', line 75 def enabled !!local_hash[:enabled] end |
.enabled=(v) ⇒ Object
70 71 72 |
# File 'lib/pry-exception_explorer.rb', line 70 def enabled=(v) local_hash[:enabled] = v end |
.enter_exception(ex, options = {}) ⇒ Object
Enter the exception context.
208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 |
# File 'lib/pry-exception_explorer.rb', line 208 def enter_exception(ex, ={}) hooks = Pry.config.hooks.dup hooks.delete_hook(:before_session, :default) hooks.add_hook(:before_session, :set_exception_flag) do |_, _, _pry_| setup_exception_context(ex, _pry_, ) end.add_hook(:before_session, :manage_intercept_recurse) do PryExceptionExplorer.intercept_object.disable! if PryExceptionExplorer.inline? && !PryExceptionExplorer.intercept_object.intercept_recurse? end.add_hook(:after_session, :manage_intercept_recurse) do PryExceptionExplorer.intercept_object.enable! if !PryExceptionExplorer.intercept_object.active? end.add_hook(:before_session, :display_exception) do |_, _, _pry_| _pry_.run_command "cat --ex" end Pry.start binding, :call_stack => ex.exception_call_stack, :hooks => hooks end |
.init ⇒ Object
Set initial state
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
# File 'lib/pry-exception_explorer.rb', line 225 def init # disable EE by default PryExceptionExplorer.enabled = false # auto-start sessions on exceptions that would kill the program PryExceptionExplorer.post_mortem = false # default is to capture all exceptions PryExceptionExplorer.intercept(Exception) # disable inline sessions by defulat PryExceptionExplorer.inline = false at_exit do ex = $! next if !PryExceptionExplorer.post_mortem? || !ex if ex.should_intercept? enter_exception(ex) else raise ex end end end |
.inline! ⇒ Object
Ensure exceptions are intercepted at the raise site, and enable EE.
38 39 40 41 |
# File 'lib/pry-exception_explorer.rb', line 38 def inline! self.enabled = true self.inline = true end |
.intercept(*exceptions) {|lazy_frame, exception| ... } ⇒ Object
This method allows the user to assert the situations where an exception interception occurs. This method can be invoked in two ways. The general form takes a block, the block is passed both the frame where the exception was raised, and the exception itself. The user then creates an assertion (a stack-assertion) based on these attributes. If the assertion is later satisfied by a raised exception, that exception will be intercepted. In the second form, the method simply takes an exception class, or a number of exception classes. If one of these exceptions is raised, it will be intercepted.
124 125 126 127 128 129 130 131 132 |
# File 'lib/pry-exception_explorer.rb', line 124 def intercept(*exceptions, &block) return if exceptions.empty? && block.nil? if !exceptions.empty? block = proc { |_, ex| exceptions.any? { |v| v === ex } } end local_hash[:intercept_object] = Intercept.new(block) end |
.intercept_object ⇒ PryExceptionExplorer::Intercept
Returns The object defined earlier by a call to PryExceptionExplorer.intercept
.
140 141 142 |
# File 'lib/pry-exception_explorer.rb', line 140 def intercept_object local_hash[:intercept_object] end |
.intercept_object=(b) ⇒ PryExceptionExplorer::Intercept
Returns The object defined earlier by a call to PryExceptionExplorer.intercept
.
135 136 137 |
# File 'lib/pry-exception_explorer.rb', line 135 def intercept_object=(b) local_hash[:intercept_object] = b end |
.local_hash ⇒ Hash
Returns A local hash.
53 54 55 |
# File 'lib/pry-exception_explorer.rb', line 53 def local_hash @hash ||= {} end |
.post_mortem! ⇒ Object
Ensure exceptions are intercepted if they would terminate the program, and enable EE.
44 45 46 47 |
# File 'lib/pry-exception_explorer.rb', line 44 def post_mortem! self.enabled = true self.post_mortem = true end |
.setup_exception_context(ex, _pry_, options = {}) ⇒ Object
Prepare the Pry
instance and associated call-stack when entering
into an exception context.
195 196 197 198 199 200 201 |
# File 'lib/pry-exception_explorer.rb', line 195 def setup_exception_context(ex, _pry_, ={}) _pry_.last_exception = ex _pry_.backtrace = (ex.backtrace || []) PryStackExplorer.frame_manager(_pry_).user[:exception] = ex PryStackExplorer.frame_manager(_pry_).user[:inline_exception] = !![:inline] end |
.should_intercept_exception?(frame, ex) ⇒ Boolean
This method invokes the PryExceptionExplorer.intercept_object
,
passing in the exception's frame and the exception object itself.
149 150 151 152 153 154 155 156 157 158 159 160 161 |
# File 'lib/pry-exception_explorer.rb', line 149 def should_intercept_exception?(frame, ex) # special case, or we go into infinite loop. CodeRay uses # exceptions for flow control :/ if defined?(CodeRay::Encoders) && frame.eval('self') == CodeRay::Encoders false # normal case elsif intercept_object intercept_object.call(LazyFrame.new(frame), ex) else false end end |
.wrap { ... } ⇒ Object
Wrap the provided block - intercepting all exceptions
that bubble out, provided they satisfy the
assertion in PryExceptionExplorer.intercept
.
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 |
# File 'lib/pry-exception_explorer.rb', line 85 def wrap old_enabled, old_inline = enabled, inline self.inline = false self.enabled = true yield rescue Exception => ex if ex.should_intercept? enter_exception(ex) else raise ex end ensure self.enabled = old_enabled self.inline = old_inline end |