bullet 7.0.7 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +14 -1
  3. data/README.md +3 -0
  4. data/lib/bullet/active_record4.rb +9 -0
  5. data/lib/bullet/active_record41.rb +9 -0
  6. data/lib/bullet/active_record42.rb +9 -0
  7. data/lib/bullet/active_record5.rb +14 -0
  8. data/lib/bullet/active_record52.rb +14 -0
  9. data/lib/bullet/active_record60.rb +14 -0
  10. data/lib/bullet/active_record61.rb +14 -0
  11. data/lib/bullet/active_record70.rb +28 -8
  12. data/lib/bullet/active_record71.rb +304 -0
  13. data/lib/bullet/dependency.rb +12 -0
  14. data/lib/bullet/detector/n_plus_one_query.rb +2 -5
  15. data/lib/bullet/detector/unused_eager_loading.rb +5 -2
  16. data/lib/bullet/ext/object.rb +2 -1
  17. data/lib/bullet/mongoid8x.rb +59 -0
  18. data/lib/bullet/notification/base.rb +9 -8
  19. data/lib/bullet/notification/counter_cache.rb +1 -1
  20. data/lib/bullet/rack.rb +4 -2
  21. data/lib/bullet/registry/association.rb +2 -1
  22. data/lib/bullet/stack_trace_filter.rb +3 -2
  23. data/lib/bullet/version.rb +1 -1
  24. data/lib/bullet.rb +7 -2
  25. metadata +7 -155
  26. data/.github/workflows/main.yml +0 -82
  27. data/.gitignore +0 -15
  28. data/.rspec +0 -2
  29. data/Gemfile +0 -24
  30. data/Gemfile.mongoid +0 -12
  31. data/Gemfile.mongoid-4.0 +0 -15
  32. data/Gemfile.mongoid-5.0 +0 -15
  33. data/Gemfile.mongoid-6.0 +0 -15
  34. data/Gemfile.mongoid-7.0 +0 -15
  35. data/Gemfile.rails-4.0 +0 -16
  36. data/Gemfile.rails-4.1 +0 -16
  37. data/Gemfile.rails-4.2 +0 -16
  38. data/Gemfile.rails-5.0 +0 -15
  39. data/Gemfile.rails-5.1 +0 -15
  40. data/Gemfile.rails-5.2 +0 -15
  41. data/Gemfile.rails-6.0 +0 -15
  42. data/Gemfile.rails-6.1 +0 -15
  43. data/Gemfile.rails-7.0 +0 -10
  44. data/Guardfile +0 -8
  45. data/Hacking.md +0 -75
  46. data/Rakefile +0 -51
  47. data/bullet.gemspec +0 -33
  48. data/perf/benchmark.rb +0 -118
  49. data/rails/init.rb +0 -3
  50. data/spec/bullet/detector/association_spec.rb +0 -28
  51. data/spec/bullet/detector/base_spec.rb +0 -10
  52. data/spec/bullet/detector/counter_cache_spec.rb +0 -58
  53. data/spec/bullet/detector/n_plus_one_query_spec.rb +0 -150
  54. data/spec/bullet/detector/unused_eager_loading_spec.rb +0 -126
  55. data/spec/bullet/ext/object_spec.rb +0 -44
  56. data/spec/bullet/ext/string_spec.rb +0 -15
  57. data/spec/bullet/notification/base_spec.rb +0 -94
  58. data/spec/bullet/notification/counter_cache_spec.rb +0 -14
  59. data/spec/bullet/notification/n_plus_one_query_spec.rb +0 -29
  60. data/spec/bullet/notification/unused_eager_loading_spec.rb +0 -18
  61. data/spec/bullet/notification_collector_spec.rb +0 -34
  62. data/spec/bullet/rack_spec.rb +0 -296
  63. data/spec/bullet/registry/association_spec.rb +0 -28
  64. data/spec/bullet/registry/base_spec.rb +0 -46
  65. data/spec/bullet/registry/object_spec.rb +0 -26
  66. data/spec/bullet/stack_trace_filter_spec.rb +0 -26
  67. data/spec/bullet_spec.rb +0 -136
  68. data/spec/integration/active_record/association_spec.rb +0 -822
  69. data/spec/integration/counter_cache_spec.rb +0 -68
  70. data/spec/integration/mongoid/association_spec.rb +0 -246
  71. data/spec/models/address.rb +0 -5
  72. data/spec/models/attachment.rb +0 -5
  73. data/spec/models/author.rb +0 -5
  74. data/spec/models/base_user.rb +0 -7
  75. data/spec/models/category.rb +0 -12
  76. data/spec/models/city.rb +0 -5
  77. data/spec/models/client.rb +0 -8
  78. data/spec/models/comment.rb +0 -8
  79. data/spec/models/company.rb +0 -5
  80. data/spec/models/country.rb +0 -5
  81. data/spec/models/deal.rb +0 -5
  82. data/spec/models/document.rb +0 -7
  83. data/spec/models/entry.rb +0 -5
  84. data/spec/models/firm.rb +0 -7
  85. data/spec/models/folder.rb +0 -4
  86. data/spec/models/group.rb +0 -4
  87. data/spec/models/mongoid/address.rb +0 -9
  88. data/spec/models/mongoid/category.rb +0 -10
  89. data/spec/models/mongoid/comment.rb +0 -9
  90. data/spec/models/mongoid/company.rb +0 -9
  91. data/spec/models/mongoid/entry.rb +0 -9
  92. data/spec/models/mongoid/post.rb +0 -14
  93. data/spec/models/mongoid/user.rb +0 -7
  94. data/spec/models/newspaper.rb +0 -5
  95. data/spec/models/page.rb +0 -4
  96. data/spec/models/person.rb +0 -5
  97. data/spec/models/pet.rb +0 -5
  98. data/spec/models/post.rb +0 -34
  99. data/spec/models/relationship.rb +0 -6
  100. data/spec/models/reply.rb +0 -5
  101. data/spec/models/role.rb +0 -7
  102. data/spec/models/student.rb +0 -5
  103. data/spec/models/submission.rb +0 -7
  104. data/spec/models/teacher.rb +0 -5
  105. data/spec/models/user.rb +0 -8
  106. data/spec/models/writer.rb +0 -4
  107. data/spec/spec_helper.rb +0 -97
  108. data/spec/support/bullet_ext.rb +0 -56
  109. data/spec/support/mongo_seed.rb +0 -59
  110. data/spec/support/rack_double.rb +0 -49
  111. data/spec/support/sqlite_seed.rb +0 -284
  112. data/test.sh +0 -15
  113. data/update.sh +0 -10
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 0f47e147f5df074217ee7a65123f54a1918f98bde162c7d4bf1f76bffc9f15a6
4
- data.tar.gz: e05e48b96a5e68bfbcac63f4dce4601d717ec1a32ee83a5c45aad738e1bfc48b
3
+ metadata.gz: f3337004db8160edc4f3fecbb4b9d9caf5db356f2bc53cbb8a87dc32661747db
4
+ data.tar.gz: cf987a580af8f2451043a7ccc0ad0fd2f4f30b4832c8889e2a7891a85e1ed062
5
5
  SHA512:
