bullet 6.0.2 → 6.1.4

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.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -1
  3. data/CHANGELOG.md +25 -0
  4. data/Gemfile.rails-6.0 +1 -1
  5. data/Gemfile.rails-6.1 +15 -0
  6. data/README.md +12 -2
  7. data/lib/bullet.rb +31 -20
  8. data/lib/bullet/active_job.rb +1 -3
  9. data/lib/bullet/active_record4.rb +9 -23
  10. data/lib/bullet/active_record41.rb +7 -19
  11. data/lib/bullet/active_record42.rb +8 -16
  12. data/lib/bullet/active_record5.rb +188 -170
  13. data/lib/bullet/active_record52.rb +187 -161
  14. data/lib/bullet/active_record60.rb +194 -175
  15. data/lib/bullet/active_record61.rb +278 -0
  16. data/lib/bullet/bullet_xhr.js +17 -23
  17. data/lib/bullet/dependency.rb +42 -34
  18. data/lib/bullet/detector/association.rb +24 -18
  19. data/lib/bullet/detector/base.rb +1 -2
  20. data/lib/bullet/detector/counter_cache.rb +10 -6
  21. data/lib/bullet/detector/n_plus_one_query.rb +18 -8
  22. data/lib/bullet/detector/unused_eager_loading.rb +5 -2
  23. data/lib/bullet/mongoid4x.rb +3 -7
  24. data/lib/bullet/mongoid5x.rb +3 -7
  25. data/lib/bullet/mongoid6x.rb +3 -7
  26. data/lib/bullet/mongoid7x.rb +3 -7
  27. data/lib/bullet/notification/base.rb +14 -18
  28. data/lib/bullet/notification/n_plus_one_query.rb +2 -4
  29. data/lib/bullet/notification/unused_eager_loading.rb +2 -4
  30. data/lib/bullet/rack.rb +23 -15
  31. data/lib/bullet/stack_trace_filter.rb +7 -16
  32. data/lib/bullet/version.rb +1 -1
  33. data/lib/generators/bullet/install_generator.rb +23 -23
  34. data/perf/benchmark.rb +8 -14
  35. data/spec/bullet/detector/counter_cache_spec.rb +6 -6
  36. data/spec/bullet/detector/n_plus_one_query_spec.rb +7 -3
  37. data/spec/bullet/detector/unused_eager_loading_spec.rb +19 -6
  38. data/spec/bullet/notification/base_spec.rb +1 -3
  39. data/spec/bullet/notification/n_plus_one_query_spec.rb +16 -3
  40. data/spec/bullet/notification/unused_eager_loading_spec.rb +5 -1
  41. data/spec/bullet/rack_spec.rb +133 -7
  42. data/spec/bullet/registry/association_spec.rb +2 -2
  43. data/spec/bullet/registry/base_spec.rb +1 -1
  44. data/spec/bullet_spec.rb +10 -29
  45. data/spec/integration/active_record/association_spec.rb +77 -122
  46. data/spec/integration/counter_cache_spec.rb +10 -30
  47. data/spec/integration/mongoid/association_spec.rb +18 -32
  48. data/spec/models/attachment.rb +5 -0
  49. data/spec/models/folder.rb +1 -2
  50. data/spec/models/group.rb +1 -2
  51. data/spec/models/page.rb +1 -2
  52. data/spec/models/submission.rb +1 -0
  53. data/spec/models/user.rb +1 -0
  54. data/spec/models/writer.rb +1 -2
  55. data/spec/spec_helper.rb +6 -10
  56. data/spec/support/bullet_ext.rb +8 -9
  57. data/spec/support/mongo_seed.rb +2 -16
  58. data/spec/support/sqlite_seed.rb +8 -0
  59. metadata +10 -6
@@ -13,247 +13,266 @@ module Bullet
13
13
  module ActiveRecord
14
14
  def self.enable
15
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)
16
+ ::ActiveRecord::Base.extend(
17
+ Module.new do
18
+ def find_by_sql(sql, binds = [], preparable: nil, &block)
19
+ result = super
20
+ if Bullet.start?
21
+ if result.is_a? Array
22
+ if result.size > 1
23
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
24
+ Bullet::Detector::CounterCache.add_possible_objects(result)
25
+ elsif result.size == 1
26
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
27
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
28
+ end
29
+ elsif result.is_a? ::ActiveRecord::Base
30
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
31
+ Bullet::Detector::CounterCache.add_impossible_object(result)
27
32
  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
33
  end
34
+ result
32
35
  end
33
- result
34
36
  end
35
- end)
37
+ )
36
38
 
