Created
April 12, 2018 17:44
-
-
Save ms-ati/c11658166c8844dfa6b1caed480d02b3 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## | |
# 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" <[email protected]> | |
## | |
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