6
- metadata.gz: 87bf095754befedb8f1137ae039191807baa1f1bcadb09c923b4f1a6abd385e73e4143e50077358a8f7155e3bf367edb1b405e3b0b260c8de5dd572699b7bafc
7
- data.tar.gz: fd692ae67a1695ee4598b24d3cd030a7f091deb42f30df0f92085b3c54f765960c0b5eabaace7b306fadf8539b514fe77076cff825c27a292c5e36f56916646b
6
+ metadata.gz: 88ba85427aeb892d39b66d6f98edfa2dd1d80a23eba3080c2497b7b2b496add6cf3835239bc81b349a5176413fe771637e1218e72eca1d15b24f9f6cd737826d
7
+ data.tar.gz: d3982d128702e65d65ec669be5a8ef0c15725e2f474db26911186aafedf6192f2278709cc1dee0f8d5857ca43105bb34fa6d2fa59eafaadb7b3284e5821a1388
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  ## Next Release
2
2
 
3
+ ## 7.1.1 (10/07/2023)
4
+
5
+ * Add support for `Content-Security-Policy-Report-Only` nonces
6
+ * Fix count method signature
7
+
8
+ ## 7.1.0 (10/06/2023)
9
+
10
+ * Support rails 7.1
11
+ * Alias `Bullet.enable?` to `enabled?`, and `Bullet.enable=` to `enabled=`
12
+ * Added `always_append_html_body` option, so the html snippet is always included even if there are no notifications
13
+ * Added detection of n+1 count queries from `count` method
14
+ * Changed the counter cache notification title to recommend using `size`
15
+
3
16
  ## 7.0.7 (03/01/2023)
4
17
 
5
18
  * Check `Rails.application.config.content_security_policy` before insert `Bullet::Rack`
@@ -97,7 +110,7 @@
97
110
 
98
111
  * Fix through reflection for rails 5.x
99
112
  * Fix false positive in after_save/after_create callbacks
100
- * Don't triger a preload error on "manual" preloads
113
+ * Don't trigger a preload error on "manual" preloads
101
114
  * Avoid Bullet from making extra queries in mongoid6
102
115
  * Support option for #first and #last on mongoid6.x
103
116
  * Fix duplicate logs in mongoid 4.x and 5.x version
data/README.md CHANGED
@@ -101,6 +101,7 @@ The code above will enable all of the Bullet notification systems:
101
101
  item is a line number, a Range of line numbers, or a (bare) method name, to exclude only particular lines in a file.
102
102
  * `Bullet.slack`: add notifications to slack
103
103
  * `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
104
+ * `Bullet.always_append_html_body`: always append the html body even if no notifications are present. Note: `console` or `add_footer` must also be true. Useful for Single Page Applications where the initial page load might not have any notifications present.
104
105
 
105
106
 
106
107
  Bullet also allows you to disable any of its detectors.
@@ -119,6 +120,8 @@ Bullet.unused_eager_loading_enable = false
119
120
  Bullet.counter_cache_enable = false
120
121
  ```
121
122
 
123
+ Note: When calling `Bullet.enable`, all other detectors are reset to their defaults (`true`) and need reconfiguring.
124
+
122
125
  ## Safe list
123
126
 
124
127
  Sometimes Bullet may notify you of query problems you don't care to fix, or
@@ -176,6 +176,15 @@ module Bullet
176
176
  result
177
177
  end
178
178
  end
179
+
180
+ ::ActiveRecord::Associations::CollectionProxy.class_eval do
181
+ def count(column_name = nil, options = {})
182
+ if Bullet.start?
183
+ Bullet::Detector::CounterCache.add_counter_cache(proxy_association.owner, proxy_association.reflection.name)
184
+ end
185
+ super(column_name, options)
186
+ end
187
+ end
179
188
  end
180
189
  end
181
190
  end
@@ -168,6 +168,15 @@ module Bullet
168
168
  origin_count_records
169
169
  end
170
170
  end
171
+
172
+ ::ActiveRecord::Associations::CollectionProxy.class_eval do
173
+ def count(column_name = nil, options = {})
174
+ if Bullet.start?
175
+ Bullet::Detector::CounterCache.add_counter_cache(proxy_association.owner, proxy_association.reflection.name)
176
+ end
177
+ super(column_name, options)
178
+ end
179
+ end
171
180
  end
172
181
  end
173
182
  end
@@ -233,6 +233,15 @@ module Bullet
233
233
  origin_count_records