37
39
  ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
38
40
 
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)
41
+ ::ActiveRecord::Relation.prepend(
42
+ Module.new do
43
+ # if select a collection of objects, then these objects have possible to cause N+1 query.
44
+ # if select only one object, then the only one object has impossible to cause N+1 query.
45
+ def records
46
+ result = super
47
+ if Bullet.start?
48
+ if result.first.class.name !~ /^HABTM_/
49
+ if result.size > 1
50
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
51
+ Bullet::Detector::CounterCache.add_possible_objects(result)
52
+ elsif result.size == 1
53
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
54
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
55
+ end
52
56
  end
53
57
  end
58
+ result
54
59
  end
55
- result
56
60
  end
57
- end)
61
+ )
58
62
 
59
- ::ActiveRecord::Associations::Preloader.prepend(Module.new do
60
- def preloaders_for_one(association, records, scope, polymorphic_parent)
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)
63
+ ::ActiveRecord::Associations::Preloader.prepend(
64
+ Module.new do
65
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
66
+ if Bullet.start?
67
+ records.compact!
68
+ if records.first.class.name !~ /^HABTM_/
69
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, association) }
70
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
66
71
  end
67
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
68
72
  end
73
+ super
69
74
  end
70
- super
71
- end
72
75
 
73
- def preloaders_for_reflection(reflection, records, scope)
74
- if Bullet.start?
75
- records.compact!
76
- if records.first.class.name !~ /^HABTM_/
77
- records.each do |record|
78
- Bullet::Detector::Association.add_object_associations(record, reflection.name)
76
+ def preloaders_for_reflection(reflection, records, scope)
77
+ if Bullet.start?
78
+ records.compact!
79
+ if records.first.class.name !~ /^HABTM_/
80
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
81
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, reflection.name)
79
82
  end
80
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, reflection.name)
81
83
  end
84
+ super
82
85
  end
83
- super
84
86
  end
85
- end)
87
+ )
86
88
 
87
- ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(Module.new do
88
- def preloaded_records
89
- if Bullet.start? && !defined?(@preloaded_records)
90
- source_preloaders.each do |source_preloader|
91
- reflection_name = source_preloader.send(:reflection).name
92
- source_preloader.send(:owners).each do |owner|
93
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
89
+ ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
90
+ Module.new do
91
+ def preloaded_records
92
+ if Bullet.start? && !defined?(@preloaded_records)
93
+ source_preloaders.each do |source_preloader|
94
+ reflection_name = source_preloader.send(:reflection).name
95
+ source_preloader.send(:owners).each do |owner|
96
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
97
+ end
94
98
  end
95
99
  end
100
+ super
96
101
  end
97
- super
98
102
  end
99
- end)
103
+ )
100
104
 
101
- ::ActiveRecord::FinderMethods.prepend(Module.new do
102
- # add includes in scope
103
- def find_with_associations
104
- return super { |r| yield r } if block_given?
105
+ ::ActiveRecord::FinderMethods.prepend(
106
+ Module.new do
107
+ # add includes in scope
108
+ def find_with_associations
109
+ return super { |r| yield r } if block_given?
105
110
 
106
- records = super
107
- if Bullet.start?
108
- associations = (eager_load_values + includes_values).uniq
109
- records.each do |record|
110
- Bullet::Detector::Association.add_object_associations(record, associations)
111
+ records = super
112
+ if Bullet.start?
113
+ associations = (eager_load_values + includes_values).uniq
114
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
115
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
111
116
  end
112
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
117
+ records
113
118
  end
114
- records
115
119
  end
116
- end)
120
+ )
117
121
 
