From a1e4263d5ebde979d0a679092af0ab556a6b77c0 Mon Sep 17 00:00:00 2001 From: schneems Date: Wed, 30 Dec 2020 09:50:49 -0600 Subject: [PATCH] Safer NoMethodError annotation Previously when a NoMethodError was raised from within the DeadEnd monkeypatch then an infinite loop would be caused by calling `e.message` since that would trigger our monkeypatch recursively. This patch fixes the issue by aliasing the original method and explicitly using that instead. This behavior is also tested. So while there still might be internal errors, at least they won't result in infinite loops with no output. --- CHANGELOG.md | 2 ++ lib/dead_end/auto.rb | 7 +++-- spec/integration/ruby_command_line_spec.rb | 32 ++++++++++++++++++++++ 3 files changed, 39 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9bd7d87..f390ad7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ ## HEAD (unreleased) +- Safer NoMethodError annotation (https://github.com/zombocom/dead_end/pull/48) + ## 1.1.0 - Annotate NoMethodError in non-production environments (https://github.com/zombocom/dead_end/pull/46) diff --git a/lib/dead_end/auto.rb b/lib/dead_end/auto.rb index 3ad6263..061f602 100644 --- a/lib/dead_end/auto.rb +++ b/lib/dead_end/auto.rb @@ -62,6 +62,8 @@ module DeadEnd # we can attempt to disable this behavior in a production context. if !DeadEnd::IsProduction.call class NoMethodError + alias :original_to_s :to_s + def to_s return super if DeadEnd::IsProduction.call @@ -91,8 +93,9 @@ def to_s message << $/ message rescue => e - puts "DeadEnd Internal error: #{e.message}" - puts "DeadEnd Internal backtrace: #{e.backtrace}" + puts "DeadEnd Internal error: #{e.original_to_s}" + puts "DeadEnd Internal backtrace:" + puts backtrace.map {|l| " " + l }.join($/) super end end diff --git a/spec/integration/ruby_command_line_spec.rb b/spec/integration/ruby_command_line_spec.rb index 0164a73..c6099c9 100644 --- a/spec/integration/ruby_command_line_spec.rb +++ b/spec/integration/ruby_command_line_spec.rb @@ -4,6 +4,38 @@ module DeadEnd RSpec.describe "Requires with ruby cli" do + it "does not get in an infinite loop when NoMethodError is raised internally" do + Dir.mktmpdir do |dir| + @tmpdir = Pathname(dir) + @script = @tmpdir.join("script.rb") + @script.write <<~'EOM' + class DeadEnd::DisplayCodeWithLineNumbers + def call + raise NoMethodError.new("foo") + end + end + + class Pet + def initialize + @name = "cinco" + end + + def call + puts "Come here #{@neam.upcase}" + end + end + + Pet.new.call + EOM + + out = `ruby -I#{lib_dir} -rdead_end/auto #{@script} 2>&1` + + expect(out).to include("DeadEnd Internal error: foo") + expect(out).to include("DeadEnd Internal backtrace") + expect($?.success?).to be_falsey + end + end + it "annotates NoMethodError" do Dir.mktmpdir do |dir| @tmpdir = Pathname(dir)