bullet 5.7.0 → 5.8.0
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 +4 -4
- data/CHANGELOG.md +16 -1
- data/Gemfile.rails-5.2 +1 -1
- data/Guardfile +1 -1
- data/README.md +9 -4
- data/Rakefile +5 -5
- data/bullet.gemspec +11 -6
- data/lib/bullet/active_record4.rb +18 -14
- data/lib/bullet/active_record41.rb +16 -14
- data/lib/bullet/active_record42.rb +24 -22
- data/lib/bullet/active_record5.rb +78 -49
- data/lib/bullet/active_record52.rb +47 -40
- data/lib/bullet/dependency.rb +3 -1
- data/lib/bullet/detector/association.rb +16 -14
- data/lib/bullet/detector/base.rb +2 -0
- data/lib/bullet/detector/counter_cache.rb +10 -7
- data/lib/bullet/detector/n_plus_one_query.rb +17 -15
- data/lib/bullet/detector/unused_eager_loading.rb +26 -21
- data/lib/bullet/detector.rb +2 -0
- data/lib/bullet/ext/object.rb +6 -4
- data/lib/bullet/ext/string.rb +3 -1
- data/lib/bullet/mongoid4x.rb +7 -2
- data/lib/bullet/mongoid5x.rb +7 -2
- data/lib/bullet/mongoid6x.rb +11 -6
- data/lib/bullet/notification/base.rb +21 -15
- data/lib/bullet/notification/counter_cache.rb +2 -0
- data/lib/bullet/notification/n_plus_one_query.rb +6 -4
- data/lib/bullet/notification/unused_eager_loading.rb +6 -4
- data/lib/bullet/notification.rb +2 -0
- data/lib/bullet/notification_collector.rb +2 -1
- data/lib/bullet/rack.rb +19 -15
- data/lib/bullet/registry/association.rb +2 -0
- data/lib/bullet/registry/base.rb +2 -0
- data/lib/bullet/registry/object.rb +2 -0
- data/lib/bullet/registry.rb +2 -0
- data/lib/bullet/stack_trace_filter.rb +56 -19
- data/lib/bullet/version.rb +2 -1
- data/lib/bullet.rb +31 -29
- data/lib/generators/bullet/install_generator.rb +5 -3
- data/perf/benchmark.rb +9 -7
- data/rails/init.rb +2 -0
- data/spec/bullet/detector/association_spec.rb +2 -0
- data/spec/bullet/detector/base_spec.rb +2 -0
- data/spec/bullet/detector/counter_cache_spec.rb +2 -0
- data/spec/bullet/detector/n_plus_one_query_spec.rb +33 -8
- data/spec/bullet/detector/unused_eager_loading_spec.rb +2 -0
- data/spec/bullet/ext/object_spec.rb +3 -1
- data/spec/bullet/ext/string_spec.rb +2 -0
- data/spec/bullet/notification/base_spec.rb +8 -6
- data/spec/bullet/notification/counter_cache_spec.rb +3 -1
- data/spec/bullet/notification/n_plus_one_query_spec.rb +3 -1
- data/spec/bullet/notification/unused_eager_loading_spec.rb +3 -1
- data/spec/bullet/notification_collector_spec.rb +2 -0
- data/spec/bullet/rack_spec.rb +10 -9
- data/spec/bullet/registry/association_spec.rb +4 -2
- data/spec/bullet/registry/base_spec.rb +5 -3
- data/spec/bullet/registry/object_spec.rb +2 -0
- data/spec/bullet_spec.rb +9 -7
- data/spec/integration/active_record/association_spec.rb +79 -17
- data/spec/integration/counter_cache_spec.rb +4 -2
- data/spec/integration/mongoid/association_spec.rb +2 -0
- data/spec/models/address.rb +2 -0
- data/spec/models/author.rb +2 -0
- data/spec/models/base_user.rb +2 -0
- data/spec/models/category.rb +2 -0
- data/spec/models/city.rb +2 -0
- data/spec/models/client.rb +4 -0
- data/spec/models/comment.rb +2 -0
- data/spec/models/company.rb +2 -0
- data/spec/models/country.rb +2 -0
- data/spec/models/document.rb +2 -0
- data/spec/models/entry.rb +2 -0
- data/spec/models/firm.rb +3 -0
- data/spec/models/folder.rb +2 -0
- data/spec/models/group.rb +4 -0
- data/spec/models/mongoid/address.rb +3 -1
- data/spec/models/mongoid/category.rb +4 -2
- data/spec/models/mongoid/comment.rb +3 -1
- data/spec/models/mongoid/company.rb +3 -1
- data/spec/models/mongoid/entry.rb +3 -1
- data/spec/models/mongoid/post.rb +6 -4
- data/spec/models/mongoid/user.rb +2 -0
- data/spec/models/newspaper.rb +2 -0
- data/spec/models/page.rb +2 -0
- data/spec/models/person.rb +2 -0
- data/spec/models/pet.rb +2 -0
- data/spec/models/post.rb +17 -0
- data/spec/models/relationship.rb +2 -0
- data/spec/models/reply.rb +2 -0
- data/spec/models/student.rb +2 -0
- data/spec/models/submission.rb +2 -0
- data/spec/models/teacher.rb +2 -0
- data/spec/models/user.rb +2 -0
- data/spec/models/writer.rb +2 -0
- data/spec/spec_helper.rb +3 -1
- data/spec/support/bullet_ext.rb +2 -0
- data/spec/support/mongo_seed.rb +32 -30
- data/spec/support/rack_double.rb +9 -16
- data/spec/support/sqlite_seed.rb +84 -75
- data/tasks/bullet_tasks.rake +2 -0
- metadata +11 -7
|
@@ -1,16 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module SaveWithBulletSupport
|
|
3
|
-
def
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
end
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def save!(*args)
|
|
11
|
-
was_new_record = new_record?
|
|
12
|
-
super(*args).tap do |result|
|
|
13
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(self) if result && was_new_record
|
|
5
|
+
def _create_record(*)
|
|
6
|
+
super do
|
|
7
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
|
8
|
+
yield(self) if block_given?
|
|
14
9
|
end
|
|
15
10
|
end
|
|
16
11
|
end
|
|
@@ -18,7 +13,7 @@ module Bullet
|
|
|
18
13
|
module ActiveRecord
|
|
19
14
|
def self.enable
|
|
20
15
|
require 'active_record'
|
|
21
|
-
::ActiveRecord::Base.extend(Module.new
|
|
16
|
+
::ActiveRecord::Base.extend(Module.new do
|
|
22
17
|
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
|
23
18
|
result = super
|
|
24
19
|
if Bullet.start?
|
|
@@ -37,11 +32,11 @@ module Bullet
|
|
|
37
32
|
end
|
|
38
33
|
result
|
|
39
34
|
end
|
|
40
|
-
|
|
35
|
+
end)
|
|
41
36
|
|
|
42
37
|
::ActiveRecord::Base.prepend(SaveWithBulletSupport)
|
|
43
38
|
|
|
44
|
-
::ActiveRecord::Relation.prepend(Module.new
|
|
39
|
+
::ActiveRecord::Relation.prepend(Module.new do
|
|
45
40
|
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
46
41
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
47
42
|
def records
|
|
@@ -59,9 +54,9 @@ module Bullet
|
|
|
59
54
|
end
|
|
60
55
|
result
|
|
61
56
|
end
|
|
62
|
-
|
|
57
|
+
end)
|
|
63
58
|
|
|
64
|
-
::ActiveRecord::Associations::Preloader.prepend(Module.new
|
|
59
|
+
::ActiveRecord::Associations::Preloader.prepend(Module.new do
|
|
65
60
|
def preloaders_for_one(association, records, scope)
|
|
66
61
|
if Bullet.start?
|
|
67
62
|
records.compact!
|
|
@@ -74,12 +69,13 @@ module Bullet
|
|
|
74
69
|
end
|
|
75
70
|
super
|
|
76
71
|
end
|
|
77
|
-
|
|
72
|
+
end)
|
|
78
73
|
|
|
79
|
-
::ActiveRecord::FinderMethods.prepend(Module.new
|
|
74
|
+
::ActiveRecord::FinderMethods.prepend(Module.new do
|
|
80
75
|
# add includes in scope
|
|
81
76
|
def find_with_associations
|
|
82
77
|
return super { |r| yield r } if block_given?
|
|
78
|
+
|
|
83
79
|
records = super
|
|
84
80
|
if Bullet.start?
|
|
85
81
|
associations = (eager_load_values + includes_values).uniq
|
|
@@ -90,15 +86,15 @@ module Bullet
|
|
|
90
86
|
end
|
|
91
87
|
records
|
|
92
88
|
end
|
|
93
|
-
|
|
89
|
+
end)
|
|
94
90
|
|
|
95
|
-
::ActiveRecord::Associations::JoinDependency.prepend(Module.new
|
|
91
|
+
::ActiveRecord::Associations::JoinDependency.prepend(Module.new do
|
|
96
92
|
def instantiate(result_set, &block)
|
|
97
93
|
@bullet_eager_loadings = {}
|
|
98
94
|
records = super
|
|
99
95
|
|
|
100
96
|
if Bullet.start?
|
|
101
|
-
@bullet_eager_loadings.each do |
|
|
97
|
+
@bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
|
|
102
98
|
objects = eager_loadings_hash.keys
|
|
103
99
|
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
104
100
|
end
|
|
@@ -112,14 +108,14 @@ module Bullet
|
|
|
112
108
|
parent.children.each do |node|
|
|
113
109
|
key = aliases.column_alias(node, node.primary_key)
|
|
114
110
|
id = row[key]
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
111
|
+
next unless id.nil?
|
|
112
|
+
|
|
113
|
+
associations = node.reflection.name
|
|
114
|
+
Bullet::Detector::Association.add_object_associations(ar_parent, associations)
|
|
115
|
+
Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
|
|
116
|
+
@bullet_eager_loadings[ar_parent.class] ||= {}
|
|
117
|
+
@bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
|
|
118
|
+
@bullet_eager_loadings[ar_parent.class][ar_parent] << associations
|
|
123
119
|
end
|
|
124
120
|
end
|
|
125
121
|
end
|
|
@@ -142,19 +138,23 @@ module Bullet
|
|
|
142
138
|
|
|
143
139
|
result
|
|
144
140
|
end
|
|
145
|
-
|
|
141
|
+
end)
|
|
146
142
|
|
|
147
|
-
::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new
|
|
143
|
+
::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new do
|
|
148
144
|
def load_target
|
|
149
145
|
records = super
|
|
150
146
|
|
|
151
147
|
if Bullet.start?
|
|
152
|
-
if
|
|
153
|
-
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
|
|
154
|
-
association =
|
|
148
|
+
if is_a? ::ActiveRecord::Associations::ThroughAssociation
|
|
149
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
|
|
150
|
+
association = owner.association reflection.through_reflection.name
|
|
155
151
|
Array(association.target).each do |through_record|
|
|
156
152
|
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
|
|
157
153
|
end
|
|
154
|
+
|
|
155
|
+
if reflection.through_reflection != through_reflection
|
|
156
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
|
|
157
|
+
end
|
|
158
158
|
end
|
|
159
159
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
|
|
160
160
|
if records.first.class.name !~ /^HABTM_/
|
|
@@ -183,9 +183,9 @@ module Bullet
|
|
|
183
183
|
end
|
|
184
184
|
super
|
|
185
185
|
end
|
|
186
|
-
|
|
186
|
+
end)
|
|
187
187
|
|
|
188
|
-
::ActiveRecord::Associations::SingularAssociation.prepend(Module.new
|
|
188
|
+
::ActiveRecord::Associations::SingularAssociation.prepend(Module.new do
|
|
189
189
|
# call has_one and belongs_to associations
|
|
190
190
|
def target
|
|
191
191
|
result = super()
|
|
@@ -201,9 +201,9 @@ module Bullet
|
|
|
201
201
|
end
|
|
202
202
|
result
|
|
203
203
|
end
|
|
204
|
-
|
|
204
|
+
end)
|
|
205
205
|
|
|
206
|
-
::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new
|
|
206
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new do
|
|
207
207
|
def empty?
|
|
208
208
|
result = super
|
|
209
209
|
if Bullet.start? && !reflection.has_cached_counter?
|
|
@@ -214,12 +214,19 @@ module Bullet
|
|
|
214
214
|
|
|
215
215
|
def count_records
|
|
216
216
|
result = reflection.has_cached_counter?
|
|
217
|
-
if Bullet.start? && !result && !
|
|
217
|
+
if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
|
|
218
218
|
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
219
219
|
end
|
|
220
220
|
super
|
|
221
221
|
end
|
|
222
|
-
|
|
222
|
+
end)
|
|
223
|
+
|
|
224
|
+
::ActiveRecord::Associations::BelongsToAssociation.prepend(Module.new do
|
|
225
|
+
def writer(record)
|
|
226
|
+
Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
|
|
227
|
+
super
|
|
228
|
+
end
|
|
229
|
+
end)
|
|
223
230
|
end
|
|
224
231
|
end
|
|
225
232
|
end
|
data/lib/bullet/dependency.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Dependency
|
|
3
5
|
def mongoid?
|
|
@@ -27,7 +29,7 @@ module Bullet
|
|
|
27
29
|
elsif active_record52?
|
|
28
30
|
'active_record52'
|
|
29
31
|
else
|
|
30
|
-
raise "Bullet does not support active_record #{::ActiveRecord::VERSION} yet"
|
|
32
|
+
raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
|
|
31
33
|
end
|
|
32
34
|
end
|
|
33
35
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Detector
|
|
3
5
|
class Association < Base
|
|
@@ -7,7 +9,7 @@ module Bullet
|
|
|
7
9
|
return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
|
|
8
10
|
return unless object.primary_key_value
|
|
9
11
|
|
|
10
|
-
Bullet.debug('Detector::Association#add_object_associations'
|
|
12
|
+
Bullet.debug('Detector::Association#add_object_associations', "object: #{object.bullet_key}, associations: #{associations}")
|
|
11
13
|
object_associations.add(object.bullet_key, associations)
|
|
12
14
|
end
|
|
13
15
|
|
|
@@ -16,7 +18,7 @@ module Bullet
|
|
|
16
18
|
return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
|
|
17
19
|
return unless object.primary_key_value
|
|
18
20
|
|
|
19
|
-
Bullet.debug('Detector::Association#add_call_object_associations'
|
|
21
|
+
Bullet.debug('Detector::Association#add_call_object_associations', "object: #{object.bullet_key}, associations: #{associations}")
|
|
20
22
|
call_object_associations.add(object.bullet_key, associations)
|
|
21
23
|
end
|
|
22
24
|
|
|
@@ -43,31 +45,31 @@ module Bullet
|
|
|
43
45
|
# e.g. { "Post:1" => [:comments] }
|
|
44
46
|
# the object_associations keep all associations that may be or may no be
|
|
45
47
|
# unpreload associations or unused preload associations.
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
48
|
+
def object_associations
|
|
49
|
+
Thread.current[:bullet_object_associations]
|
|
50
|
+
end
|
|
49
51
|
|
|
50
52
|
# call_object_associations keep the object relationships
|
|
51
53
|
# that object.associations is called.
|
|
52
54
|
# e.g. { "Post:1" => [:comments] }
|
|
53
55
|
# they are used to detect unused preload associations.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
def call_object_associations
|
|
57
|
+
Thread.current[:bullet_call_object_associations]
|
|
58
|
+
end
|
|
57
59
|
|
|
58
60
|
# inversed_objects keeps object relationships
|
|
59
61
|
# that association is inversed.
|
|
60
62
|
# e.g. { "Comment:1" => ["post"] }
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
63
|
+
def inversed_objects
|
|
64
|
+
Thread.current[:bullet_inversed_objects]
|
|
65
|
+
end
|
|
64
66
|
|
|
65
67
|
# eager_loadings keep the object relationships
|
|
66
68
|
# that the associations are preloaded by find :include.
|
|
67
69
|
# e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
70
|
+
def eager_loadings
|
|
71
|
+
Thread.current[:bullet_eager_loadings]
|
|
72
|
+
end
|
|
71
73
|
end
|
|
72
74
|
end
|
|
73
75
|
end
|
data/lib/bullet/detector/base.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Detector
|
|
3
5
|
class CounterCache < Base
|
|
@@ -16,6 +18,7 @@ module Bullet
|
|
|
16
18
|
def add_possible_objects(object_or_objects)
|
|
17
19
|
return unless Bullet.start?
|
|
18
20
|
return unless Bullet.counter_cache_enable?
|
|
21
|
+
|
|
19
22
|
objects = Array(object_or_objects)
|
|
20
23
|
return if objects.map(&:primary_key_value).compact.empty?
|
|
21
24
|
|
|
@@ -32,7 +35,7 @@ module Bullet
|
|
|
32
35
|
impossible_objects.add object.bullet_key
|
|
33
36
|
end
|
|
34
37
|
|
|
35
|
-
def conditions_met?(object,
|
|
38
|
+
def conditions_met?(object, _associations)
|
|
36
39
|
possible_objects.include?(object.bullet_key) && !impossible_objects.include?(object.bullet_key)
|
|
37
40
|
end
|
|
38
41
|
|
|
@@ -46,14 +49,14 @@ module Bullet
|
|
|
46
49
|
|
|
47
50
|
private
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
def create_notification(klazz, associations)
|
|
53
|
+
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:counter_cache, klazz)
|
|
51
54
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
end
|
|
55
|
+
if notify_associations.present?
|
|
56
|
+
notice = Bullet::Notification::CounterCache.new klazz, notify_associations
|
|
57
|
+
Bullet.notification_collector.add notice
|
|
56
58
|
end
|
|
59
|
+
end
|
|
57
60
|
end
|
|
58
61
|
end
|
|
59
62
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Detector
|
|
3
5
|
class NPlusOneQuery < Association
|
|
@@ -14,9 +16,10 @@ module Bullet
|
|
|
14
16
|
return unless Bullet.n_plus_one_query_enable?
|
|
15
17
|
return unless object.primary_key_value
|
|
16
18
|
return if inversed_objects.include?(object.bullet_key, associations)
|
|
19
|
+
|
|
17
20
|
add_call_object_associations(object, associations)
|
|
18
21
|
|
|
19
|
-
Bullet.debug('Detector::NPlusOneQuery#call_association'
|
|
22
|
+
Bullet.debug('Detector::NPlusOneQuery#call_association', "object: #{object.bullet_key}, associations: #{associations}")
|
|
20
23
|
if !excluded_stacktrace_path? && conditions_met?(object, associations)
|
|
21
24
|
Bullet.debug('detect n + 1 query', "object: #{object.bullet_key}, associations: #{associations}")
|
|
22
25
|
create_notification caller_in_project, object.class.to_s, associations
|
|
@@ -26,10 +29,11 @@ module Bullet
|
|
|
26
29
|
def add_possible_objects(object_or_objects)
|
|
27
30
|
return unless Bullet.start?
|
|
28
31
|
return unless Bullet.n_plus_one_query_enable?
|
|
32
|
+
|
|
29
33
|
objects = Array(object_or_objects)
|
|
30
34
|
return if objects.map(&:primary_key_value).compact.empty?
|
|
31
35
|
|
|
32
|
-
Bullet.debug('Detector::NPlusOneQuery#add_possible_objects'
|
|
36
|
+
Bullet.debug('Detector::NPlusOneQuery#add_possible_objects', "objects: #{objects.map(&:bullet_key).join(', ')}")
|
|
33
37
|
objects.each { |object| possible_objects.add object.bullet_key }
|
|
34
38
|
end
|
|
35
39
|
|
|
@@ -38,7 +42,7 @@ module Bullet
|
|
|
38
42
|
return unless Bullet.n_plus_one_query_enable?
|
|
39
43
|
return unless object.primary_key_value
|
|
40
44
|
|
|
41
|
-
Bullet.debug('Detector::NPlusOneQuery#add_impossible_object'
|
|
45
|
+
Bullet.debug('Detector::NPlusOneQuery#add_impossible_object', "object: #{object.bullet_key}")
|
|
42
46
|
impossible_objects.add object.bullet_key
|
|
43
47
|
end
|
|
44
48
|
|
|
@@ -47,7 +51,7 @@ module Bullet
|
|
|
47
51
|
return unless Bullet.n_plus_one_query_enable?
|
|
48
52
|
return unless object.primary_key_value
|
|
49
53
|
|
|
50
|
-
Bullet.debug('Detector::NPlusOneQuery#add_inversed_object'
|
|
54
|
+
Bullet.debug('Detector::NPlusOneQuery#add_inversed_object', "object: #{object.bullet_key}, association: #{association}")
|
|
51
55
|
inversed_objects.add object.bullet_key, association
|
|
52
56
|
end
|
|
53
57
|
|
|
@@ -67,14 +71,12 @@ module Bullet
|
|
|
67
71
|
# check if object => associations already exists in object_associations.
|
|
68
72
|
def association?(object, associations)
|
|
69
73
|
value = object_associations[object.bullet_key]
|
|
70
|
-
|
|
71
|
-
value.each do |v|
|
|
74
|
+
value&.each do |v|
|
|
72
75
|
# associations == v comparison order is important here because
|
|
73
76
|
# v variable might be a squeel node where :== method is redefined,
|
|
74
77
|
# so it does not compare values at all and return unexpected results
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
end
|
|
78
|
+
result = v.is_a?(Hash) ? v.key?(associations) : associations == v
|
|
79
|
+
return true if result
|
|
78
80
|
end
|
|
79
81
|
|
|
80
82
|
false
|
|
@@ -82,14 +84,14 @@ module Bullet
|
|
|
82
84
|
|
|
83
85
|
private
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
+
def create_notification(callers, klazz, associations)
|
|
88
|
+
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:n_plus_one_query, klazz)
|
|
87
89
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
end
|
|
90
|
+
if notify_associations.present?
|
|
91
|
+
notice = Bullet::Notification::NPlusOneQuery.new(callers, klazz, notify_associations)
|
|
92
|
+
Bullet.notification_collector.add(notice)
|
|
92
93
|
end
|
|
94
|
+
end
|
|
93
95
|
end
|
|
94
96
|
end
|
|
95
97
|
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Detector
|
|
3
5
|
class UnusedEagerLoading < Association
|
|
@@ -30,13 +32,15 @@ module Bullet
|
|
|
30
32
|
Bullet.debug('Detector::UnusedEagerLoading#add_eager_loadings', "objects: #{objects.map(&:bullet_key).join(', ')}, associations: #{associations}")
|
|
31
33
|
bullet_keys = objects.map(&:bullet_key)
|
|
32
34
|
|
|
33
|
-
to_add
|
|
34
|
-
|
|
35
|
+
to_add = []
|
|
36
|
+
to_merge = []
|
|
37
|
+
to_delete = []
|
|
38
|
+
eager_loadings.each do |k, _v|
|
|
35
39
|
key_objects_overlap = k & bullet_keys
|
|
36
40
|
|
|
37
41
|
next if key_objects_overlap.empty?
|
|
38
42
|
|
|
39
|
-
bullet_keys
|
|
43
|
+
bullet_keys -= k
|
|
40
44
|
if key_objects_overlap == k
|
|
41
45
|
to_add << [k, associations]
|
|
42
46
|
else
|
|
@@ -57,29 +61,30 @@ module Bullet
|
|
|
57
61
|
|
|
58
62
|
private
|
|
59
63
|
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
def create_notification(callers, klazz, associations)
|
|
65
|
+
notify_associations = Array(associations) - Bullet.get_whitelist_associations(:unused_eager_loading, klazz)
|
|
62
66
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
end
|
|
67
|
+
if notify_associations.present?
|
|
68
|
+
notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
|
|
69
|
+
Bullet.notification_collector.add(notice)
|
|
67
70
|
end
|
|
71
|
+
end
|
|
68
72
|
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
all.merge coa
|
|
75
|
-
end
|
|
76
|
-
all.to_a
|
|
77
|
-
end
|
|
73
|
+
def call_associations(bullet_key, associations)
|
|
74
|
+
all = Set.new
|
|
75
|
+
eager_loadings.similarly_associated(bullet_key, associations).each do |related_bullet_key|
|
|
76
|
+
coa = call_object_associations[related_bullet_key]
|
|
77
|
+
next if coa.nil?
|
|
78
78
|
|
|
79
|
-
|
|
80
|
-
potential_associations = associations - call_associations(bullet_key, associations)
|
|
81
|
-
potential_associations.reject { |a| a.is_a?(Hash) }
|
|
79
|
+
all.merge coa
|
|
82
80
|
end
|
|
81
|
+
all.to_a
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def diff_object_associations(bullet_key, associations)
|
|
85
|
+
potential_associations = associations - call_associations(bullet_key, associations)
|
|
86
|
+
potential_associations.reject { |a| a.is_a?(Hash) }
|
|
87
|
+
end
|
|
83
88
|
end
|
|
84
89
|
end
|
|
85
90
|
end
|
data/lib/bullet/detector.rb
CHANGED
data/lib/bullet/ext/object.rb
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Object
|
|
2
4
|
def bullet_key
|
|
3
|
-
"#{self.class}:#{
|
|
5
|
+
"#{self.class}:#{primary_key_value}"
|
|
4
6
|
end
|
|
5
7
|
|
|
6
8
|
def primary_key_value
|
|
7
9
|
if self.class.respond_to?(:primary_keys) && self.class.primary_keys
|
|
8
|
-
self.class.primary_keys.map { |primary_key|
|
|
10
|
+
self.class.primary_keys.map { |primary_key| send primary_key }.join(',')
|
|
9
11
|
elsif self.class.respond_to?(:primary_key) && self.class.primary_key
|
|
10
|
-
|
|
12
|
+
send self.class.primary_key
|
|
11
13
|
else
|
|
12
|
-
|
|
14
|
+
id
|
|
13
15
|
end
|
|
14
16
|
end
|
|
15
17
|
end
|
data/lib/bullet/ext/string.rb
CHANGED
data/lib/bullet/mongoid4x.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Mongoid
|
|
3
5
|
def self.enable
|
|
@@ -21,13 +23,16 @@ module Bullet
|
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def each(&block)
|
|
24
|
-
|
|
26
|
+
return to_enum unless block_given?
|
|
27
|
+
|
|
28
|
+
records = []
|
|
29
|
+
origin_each { |record| records << record }
|
|
25
30
|
if records.length > 1
|
|
26
31
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
27
32
|
elsif records.size == 1
|
|
28
33
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
29
34
|
end
|
|
30
|
-
|
|
35
|
+
records.each(&block)
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
def eager_load(docs)
|
data/lib/bullet/mongoid5x.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Mongoid
|
|
3
5
|
def self.enable
|
|
@@ -21,13 +23,16 @@ module Bullet
|
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def each(&block)
|
|
24
|
-
|
|
26
|
+
return to_enum unless block_given?
|
|
27
|
+
|
|
28
|
+
records = []
|
|
29
|
+
origin_each { |record| records << record }
|
|
25
30
|
if records.length > 1
|
|
26
31
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
27
32
|
elsif records.size == 1
|
|
28
33
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
29
34
|
end
|
|
30
|
-
|
|
35
|
+
records.each(&block)
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
def eager_load(docs)
|
data/lib/bullet/mongoid6x.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Bullet
|
|
2
4
|
module Mongoid
|
|
3
5
|
def self.enable
|
|
@@ -8,26 +10,29 @@ module Bullet
|
|
|
8
10
|
alias_method :origin_each, :each
|
|
9
11
|
alias_method :origin_eager_load, :eager_load
|
|
10
12
|
|
|
11
|
-
def first
|
|
12
|
-
result = origin_first
|
|
13
|
+
def first(opt = {})
|
|
14
|
+
result = origin_first(opt)
|
|
13
15
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
14
16
|
result
|
|
15
17
|
end
|
|
16
18
|
|
|
17
|
-
def last
|
|
18
|
-
result = origin_last
|
|
19
|
+
def last(opt = {})
|
|
20
|
+
result = origin_last(opt)
|
|
19
21
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
20
22
|
result
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
def each(&block)
|
|
24
|
-
|
|
26
|
+
return to_enum unless block_given?
|
|
27
|
+
|
|
28
|
+
records = []
|
|
29
|
+
origin_each { |record| records << record }
|
|
25
30
|
if records.length > 1
|
|
26
31
|
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
27
32
|
elsif records.size == 1
|
|
28
33
|
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
29
34
|
end
|
|
30
|
-
|
|
35
|
+
records.each(&block)
|
|
31
36
|
end
|
|
32
37
|
|
|
33
38
|
def eager_load(docs)
|