Skip to content

Commit

Permalink
add support for rails 8
Browse files Browse the repository at this point in the history
  • Loading branch information
adiaz04 committed Nov 1, 2024
1 parent f0defa4 commit e561ed7
Show file tree
Hide file tree
Showing 2 changed files with 328 additions and 0 deletions.
318 changes: 318 additions & 0 deletions lib/bullet/active_record80.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,318 @@
# frozen_string_literal: true

module Bullet
module SaveWithBulletSupport
def _create_record(*)
super do
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
yield(self) if block_given?
end
end
end

module ActiveRecord
def self.enable
require 'active_record'
::ActiveRecord::Base.extend(
Module.new do
def find_by_sql(sql, binds = [], preparable: nil, allow_retry: false, &block)
result = super
if Bullet.start?
if result.is_a? Array
if result.size > 1
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
Bullet::Detector::CounterCache.add_possible_objects(result)
elsif result.size == 1
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
Bullet::Detector::CounterCache.add_impossible_object(result.first)
end
elsif result.is_a? ::ActiveRecord::Base
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
Bullet::Detector::CounterCache.add_impossible_object(result)
end
end
result
end
end
)

::ActiveRecord::Base.prepend(SaveWithBulletSupport)

::ActiveRecord::Relation.prepend(
Module.new do
# if select a collection of objects, then these objects have possible to cause N+1 query.
# if select only one object, then the only one object has impossible to cause N+1 query.
def records
result = super
if Bullet.start?
if result.first.class.name !~ /^HABTM_/
if result.size > 1
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
Bullet::Detector::CounterCache.add_possible_objects(result)
elsif result.size == 1
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
Bullet::Detector::CounterCache.add_impossible_object(result.first)
end
end
end
result
end
end
)

::ActiveRecord::Associations::Preloader::Batch.prepend(
Module.new do
def call
if Bullet.start?
@preloaders.each do |preloader|
preloader.records.each { |record|
Bullet::Detector::Association.add_object_associations(record, preloader.associations)
}
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(preloader.records, preloader.associations)
end
end
super
end
end
)

::ActiveRecord::Associations::Preloader::Branch.prepend(
Module.new do
def preloaders_for_reflection(reflection, reflection_records)
if Bullet.start?
reflection_records.compact!
if reflection_records.first.class.name !~ /^HABTM_/
reflection_records.each { |record|
Bullet::Detector::Association.add_object_associations(record, reflection.name)
}
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(reflection_records, reflection.name)
end
end
super
end
end
)

::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
Module.new do
def source_preloaders
if Bullet.start? && !defined?(@source_preloaders)
preloaders = super
preloaders.each do |preloader|
reflection_name = preloader.send(:reflection).name
preloader.send(:owners).each do |owner|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
end
end
else
super
end
end
end
)

::ActiveRecord::Associations::JoinDependency.prepend(
Module.new do
def instantiate(result_set, strict_loading_value, &block)
@bullet_eager_loadings = {}
records = super

if Bullet.start?
@bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
objects = eager_loadings_hash.keys
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
objects,
eager_loadings_hash[objects.first].to_a
)
end
end
records
end

def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
if Bullet.start?
unless ar_parent.nil?
parent.children.each do |node|
key = aliases.column_alias(node, node.primary_key)
id = row[key]
next unless id.nil?

associations = [node.reflection.name]
if node.reflection.through_reflection?
associations << node.reflection.through_reflection.name
end
associations.each do |association|
Bullet::Detector::Association.add_object_associations(ar_parent, association)
Bullet::Detector::NPlusOneQuery.call_association(ar_parent, association)
@bullet_eager_loadings[ar_parent.class] ||= {}
@bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
@bullet_eager_loadings[ar_parent.class][ar_parent] << association
end
end
end
end

super
end

# call join associations
def construct_model(record, node, row, model_cache, id, strict_loading_value)
result = super

