Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
##
# Monkey-patch IRB in Ruby 2.3.4 to address the following Ruby issue:
#
# * https://bugs.ruby-lang.org/issues/14685
# IRB doesn't print exception cause
##
# After loading this file, one should observe in an IRB session:
#
# * Printed exceptions contain the `#cause`
# In fact, this should work for a chain of `#cause` relationships
##
# This file is a copied and modified version of the method `IRB::Irb#eval_input`
# from Ruby version 2.3.4.
#
# It would be great to turn this into a PR to Ruby. To do so, one would first
# need to:
# a. Make an updated version based on the latest trunk Ruby
# b. Add tests
#
# To make a later PR easier, the modified lines are called out with comments
# below.
#
# Author: "Marc Siegel" <msiegel@panoramaed.com>
##
require "irb"
module IRB
class Irb
# Monkey-patch this large method by copying it and modifying.
def eval_input
@scanner.set_prompt do
|ltype, indent, continue, line_no|
if ltype
f = @context.prompt_s
elsif continue
f = @context.prompt_c
elsif indent > 0
f = @context.prompt_n
else
f = @context.prompt_i
end
f = "" unless f
if @context.prompting?
@context.io.prompt = p = prompt(f, ltype, indent, line_no)
else
@context.io.prompt = p = ""
end
if @context.auto_indent_mode
unless ltype
ind = prompt(@context.prompt_i, ltype, indent, line_no)[/.*\z/].size +
indent * 2 - p.size
ind += 2 if continue
@context.io.prompt = p + " " * ind if ind > 0
end
end
end
@scanner.set_input(@context.io) do
signal_status(:IN_INPUT) do
if l = @context.io.gets
print l if @context.verbose?
else
if @context.ignore_eof? and @context.io.readable_after_eof?
l = "\n"
if @context.verbose?
printf "Use \"exit\" to leave %s\n", @context.ap_name
end
else
print "\n"
end
end
l
end
end
@scanner.each_top_level_statement do |line, line_no|
signal_status(:IN_EVAL) do
begin
line.untaint
@context.evaluate(line, line_no)
output_value if @context.echo?
exc = nil
rescue Interrupt => exc
rescue SystemExit, SignalException
raise
rescue Exception => exc
end
## BEGIN Monkey-patch 1
#- if exc
is_cause = false
while exc
#- print exc.class, ": ", exc, "\n"
print (is_cause ? "Caused by: " : ""), exc.class, ": ", exc, "\n"
## END Monkey-patch 1
if exc.backtrace && exc.backtrace[0] =~ /irb(2)?(\/.*|-.*|\.rb)?:/ && exc.class.to_s !~ /^IRB/ &&
!(SyntaxError === exc)
irb_bug = true
else
irb_bug = false
end
messages = []
lasts = []
levels = 0
if exc.backtrace
for m in exc.backtrace
m = @context.workspace.filter_backtrace(m) unless irb_bug
if m
if messages.size < @context.back_trace_limit
messages.push "\tfrom "+m
else
lasts.push "\tfrom "+m
if lasts.size > @context.back_trace_limit
lasts.shift
levels += 1
end
end
end
end
end
print messages.join("\n"), "\n"
unless lasts.empty?
printf "... %d levels...\n", levels if levels > 0
print lasts.join("\n"), "\n"
end
print "Maybe IRB bug!\n" if irb_bug
## BEGIN Monkey-patch 2
exc = exc.cause
is_cause = true
## END Monkey-patch 2
end
if $SAFE > 2
abort "Error: irb does not work for $SAFE level higher than 2"
end
end
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.