summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKyrylo Silin <silin@kyrylo.org>2019-04-14 19:14:16 +0300
committerKyrylo Silin <silin@kyrylo.org>2019-04-15 00:08:24 +0300
commit95d3a74e9b22c44ea48ce870171640f0471db09b (patch)
tree5dae8107c718d96c11a0340d7b3c55a550fab0d0
parent9306e69bda2f44985944cbc162db4723cb8a0ec4 (diff)
downloadpry-exception-handler-refactoring.tar.gz
config: factor out exception_handler hook to a separate classexception-handler-refactoring
-rw-r--r--lib/pry.rb1
-rw-r--r--lib/pry/config.rb23
-rw-r--r--lib/pry/exception_handler.rb41
-rw-r--r--spec/exception_handler_spec.rb64
4 files changed, 107 insertions, 22 deletions
diff --git a/lib/pry.rb b/lib/pry.rb
index 4596ed2c..1c972b7d 100644
--- a/lib/pry.rb
+++ b/lib/pry.rb
@@ -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