234
234
  end
235
235
  end
236
+
237
+ ::ActiveRecord::Associations::CollectionProxy.class_eval do
238
+ def count(column_name = nil, options = {})
239
+ if Bullet.start?
240
+ Bullet::Detector::CounterCache.add_counter_cache(proxy_association.owner, proxy_association.reflection.name)
241
+ end
242
+ super(column_name, options)
243
+ end
244
+ end
236
245
  end
237
246
  end
238
247
  end
@@ -260,6 +260,20 @@ module Bullet
260
260
  end
261
261
  end
262
262
  )
263
+
264
+ ::ActiveRecord::Associations::CollectionProxy.prepend(
265
+ Module.new do
266
+ def count
267
+ if Bullet.start? && !proxy_association.is_a?(::ActiveRecord::Associations::ThroughAssociation)
268
+ Bullet::Detector::CounterCache.add_counter_cache(
269
+ proxy_association.owner,
270
+ proxy_association.reflection.name
271
+ )
272
+ end
273
+ super
274
+ end
275
+ end
276
+ )
263
277
  end
264
278
  end
265
279
  end
@@ -242,6 +242,20 @@ module Bullet
242
242
  end
243
243
  end
244
244
  )
245
+
246
+ ::ActiveRecord::Associations::CollectionProxy.prepend(
247
+ Module.new do
248
+ def count(column_name = nil)
249
+ if Bullet.start? && !proxy_association.is_a?(::ActiveRecord::Associations::ThroughAssociation)
250
+ Bullet::Detector::CounterCache.add_counter_cache(
251
+ proxy_association.owner,
252
+ proxy_association.reflection.name
253
+ )
254
+ end
255
+ super(column_name)
256
+ end
257
+ end
258
+ )
245
259
  end
246
260
  end
247
261
  end
@@ -269,6 +269,20 @@ module Bullet
269
269
  end
270
270
  end
271
271
  )
272
+
273
+ ::ActiveRecord::Associations::CollectionProxy.prepend(
274
+ Module.new do
275
+ def count
276
+ if Bullet.start? && !proxy_association.is_a?(::ActiveRecord::Associations::ThroughAssociation)
277
+ Bullet::Detector::CounterCache.add_counter_cache(
278
+ proxy_association.owner,
279
+ proxy_association.reflection.name
280
+ )
281
+ end
282
+ super
283
+ end
284
+ end
285
+ )
272
286
  end
273
287
  end
274
288
  end
@@ -269,6 +269,20 @@ module Bullet
269
269
  end
270
270
  end
271
271
  )
272
+
273
+ ::ActiveRecord::Associations::CollectionProxy.prepend(
274
+ Module.new do
275
+ def count(column_name = nil)
276
+ if Bullet.start? && !proxy_association.is_a?(::ActiveRecord::Associations::ThroughAssociation)
277
+ Bullet::Detector::CounterCache.add_counter_cache(
278
+ proxy_association.owner,
279
+ proxy_association.reflection.name
280
+ )
281
+ end
282
+ super(column_name)
283
+ end
284
+ end
285
+ )
272
286
  end
273
287
  end
274
288
  end
@@ -65,7 +65,9 @@ module Bullet
65
65
  def call
66
66
  if Bullet.start?
67
67
  @preloaders.each do |preloader|
68
- preloader.records.each { |record| Bullet::Detector::Association.add_object_associations(record, preloader.associations) }
68
+ preloader.records.each { |record|
69
+ Bullet::Detector::Association.add_object_associations(record, preloader.associations)
70
+ }
69
71
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(preloader.records, preloader.associations)
70
72
  end
71
73
  end
@@ -80,7 +82,9 @@ module Bullet
80
82
  if Bullet.start?
81
83
  reflection_records.compact!
82
84
  if reflection_records.first.class.name !~ /^HABTM_/