118
- ::ActiveRecord::Associations::JoinDependency.prepend(Module.new do
119
- def instantiate(result_set, &block)
120
- @bullet_eager_loadings = {}
121
- records = super
122
+ ::ActiveRecord::Associations::JoinDependency.prepend(
123
+ Module.new do
124
+ def instantiate(result_set, &block)
125
+ @bullet_eager_loadings = {}
126
+ records = super
122
127
 
123
- if Bullet.start?
124
- @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
125
- objects = eager_loadings_hash.keys
126
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
128
+ if Bullet.start?
129
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
130
+ objects = eager_loadings_hash.keys
131
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
132
+ objects,
133
+ eager_loadings_hash[objects.first].to_a
134
+ )
135
+ end
127
136
  end
137
+ records
128
138
  end
129
- records
130
- end
131
139
 
132
- def construct(ar_parent, parent, row, seen, model_cache)
133
- if Bullet.start?
134
- unless ar_parent.nil?
135
- parent.children.each do |node|
136
- key = aliases.column_alias(node, node.primary_key)
137
- id = row[key]
138
- next unless id.nil?
140
+ def construct(ar_parent, parent, row, seen, model_cache)
141
+ if Bullet.start?
142
+ unless ar_parent.nil?
143
+ parent.children.each do |node|
144
+ key = aliases.column_alias(node, node.primary_key)
145
+ id = row[key]
146
+ next unless id.nil?
139
147
 
140
- associations = node.reflection.name
141
- Bullet::Detector::Association.add_object_associations(ar_parent, associations)
142
- Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
143
- @bullet_eager_loadings[ar_parent.class] ||= {}
144
- @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
145
- @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
148
+ associations = node.reflection.name
149
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
150
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
151
+ @bullet_eager_loadings[ar_parent.class] ||= {}
152
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
153
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
154
+ end
146
155
  end
147
156
  end
157
+
158
+ super
148
159
  end
149
160
 
150
- super
151
- end
161
+ # call join associations
162
+ def construct_model(record, node, row, model_cache, id)
163
+ result = super
152
164
 
153
- # call join associations
154
- def construct_model(record, node, row, model_cache, id)
155
- result = super
165
+ if Bullet.start?
166
+ associations = node.reflection.name
167
+ Bullet::Detector::Association.add_object_associations(record, associations)
168
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
169
+ @bullet_eager_loadings[record.class] ||= {}
170
+ @bullet_eager_loadings[record.class][record] ||= Set.new
171
+ @bullet_eager_loadings[record.class][record] << associations
172
+ end
156
173
 
157
- if Bullet.start?
158
- associations = node.reflection.name
159
- Bullet::Detector::Association.add_object_associations(record, associations)
160
- Bullet::Detector::NPlusOneQuery.call_association(record, associations)
161
- @bullet_eager_loadings[record.class] ||= {}
162
- @bullet_eager_loadings[record.class][record] ||= Set.new
163
- @bullet_eager_loadings[record.class][record] << associations
174
+ result
164
175
  end
165
-
166
- result
167
176
  end
168
- end)
177
+ )
169
178
 
170
- ::ActiveRecord::Associations::CollectionAssociation.prepend(Module.new do
171
- def load_target
172
- records = super
179
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
180
+ Module.new do
181
+ def load_target
182
+ records = super
173
183
 
174
- if Bullet.start?
175
- if is_a? ::ActiveRecord::Associations::ThroughAssociation
176
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
177
- association = owner.association(reflection.through_reflection.name)
178
- Array(association.target).each do |through_record|
179
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
180
- end
184
+ if Bullet.start?
185
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
186
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
187
+ association = owner.association(reflection.through_reflection.name)
188
+ Array(association.target).each do |through_record|
189
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
190
+ end
181
191
 
182
- if reflection.through_reflection != through_reflection
183
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
192
+ if reflection.through_reflection != through_reflection
193
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
194
+ end
184
195
  end
185
- end
186
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
187
- if records.first.class.name !~ /^HABTM_/
188
- if records.size > 1
189
- Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
190
- Bullet::Detector::CounterCache.add_possible_objects(records)
191
- elsif records.size == 1
192
- Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
193
- Bullet::Detector::CounterCache.add_impossible_object(records.first)
196
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
197
+ if records.first.class.name !~ /^HABTM_/
198
+ if records.size > 1
199
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
200
+ Bullet::Detector::CounterCache.add_possible_objects(records)
201
+ elsif records.size == 1
202
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
203
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
204
+ end
194
205
  end
195
206
  end
207
+ records
196
208
  end
197
- records
198
- end
199
209
 
200
- def empty?
201
- if Bullet.start? && !reflection.has_cached_counter?
202
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
210
+ def empty?
211
+ if Bullet.start? && !reflection.has_cached_counter?
212
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
213
+ end
214
+ super
203
215
  end
204
- super
205
- end
206
216
 
207
- def include?(object)
208
- if Bullet.start?
209
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
217
+ def include?(object)
218
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
219
+ super
210
220
  end
211
- super
212
221
  end
213
- end)
222
+ )
214
223
 
