bullet 8.1.2 → 8.1.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 42ddb49df9a6b4d4176a38561476cda975818bf5861606b4c279af59378a6b1e
4
- data.tar.gz: 8ae04173fe4f709c39f3a2011403b42617eb91d10de4f25eb29a2a28d06419c3
3
+ metadata.gz: ebbf526d81da50f5e04ae03c0e33af01ab71071747711f62dd3790a6debc0551
4
+ data.tar.gz: 7a1551a54344f9c363402854bf8129bc58ec21927b1ee9a9ce2f0b6a5b8f3e16
5
5
  SHA512:
6
- metadata.gz: 990f264d654fb565f413d015aadc0b5dd85ff418f881edf6ca9a50bd8df0a228d101cc7f92c3c2d868eff3c23cbfda3e1fbec554cb05cdb0a275133b984f5ee2
7
- data.tar.gz: 9f6e68da52ba03e7e434c879baa886d054f18ed5fb0b27606a79094c055f6da0d84702b319c5108812ca71eea94da6d07f07f92dcce31fac889be3858e91a7aa
6
+ metadata.gz: 5b24c5aea3986b30399e0456268bdb00462540324b745aa4ccc451f77b0e4d78222fec5c4fb781b72114f5f497c12069ad5daf3b3678ae674ae7c58d688d824a
7
+ data.tar.gz: 449c1c448f20f778053ec7ea032956f7446c2a14c1ea11293f3bc13cf7cf0d794beecf46dfb8052d58c644f35679a5d671f7def8b6d3b6f8eafd0a4689f7b565
data/CHANGELOG.md CHANGED
@@ -1,5 +1,9 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 8.1.3 (06/02/2026)
4
+
5
+ * Handle inversed polymorphic belongs_to false positives
6
+
3
7
  ## 8.1.2 (05/25/2026)
4
8
 
5
9
  * Skip N+1 detection for optional polymorphic `belongs_to` whose `*_type` column is nil. ActiveRecord short-circuits the reader to nil without issuing SQL, so the access cannot represent an N+1 query and preloading would be a no-op.
@@ -166,10 +166,8 @@ module Bullet
166
166
  def reader(force_reload = false)
167
167
  result = origin_reader(force_reload)
168
168
  if Bullet.start?
169
- unless @inversed
170
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
171
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
172
- end
169
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name, inversed: @inversed)
170
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) unless @inversed
173
171
  end
174
172
  result
175
173
  end
@@ -134,7 +134,7 @@ module Bullet
134
134
  # call one to many associations
135
135
  alias_method :origin_load_target, :load_target
136
136
  def load_target
137
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start? && !@inversed
137
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name, inversed: @inversed) if Bullet.start?
138
138
  origin_load_target
139
139
  end
140
140
 
@@ -159,9 +159,9 @@ module Bullet
159
159
  def reader(force_reload = false)
160
160
  result = origin_reader(force_reload)
161
161
  if Bullet.start?
162
- if @owner.class.name !~ /^HABTM_/ && !@inversed
163
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
164
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
162
+ if @owner.class.name !~ /^HABTM_/
163
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name, inversed: @inversed)
164
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) unless @inversed
165
165
  end
166
166
  end
167
167
  result
@@ -179,7 +179,7 @@ module Bullet
179
179
  records = origin_load_target
180
180
 
181
181
  if Bullet.start?
182
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) unless @inversed
182
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name, inversed: @inversed)
183
183
  if records.first.class.name !~ /^HABTM_/
184
184
  if records.size > 1
185
185
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -215,13 +215,14 @@ module Bullet
215
215
  result = origin_reader(force_reload)
216
216
 
217
217
  if Bullet.start?
218
- if @owner.class.name !~ /^HABTM_/ && !@inversed
219
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
220
-
221
- if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
222
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
223
- else
224
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
218
+ if @owner.class.name !~ /^HABTM_/
219
+ Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name, inversed: @inversed)
220
+ unless @inversed
221
+ if Bullet::Detector::NPlusOneQuery.impossible?(@owner)
222
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
223
+ else
224
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
225
+ end
225
226
  end
226
227
  end
227
228
  end
@@ -202,7 +202,7 @@ module Bullet
202
202
  end
203
203
  end
204
204
  end
205
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
205
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name, inversed: @inversed)
206
206
  if records.first.class.name !~ /^HABTM_/
207
207
  if records.size > 1
208
208
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -237,13 +237,14 @@ module Bullet
237
237
  result = super()
238
238
 
239
239
  if Bullet.start?
240
- if owner.class.name !~ /^HABTM_/ && !@inversed
241
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
242
-
243
- if Bullet::Detector::NPlusOneQuery.impossible?(owner)
244
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
245
- else
246
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
240
+ if owner.class.name !~ /^HABTM_/
241
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name, inversed: @inversed)
242
+ unless @inversed
243
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
244
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
245
+ else
246
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
247
+ end
247
248
  end
248
249
  end
249
250
  end
@@ -173,7 +173,7 @@ module Bullet
173
173
  end
174
174
  end
175
175
  end
176
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
176
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name, inversed: @inversed)
177
177
  if records.first.class.name !~ /^HABTM_/
178
178
  if records.size > 1
179
179
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -208,24 +208,27 @@ module Bullet
208
208
  result = super()
209
209
 
210
210
  if Bullet.start?