if Bullet.start?
associations = [node.reflection.name]
if node.reflection.through_reflection?
associations << node.reflection.through_reflection.name
end
associations.each do |association|
Bullet::Detector::Association.add_object_associations(record, association)
Bullet::Detector::NPlusOneQuery.call_association(record, association)
@bullet_eager_loadings[record.class] ||= {}
@bullet_eager_loadings[record.class][record] ||= Set.new
@bullet_eager_loadings[record.class][record] << association
end
end

result
end
end
)

::ActiveRecord::Associations::Association.prepend(
Module.new do
def inversed_from(record)
if Bullet.start?
Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
end
super
end

def inversed_from_queries(record)
if Bullet.start? && inversable?(record)
Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
end
super
end
end
)

::ActiveRecord::Associations::CollectionAssociation.prepend(
Module.new do
def load_target
records = super

if Bullet.start?
if is_a? ::ActiveRecord::Associations::ThroughAssociation
association = owner.association(reflection.through_reflection.name)
if association.loaded?
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
Array.wrap(association.target).each do |through_record|
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
end

if reflection.through_reflection != through_reflection
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
end
end
end
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
if records.first.class.name !~ /^HABTM_/
if records.size > 1
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
Bullet::Detector::CounterCache.add_possible_objects(records)
elsif records.size == 1
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
Bullet::Detector::CounterCache.add_impossible_object(records.first)
end
end
end
records
end

def empty?
if Bullet.start? && !reflection.has_cached_counter?
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
end
super
end

def include?(object)
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
super
end
end
)

::ActiveRecord::Associations::SingularAssociation.prepend(
Module.new do
# call has_one and belongs_to associations
def reader
result = super

if Bullet.start?
if owner.class.name !~ /^HABTM_/
if is_a? ::ActiveRecord::Associations::ThroughAssociation
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
association = owner.association(reflection.through_reflection.name)
Array.wrap(association.target).each do |through_record|
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
end

if reflection.through_reflection != through_reflection
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
end
end
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)

if Bullet::Detector::NPlusOneQuery.impossible?(owner)
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
else
Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
end
end
end
result
end
end
)

::ActiveRecord::Associations::HasManyAssociation.prepend(
Module.new do
def empty?
result = super
if Bullet.start? && !reflection.has_cached_counter?
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
end
result
end

def count_records
result = reflection.has_cached_counter?
if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
end
super
end
end
)

::ActiveRecord::Associations::CollectionProxy.prepend(
Module.new do
def count(column_name = nil)
if Bullet.start? && !proxy_association.is_a?(::ActiveRecord::Associations::ThroughAssociation)
Bullet::Detector::CounterCache.add_counter_cache(
proxy_association.owner,
proxy_association.reflection.name
)
Bullet::Detector::NPlusOneQuery.call_association(
proxy_association.owner,
proxy_association.reflection.name
)
end
super(column_name)
end
end
)
end
end
end
10 changes: 10 additions & 0 deletions lib/bullet/dependency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def active_record_version
'active_record71'
elsif active_record72?
'active_record72'
elsif active_record80?
'active_record80'
else
raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
end
Expand Down Expand Up @@ -76,6 +78,10 @@ def active_record7?
active_record? && ::ActiveRecord::VERSION::MAJOR == 7
end

def active_record8?
active_record? && ::ActiveRecord::VERSION::MAJOR == 8
end

def active_record40?
active_record4? && ::ActiveRecord::VERSION::MINOR == 0
end
Expand Down Expand Up @@ -120,6 +126,10 @@ def active_record72?
active_record7? && ::ActiveRecord::VERSION::MINOR == 2
end

def active_record80?
active_record8? && ::ActiveRecord::VERSION::MINOR == 0
end

def mongoid4x?
mongoid? && ::Mongoid::VERSION =~ /\A4/
end
Expand Down

0 comments on commit e561ed7

Please sign in to comment.