215
- ::ActiveRecord::Associations::SingularAssociation.prepend(Module.new do
216
- # call has_one and belongs_to associations
217
- def target
218
- result = super()
219
- if Bullet.start?
220
- if owner.class.name !~ /^HABTM_/ && !@inversed
221
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
222
- if Bullet::Detector::NPlusOneQuery.impossible?(owner)
223
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
224
- else
225
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
224
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
225
+ Module.new do
226
+ # call has_one and belongs_to associations
227
+ def target
228
+ result = super()
229
+
230
+ if Bullet.start?
231
+ if owner.class.name !~ /^HABTM_/ && !@inversed
232
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
233
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
234
+ association = owner.association(reflection.through_reflection.name)
235
+ Array(association.target).each do |through_record|
236
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
237
+ end
238
+
239
+ if reflection.through_reflection != through_reflection
240
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
241
+ end
242
+ end
243
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
244
+
245
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
246
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
247
+ else
248
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
249
+ end
226
250
  end
227
251
  end
252
+ result
228
253
  end
229
- result
230
254
  end
231
- end)
255
+ )
232
256
 
233
- ::ActiveRecord::Associations::HasManyAssociation.prepend(Module.new do
234
- def empty?
235
- result = super
236
- if Bullet.start? && !reflection.has_cached_counter?
237
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
257
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
258
+ Module.new do
259
+ def empty?
260
+ result = super
261
+ if Bullet.start? && !reflection.has_cached_counter?
262
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
263
+ end
264
+ result
238
265
  end
239
- result
240
- end
241
266
 
242
- def count_records
243
- result = reflection.has_cached_counter?
244
- if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
245
- Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
267
+ def count_records
268
+ result = reflection.has_cached_counter?
269
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
270
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
271
+ end
272
+ super
246
273
  end
247
- super
248
- end
249
- end)
250
-
251
- ::ActiveRecord::Associations::BelongsToAssociation.prepend(Module.new do
252
- def writer(record)
253
- Bullet::Detector::Association.add_object_associations(owner, reflection.name) if Bullet.start?
254
- super
255
274
  end
256
- end)
275
+ )
257
276
  end
258
277
  end
259
278
  end