211
- if owner.class.name !~ /^HABTM_/ && !@inversed
212
- if is_a? ::ActiveRecord::Associations::ThroughAssociation
213
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
214
- association = owner.association reflection.through_reflection.name
215
- Array.wrap(association.target).each do |through_record|
216
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
217
- end
211
+ if owner.class.name !~ /^HABTM_/
212
+ unless @inversed
213
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
214
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
215
+ association = owner.association reflection.through_reflection.name
216
+ Array.wrap(association.target).each do |through_record|
217
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
218
+ end
218
219
 
219
- if reflection.through_reflection != through_reflection
220
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
220
+ if reflection.through_reflection != through_reflection
221
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
222
+ end
221
223
  end
222
224
  end
223
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
224
-
225
- if Bullet::Detector::NPlusOneQuery.impossible?(owner)
226
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
227
- else
228
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
225
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name, inversed: @inversed)
226
+ unless @inversed
227
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
228
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
229
+ else
230
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
231
+ end
229
232
  end
230
233
  end
231
234
  end
@@ -200,7 +200,11 @@ module Bullet
200
200
  end
201
201
  end
202
202
  end
203
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
203
+ if @inversed
204
+ Bullet::Detector::Association.add_call_object_associations(owner, reflection.name)
205
+ else
206
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
207
+ end
204
208
  if records.first.class.name !~ /^HABTM_/
205
209
  if records.size > 1
206
210
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -235,24 +239,27 @@ module Bullet
235
239
  result = super()
236
240
 
237
241
  if Bullet.start?
238
- if owner.class.name !~ /^HABTM_/ && !@inversed
239
- if is_a? ::ActiveRecord::Associations::ThroughAssociation
240
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
241
- association = owner.association(reflection.through_reflection.name)
242
- Array.wrap(association.target).each do |through_record|
243
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
244
- end
242
+ if owner.class.name !~ /^HABTM_/
243
+ unless @inversed
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
245
250
 
246
- if reflection.through_reflection != through_reflection
247
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
251
+ if reflection.through_reflection != through_reflection
252
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
253
+ end
248
254
  end
249
255
  end
250
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
251
-
252
- if Bullet::Detector::NPlusOneQuery.impossible?(owner)
253
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
254
- else
255
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
256
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name, inversed: @inversed)
257
+ unless @inversed
258
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
259
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
260
+ else
261
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
262
+ end
256
263
  end
257
264
  end
258
265
  end
@@ -200,7 +200,11 @@ module Bullet
200
200
  end
201
201
  end
202
202
  end
203
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name) unless @inversed
203
+ if @inversed
204
+ Bullet::Detector::Association.add_call_object_associations(owner, reflection.name)
205
+ else
206
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
207
+ end
204
208
  if records.first.class.name !~ /^HABTM_/
205
209
  if records.size > 1
206
210
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -235,24 +239,27 @@ module Bullet
235
239
  result = super()
236
240
 
237
241
  if Bullet.start?
238
- if owner.class.name !~ /^HABTM_/ && !@inversed
239
- if is_a? ::ActiveRecord::Associations::ThroughAssociation
240
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
241
- association = owner.association(reflection.through_reflection.name)
242
- Array.wrap(association.target).each do |through_record|
243
- Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
244
- end
242
+ if owner.class.name !~ /^HABTM_/
243
+ unless @inversed
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
245
250
 
246
- if reflection.through_reflection != through_reflection
247
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
251
+ if reflection.through_reflection != through_reflection
252
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
253
+ end
248
254
  end
249
255
  end
250
- Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
251
-
252
- if Bullet::Detector::NPlusOneQuery.impossible?(owner)
253
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
254
- else
255
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
256
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name, inversed: @inversed)
257
+ unless @inversed
258
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
259
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
260
+ else
261
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
262
+ end
256
263
  end
257
264
  end
258
265
  end
@@ -13,24 +13,20 @@ module Bullet
13
13
  # first, it keeps this method call for object.association.
14
14
  # then, it checks if this associations call is unpreload.
15
15
  # if it is, keeps this unpreload associations and caller.
16
- def call_association(object, associations, caller_stack = nil)
16
+ def call_association(object, associations, caller_stack = nil, inversed: false)
17
17
  return unless Bullet.start?
18
18
  return unless Bullet.n_plus_one_query_enable?
19
19
  return unless object.bullet_primary_key_value
20
- return if inversed_objects.include?(object.bullet_key, associations)
21
-
22
- # AR short-circuits a polymorphic belongs_to read to nil when the
23
- # *_type column is nil — no SQL is issued, so this cannot be an N+1.
24
- # Still record the call so a present preload isn't flagged as unused.
25
- if optional_polymorphic_belongs_to_with_nil_type?(object, associations)
26
- add_call_object_associations(object, associations)
27
- call_stacks.add(object.bullet_key, caller_stack) if caller_stack
28
- return
29
- end
30
20
 
21
+ # Record before early-returns so legitimate reads that bypass SQL
22
+ # (inversed, nil-polymorphic) aren't flagged as unused preloads.
31
23
  add_call_object_associations(object, associations)
32
24
  call_stacks.add(object.bullet_key, caller_stack) if caller_stack
33
25
 
26
+ return if inversed
27
+ return if inversed_objects.include?(object.bullet_key, associations)
28
+ return if optional_polymorphic_belongs_to_with_nil_type?(object, associations)
29
+
34
30
  Bullet.debug(
35
31
  'Detector::NPlusOneQuery#call_association',
36
32
  "object: #{object.bullet_key}, associations: #{associations}"
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '8.1.2'
4
+ VERSION = '8.1.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bullet
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.2
4
+ version: 8.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang