config: factor out exception_handler hook to a separate class

This commit is contained in:
Kyrylo Silin 2019-04-14 19:14:16 +03:00
parent 9306e69bda
commit 95d3a74e9b
4 changed files with 107 additions and 22 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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