diff --git a/lib/bullet.rb b/lib/bullet.rb index 34a16f61..611d0271 100644 --- a/lib/bullet.rb +++ b/lib/bullet.rb @@ -148,6 +148,7 @@ def start_request Thread.current[:bullet_impossible_objects] = Bullet::Registry::Object.new Thread.current[:bullet_inversed_objects] = Bullet::Registry::Base.new Thread.current[:bullet_eager_loadings] = Bullet::Registry::Association.new + Thread.current[:bullet_call_stacks] = Bullet::Registry::CallStack.new Thread.current[:bullet_counter_possible_objects] ||= Bullet::Registry::Object.new Thread.current[:bullet_counter_impossible_objects] ||= Bullet::Registry::Object.new diff --git a/lib/bullet/detector/association.rb b/lib/bullet/detector/association.rb index 432adf5c..93cafe32 100644 --- a/lib/bullet/detector/association.rb +++ b/lib/bullet/detector/association.rb @@ -13,6 +13,7 @@ def add_object_associations(object, associations) 'Detector::Association#add_object_associations', "object: #{object.bullet_key}, associations: #{associations}" ) + call_stacks.add(object.bullet_key) object_associations.add(object.bullet_key, associations) end @@ -25,6 +26,7 @@ def add_call_object_associations(object, associations) 'Detector::Association#add_call_object_associations', "object: #{object.bullet_key}, associations: #{associations}" ) + call_stacks.add(object.bullet_key) call_object_associations.add(object.bullet_key, associations) end @@ -76,6 +78,12 @@ def inversed_objects def eager_loadings Thread.current[:bullet_eager_loadings] end + + # cal_stacks keeps stacktraces where querie-objects were called from. + # e.g. { 'Object:111' => [SomeProject/app/controllers/...] } + def call_stacks + Thread.current[:bullet_call_stacks] + end end end end diff --git a/lib/bullet/detector/n_plus_one_query.rb b/lib/bullet/detector/n_plus_one_query.rb index f4f9eccf..dbaf7075 100644 --- a/lib/bullet/detector/n_plus_one_query.rb +++ b/lib/bullet/detector/n_plus_one_query.rb @@ -25,7 +25,7 @@ def call_association(object, associations) ) if !excluded_stacktrace_path? && conditions_met?(object, associations) Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}") - create_notification caller_in_project, object.class.to_s, associations + create_notification caller_in_project(object.bullet_key), object.class.to_s, associations end end diff --git a/lib/bullet/detector/unused_eager_loading.rb b/lib/bullet/detector/unused_eager_loading.rb index fcb603f4..ba0cd0ec 100644 --- a/lib/bullet/detector/unused_eager_loading.rb +++ b/lib/bullet/detector/unused_eager_loading.rb @@ -20,7 +20,7 @@ def check_unused_preload_associations next if object_association_diff.empty? Bullet.debug('detect unused preload', "object: #{bullet_key}, associations: #{object_association_diff}") - create_notification(caller_in_project, bullet_key.bullet_class_name, object_association_diff) + create_notification(caller_in_project(bullet_key), bullet_key.bullet_class_name, object_association_diff) end end diff --git a/lib/bullet/registry.rb b/lib/bullet/registry.rb index 8182abe5..0923c502 100644 --- a/lib/bullet/registry.rb +++ b/lib/bullet/registry.rb @@ -5,5 +5,6 @@ module Registry autoload :Base, 'bullet/registry/base' autoload :Object, 'bullet/registry/object' autoload :Association, 'bullet/registry/association' + autoload :CallStack, 'bullet/registry/call_stack' end end diff --git a/lib/bullet/registry/call_stack.rb b/lib/bullet/registry/call_stack.rb new file mode 100644 index 00000000..ee32b748 --- /dev/null +++ b/lib/bullet/registry/call_stack.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Bullet + module Registry + class CallStack < Base + # remembers found association backtrace + def add(key) + @registry[key] = Thread.current.backtrace + end + end + end +end diff --git a/lib/bullet/stack_trace_filter.rb b/lib/bullet/stack_trace_filter.rb index d652fd11..bef8a73f 100644 --- a/lib/bullet/stack_trace_filter.rb +++ b/lib/bullet/stack_trace_filter.rb @@ -6,10 +6,11 @@ module StackTraceFilter VENDOR_PATH = '/vendor' IS_RUBY_19 = Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0') - def caller_in_project + # @param bullet_key[String] - use this to get stored call stack from call_stacks object. + def caller_in_project(bullet_key = nil) vendor_root = Bullet.app_root + VENDOR_PATH bundler_path = Bundler.bundle_path.to_s - select_caller_locations do |location| + select_caller_locations(bullet_key) do |location| caller_path = location_as_path(location) caller_path.include?(Bullet.app_root) && !caller_path.include?(vendor_root) && !caller_path.include?(bundler_path) || Bullet.stacktrace_includes.any? { |include_pattern| @@ -50,15 +51,16 @@ def pattern_matches?(location, pattern) end def location_as_path(location) + return location if location.is_a?(String) + IS_RUBY_19 ? location : location.absolute_path.to_s end - def select_caller_locations - if IS_RUBY_19 - caller.select { |caller_path| yield caller_path } - else - caller_locations.select { |location| yield location } - end + def select_caller_locations(bullet_key = nil) + return caller.select { |caller_path| yield caller_path } if IS_RUBY_19 + + call_stack = bullet_key ? call_stacks[bullet_key] : caller_locations + call_stack.select { |location| yield location } end end end diff --git a/spec/bullet/detector/unused_eager_loading_spec.rb b/spec/bullet/detector/unused_eager_loading_spec.rb index 4427b92f..1582d5df 100644 --- a/spec/bullet/detector/unused_eager_loading_spec.rb +++ b/spec/bullet/detector/unused_eager_loading_spec.rb @@ -65,6 +65,11 @@ module Detector expect(UnusedEagerLoading).not_to receive(:create_notification).with('Post', [:association]) UnusedEagerLoading.check_unused_preload_associations end + + it 'should create call stack for notification' do + UnusedEagerLoading.add_object_associations(@post, :association) + expect(UnusedEagerLoading.send(:call_stacks).registry).not_to be_empty + end end context '.add_eager_loadings' do