diff options
author | Kyrylo Silin <silin@kyrylo.org> | 2019-04-14 19:14:16 +0300 |
---|---|---|
committer | Kyrylo Silin <silin@kyrylo.org> | 2019-04-15 00:08:24 +0300 |
commit | 95d3a74e9b22c44ea48ce870171640f0471db09b (patch) | |
tree | 5dae8107c718d96c11a0340d7b3c55a550fab0d0 | |
parent | 9306e69bda2f44985944cbc162db4723cb8a0ec4 (diff) | |
download | pry-exception-handler-refactoring.tar.gz |
config: factor out exception_handler hook to a separate classexception-handler-refactoring
-rw-r--r-- | lib/pry.rb | 1 | ||||
-rw-r--r-- | lib/pry/config.rb | 23 | ||||
-rw-r--r-- | lib/pry/exception_handler.rb | 41 | ||||
-rw-r--r-- | spec/exception_handler_spec.rb | 64 |
4 files changed, 107 insertions, 22 deletions
@@ -21,6 +21,7 @@ require 'pry/syntax_highlighter' require 'pry/editor' require 'pry/history' require 'pry/color_printer' +require 'pry/exception_handler' Pry::Commands = Pry::CommandSet.new unless defined?(Pry::Commands) diff --git a/lib/pry/config.rb b/lib/pry/config.rb index 6b7e4fcf..6fcf9920 100644 --- a/lib/pry/config.rb +++ b/lib/pry/config.rb @@ -7,7 +7,6 @@ class Pry # @return [Pry::Config] # An object who implements the default configuration for all # Pry sessions. - # rubocop:disable Metrics/AbcSize def self.defaults defaults = from_hash( input: Pry.lazy { lazy_readline(defaults) }, @@ -18,26 +17,7 @@ class Pry prompt_safe_contexts: Pry::Prompt::SAFE_CONTEXTS, print: Pry::ColorPrinter.method(:default), quiet: false, - - # Will only show the first line of the backtrace - exception_handler: proc do |output, exception, _| - if exception.is_a?(UserError) && exception.is_a?(SyntaxError) - output.puts "SyntaxError: #{exception.message.sub(/.*syntax error, */m, '')}" - else - output.puts "#{exception.class}: #{exception.message}" - output.puts "from #{exception.backtrace.first}" - - if exception.respond_to? :cause - cause = exception.cause - while cause - output.puts "Caused by #{cause.class}: #{cause}\n" - output.puts "from #{cause.backtrace.first}" - cause = cause.cause - end - end - end - end, - + exception_handler: Pry::ExceptionHandler.method(:handle_exception), unrescued_exceptions: [ ::SystemExit, ::SignalException, Pry::TooSafeException ], @@ -113,7 +93,6 @@ class Pry exec_string: "" ) end - # rubocop:enable Metrics/AbcSize def self.shortcuts Convenience::SHORTCUTS diff --git a/lib/pry/exception_handler.rb b/lib/pry/exception_handler.rb new file mode 100644 index 00000000..9b50fe80 --- /dev/null +++ b/lib/pry/exception_handler.rb @@ -0,0 +1,41 @@ +class Pry + # @api private + # @since ?.?.? + module ExceptionHandler + class << self + # Will only show the first line of the backtrace. + def handle_exception(output, exception, _pry_instance) + if exception.is_a?(UserError) && exception.is_a?(SyntaxError) + output.puts "SyntaxError: #{exception.message.sub(/.*syntax error, */m, '')}" + else + output.puts standard_error_text_for(exception) + end + end + + private + + def standard_error_text_for(exception) + text = exception_text(exception) + return text unless exception.respond_to?(:cause) + + cause = exception.cause + while cause + text += cause_text(cause) + cause = cause.cause + end + + text + end + + def exception_text(exception) + "#{exception.class}: #{exception.message}\n" \ + "from #{exception.backtrace.first}\n" + end + + def cause_text(cause) + "Caused by #{cause.class}: #{cause}\n" \ + "from #{cause.backtrace.first}\n" + end + end + end +end diff --git a/spec/exception_handler_spec.rb b/spec/exception_handler_spec.rb new file mode 100644 index 00000000..62138153 --- /dev/null +++ b/spec/exception_handler_spec.rb @@ -0,0 +1,64 @@ +RSpec.describe Pry::ExceptionHandler do + describe ".handle_exception" do + let(:output) { StringIO.new } + let(:pry_instance) { Pry.new } + + context "when exception is a UserError and a SyntaxError" do + let(:exception) do + SyntaxError.new('cool syntax error, dude').extend(Pry::UserError) + end + + it "prints the syntax error with customized message" do + described_class.handle_exception(output, exception, pry_instance) + expect(output.string).to start_with("SyntaxError: dude\n") + end + end + + context "when exception is a standard error" do + let(:exception) do + error = StandardError.new('oops') + error.set_backtrace(["/bin/pry:23:in `<main>'"]) + error + end + + it "prints standard error message" do + described_class.handle_exception(output, exception, pry_instance) + expect(output.string) + .to eq("StandardError: oops\nfrom /bin/pry:23:in `<main>'\n") + end + end + + context "when exception is a nested standard error" do + let(:exception) do + error = nil + begin + begin + raise 'nested oops' + rescue # rubocop:disable Style/RescueStandardError + raise 'outer oops' + end + rescue StandardError => outer_error + error = outer_error + end + + error + end + + before do + if RUBY_VERSION.start_with?('1.9', '2.0') + skip("Ruby #{RUBY_VERSION} doesn't support nested exceptions") + end + end + + it "prints standard error message" do + described_class.handle_exception(output, exception, pry_instance) + expect(output.string).to match( + /RuntimeError:\souter\soops\n + from\s.+\n + Caused\sby\sRuntimeError:\snested\soops\n + from.+/x + ) + end + end + end +end |