From def1671ce4993cac32cbc329be3d4c7685b7a1d4 Mon Sep 17 00:00:00 2001 From: Taichi Ishitani Date: Wed, 23 Oct 2019 02:29:58 +0900 Subject: [PATCH] Filter Docile's source files from backtrace (#36) Extend any raised NoMethodError with a module to filter out Docile code from the {#backtrace} and {#backtrace_locations} methods. * Fixed #35 --- lib/docile.rb | 1 + lib/docile/backtrace_filter.rb | 22 ++++++++++++++++++++++ lib/docile/fallback_context_proxy.rb | 7 ++++++- spec/docile_spec.rb | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) create mode 100644 lib/docile/backtrace_filter.rb diff --git a/lib/docile.rb b/lib/docile.rb index f5b3f77..85e348d 100644 --- a/lib/docile.rb +++ b/lib/docile.rb @@ -2,6 +2,7 @@ require "docile/version" require "docile/execution" require "docile/fallback_context_proxy" require "docile/chaining_fallback_context_proxy" +require "docile/backtrace_filter" # Docile keeps your Ruby DSLs tame and well-behaved. module Docile diff --git a/lib/docile/backtrace_filter.rb b/lib/docile/backtrace_filter.rb new file mode 100644 index 0000000..fd8f1f0 --- /dev/null +++ b/lib/docile/backtrace_filter.rb @@ -0,0 +1,22 @@ +module Docile + # @api private + # + # This is used to remove entries pointing to Docile's source files + # from {Exception#backtrace} and {Exception#backtrace_locations}. + # + # If {NoMethodError} is caught then the exception object will be extended + # by this module to add filter functionalities. + module BacktraceFilter + FILTER_PATTERN = /lib\/docile/ + + def backtrace + super.select { |trace| trace !~ FILTER_PATTERN } + end + + if ::Exception.public_method_defined?(:backtrace_locations) + def backtrace_locations + super.select { |location| location.absolute_path !~ FILTER_PATTERN } + end + end + end +end diff --git a/lib/docile/fallback_context_proxy.rb b/lib/docile/fallback_context_proxy.rb index 6d04607..db8a608 100644 --- a/lib/docile/fallback_context_proxy.rb +++ b/lib/docile/fallback_context_proxy.rb @@ -86,7 +86,12 @@ module Docile if @__receiver__.respond_to?(method.to_sym) @__receiver__.__send__(method.to_sym, *args, &block) else - @__fallback__.__send__(method.to_sym, *args, &block) + begin + @__fallback__.__send__(method.to_sym, *args, &block) + rescue NoMethodError => e + e.extend(BacktraceFilter) + raise e + end end end end diff --git a/spec/docile_spec.rb b/spec/docile_spec.rb index 70454e6..34b72e5 100644 --- a/spec/docile_spec.rb +++ b/spec/docile_spec.rb @@ -416,6 +416,25 @@ describe Docile do end end + context "when NoMethodError is raised" do + specify "#backtrace does not include path to Docile's source file" do + begin + Docile.dsl_eval(Object.new) { foo } + rescue NoMethodError => e + expect(e.backtrace).not_to include(match(/lib\/docile/)) + end + end + + if ::Exception.public_method_defined?(:backtrace_locations) + specify "#backtrace_locations also does not include path to Docile's source file" do + begin + Docile.dsl_eval(Object.new) { foo } + rescue NoMethodError => e + expect(e.backtrace_locations.map(&:absolute_path)).not_to include(match(/lib\/docile/)) + end + end + end + end end describe ".dsl_eval_with_block_return" do