class Bullet::Detector::NPlusOneQuery
def add_impossible_object(object)
def add_impossible_object(object) return unless Bullet.start? return unless Bullet.n_plus_one_query_enable? return unless object.id Bullet.debug("Detector::NPlusOneQuery#add_impossible_object", "object: #{object.bullet_ar_key}") impossible_objects.add object.bullet_ar_key end
def add_possible_objects(object_or_objects)
def add_possible_objects(object_or_objects) return unless Bullet.start? return unless Bullet.n_plus_one_query_enable? objects = Array(object_or_objects) return if objects.map(&:id).compact.empty? Bullet.debug("Detector::NPlusOneQuery#add_possible_objects", "objects: #{objects.map(&:bullet_ar_key).join(', ')}") objects.each { |object| possible_objects.add object.bullet_ar_key } end
def association?(bullet_ar_key, associations)
def association?(bullet_ar_key, associations) value = object_associations[bullet_ar_key] if value value.each do |v| result = v.is_a?(Hash) ? v.has_key?(associations) : v == associations return true if result end end return false end
def call_association(object, associations)
then, it checks if this associations call is unpreload.
first, it keeps this method call for object.association.
executed when object.assocations is called.
def call_association(object, associations) return unless Bullet.start? return unless object.id add_call_object_associations(object, associations) Bullet.debug("Detector::NPlusOneQuery#call_association", "object: #{object.bullet_ar_key}, associations: #{associations}") if conditions_met?(object.bullet_ar_key, associations) Bullet.debug("detect n + 1 query", "object: #{object.bullet_ar_key}, associations: #{associations}") create_notification caller_in_project, object.class.to_s, associations end end
def caller_in_project
def caller_in_project app_root = rails? ? Rails.root.to_s : Dir.pwd vendor_root = app_root + "/vendor" caller.select do |c| c.include?(app_root) && !c.include?(vendor_root) || Bullet.stacktrace_includes.any? { |include| c.include?(include) } end end
def conditions_met?(bullet_ar_key, associations)
def conditions_met?(bullet_ar_key, associations) possible?(bullet_ar_key) && !impossible?(bullet_ar_key) && !association?(bullet_ar_key, associations) end
def create_notification(callers, klazz, associations)
def create_notification(callers, klazz, associations) notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz) if notify_associations.present? notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations) Bullet.notification_collector.add(notice) end end
def impossible?(bullet_ar_key)
def impossible?(bullet_ar_key) impossible_objects.include? bullet_ar_key end
def possible?(bullet_ar_key)
def possible?(bullet_ar_key) possible_objects.include? bullet_ar_key end