@@ -0,0 +1,278 @@
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(
17
+ Module.new do
18
+ def find_by_sql(sql, binds = [], preparable: nil, &block)
19
+ result = super
20
+ if Bullet.start?
21
+ if result.is_a? Array
22
+ if result.size > 1
23
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
24
+ Bullet::Detector::CounterCache.add_possible_objects(result)
25
+ elsif result.size == 1
26
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
27
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
28
+ end
29
+ elsif result.is_a? ::ActiveRecord::Base
30
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
31
+ Bullet::Detector::CounterCache.add_impossible_object(result)
32
+ end
33
+ end
34
+ result
35
+ end
36
+ end
37
+ )
38
+
39
+ ::ActiveRecord::Base.prepend(SaveWithBulletSupport)
40
+
41
+ ::ActiveRecord::Relation.prepend(
42
+ Module.new do
43
+ # if select a collection of objects, then these objects have possible to cause N+1 query.
44
+ # if select only one object, then the only one object has impossible to cause N+1 query.
45
+ def records
46
+ result = super
47
+ if Bullet.start?
48
+ if result.first.class.name !~ /^HABTM_/
49
+ if result.size > 1
50
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
51
+ Bullet::Detector::CounterCache.add_possible_objects(result)
52
+ elsif result.size == 1
53
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
54
+ Bullet::Detector::CounterCache.add_impossible_object(result.first)
55
+ end
56
+ end
57
+ end
58
+ result
59
+ end
60
+ end
61
+ )
62
+
63
+ ::ActiveRecord::Associations::Preloader.prepend(
64
+ Module.new do
65
+ def preloaders_for_one(association, records, scope, polymorphic_parent)
66
+ if Bullet.start?
67
+ records.compact!
68
+ if records.first.class.name !~ /^HABTM_/
69
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, association) }
70
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
71
+ end
72
+ end
73
+ super
74
+ end
75
+
76
+ def preloaders_for_reflection(reflection, records, scope)
77
+ if Bullet.start?
78
+ records.compact!
79
+ if records.first.class.name !~ /^HABTM_/
80
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
81
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, reflection.name)
82
+ end
83
+ end
84
+ super
85
+ end
86
+ end
87
+ )
88
+
89
+ ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
90
+ Module.new do
91
+ def preloaded_records
92
+ if Bullet.start? && !defined?(@preloaded_records)
93
+ source_preloaders.each do |source_preloader|
94
+ reflection_name = source_preloader.send(:reflection).name
95
+ source_preloader.send(:owners).each do |owner|
96
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
97
+ end
98
+ end
99
+ end
100
+ super
101
+ end
102
+ end
103
+ )
104
+
105
+ ::ActiveRecord::FinderMethods.prepend(
106
+ Module.new do
107
+ # add includes in scope
108
+ def find_with_associations
109
+ return super { |r| yield r } if block_given?
110
+
111
+ records = super
112
+ if Bullet.start?
113
+ associations = (eager_load_values + includes_values).uniq
114
+ records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
115
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
116
+ end
117
+ records
118
+ end
119
+ end
120
+ )
121
+
122
+ ::ActiveRecord::Associations::JoinDependency.prepend(
123
+ Module.new do
124
+ def instantiate(result_set, strict_loading_value, &block)
125
+ @bullet_eager_loadings = {}
126
+ records = super
127
+
128
+ if Bullet.start?
129
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
130
+ objects = eager_loadings_hash.keys
131
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
132
+ objects,
133
+ eager_loadings_hash[objects.first].to_a
134
+ )
135
+ end
136
+ end
137
+ records
138
+ end
139
+
140
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
141
+ if Bullet.start?
142
+ unless ar_parent.nil?
143
+ parent.children.each do |node|
144
+ key = aliases.column_alias(node, node.primary_key)
145
+ id = row[key]
146
+ next unless id.nil?
147
+
148
+ associations = node.reflection.name
149
+ Bullet::Detector::Association.add_object_associations(ar_parent, associations)
150
+ Bullet::Detector::NPlusOneQuery.call_association(ar_parent, associations)
151
+ @bullet_eager_loadings[ar_parent.class] ||= {}
152
+ @bullet_eager_loadings[ar_parent.class][ar_parent] ||= Set.new
153
+ @bullet_eager_loadings[ar_parent.class][ar_parent] << associations
154
+ end
155
+ end
156
+ end
157
+
158
+ super
159
+ end
160
+
161
+ # call join associations
162
+ def construct_model(record, node, row, model_cache, id, strict_loading_value)
163
+ result = super
164
+
165
+ if Bullet.start?
166
+ associations = node.reflection.name
167
+ Bullet::Detector::Association.add_object_associations(record, associations)
168
+ Bullet::Detector::NPlusOneQuery.call_association(record, associations)
169
+ @bullet_eager_loadings[record.class] ||= {}
170
+ @bullet_eager_loadings[record.class][record] ||= Set.new
171
+ @bullet_eager_loadings[record.class][record] << associations
172
+ end
173
+
174
+ result
175
+ end
176
+ end
177
+ )
178
+
179
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
180
+ Module.new do
181
+ def load_target
182
+ records = super
183
+
184
+ if Bullet.start?
185
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
186
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
187
+ association = owner.association(reflection.through_reflection.name)
188
+ Array(association.target).each do |through_record|
189
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
190
+ end
191
+
192
+ if reflection.through_reflection != through_reflection
193
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
194
+ end
195
+ end
196
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
197
+ if records.first.class.name !~ /^HABTM_/
198
+ if records.size > 1
199
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
200
+ Bullet::Detector::CounterCache.add_possible_objects(records)
201
+ elsif records.size == 1
202
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
203
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
204
+ end
205
+ end
206
+ end
207
+ records
208
+ end
209
+
210
+ def empty?
211
+ if Bullet.start? && !reflection.has_cached_counter?
212
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
213
+ end
214
+ super
215
+ end
216
+
217
+ def include?(object)
218
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
219
+ super
220
+ end
221
+ end
222
+ )
223
+
224
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
225
+ Module.new do
226
+ # call has_one and belongs_to associations
227
+ def target
228
+ result = super()
229
+
230
+ if Bullet.start?
231
+ if owner.class.name !~ /^HABTM_/ && !@inversed
232
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
233
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
234
+ association = owner.association(reflection.through_reflection.name)
235
+ Array(association.target).each do |through_record|
236
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
237
+ end
238
+
239
+ if reflection.through_reflection != through_reflection
240
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
241
+ end
242
+ end
243
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
244
+
245
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
246
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
247
+ else
248
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
249
+ end
250
+ end
251
+ end
252
+ result
253
+ end
254
+ end
255
+ )
256
+
257
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
258
+ Module.new do
259
+ def empty?
260
+ result = super
261
+ if Bullet.start? && !reflection.has_cached_counter?
262
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
263
+ end
264
+ result
265
+ end
266
+
267
+ def count_records
268
+ result = reflection.has_cached_counter?
269
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
270
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
271
+ end
272
+ super
273
+ end
274
+ end
275
+ )
276
+ end
277
+ end
278
+ end