Skip to content

Commit

Permalink
feat: Add Support ActiveRecord 7.2
Browse files Browse the repository at this point in the history
  • Loading branch information
hatsu38 committed Jul 11, 2024
1 parent 6abe9a5 commit 1390d5b
Show file tree
Hide file tree
Showing 7 changed files with 338 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
gemfile: ['Gemfile.rails-7.0', 'Gemfile.rails-7.1']
gemfile: ['Gemfile.rails-7.0', 'Gemfile.rails-7.1', 'Gemfile.rails-7.2']
env: # $BUNDLE_GEMFILE must be set at the job level, so it is set for all steps
BUNDLE_GEMFILE: ${{ github.workspace }}/${{ matrix.gemfile }}
steps:
Expand Down
2 changes: 1 addition & 1 deletion Gemfile.rails-7.0
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ source "https://rubygems.org"
gemspec

gem 'rails', '~> 7.0.0'
gem 'sqlite3'
gem 'sqlite3', '~> 1.4'
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
gem 'activerecord-import'

Expand Down
2 changes: 1 addition & 1 deletion Gemfile.rails-7.1
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ source "https://rubygems.org"
gemspec

gem 'rails', '~> 7.1.0'
gem 'sqlite3'
gem 'sqlite3', '~> 1.4'
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
gem 'activerecord-import'

Expand Down
10 changes: 10 additions & 0 deletions Gemfile.rails-7.2
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
source "https://rubygems.org"

gemspec

gem "rails", ">= 7.2.0.beta2", "< 7.3"
gem 'sqlite3'
gem 'activerecord-jdbcsqlite3-adapter', platforms: [:jruby]
gem 'activerecord-import'

gem "rspec"
318 changes: 318 additions & 0 deletions lib/bullet/active_record72.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
6 changes: 6 additions & 0 deletions lib/bullet/dependency.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ def active_record_version
'active_record70'
elsif active_record71?
'active_record71'
elsif active_record72?
'active_record72'
else
raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
end
Expand Down Expand Up @@ -114,6 +116,10 @@ def active_record71?
active_record7? && ::ActiveRecord::VERSION::MINOR == 1
end

def active_record72?
active_record7? && ::ActiveRecord::VERSION::MINOR == 2
end

def mongoid4x?
mongoid? && ::Mongoid::VERSION =~ /\A4/
end
Expand Down
1 change: 1 addition & 0 deletions test.sh
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#bundle update rails && bundle exec rspec spec
#BUNDLE_GEMFILE=Gemfile.mongoid bundle update mongoid && BUNDLE_GEMFILE=Gemfile.mongoid bundle exec rspec spec
BUNDLE_GEMFILE=Gemfile.rails-7.2 bundle && BUNDLE_GEMFILE=Gemfile.rails-7.2 bundle exec rspec spec
BUNDLE_GEMFILE=Gemfile.rails-7.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-7.1 bundle exec rspec spec
BUNDLE_GEMFILE=Gemfile.rails-7.0 bundle && BUNDLE_GEMFILE=Gemfile.rails-7.0 bundle exec rspec spec
BUNDLE_GEMFILE=Gemfile.rails-6.1 bundle && BUNDLE_GEMFILE=Gemfile.rails-6.1 bundle exec rspec spec
Expand Down

0 comments on commit 1390d5b

Please sign in to comment.