83
- reflection_records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
85
+ reflection_records.each { |record|
86
+ Bullet::Detector::Association.add_object_associations(record, reflection.name)
87
+ }
84
88
  Bullet::Detector::UnusedEagerLoading.add_eager_loadings(reflection_records, reflection.name)
85
89
  end
86
90
  end
@@ -91,16 +95,18 @@ module Bullet
91
95
 
92
96
  ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
93
97
  Module.new do
94
- def preloaded_records
95
- if Bullet.start? && !defined?(@preloaded_records)
96
- source_preloaders.each do |source_preloader|
97
- reflection_name = source_preloader.send(:reflection).name
98
- source_preloader.send(:owners).each do |owner|
98
+ def source_preloaders
99
+ if Bullet.start? && !defined?(@source_preloaders)
100
+ preloaders = super
101
+ preloaders.each do |preloader|
102
+ reflection_name = preloader.send(:reflection).name
103
+ preloader.send(:owners).each do |owner|
99
104
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
100
105
  end
101
106
  end
107
+ else
108
+ super
102
109
  end
103
- super
104
110
  end
105
111
  end
106
112
  )
@@ -279,6 +285,20 @@ module Bullet
279
285
  end
280
286
  end
281
287
  )
288
+
289
+ ::ActiveRecord::Associations::CollectionProxy.prepend(
290
+ Module.new do
291
+ def count(column_name = nil)
292
+ if Bullet.start? && !proxy_association.is_a?(::ActiveRecord::Associations::ThroughAssociation)
293
+ Bullet::Detector::CounterCache.add_counter_cache(
294
+ proxy_association.owner,
295
+ proxy_association.reflection.name
296
+ )
297
+ end
298
+ super(column_name)
299
+ end
300
+ end
301
+ )
282
302
  end
283
303
  end
284
304
  end
