bullet 5.5.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/.travis.yml +1 -5
- data/CHANGELOG.md +31 -1
- data/Gemfile +6 -1
- data/Gemfile.rails-5.0 +1 -1
- data/Gemfile.rails-5.1 +15 -0
- data/Gemfile.rails-5.2 +15 -0
- data/Guardfile +2 -2
- data/Hacking.md +1 -1
- data/README.md +20 -23
- data/Rakefile +20 -21
- data/bullet.gemspec +20 -15
- 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 +102 -91
- data/lib/bullet/active_record52.rb +232 -0
- data/lib/bullet/dependency.rb +9 -1
- data/lib/bullet/detector/association.rb +17 -14
- data/lib/bullet/detector/base.rb +2 -0
- data/lib/bullet/detector/counter_cache.rb +14 -10
- data/lib/bullet/detector/n_plus_one_query.rb +21 -17
- data/lib/bullet/detector/unused_eager_loading.rb +30 -24
- 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 +26 -19
- data/lib/bullet/notification/counter_cache.rb +3 -1
- data/lib/bullet/notification/n_plus_one_query.rb +7 -4
- data/lib/bullet/notification/unused_eager_loading.rb +7 -4
- data/lib/bullet/notification.rb +2 -0
- data/lib/bullet/notification_collector.rb +2 -1
- data/lib/bullet/rack.rb +22 -18
- 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 -18
- data/lib/bullet/version.rb +3 -2
- data/lib/bullet.rb +43 -33
- data/lib/generators/bullet/install_generator.rb +48 -0
- data/perf/benchmark.rb +12 -12
- data/rails/init.rb +2 -0
- data/spec/bullet/detector/association_spec.rb +6 -4
- data/spec/bullet/detector/base_spec.rb +2 -0
- data/spec/bullet/detector/counter_cache_spec.rb +13 -11
- data/spec/bullet/detector/n_plus_one_query_spec.rb +71 -46
- data/spec/bullet/detector/unused_eager_loading_spec.rb +21 -19
- data/spec/bullet/ext/object_spec.rb +10 -8
- data/spec/bullet/ext/string_spec.rb +7 -5
- data/spec/bullet/notification/base_spec.rb +39 -38
- data/spec/bullet/notification/counter_cache_spec.rb +5 -3
- data/spec/bullet/notification/n_plus_one_query_spec.rb +5 -3
- data/spec/bullet/notification/unused_eager_loading_spec.rb +4 -2
- data/spec/bullet/notification_collector_spec.rb +12 -10
- data/spec/bullet/rack_spec.rb +48 -47
- data/spec/bullet/registry/association_spec.rb +12 -10
- data/spec/bullet/registry/base_spec.rb +22 -20
- data/spec/bullet/registry/object_spec.rb +6 -4
- data/spec/bullet_spec.rb +24 -3
- data/spec/integration/{active_record4 → active_record}/association_spec.rb +176 -114
- data/spec/integration/counter_cache_spec.rb +15 -13
- data/spec/integration/mongoid/association_spec.rb +35 -33
- 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 +3 -1
- data/spec/models/company.rb +2 -0
- data/spec/models/country.rb +2 -0
- data/spec/models/document.rb +4 -2
- 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 +3 -1
- 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 +15 -13
- data/spec/support/bullet_ext.rb +2 -0
- data/spec/support/mongo_seed.rb +36 -42
- data/spec/support/rack_double.rb +13 -19
- data/spec/support/sqlite_seed.rb +84 -76
- data/tasks/bullet_tasks.rake +4 -2
- data/test.sh +2 -0
- data/update.sh +2 -0
- metadata +17 -11
- data/spec/integration/active_record5/association_spec.rb +0 -768
|
@@ -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,38 +13,34 @@ module Bullet
|
|
|
18
13
|
module ActiveRecord
|
|
19
14
|
def self.enable
|
|
20
15
|
require 'active_record'
|
|
21
|
-
::ActiveRecord::Base.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
result
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
|
33
|
-
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
|
34
|
-
end
|
|
35
|
-
elsif result.is_a? ::ActiveRecord::Base
|
|
36
|
-
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
|
37
|
-
Bullet::Detector::CounterCache.add_impossible_object(result)
|
|
16
|
+
::ActiveRecord::Base.extend(Module.new do
|
|
17
|
+
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
|
18
|
+
result = super
|
|
19
|
+
if Bullet.start?
|
|
20
|
+
if result.is_a? Array
|
|
21
|
+
if result.size > 1
|
|
22
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
23
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
|
24
|
+
elsif result.size == 1
|
|
25
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
|
26
|
+
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
|
38
27
|
end
|
|
28
|
+
elsif result.is_a? ::ActiveRecord::Base
|
|
29
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
|
30
|
+
Bullet::Detector::CounterCache.add_impossible_object(result)
|
|
39
31
|
end
|
|
40
|
-
result
|
|
41
32
|
end
|
|
33
|
+
result
|
|
42
34
|
end
|
|
43
|
-
end
|
|
35
|
+
end)
|
|
44
36
|
|
|
45
37
|
::ActiveRecord::Base.prepend(SaveWithBulletSupport)
|
|
46
38
|
|
|
47
|
-
::ActiveRecord::Relation.
|
|
48
|
-
alias_method :origin_records, :records
|
|
39
|
+
::ActiveRecord::Relation.prepend(Module.new do
|
|
49
40
|
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
50
41
|
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
51
42
|
def records
|
|
52
|
-
result =
|
|
43
|
+
result = super
|
|
53
44
|
if Bullet.start?
|
|
54
45
|
if result.first.class.name !~ /^HABTM_/
|
|
55
46
|
if result.size > 1
|
|
@@ -63,11 +54,9 @@ module Bullet
|
|
|
63
54
|
end
|
|
64
55
|
result
|
|
65
56
|
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
::ActiveRecord::Associations::Preloader.class_eval do
|
|
69
|
-
alias_method :origin_preloaders_for_one, :preloaders_for_one
|
|
57
|
+
end)
|
|
70
58
|
|
|
59
|
+
::ActiveRecord::Associations::Preloader.prepend(Module.new do
|
|
71
60
|
def preloaders_for_one(association, records, scope)
|
|
72
61
|
if Bullet.start?
|
|
73
62
|
records.compact!
|
|
@@ -78,16 +67,16 @@ module Bullet
|
|
|
78
67
|
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
79
68
|
end
|
|
80
69
|
end
|
|
81
|
-
|
|
70
|
+
super
|
|
82
71
|
end
|
|
83
|
-
end
|
|
72
|
+
end)
|
|
84
73
|
|
|
85
|
-
::ActiveRecord::FinderMethods.
|
|
74
|
+
::ActiveRecord::FinderMethods.prepend(Module.new do
|
|
86
75
|
# add includes in scope
|
|
87
|
-
alias_method :origin_find_with_associations, :find_with_associations
|
|
88
76
|
def find_with_associations
|
|
89
|
-
return
|
|
90
|
-
|
|
77
|
+
return super { |r| yield r } if block_given?
|
|
78
|
+
|
|
79
|
+
records = super
|
|
91
80
|
if Bullet.start?
|
|
92
81
|
associations = (eager_load_values + includes_values).uniq
|
|
93
82
|
records.each do |record|
|
|
@@ -97,24 +86,37 @@ module Bullet
|
|
|
97
86
|
end
|
|
98
87
|
records
|
|
99
88
|
end
|
|
100
|
-
end
|
|
89
|
+
end)
|
|
101
90
|
|
|
102
|
-
::ActiveRecord::Associations::JoinDependency.
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
91
|
+
::ActiveRecord::Associations::JoinDependency.prepend(Module.new do
|
|
92
|
+
if ::ActiveRecord::Associations::JoinDependency.instance_method(:instantiate).parameters.last[0] == :block
|
|
93
|
+
# ActiveRecord >= 5.1.5
|
|
94
|
+
def instantiate(result_set, &block)
|
|
95
|
+
@bullet_eager_loadings = {}
|
|
96
|
+
records = super
|
|
106
97
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
98
|
+
if Bullet.start?
|
|
99
|
+
@bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
|
|
100
|
+
objects = eager_loadings_hash.keys
|
|
101
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
records
|
|
105
|
+
end
|
|
106
|
+
else
|
|
107
|
+
# ActiveRecord <= 5.1.4
|
|
108
|
+
def instantiate(result_set, aliases)
|
|
109
|
+
@bullet_eager_loadings = {}
|
|
110
|
+
records = super
|
|
110
111
|
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
112
|
+
if Bullet.start?
|
|
113
|
+
@bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
|
|
114
|
+
objects = eager_loadings_hash.keys
|
|
115
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
116
|
+
end
|
|
115
117
|
end
|
|
118
|
+
records
|
|
116
119
|
end
|
|
117
|
-
records
|
|
118
120
|
end
|
|
119
121
|
|
|
120
122
|
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
|
@@ -123,24 +125,24 @@ module Bullet
|
|
|
123
125
|
parent.children.each do |node|
|
|
124
126
|
key = aliases.column_alias(node, node.primary_key)
|
|
125
127
|
id = row[key]
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
128
|
+
next unless id.nil?
|
|
129
|
+
|
|
130
|
+
associations = node.reflection.name
|
|
131
|
+
Bullet::Detector::Association.add_object_associations(ar_parent, associations)
|
|
132
|
+
Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
|
|
133
|
+
@bullet_eager_loadings[ar_parent.class] ||= {}
|
|
134
|
+
@bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
|
|
135
|
+
@bullet_eager_loadings[ar_parent.class][ar_parent] << associations
|
|
134
136
|
end
|
|
135
137
|
end
|
|
136
138
|
end
|
|
137
139
|
|
|
138
|
-
|
|
140
|
+
super
|
|
139
141
|
end
|
|
140
142
|
|
|
141
143
|
# call join associations
|
|
142
144
|
def construct_model(record, node, row, model_cache, id, aliases)
|
|
143
|
-
result =
|
|
145
|
+
result = super
|
|
144
146
|
|
|
145
147
|
if Bullet.start?
|
|
146
148
|
associations = node.reflection.name
|
|
@@ -153,21 +155,28 @@ module Bullet
|
|
|
153
155
|
|
|
154
156
|
result
|
|
155
157
|
end
|
|
156
|
-
end
|
|
158
|
+
end)
|
|
157
159
|
|
|
158
|
-
::ActiveRecord::Associations::CollectionAssociation.
|
|
159
|
-
# call one to many associations
|
|
160
|
-
alias_method :origin_load_target, :load_target
|
|
160
|
+
::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new do
|
|
161
161
|
def load_target
|
|
162
|
-
records =
|
|
162
|
+
records = super
|
|
163
163
|
|
|
164
164
|
if Bullet.start?
|
|
165
|
-
if
|
|
166
|
-
|
|
167
|
-
|
|
165
|
+
if is_a? ::ActiveRecord::Associations::ThroughAssociation
|
|
166
|
+
refl = reflection.through_reflection
|
|
167
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
|
|
168
|
+
association = owner.association refl.name
|
|
168
169
|
Array(association.target).each do |through_record|
|
|
169
170
|
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
|
|
170
171
|
end
|
|
172
|
+
|
|
173
|
+
if refl.through_reflection?
|
|
174
|
+
while refl.through_reflection?
|
|
175
|
+
refl = refl.through_reflection
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, refl.name)
|
|
179
|
+
end
|
|
171
180
|
end
|
|
172
181
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
|
|
173
182
|
if records.first.class.name !~ /^HABTM_/
|
|
@@ -183,28 +192,25 @@ module Bullet
|
|
|
183
192
|
records
|
|
184
193
|
end
|
|
185
194
|
|
|
186
|
-
alias_method :origin_empty?, :empty?
|
|
187
195
|
def empty?
|
|
188
196
|
if Bullet.start? && !reflection.has_cached_counter?
|
|
189
197
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
190
198
|
end
|
|
191
|
-
|
|
199
|
+
super
|
|
192
200
|
end
|
|
193
201
|
|
|
194
|
-
alias_method :origin_include?, :include?
|
|
195
202
|
def include?(object)
|
|
196
203
|
if Bullet.start?
|
|
197
204
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
198
205
|
end
|
|
199
|
-
|
|
206
|
+
super
|
|
200
207
|
end
|
|
201
|
-
end
|
|
208
|
+
end)
|
|
202
209
|
|
|
203
|
-
::ActiveRecord::Associations::SingularAssociation.
|
|
210
|
+
::ActiveRecord::Associations::SingularAssociation.prepend(Module.new do
|
|
204
211
|
# call has_one and belongs_to associations
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
result = origin_reader(force_reload)
|
|
212
|
+
def target
|
|
213
|
+
result = super()
|
|
208
214
|
if Bullet.start?
|
|
209
215
|
if owner.class.name !~ /^HABTM_/ && !@inversed
|
|
210
216
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
@@ -217,27 +223,32 @@ module Bullet
|
|
|
217
223
|
end
|
|
218
224
|
result
|
|
219
225
|
end
|
|
220
|
-
end
|
|
226
|
+
end)
|
|
221
227
|
|
|
222
|
-
::ActiveRecord::Associations::HasManyAssociation.
|
|
223
|
-
alias_method :origin_many_empty?, :empty?
|
|
228
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new do
|
|
224
229
|
def empty?
|
|
225
|
-
result =
|
|
230
|
+
result = super
|
|
226
231
|
if Bullet.start? && !reflection.has_cached_counter?
|
|
227
232
|
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
228
233
|
end
|
|
229
234
|
result
|
|
230
235
|
end
|
|
231
236
|
|
|
232
|
-
alias_method :origin_count_records, :count_records
|
|
233
237
|
def count_records
|
|
234
238
|
result = reflection.has_cached_counter?
|
|
235
|
-
if Bullet.start? && !result && !
|
|
239
|
+
if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
|
|
236
240
|
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
237
241
|
end
|
|
238
|
-
|
|
242
|
+
super
|
|
239
243
|
end
|
|
240
|
-
end
|
|
244
|
+
end)
|
|
245
|
+
|
|
246
|
+
::ActiveRecord::Associations::BelongsToAssociation.prepend(Module.new do
|
|
247
|
+
def writer(record)
|
|
248
|
+
Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
|
|
249
|
+
super
|
|
250
|
+
end
|
|
251
|
+
end)
|
|
241
252
|
end
|
|
242
253
|
end
|
|
243
254
|
end
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Bullet
|
|
4
|
+
module SaveWithBulletSupport
|
|
5
|
+
def _create_record(*)
|
|
6
|
+
super do
|
|
7
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
|
8
|
+
yield(self) if block_given?
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ActiveRecord
|
|
14
|
+
def self.enable
|
|
15
|
+
require 'active_record'
|
|
16
|
+
::ActiveRecord::Base.extend(Module.new do
|
|
17
|
+
def find_by_sql(sql, binds = [], preparable: nil, &block)
|
|
18
|
+
result = super
|
|
19
|
+
if Bullet.start?
|
|
20
|
+
if result.is_a? Array
|
|
21
|
+
if result.size > 1
|
|
22
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
23
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
|
24
|
+
elsif result.size == 1
|
|
25
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
|
26
|
+
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
|
27
|
+
end
|
|
28
|
+
elsif result.is_a? ::ActiveRecord::Base
|
|
29
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
|
|
30
|
+
Bullet::Detector::CounterCache.add_impossible_object(result)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
result
|
|
34
|
+
end
|
|
35
|
+
end)
|
|
36
|
+
|
|
37
|
+
::ActiveRecord::Base.prepend(SaveWithBulletSupport)
|
|
38
|
+
|
|
39
|
+
::ActiveRecord::Relation.prepend(Module.new do
|
|
40
|
+
# if select a collection of objects, then these objects have possible to cause N+1 query.
|
|
41
|
+
# if select only one object, then the only one object has impossible to cause N+1 query.
|
|
42
|
+
def records
|
|
43
|
+
result = super
|
|
44
|
+
if Bullet.start?
|
|
45
|
+
if result.first.class.name !~ /^HABTM_/
|
|
46
|
+
if result.size > 1
|
|
47
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
|
|
48
|
+
Bullet::Detector::CounterCache.add_possible_objects(result)
|
|
49
|
+
elsif result.size == 1
|
|
50
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
|
|
51
|
+
Bullet::Detector::CounterCache.add_impossible_object(result.first)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
result
|
|
56
|
+
end
|
|
57
|
+
end)
|
|
58
|
+
|
|
59
|
+
::ActiveRecord::Associations::Preloader.prepend(Module.new do
|
|
60
|
+
def preloaders_for_one(association, records, scope)
|
|
61
|
+
if Bullet.start?
|
|
62
|
+
records.compact!
|
|
63
|
+
if records.first.class.name !~ /^HABTM_/
|
|
64
|
+
records.each do |record|
|
|
65
|
+
Bullet::Detector::Association.add_object_associations(record, association)
|
|
66
|
+
end
|
|
67
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
super
|
|
71
|
+
end
|
|
72
|
+
end)
|
|
73
|
+
|
|
74
|
+
::ActiveRecord::FinderMethods.prepend(Module.new do
|
|
75
|
+
# add includes in scope
|
|
76
|
+
def find_with_associations
|
|
77
|
+
return super { |r| yield r } if block_given?
|
|
78
|
+
|
|
79
|
+
records = super
|
|
80
|
+
if Bullet.start?
|
|
81
|
+
associations = (eager_load_values + includes_values).uniq
|
|
82
|
+
records.each do |record|
|
|
83
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
84
|
+
end
|
|
85
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
|
|
86
|
+
end
|
|
87
|
+
records
|
|
88
|
+
end
|
|
89
|
+
end)
|
|
90
|
+
|
|
91
|
+
::ActiveRecord::Associations::JoinDependency.prepend(Module.new do
|
|
92
|
+
def instantiate(result_set, &block)
|
|
93
|
+
@bullet_eager_loadings = {}
|
|
94
|
+
records = super
|
|
95
|
+
|
|
96
|
+
if Bullet.start?
|
|
97
|
+
@bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
|
|
98
|
+
objects = eager_loadings_hash.keys
|
|
99
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
records
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def construct(ar_parent, parent, row, rs, seen, model_cache, aliases)
|
|
106
|
+
if Bullet.start?
|
|
107
|
+
unless ar_parent.nil?
|
|
108
|
+
parent.children.each do |node|
|
|
109
|
+
key = aliases.column_alias(node, node.primary_key)
|
|
110
|
+
id = row[key]
|
|
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
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
super
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# call join associations
|
|
127
|
+
def construct_model(record, node, row, model_cache, id, aliases)
|
|
128
|
+
result = super
|
|
129
|
+
|
|
130
|
+
if Bullet.start?
|
|
131
|
+
associations = node.reflection.name
|
|
132
|
+
Bullet::Detector::Association.add_object_associations(record, associations)
|
|
133
|
+
Bullet::Detector::NPlusOneQuery.call_association(record, associations)
|
|
134
|
+
@bullet_eager_loadings[record.class] ||= {}
|
|
135
|
+
@bullet_eager_loadings[record.class][record] ||= Set.new
|
|
136
|
+
@bullet_eager_loadings[record.class][record] << associations
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
result
|
|
140
|
+
end
|
|
141
|
+
end)
|
|
142
|
+
|
|
143
|
+
::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new do
|
|
144
|
+
def load_target
|
|
145
|
+
records = super
|
|
146
|
+
|
|
147
|
+
if Bullet.start?
|
|
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
|
|
151
|
+
Array(association.target).each do |through_record|
|
|
152
|
+
Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
if reflection.through_reflection != through_reflection
|
|
156
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
|
|
160
|
+
if records.first.class.name !~ /^HABTM_/
|
|
161
|
+
if records.size > 1
|
|
162
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
|
|
163
|
+
Bullet::Detector::CounterCache.add_possible_objects(records)
|
|
164
|
+
elsif records.size == 1
|
|
165
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
|
|
166
|
+
Bullet::Detector::CounterCache.add_impossible_object(records.first)
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
records
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def empty?
|
|
174
|
+
if Bullet.start? && !reflection.has_cached_counter?
|
|
175
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
176
|
+
end
|
|
177
|
+
super
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def include?(object)
|
|
181
|
+
if Bullet.start?
|
|
182
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
183
|
+
end
|
|
184
|
+
super
|
|
185
|
+
end
|
|
186
|
+
end)
|
|
187
|
+
|
|
188
|
+
::ActiveRecord::Associations::SingularAssociation.prepend(Module.new do
|
|
189
|
+
# call has_one and belongs_to associations
|
|
190
|
+
def target
|
|
191
|
+
result = super()
|
|
192
|
+
if Bullet.start?
|
|
193
|
+
if owner.class.name !~ /^HABTM_/ && !@inversed
|
|
194
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
195
|
+
if Bullet::Detector::NPlusOneQuery.impossible?(owner)
|
|
196
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
|
197
|
+
else
|
|
198
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
result
|
|
203
|
+
end
|
|
204
|
+
end)
|
|
205
|
+
|
|
206
|
+
::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new do
|
|
207
|
+
def empty?
|
|
208
|
+
result = super
|
|
209
|
+
if Bullet.start? && !reflection.has_cached_counter?
|
|
210
|
+
Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
|
|
211
|
+
end
|
|
212
|
+
result
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def count_records
|
|
216
|
+
result = reflection.has_cached_counter?
|
|
217
|
+
if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
|
|
218
|
+
Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
|
|
219
|
+
end
|
|
220
|
+
super
|
|
221
|
+
end
|
|
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)
|
|
230
|
+
end
|
|
231
|
+
end
|
|
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?
|
|
@@ -24,8 +26,10 @@ module Bullet
|
|
|
24
26
|
'active_record5'
|
|
25
27
|
elsif active_record51?
|
|
26
28
|
'active_record5'
|
|
29
|
+
elsif active_record52?
|
|
30
|
+
'active_record52'
|
|
27
31
|
else
|
|
28
|
-
raise "Bullet does not support active_record #{::ActiveRecord::VERSION} yet"
|
|
32
|
+
raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
|
|
29
33
|
end
|
|
30
34
|
end
|
|
31
35
|
end
|
|
@@ -72,6 +76,10 @@ module Bullet
|
|
|
72
76
|
active_record5? && ::ActiveRecord::VERSION::MINOR == 1
|
|
73
77
|
end
|
|
74
78
|
|
|
79
|
+
def active_record52?
|
|
80
|
+
active_record5? && ::ActiveRecord::VERSION::MINOR == 2
|
|
81
|
+
end
|
|
82
|
+
|
|
75
83
|
def mongoid4x?
|
|
76
84
|
mongoid? && ::Mongoid::VERSION =~ /\A4/
|
|
77
85
|
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(
|
|
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(
|
|
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
|
|
|
@@ -37,36 +39,37 @@ module Bullet
|
|
|
37
39
|
end
|
|
38
40
|
|
|
39
41
|
private
|
|
42
|
+
|
|
40
43
|
# object_associations keep the object relationships
|
|
41
44
|
# that the object has many associations.
|
|
42
45
|
# e.g. { "Post:1" => [:comments] }
|
|
43
46
|
# the object_associations keep all associations that may be or may no be
|
|
44
47
|
# unpreload associations or unused preload associations.
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
+
def object_associations
|
|
49
|
+
Thread.current[:bullet_object_associations]
|
|
50
|
+
end
|
|
48
51
|
|
|
49
52
|
# call_object_associations keep the object relationships
|
|
50
53
|
# that object.associations is called.
|
|
51
54
|
# e.g. { "Post:1" => [:comments] }
|
|
52
55
|
# they are used to detect unused preload associations.
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
+
def call_object_associations
|
|
57
|
+
Thread.current[:bullet_call_object_associations]
|
|
58
|
+
end
|
|
56
59
|
|
|
57
60
|
# inversed_objects keeps object relationships
|
|
58
61
|
# that association is inversed.
|
|
59
62
|
# e.g. { "Comment:1" => ["post"] }
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
+
def inversed_objects
|
|
64
|
+
Thread.current[:bullet_inversed_objects]
|
|
65
|
+
end
|
|
63
66
|
|
|
64
67
|
# eager_loadings keep the object relationships
|
|
65
68
|
# that the associations are preloaded by find :include.
|
|
66
69
|
# e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
def eager_loadings
|
|
71
|
+
Thread.current[:bullet_eager_loadings]
|
|
72
|
+
end
|
|
70
73
|
end
|
|
71
74
|
end
|
|
72
75
|
end
|