bullet_instructure 4.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.ruby-version +1 -0
- data/.travis.yml +20 -0
- data/CHANGELOG.md +75 -0
- data/Gemfile +19 -0
- data/Gemfile.mongoid +14 -0
- data/Gemfile.mongoid-2.4 +19 -0
- data/Gemfile.mongoid-2.5 +19 -0
- data/Gemfile.mongoid-2.6 +19 -0
- data/Gemfile.mongoid-2.7 +19 -0
- data/Gemfile.mongoid-2.8 +19 -0
- data/Gemfile.mongoid-3.0 +19 -0
- data/Gemfile.mongoid-3.1 +19 -0
- data/Gemfile.mongoid-4.0 +19 -0
- data/Gemfile.rails-3.0 +19 -0
- data/Gemfile.rails-3.1 +19 -0
- data/Gemfile.rails-3.2 +19 -0
- data/Gemfile.rails-4.0 +19 -0
- data/Gemfile.rails-4.1 +19 -0
- data/Guardfile +8 -0
- data/Hacking.md +74 -0
- data/MIT-LICENSE +20 -0
- data/README.md +428 -0
- data/Rakefile +52 -0
- data/bullet_instructure.gemspec +27 -0
- data/lib/bullet.rb +196 -0
- data/lib/bullet/active_record3.rb +148 -0
- data/lib/bullet/active_record3x.rb +128 -0
- data/lib/bullet/active_record4.rb +128 -0
- data/lib/bullet/active_record41.rb +121 -0
- data/lib/bullet/dependency.rb +81 -0
- data/lib/bullet/detector.rb +9 -0
- data/lib/bullet/detector/association.rb +67 -0
- data/lib/bullet/detector/base.rb +6 -0
- data/lib/bullet/detector/counter_cache.rb +59 -0
- data/lib/bullet/detector/n_plus_one_query.rb +89 -0
- data/lib/bullet/detector/unused_eager_loading.rb +84 -0
- data/lib/bullet/ext/object.rb +9 -0
- data/lib/bullet/ext/string.rb +5 -0
- data/lib/bullet/mongoid2x.rb +56 -0
- data/lib/bullet/mongoid3x.rb +56 -0
- data/lib/bullet/mongoid4x.rb +56 -0
- data/lib/bullet/notification.rb +10 -0
- data/lib/bullet/notification/base.rb +97 -0
- data/lib/bullet/notification/counter_cache.rb +13 -0
- data/lib/bullet/notification/n_plus_one_query.rb +28 -0
- data/lib/bullet/notification/unused_eager_loading.rb +13 -0
- data/lib/bullet/notification_collector.rb +24 -0
- data/lib/bullet/rack.rb +81 -0
- data/lib/bullet/registry.rb +7 -0
- data/lib/bullet/registry/association.rb +13 -0
- data/lib/bullet/registry/base.rb +40 -0
- data/lib/bullet/registry/object.rb +13 -0
- data/lib/bullet/version.rb +4 -0
- data/perf/benchmark.rb +121 -0
- data/rails/init.rb +1 -0
- data/spec/bullet/detector/association_spec.rb +26 -0
- data/spec/bullet/detector/base_spec.rb +8 -0
- data/spec/bullet/detector/counter_cache_spec.rb +56 -0
- data/spec/bullet/detector/n_plus_one_query_spec.rb +138 -0
- data/spec/bullet/detector/unused_eager_loading_spec.rb +88 -0
- data/spec/bullet/ext/object_spec.rb +17 -0
- data/spec/bullet/ext/string_spec.rb +13 -0
- data/spec/bullet/notification/base_spec.rb +83 -0
- data/spec/bullet/notification/counter_cache_spec.rb +12 -0
- data/spec/bullet/notification/n_plus_one_query_spec.rb +14 -0
- data/spec/bullet/notification/unused_eager_loading_spec.rb +12 -0
- data/spec/bullet/notification_collector_spec.rb +32 -0
- data/spec/bullet/rack_spec.rb +97 -0
- data/spec/bullet/registry/association_spec.rb +26 -0
- data/spec/bullet/registry/base_spec.rb +44 -0
- data/spec/bullet/registry/object_spec.rb +24 -0
- data/spec/bullet_spec.rb +41 -0
- data/spec/integration/active_record3/association_spec.rb +651 -0
- data/spec/integration/active_record4/association_spec.rb +649 -0
- data/spec/integration/counter_cache_spec.rb +63 -0
- data/spec/integration/mongoid/association_spec.rb +258 -0
- data/spec/models/address.rb +3 -0
- data/spec/models/author.rb +3 -0
- data/spec/models/base_user.rb +5 -0
- data/spec/models/category.rb +7 -0
- data/spec/models/city.rb +3 -0
- data/spec/models/client.rb +4 -0
- data/spec/models/comment.rb +4 -0
- data/spec/models/company.rb +3 -0
- data/spec/models/country.rb +3 -0
- data/spec/models/document.rb +5 -0
- data/spec/models/entry.rb +3 -0
- data/spec/models/firm.rb +4 -0
- data/spec/models/folder.rb +2 -0
- data/spec/models/mongoid/address.rb +7 -0
- data/spec/models/mongoid/category.rb +8 -0
- data/spec/models/mongoid/comment.rb +7 -0
- data/spec/models/mongoid/company.rb +7 -0
- data/spec/models/mongoid/entry.rb +7 -0
- data/spec/models/mongoid/post.rb +12 -0
- data/spec/models/mongoid/user.rb +5 -0
- data/spec/models/newspaper.rb +3 -0
- data/spec/models/page.rb +2 -0
- data/spec/models/person.rb +3 -0
- data/spec/models/pet.rb +3 -0
- data/spec/models/post.rb +10 -0
- data/spec/models/relationship.rb +4 -0
- data/spec/models/student.rb +3 -0
- data/spec/models/submission.rb +4 -0
- data/spec/models/teacher.rb +3 -0
- data/spec/models/user.rb +4 -0
- data/spec/models/writer.rb +2 -0
- data/spec/spec_helper.rb +103 -0
- data/spec/support/bullet_ext.rb +55 -0
- data/spec/support/mongo_seed.rb +65 -0
- data/spec/support/rack_double.rb +55 -0
- data/spec/support/sqlite_seed.rb +229 -0
- data/tasks/bullet_tasks.rake +9 -0
- data/test.sh +15 -0
- metadata +246 -0
@@ -0,0 +1,128 @@
|
|
1
|
+
module Bullet
|
2
|
+
module ActiveRecord
|
3
|
+
def self.enable
|
4
|
+
require 'active_record'
|
5
|
+
::ActiveRecord::Relation.class_eval do
|
6
|
+
alias_method :origin_to_a, :to_a
|
7
|
+
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
8
|
+
# if select only one object, then the only one object has impossible to cause N+1 query.
|
9
|
+
def to_a
|
10
|
+
records = origin_to_a
|
11
|
+
if records.size > 1
|
12
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
13
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
14
|
+
elsif records.size == 1
|
15
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
16
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
17
|
+
end
|
18
|
+
records
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
::ActiveRecord::Associations::Preloader.class_eval do
|
23
|
+
# include query for one to many associations.
|
24
|
+
# keep this eager loadings.
|
25
|
+
alias_method :origin_initialize, :initialize
|
26
|
+
def initialize(records, associations, preload_scope = nil)
|
27
|
+
origin_initialize(records, associations, preload_scope)
|
28
|
+
records = [records].flatten.compact.uniq
|
29
|
+
return if records.empty?
|
30
|
+
records.each do |record|
|
31
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
32
|
+
end
|
33
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
::ActiveRecord::FinderMethods.class_eval do
|
38
|
+
# add includes in scope
|
39
|
+
alias_method :origin_find_with_associations, :find_with_associations
|
40
|
+
def find_with_associations
|
41
|
+
records = origin_find_with_associations
|
42
|
+
associations = (eager_load_values + includes_values).uniq
|
43
|
+
records.each do |record|
|
44
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
45
|
+
end
|
46
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
47
|
+
records
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
::ActiveRecord::Associations::JoinDependency.class_eval do
|
52
|
+
alias_method :origin_instantiate, :instantiate
|
53
|
+
alias_method :origin_construct_association, :construct_association
|
54
|
+
|
55
|
+
def instantiate(rows)
|
56
|
+
@bullet_eager_loadings = {}
|
57
|
+
records = origin_instantiate(rows)
|
58
|
+
|
59
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
60
|
+
objects = eager_loadings_hash.keys
|
61
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
62
|
+
end
|
63
|
+
records
|
64
|
+
end
|
65
|
+
|
66
|
+
# call join associations
|
67
|
+
def construct_association(record, join, row)
|
68
|
+
result = origin_construct_association(record, join, row)
|
69
|
+
|
70
|
+
associations = join.reflection.name
|
71
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
72
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
73
|
+
@bullet_eager_loadings[record.class] ||= {}
|
74
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
75
|
+
@bullet_eager_loadings[record.class][record] << associations
|
76
|
+
|
77
|
+
result
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
::ActiveRecord::Associations::CollectionAssociation.class_eval do
|
82
|
+
# call one to many associations
|
83
|
+
alias_method :origin_load_target, :load_target
|
84
|
+
def load_target
|
85
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
86
|
+
origin_load_target
|
87
|
+
end
|
88
|
+
|
89
|
+
alias_method :origin_empty?, :empty?
|
90
|
+
def empty?
|
91
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
92
|
+
origin_empty?
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
::ActiveRecord::Associations::SingularAssociation.class_eval do
|
97
|
+
# call has_one and belongs_to associations
|
98
|
+
alias_method :origin_reader, :reader
|
99
|
+
def reader(force_reload = false)
|
100
|
+
result = origin_reader(force_reload)
|
101
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
102
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
103
|
+
result
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
::ActiveRecord::Associations::Association.class_eval do
|
108
|
+
alias_method :origin_set_inverse_instance, :set_inverse_instance
|
109
|
+
def set_inverse_instance(record)
|
110
|
+
if record && invertible_for?(record)
|
111
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(record)
|
112
|
+
end
|
113
|
+
origin_set_inverse_instance(record)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
118
|
+
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
119
|
+
|
120
|
+
def has_cached_counter?(reflection = reflection())
|
121
|
+
result = origin_has_cached_counter?(reflection)
|
122
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
|
123
|
+
result
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
module Bullet
|
2
|
+
module ActiveRecord
|
3
|
+
def self.enable
|
4
|
+
require 'active_record'
|
5
|
+
::ActiveRecord::Relation.class_eval do
|
6
|
+
alias_method :origin_to_a, :to_a
|
7
|
+
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
8
|
+
# if select only one object, then the only one object has impossible to cause N+1 query.
|
9
|
+
def to_a
|
10
|
+
records = origin_to_a
|
11
|
+
if records.first.class.name !~ /^HABTM_/
|
12
|
+
if records.size > 1
|
13
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
14
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
15
|
+
elsif records.size == 1
|
16
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
17
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
records
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
::ActiveRecord::Associations::Preloader.class_eval do
|
25
|
+
alias_method :origin_preloaders_on, :preloaders_on
|
26
|
+
|
27
|
+
def preloaders_on(association, records, scope)
|
28
|
+
if records.first.class.name !~ /^HABTM_/
|
29
|
+
records.each do |record|
|
30
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
31
|
+
end
|
32
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
33
|
+
end
|
34
|
+
origin_preloaders_on(association, records, scope)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
::ActiveRecord::FinderMethods.class_eval do
|
39
|
+
# add includes in scope
|
40
|
+
alias_method :origin_find_with_associations, :find_with_associations
|
41
|
+
def find_with_associations
|
42
|
+
records = origin_find_with_associations
|
43
|
+
associations = (eager_load_values + includes_values).uniq
|
44
|
+
records.each do |record|
|
45
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
46
|
+
end
|
47
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
48
|
+
records
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
::ActiveRecord::Associations::JoinDependency.class_eval do
|
53
|
+
alias_method :origin_instantiate, :instantiate
|
54
|
+
alias_method :origin_construct_model, :construct_model
|
55
|
+
|
56
|
+
def instantiate(result_set, aliases)
|
57
|
+
@bullet_eager_loadings = {}
|
58
|
+
records = origin_instantiate(result_set, aliases)
|
59
|
+
|
60
|
+
@bullet_eager_loadings.each do |klazz, eager_loadings_hash|
|
61
|
+
objects = eager_loadings_hash.keys
|
62
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
63
|
+
end
|
64
|
+
records
|
65
|
+
end
|
66
|
+
|
67
|
+
# call join associations
|
68
|
+
def construct_model(record, node, row, model_cache, id, aliases)
|
69
|
+
result = origin_construct_model(record, node, row, model_cache, id, aliases)
|
70
|
+
|
71
|
+
associations = node.reflection.name
|
72
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
73
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
74
|
+
@bullet_eager_loadings[record.class] ||= {}
|
75
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
76
|
+
@bullet_eager_loadings[record.class][record] << associations
|
77
|
+
|
78
|
+
result
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
::ActiveRecord::Associations::CollectionAssociation.class_eval do
|
83
|
+
# call one to many associations
|
84
|
+
alias_method :origin_load_target, :load_target
|
85
|
+
def load_target
|
86
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
87
|
+
origin_load_target
|
88
|
+
end
|
89
|
+
|
90
|
+
alias_method :origin_empty?, :empty?
|
91
|
+
def empty?
|
92
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
|
93
|
+
origin_empty?
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
::ActiveRecord::Associations::SingularAssociation.class_eval do
|
98
|
+
# call has_one and belongs_to associations
|
99
|
+
alias_method :origin_reader, :reader
|
100
|
+
def reader(force_reload = false)
|
101
|
+
result = origin_reader(force_reload)
|
102
|
+
if @owner.class.name !~ /^HABTM_/
|
103
|
+
Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
|
104
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
105
|
+
end
|
106
|
+
result
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
::ActiveRecord::Associations::HasManyAssociation.class_eval do
|
111
|
+
alias_method :origin_has_cached_counter?, :has_cached_counter?
|
112
|
+
|
113
|
+
def has_cached_counter?(reflection = reflection())
|
114
|
+
result = origin_has_cached_counter?(reflection)
|
115
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) unless result
|
116
|
+
result
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Dependency
|
3
|
+
def mongoid?
|
4
|
+
@mongoid ||= defined? ::Mongoid
|
5
|
+
end
|
6
|
+
|
7
|
+
def active_record?
|
8
|
+
@active_record ||= defined? ::ActiveRecord
|
9
|
+
end
|
10
|
+
|
11
|
+
def rails?
|
12
|
+
@rails ||= defined? ::Rails
|
13
|
+
end
|
14
|
+
|
15
|
+
def active_record_version
|
16
|
+
@active_record_version ||= begin
|
17
|
+
if active_record30?
|
18
|
+
'active_record3'
|
19
|
+
elsif active_record31? || active_record32?
|
20
|
+
'active_record3x'
|
21
|
+
elsif active_record40?
|
22
|
+
'active_record4'
|
23
|
+
elsif active_record41?
|
24
|
+
'active_record41'
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def mongoid_version
|
30
|
+
@mongoid_version ||= begin
|
31
|
+
if mongoid2x?
|
32
|
+
'mongoid2x'
|
33
|
+
elsif mongoid3x?
|
34
|
+
'mongoid3x'
|
35
|
+
elsif mongoid4x?
|
36
|
+
'mongoid4x'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def active_record3?
|
42
|
+
active_record? && ::ActiveRecord::VERSION::MAJOR == 3
|
43
|
+
end
|
44
|
+
|
45
|
+
def active_record4?
|
46
|
+
active_record? && ::ActiveRecord::VERSION::MAJOR == 4
|
47
|
+
end
|
48
|
+
|
49
|
+
def active_record30?
|
50
|
+
active_record3? && ::ActiveRecord::VERSION::MINOR == 0
|
51
|
+
end
|
52
|
+
|
53
|
+
def active_record31?
|
54
|
+
active_record3? && ::ActiveRecord::VERSION::MINOR == 1
|
55
|
+
end
|
56
|
+
|
57
|
+
def active_record32?
|
58
|
+
active_record3? && ::ActiveRecord::VERSION::MINOR == 2
|
59
|
+
end
|
60
|
+
|
61
|
+
def active_record40?
|
62
|
+
active_record4? && ::ActiveRecord::VERSION::MINOR == 0
|
63
|
+
end
|
64
|
+
|
65
|
+
def active_record41?
|
66
|
+
active_record4? && ::ActiveRecord::VERSION::MINOR == 1
|
67
|
+
end
|
68
|
+
|
69
|
+
def mongoid2x?
|
70
|
+
mongoid? && ::Mongoid::VERSION =~ /\A2\.[4-8]/
|
71
|
+
end
|
72
|
+
|
73
|
+
def mongoid3x?
|
74
|
+
mongoid? && ::Mongoid::VERSION =~ /\A3/
|
75
|
+
end
|
76
|
+
|
77
|
+
def mongoid4x?
|
78
|
+
mongoid? && ::Mongoid::VERSION =~ /\A4/
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Detector
|
3
|
+
autoload :Base, 'bullet/detector/base'
|
4
|
+
autoload :Association, 'bullet/detector/association'
|
5
|
+
autoload :NPlusOneQuery, 'bullet/detector/n_plus_one_query'
|
6
|
+
autoload :UnusedEagerLoading, 'bullet/detector/unused_eager_loading'
|
7
|
+
autoload :CounterCache, 'bullet/detector/counter_cache'
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Detector
|
3
|
+
class Association < Base
|
4
|
+
class <<self
|
5
|
+
def add_object_associations(object, associations)
|
6
|
+
return unless Bullet.start?
|
7
|
+
return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
|
8
|
+
return unless object.id
|
9
|
+
|
10
|
+
Bullet.debug("Detector::Association#add_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
|
11
|
+
object_associations.add(object.bullet_key, associations)
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_call_object_associations(object, associations)
|
15
|
+
return unless Bullet.start?
|
16
|
+
return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
|
17
|
+
return unless object.id
|
18
|
+
|
19
|
+
Bullet.debug("Detector::Association#add_call_object_associations", "object: #{object.bullet_key}, associations: #{associations}")
|
20
|
+
call_object_associations.add(object.bullet_key, associations)
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
# object_associations keep the object relationships
|
25
|
+
# that the object has many associations.
|
26
|
+
# e.g. { "Post:1" => [:comments] }
|
27
|
+
# the object_associations keep all associations that may be or may no be
|
28
|
+
# unpreload associations or unused preload associations.
|
29
|
+
def object_associations
|
30
|
+
Thread.current[:bullet_object_associations]
|
31
|
+
end
|
32
|
+
|
33
|
+
# call_object_associations keep the object relationships
|
34
|
+
# that object.associations is called.
|
35
|
+
# e.g. { "Post:1" => [:comments] }
|
36
|
+
# they are used to detect unused preload associations.
|
37
|
+
def call_object_associations
|
38
|
+
Thread.current[:bullet_call_object_associations]
|
39
|
+
end
|
40
|
+
|
41
|
+
# possible_objects keep the class to object relationships
|
42
|
+
# that the objects may cause N+1 query.
|
43
|
+
# e.g. { Post => ["Post:1", "Post:2"] }
|
44
|
+
def possible_objects
|
45
|
+
Thread.current[:bullet_possible_objects]
|
46
|
+
end
|
47
|
+
|
48
|
+
# impossible_objects keep the class to objects relationships
|
49
|
+
# that the objects may not cause N+1 query.
|
50
|
+
# e.g. { Post => ["Post:1", "Post:2"] }
|
51
|
+
# Notice: impossible_objects are not accurate,
|
52
|
+
# if find collection returns only one object, then the object is impossible object,
|
53
|
+
# impossible_objects are used to avoid treating 1+1 query to N+1 query.
|
54
|
+
def impossible_objects
|
55
|
+
Thread.current[:bullet_impossible_objects]
|
56
|
+
end
|
57
|
+
|
58
|
+
# eager_loadings keep the object relationships
|
59
|
+
# that the associations are preloaded by find :include.
|
60
|
+
# e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
|
61
|
+
def eager_loadings
|
62
|
+
Thread.current[:bullet_eager_loadings]
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Bullet
|
2
|
+
module Detector
|
3
|
+
class CounterCache < Base
|
4
|
+
class <<self
|
5
|
+
def add_counter_cache(object, associations)
|
6
|
+
return unless Bullet.start?
|
7
|
+
return unless Bullet.counter_cache_enable?
|
8
|
+
return unless object.id
|
9
|
+
|
10
|
+
Bullet.debug("Detector::CounterCache#add_counter_cache", "object: #{object.bullet_key}, associations: #{associations}")
|
11
|
+
if conditions_met?(object.bullet_key, associations)
|
12
|
+
create_notification object.class.to_s, associations
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def add_possible_objects(object_or_objects)
|
17
|
+
return unless Bullet.start?
|
18
|
+
return unless Bullet.counter_cache_enable?
|
19
|
+
objects = Array(object_or_objects)
|
20
|
+
return if objects.map(&:id).compact.empty?
|
21
|
+
|
22
|
+
Bullet.debug("Detector::CounterCache#add_possible_objects", "objects: #{objects.map(&:bullet_key).join(', ')}")
|
23
|
+
objects.each { |object| possible_objects.add object.bullet_key }
|
24
|
+
end
|
25
|
+
|
26
|
+
def add_impossible_object(object)
|
27
|
+
return unless Bullet.start?
|
28
|
+
return unless Bullet.counter_cache_enable?
|
29
|
+
return unless object.id
|
30
|
+
|
31
|
+
Bullet.debug("Detector::CounterCache#add_impossible_object", "object: #{object.bullet_key}")
|
32
|
+
impossible_objects.add object.bullet_key
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
def create_notification(klazz, associations)
|
37
|
+
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
|
38
|
+
|
39
|
+
if notify_associations.present?
|
40
|
+
notice = Bullet::Notification::CounterCache.new klazz, notify_associations
|
41
|
+
Bullet.notification_collector.add notice
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def possible_objects
|
46
|
+
Thread.current[:bullet_counter_possible_objects]
|
47
|
+
end
|
48
|
+
|
49
|
+
def impossible_objects
|
50
|
+
Thread.current[:bullet_counter_impossible_objects]
|
51
|
+
end
|
52
|
+
|
53
|
+
def conditions_met?(bullet_key, associations)
|
54
|
+
possible_objects.include?(bullet_key) && !impossible_objects.include?(bullet_key)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|