@@ -0,0 +1,304 @@
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::Batch.prepend(
64
+ Module.new do
65
+ def call
66
+ if Bullet.start?
67
+ @preloaders.each do |preloader|
68
+ preloader.records.each { |record|
69
+ Bullet::Detector::Association.add_object_associations(record, preloader.associations)
70
+ }
71
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(preloader.records, preloader.associations)
72
+ end
73
+ end
74
+ super
75
+ end
76
+ end
77
+ )
78
+
79
+ ::ActiveRecord::Associations::Preloader::Branch.prepend(
80
+ Module.new do
81
+ def preloaders_for_reflection(reflection, reflection_records)
82
+ if Bullet.start?
83
+ reflection_records.compact!
84
+ if reflection_records.first.class.name !~ /^HABTM_/
85
+ reflection_records.each { |record|
86
+ Bullet::Detector::Association.add_object_associations(record, reflection.name)
87
+ }
88
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(reflection_records, reflection.name)
89
+ end
90
+ end
91
+ super
92
+ end
93
+ end
94
+ )
95
+
96
+ ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
97
+ Module.new do
98
+ def source_preloaders
99
+ if Bullet.start? && !defined?(@source_preloaders)
100
+ preloaders = super
101
+ preloaders.each do |preloader|
102
+ reflection_name = preloader.send(:reflection).name
103
+ preloader.send(:owners).each do |owner|
104
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
105
+ end
106
+ end
107
+ else
108
+ super
109
+ end
110
+ end
111
+ end
112
+ )
113
+
114
+ ::ActiveRecord::Associations::JoinDependency.prepend(
115
+ Module.new do
116
+ def instantiate(result_set, strict_loading_value, &block)
117
+ @bullet_eager_loadings = {}
118
+ records = super
119
+
120
+ if Bullet.start?
121
+ @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
122
+ objects = eager_loadings_hash.keys
123
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(
124
+ objects,
125
+ eager_loadings_hash[objects.first].to_a
126
+ )
127
+ end
128
+ end
129
+ records
130
+ end
131
+
132
+ def construct(ar_parent, parent, row, seen, model_cache, strict_loading_value)
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?
139
+
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
146
+ end
147
+ end
148
+ end
149
+
150
+ super
151
+ end
152
+
153
+ # call join associations
154
+ def construct_model(record, node, row, model_cache, id, strict_loading_value)
155
+ result = super
156
+
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
164
+ end
165
+
166
+ result
167
+ end
168
+ end
169
+ )
170
+
171
+ ::ActiveRecord::Associations::Association.prepend(
172
+ Module.new do
173
+ def inversed_from(record)
174
+ if Bullet.start?
175
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
176
+ end
177
+ super
178
+ end
179
+
180
+ def inversed_from_queries(record)
181
+ if Bullet.start? && inversable?(record)
182
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
183
+ end
184
+ super
185
+ end
186
+ end
187
+ )
188
+
189
+ ::ActiveRecord::Associations::CollectionAssociation.prepend(
190
+ Module.new do
191
+ def load_target
192
+ records = super
193
+
194
+ if Bullet.start?
195
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
196
+ association = owner.association(reflection.through_reflection.name)
197
+ if association.loaded?
198
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
199
+ Array.wrap(association.target).each do |through_record|
200
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
201
+ end
202
+
203
+ if reflection.through_reflection != through_reflection
204
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
205
+ end
206
+ end
207
+ end
208
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
209
+ if records.first.class.name !~ /^HABTM_/
210
+ if records.size > 1
211
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
212
+ Bullet::Detector::CounterCache.add_possible_objects(records)
213
+ elsif records.size == 1
214
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
215
+ Bullet::Detector::CounterCache.add_impossible_object(records.first)
216
+ end
217
+ end
218
+ end
219
+ records
220
+ end
221
+
222
+ def empty?
223
+ if Bullet.start? && !reflection.has_cached_counter?
224
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
225
+ end
226
+ super
227
+ end
228
+
229
+ def include?(object)
230
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) if Bullet.start?
231
+ super
232
+ end
233
+ end
234
+ )
235
+
236
+ ::ActiveRecord::Associations::SingularAssociation.prepend(
237
+ Module.new do
238
+ # call has_one and belongs_to associations
239
+ def reader
240
+ result = super
241
+
242
+ if Bullet.start?
243
+ if owner.class.name !~ /^HABTM_/
244
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
245
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
246
+ association = owner.association(reflection.through_reflection.name)
247
+ Array.wrap(association.target).each do |through_record|
248
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
249
+ end
250
+
251
+ if reflection.through_reflection != through_reflection
252
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
253
+ end
254
+ end
255
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
256
+
257
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
258
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
259
+ else
260
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
261
+ end
262
+ end
263
+ end
264
+ result
265
+ end
266
+ end
267
+ )
268
+
269
+ ::ActiveRecord::Associations::HasManyAssociation.prepend(
270
+ Module.new do
271
+ def empty?
272
+ result = super
273
+ if Bullet.start? && !reflection.has_cached_counter?
274
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
275
+ end
276
+ result
277
+ end
278
+
279
+ def count_records
280
+ result = reflection.has_cached_counter?
281
+ if Bullet.start? && !result && !is_a?(::ActiveRecord::Associations::ThroughAssociation)
282
+ Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name)
283
+ end
284
+ super
285
+ end
286
+ end
287
+ )
288
+
289
+ ::ActiveRecord::Associations::CollectionProxy.prepend(
290
+ Module.new do
291
+ def count(column_name = nil)
292
+ if Bullet.start? && !proxy_association.is_a?(::ActiveRecord::Associations::ThroughAssociation)
293
+ Bullet::Detector::CounterCache.add_counter_cache(
294
+ proxy_association.owner,
295
+ proxy_association.reflection.name
296
+ )
297
+ end
298
+ super(column_name)
299
+ end
300
+ end
301
+ )
302
+ end
303
+ end
304
+ end
@@ -31,6 +31,8 @@ module Bullet
31
31
  'active_record61'
32
32
  elsif active_record70?
33
33
  'active_record70'
34
+ elsif active_record71?
35
+ 'active_record71'
34
36
  else
35
37
  raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
36
38
  end
@@ -48,6 +50,8 @@ module Bullet
48
50
  'mongoid6x'
49
51
  elsif mongoid7x?
50
52
  'mongoid7x'
53
+ elsif mongoid8x?
54
+ 'mongoid8x'
51
55
  else
52
56
  raise "Bullet does not support mongoid #{::Mongoid::VERSION} yet"
53
57
  end
@@ -106,6 +110,10 @@ module Bullet
106
110
  active_record7? && ::ActiveRecord::VERSION::MINOR == 0
107
111
  end
108
112
 
113
+ def active_record71?
114
+ active_record7? && ::ActiveRecord::VERSION::MINOR == 1
115
+ end
116
+
109
117
  def mongoid4x?
110
118
  mongoid? && ::Mongoid::VERSION =~ /\A4/
111
119
  end
@@ -121,5 +129,9 @@ module Bullet
121
129
  def mongoid7x?
122
130
  mongoid? && ::Mongoid::VERSION =~ /\A7/
123
131
  end
132
+
133
+ def mongoid8x?
134
+ mongoid? && ::Mongoid::VERSION =~ /\A8/
135
+ end
124
136
  end
125
137
  end
@@ -44,13 +44,10 @@ module Bullet
44
44
  unless obj.bullet_primary_key_value.nil?
45
45
  primary_key_values_are_empty = false
46
46
  end
47
- keys_joined += "#{(keys_joined.empty?? '' : ', ')}#{obj.bullet_key}"
47
+ keys_joined += "#{(keys_joined.empty? ? '' : ', ')}#{obj.bullet_key}"
48
48
  end
49
49
  unless class_names_match_regex || primary_key_values_are_empty
50
- Bullet.debug(
51
- 'Detector::NPlusOneQuery#add_possible_objects',
52
- "objects: #{keys_joined}"
53
- )
50
+ Bullet.debug('Detector::NPlusOneQuery#add_possible_objects', "objects: #{keys_joined}")
54
51
  objects.each { |object| possible_objects.add object.bullet_key }
55
52
  end
56
53
  end
@@ -10,7 +10,7 @@ module Bullet
10
10
  # check if there are unused preload associations.
11
11
  # get related_objects from eager_loadings associated with object and associations
12
12
  # get call_object_association from associations of call_object_associations whose object is in related_objects
13
- # if association not in call_object_association, then the object => association - call_object_association is ununsed preload associations
13
+ # if association not in call_object_association, then the object => association - call_object_association is unused preload associations
14
14
  def check_unused_preload_associations
15
15
  return unless Bullet.start?
16
16
  return unless Bullet.unused_eager_loading_enable?
@@ -65,7 +65,10 @@ module Bullet
65
65
  private
66
66
 
67
67
  def create_notification(callers, klazz, associations)
68
- notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(:unused_eager_loading, klazz)
68
+ notify_associations = Array.wrap(associations) - Bullet.get_safelist_associations(
69
+ :unused_eager_loading,
70
+ klazz
71
+ )
69
72
 
70
73
  if notify_associations.present?
71
74
  notice = Bullet::Notification::UnusedEagerLoading.new(callers, klazz, notify_associations)
@@ -9,7 +9,8 @@ class Object
9
9
  return if respond_to?(:persisted?) && !persisted?
10
10
 
11
11
  if self.class.respond_to?(:primary_keys) && self.class.primary_keys
12
- self.class.primary_keys.map { |primary_key| send primary_key }.join(',')
12
+ self.class.primary_keys.map { |primary_key| send primary_key }
13
+ .join(',')
13
14
  elsif self.class.respond_to?(:primary_key) && self.class.primary_key
14
15
  send self.class.primary_key
15
16
  else