bullet 8.1.3 → 8.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ebbf526d81da50f5e04ae03c0e33af01ab71071747711f62dd3790a6debc0551
4
- data.tar.gz: 7a1551a54344f9c363402854bf8129bc58ec21927b1ee9a9ce2f0b6a5b8f3e16
3
+ metadata.gz: fff8caa10888c5cc1aee062b08ac4c4df6e5911e9b4c592b3e0c89c3faedf089
4
+ data.tar.gz: aebe5eb8c7b8624fc2bbe05f42c4c6e8ba5ef8122f519e4e2f984f83d1ac8762
5
5
  SHA512:
6
- metadata.gz: 5b24c5aea3986b30399e0456268bdb00462540324b745aa4ccc451f77b0e4d78222fec5c4fb781b72114f5f497c12069ad5daf3b3678ae674ae7c58d688d824a
7
- data.tar.gz: 449c1c448f20f778053ec7ea032956f7446c2a14c1ea11293f3bc13cf7cf0d794beecf46dfb8052d58c644f35679a5d671f7def8b6d3b6f8eafd0a4689f7b565
6
+ metadata.gz: 6d60f48c25171229b660274b31ae90689f402d0ea1f46fdf2c63467a550765520b46afb70ff683a8beca75da7ddb86629ba5f58e276667a784d37653f3a94688
7
+ data.tar.gz: '06973354f3d5158e1ade0f2898577d772b6669d622b176010b6176dbb8b02e5f67c611fbb55668bfee9c93ea873c1183cd28ba30e778a5a6692d0e050a9a3794'
data/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 8.2.0 (08/29/2026)
4
+
5
+ * Add thread-safe `Bullet.pause`, `Bullet.resume`, `Bullet.paused?`, and `Bullet.skip` APIs
6
+ * Add configurable footer positioning
7
+ * Fix unused eager loading false positives when N+1 detection is disabled
8
+ * Fix unused eager loading false positives for conditional `has_many :through` associations
9
+ * Fix Ruby 4.0 compatibility issues
10
+ * Drop support for ActiveRecord versions older than 7 and Mongoid versions older than 8
11
+
3
12
  ## 8.1.3 (06/02/2026)
4
13
 
5
14
  * Handle inversed polymorphic belongs_to false positives
data/README.md CHANGED
@@ -69,6 +69,7 @@ config.after_initialize do
69
69
  Bullet.airbrake = true
70
70
  Bullet.rollbar = true
71
71
  Bullet.add_footer = true
72
+ Bullet.footer_position = 'bottom_left' # or 'bottom_right', 'top_left', 'top_right'
72
73
  Bullet.skip_html_injection = false
73
74
  Bullet.skip_http_headers = false
74
75
  Bullet.stacktrace_includes = [ 'your_gem', 'your_middleware' ]
@@ -97,6 +98,7 @@ The code above will enable all of the Bullet notification systems:
97
98
  * `Bullet.airbrake`: add notifications to airbrake
98
99
  * `Bullet.rollbar`: add notifications to rollbar
99
100
  * `Bullet.add_footer`: adds the details in the bottom left corner of the page. Double click the footer or use close button to hide footer.
101
+ * `Bullet.footer_position`: position of the footer on the page. Valid values are 'bottom_left' (default), 'bottom_right', 'top_left', or 'top_right'. This is useful when the default position clashes with browser UI elements like Chrome's URL preview.
100
102
  * `Bullet.skip_html_injection`: prevents Bullet from injecting code into the returned HTML. This must be false for receiving alerts, showing the footer or console logging.
101
103
  * `Bullet.skip_http_headers`: don't add headers to API requests, and remove the javascript that relies on them. Note that this prevents bullet from logging warnings to the browser console or updating the footer.
102
104
  * `Bullet.stacktrace_includes`: include paths with any of these substrings in the stack trace, even if they are not in your main app
@@ -137,23 +139,34 @@ Bullet.add_safelist :type => :unused_eager_loading, :class_name => "Post", :asso
137
139
  Bullet.add_safelist :type => :counter_cache, :class_name => "Country", :association => :cities
138
140
  ```
139
141
 
142
+ ## Skip Bullet in specific actions
143
+
140
144
  If you want to skip bullet in some specific controller actions, you can
141
- do like
145
+ use `Bullet.skip`, which is thread-safe and works correctly in multi-threaded
146
+ environments like Puma.
142
147
 
143
148
  ```ruby
144
149
  class ApplicationController < ActionController::Base
145
150
  around_action :skip_bullet, if: -> { defined?(Bullet) }
146
151
 
147
152
  def skip_bullet
148
- previous_value = Bullet.enable?
149
- Bullet.enable = false
150
- yield
151
- ensure
152
- Bullet.enable = previous_value
153
+ Bullet.skip { yield }
153
154
  end
154
155
  end
155
156
  ```
156
157
 
158
+ For more granular control, you can also use `Bullet.pause` and `Bullet.resume`:
159
+
160
+ ```ruby
161
+ Bullet.pause # Temporarily disable detection for current thread
162
+ Bullet.resume # Re-enable detection for current thread
163
+ Bullet.paused? # Check if detection is paused for current thread
164
+ ```
165
+
166
+ **Note**: Do not toggle `Bullet.enable` at runtime, as it modifies a global
167
+ flag that is not thread-safe. Use `Bullet.skip`, `Bullet.pause`, or
168
+ `Bullet.start_request`/`Bullet.end_request` instead.
169
+
157
170
  ## Log
158
171
 
159
172
  The Bullet log `log/bullet.log` will look something like this:
@@ -109,6 +109,20 @@ module Bullet
109
109
  super
110
110
  end
111
111
  end
112
+
113
+ def through_preloaders
114
+ if Bullet.start? && !defined?(@through_preloaders)
115
+ preloaders = super
116
+ preloaders.each do |preloader|
117
+ reflection_name = preloader.send(:reflection).name
118
+ preloader.send(:owners).each do |owner|
119
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
120
+ end
121
+ end
122
+ else
123
+ super
124
+ end
125
+ end
112
126
  end
113
127
  )
114
128
 
@@ -109,6 +109,20 @@ module Bullet
109
109
  super
110
110
  end
111
111
  end
112
+
113
+ def through_preloaders
114
+ if Bullet.start? && !defined?(@through_preloaders)
115
+ preloaders = super
116
+ preloaders.each do |preloader|
117
+ reflection_name = preloader.send(:reflection).name
118
+ preloader.send(:owners).each do |owner|
119
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
120
+ end
121
+ end
122
+ else
123
+ super
124
+ end
125
+ end
112
126
  end
113
127
  )
114
128
 
@@ -109,6 +109,20 @@ module Bullet
109
109
  super
110
110
  end
111
111
  end
112
+
113
+ def through_preloaders
114
+ if Bullet.start? && !defined?(@through_preloaders)
115
+ preloaders = super
116
+ preloaders.each do |preloader|
117
+ reflection_name = preloader.send(:reflection).name
118
+ preloader.send(:owners).each do |owner|
119
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
120
+ end
121
+ end
122
+ else
123
+ super
124
+ end
125
+ end
112
126
  end
113
127
  )
114
128
 
@@ -109,6 +109,20 @@ module Bullet
109
109
  super
110
110
  end
111
111
  end
112
+
113
+ def through_preloaders
114
+ if Bullet.start? && !defined?(@through_preloaders)
115
+ preloaders = super
116
+ preloaders.each do |preloader|
117
+ reflection_name = preloader.send(:reflection).name
118
+ preloader.send(:owners).each do |owner|
119
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
120
+ end
121
+ end
122
+ else
123
+ super
124
+ end
125
+ end
112
126
  end
113
127
  )
114
128
 
@@ -109,6 +109,20 @@ module Bullet
109
109
  super
110
110
  end
111
111
  end
112
+
113
+ def through_preloaders
114
+ if Bullet.start? && !defined?(@through_preloaders)
115
+ preloaders = super
116
+ preloaders.each do |preloader|
117
+ reflection_name = preloader.send(:reflection).name
118
+ preloader.send(:owners).each do |owner|
119
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
120
+ end
121
+ end
122
+ else
123
+ super
124
+ end
125
+ end
112
126
  end
113
127
  )
114
128
 
@@ -13,22 +13,8 @@ module Bullet
13
13
  def active_record_version
14
14
  @active_record_version ||=
15
15
  begin
16
- if active_record40?
17
- 'active_record4'
18
- elsif active_record41?
19
- 'active_record41'
20
- elsif active_record42?
21
- 'active_record42'
22
- elsif active_record50?
23
- 'active_record5'
24
- elsif active_record51?
25
- 'active_record5'
26
- elsif active_record52?
27
- 'active_record52'
28
- elsif active_record60?
29
- 'active_record60'
30
- elsif active_record61?
31
- 'active_record61'
16
+ if active_record6_or_older?
17
+ raise "Bullet no longer supports active_record #{::ActiveRecord::VERSION::STRING}"
32
18
  elsif active_record70?
33
19
  'active_record70'
34
20
  elsif active_record71?
@@ -66,16 +52,8 @@ module Bullet
66
52
  end
67
53
  end
68
54
 
69
- def active_record4?
70
- active_record? && ::ActiveRecord::VERSION::MAJOR == 4
71
- end
72
-
73
- def active_record5?
74
- active_record? && ::ActiveRecord::VERSION::MAJOR == 5
75
- end
76
-
77
- def active_record6?
78
- active_record? && ::ActiveRecord::VERSION::MAJOR == 6
55
+ def active_record6_or_older?
56
+ active_record? && ::ActiveRecord::VERSION::MAJOR < 7
79
57
  end
80
58
 
81
59
  def active_record7?
@@ -86,38 +64,6 @@ module Bullet
86
64
  active_record? && ::ActiveRecord::VERSION::MAJOR == 8
87
65
  end
88
66
 
89
- def active_record40?
90
- active_record4? && ::ActiveRecord::VERSION::MINOR == 0
91
- end
92
-
93
- def active_record41?
94
- active_record4? && ::ActiveRecord::VERSION::MINOR == 1
95
- end
96
-
97
- def active_record42?
98
- active_record4? && ::ActiveRecord::VERSION::MINOR == 2
99
- end
100
-
101
- def active_record50?
102
- active_record5? && ::ActiveRecord::VERSION::MINOR == 0
103
- end
104
-
105
- def active_record51?
106
- active_record5? && ::ActiveRecord::VERSION::MINOR == 1
107
- end
108
-
109
- def active_record52?
110
- active_record5? && ::ActiveRecord::VERSION::MINOR == 2
111
- end
112
-
113
- def active_record60?
114
- active_record6? && ::ActiveRecord::VERSION::MINOR == 0
115
- end
116
-
117
- def active_record61?
118
- active_record6? && ::ActiveRecord::VERSION::MINOR == 1
119
- end
120
-
121
67
  def active_record70?
122
68
  active_record7? && ::ActiveRecord::VERSION::MINOR == 0
123
69
  end
@@ -8,7 +8,7 @@ module Bullet
8
8
  class << self
9
9
  def add_object_associations(object, associations)
10
10
  return unless Bullet.start?
11
- return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
11
+ return unless Bullet.n_plus_one_query_enable? || Bullet.unused_eager_loading_enable?
12
12
  return unless object.bullet_primary_key_value
13
13
 
14
14
  Bullet.debug(
@@ -21,7 +21,7 @@ module Bullet
21
21
 
22
22
  def add_call_object_associations(object, associations)
23
23
  return unless Bullet.start?
24
- return if !Bullet.n_plus_one_query_enable? && !Bullet.unused_eager_loading_enable?
24
+ return unless Bullet.n_plus_one_query_enable? || Bullet.unused_eager_loading_enable?
25
25
  return unless object.bullet_primary_key_value
26
26
 
27
27
  Bullet.debug(
@@ -15,7 +15,7 @@ module Bullet
15
15
  # if it is, keeps this unpreload associations and caller.
16
16
  def call_association(object, associations, caller_stack = nil, inversed: false)
17
17
  return unless Bullet.start?
18
- return unless Bullet.n_plus_one_query_enable?
18
+ return unless Bullet.n_plus_one_query_enable? || Bullet.unused_eager_loading_enable?
19
19
  return unless object.bullet_primary_key_value
20
20
 
21
21
  # Record before early-returns so legitimate reads that bypass SQL
data/lib/bullet/rack.rb CHANGED
@@ -76,8 +76,9 @@ module Bullet
76
76
 
77
77
  # Make footer styles work with ContentSecurityPolicy style-src as self
78
78
  def footer_style(nonce = nil)
79
+ position_css = footer_position_css
79
80
  css = <<~CSS
80
- details#bullet-footer {cursor: pointer; position: fixed; left: 0px; bottom: 0px; z-index: 9999; background: #fdf2f2; color: #9b1c1c; font-size: 12px; border-radius: 0px 8px 0px 0px; border: 1px solid #9b1c1c;}
81
+ details#bullet-footer {cursor: pointer; position: fixed; #{position_css}; z-index: 9999; background: #fdf2f2; color: #9b1c1c; font-size: 12px; #{footer_border_radius}; border: 1px solid #9b1c1c;}
81
82
  details#bullet-footer summary {font-weight: 600; padding: 2px 8px;}
82
83
  details#bullet-footer div {padding: 8px; border-top: 1px solid #9b1c1c;}
83
84
  CSS
@@ -88,6 +89,38 @@ module Bullet
88
89
  end
89
90
  end
90
91
 
92
+ private
93
+
94
+ def footer_position_css
95
+ case Bullet.footer_position
96
+ when 'bottom_left'
97
+ 'left: 0px; bottom: 0px'
98
+ when 'bottom_right'
99
+ 'right: 0px; bottom: 0px'
100
+ when 'top_left'
101
+ 'left: 0px; top: 0px'
102
+ when 'top_right'
103
+ 'right: 0px; top: 0px'
104
+ else
105
+ 'left: 0px; bottom: 0px'
106
+ end
107
+ end
108
+
109
+ def footer_border_radius
110
+ case Bullet.footer_position
111
+ when 'bottom_left'
112
+ 'border-radius: 0px 8px 0px 0px'
113
+ when 'bottom_right'
114
+ 'border-radius: 8px 0px 0px 0px'
115
+ when 'top_left'
116
+ 'border-radius: 0px 0px 8px 0px'
117
+ when 'top_right'
118
+ 'border-radius: 0px 0px 0px 8px'
119
+ else
120
+ 'border-radius: 0px 8px 0px 0px'
121
+ end
122
+ end
123
+
91
124
  def set_header(headers, header_name, header_array)
92
125
  # Many proxy applications such as Nginx and AWS ELB limit
93
126
  # the size a header to 8KB, so truncate the list of reports to
@@ -123,7 +156,7 @@ module Bullet
123
156
  end
124
157
 
125
158
  def html_request?(headers, response)
126
- headers['Content-Type']&.include?('text/html')
159
+ headers['Content-Type']&.include?('text/html') || false
127
160
  end
128
161
 
129
162
  def response_body(response)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '8.1.3'
4
+ VERSION = '8.2.0'
5
5
  end
data/lib/bullet.rb CHANGED
@@ -42,6 +42,7 @@ module Bullet
42
42
  :skip_html_injection
43
43
  attr_reader :safelist
44
44
  attr_accessor :add_footer,
45
+ :footer_position,
45
46
  :orm_patches_applied,
46
47
  :skip_http_headers,
47
48
  :always_append_html_body,
@@ -188,6 +189,26 @@ module Bullet
188
189
  enable? && Thread.current.thread_variable_get(:bullet_start)
189
190
  end
190
191
 
192
+ def pause
193
+ Thread.current.thread_variable_set(:bullet_start, false)
194
+ end
195
+
196
+ def resume
197
+ Thread.current.thread_variable_set(:bullet_start, true)
198
+ end
199
+
200
+ def paused?
201
+ enable? && !Thread.current.thread_variable_get(:bullet_start)
202
+ end
203
+
204
+ def skip
205
+ bullet_was_started = start?
206
+ pause if bullet_was_started
207
+ yield
208
+ ensure
209
+ resume if bullet_was_started
210
+ end
211
+
191
212
  def notification_collector
192
213
  Thread.current.thread_variable_get(:bullet_notification_collector)
193
214
  end
@@ -259,6 +280,10 @@ module Bullet
259
280
  UniformNotifier.active_notifiers.include?(UniformNotifier::JavascriptConsole)
260
281
  end
261
282
 
283
+ def footer_position
284
+ @footer_position || 'bottom_left'
285
+ end
286
+
262
287
  def inject_into_page?
263
288
  return false if defined?(@skip_html_injection) && @skip_html_injection
264
289
 
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.3
4
+ version: 8.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
@@ -37,6 +37,34 @@ dependencies:
37
37
  - - "~>"
38
38
  - !ruby/object:Gem::Version
39
39
  version: '1.11'
40
+ - !ruby/object:Gem::Dependency
41
+ name: ostruct
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: logger
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
40
68
  description: help to kill N+1 queries and unused eager loading.
41
69
  email:
42
70
  - flyerhzm@gmail.com
@@ -49,13 +77,6 @@ files:
49
77
  - README.md
50
78
  - lib/bullet.rb
51
79
  - lib/bullet/active_job.rb
52
- - lib/bullet/active_record4.rb
53
- - lib/bullet/active_record41.rb
54
- - lib/bullet/active_record42.rb
55
- - lib/bullet/active_record5.rb
56
- - lib/bullet/active_record52.rb
57
- - lib/bullet/active_record60.rb
58
- - lib/bullet/active_record61.rb
59
80
  - lib/bullet/active_record70.rb
60
81
  - lib/bullet/active_record71.rb
61
82
  - lib/bullet/active_record72.rb
@@ -1,197 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Bullet
4
- module ActiveRecord
5
- def self.enable
6
- require 'active_record'
7
- ::ActiveRecord::Base.class_eval do
8
- class << self
9
- alias_method :origin_find_by_sql, :find_by_sql
10
- def find_by_sql(sql, binds = [])
11
- result = origin_find_by_sql(sql, binds)
12
- if Bullet.start?
13
- if result.is_a? Array
14
- if result.size > 1
15
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result)
16
- Bullet::Detector::CounterCache.add_possible_objects(result)
17
- elsif result.size == 1
18
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result.first)
19
- Bullet::Detector::CounterCache.add_impossible_object(result.first)
20
- end
21
- elsif result.is_a? ::ActiveRecord::Base
22
- Bullet::Detector::NPlusOneQuery.add_impossible_object(result)
23
- Bullet::Detector::CounterCache.add_impossible_object(result)
24
- end
25
- end
26
- result
27
- end
28
- end
29
- end
30
-
31
- ::ActiveRecord::Relation.class_eval do
32
- alias_method :origin_to_a, :to_a
33
- # if select a collection of objects, then these objects have possible to cause N+1 query.
34
- # if select only one object, then the only one object has impossible to cause N+1 query.
35
- def to_a
36
- records = origin_to_a
37
- if Bullet.start?
38
- if records.size > 1
39
- Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
40
- Bullet::Detector::CounterCache.add_possible_objects(records)
41
- elsif records.size == 1
42
- Bullet::Detector::NPlusOneQuery.add_impossible_object(records.first)
43
- Bullet::Detector::CounterCache.add_impossible_object(records.first)
44
- end
45
- end
46
- records
47
- end
48
- end
49
-
50
- ::ActiveRecord::Persistence.class_eval do
51
- def _create_record_with_bullet(*args)
52
- _create_record_without_bullet(*args).tap do
53
- Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
54
- Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
55
- end
56
- end
57
- alias_method_chain :_create_record, :bullet
58
- end
59
-
60
- ::ActiveRecord::Associations::Preloader.class_eval do
61
- # include query for one to many associations.
62
- # keep this eager loadings.
63
- alias_method :origin_initialize, :initialize
64
- def initialize(records, associations, preload_scope = nil)
65
- origin_initialize(records, associations, preload_scope)
66
-
67
- if Bullet.start?
68
- records = [records].flatten.compact.uniq
69
- return if records.empty?
70
-
71
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
72
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
73
- end
74
- end
75
- end
76
-
77
- ::ActiveRecord::FinderMethods.class_eval do
78
- # add includes in scope
79
- alias_method :origin_find_with_associations, :find_with_associations
80
- def find_with_associations
81
- records = origin_find_with_associations
82
- if Bullet.start?
83
- associations = (eager_load_values + includes_values).uniq
84
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, associations) }
85
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, associations)
86
- end
87
- records
88
- end
89
- end
90
-
91
- ::ActiveRecord::Associations::JoinDependency.class_eval do
92
- alias_method :origin_instantiate, :instantiate
93
- alias_method :origin_construct_association, :construct_association
94
-
95
- def instantiate(rows)
96
- @bullet_eager_loadings = {}
97
- records = origin_instantiate(rows)
98
-
99
- if Bullet.start?
100
- @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
101
- objects = eager_loadings_hash.keys
102
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(objects, eager_loadings_hash[objects.first].to_a)
103
- end
104
- end
105
- records
106
- end
107
-
108
- # call join associations
109
- def construct_association(record, join, row)
110
- result = origin_construct_association(record, join, row)
111
-
112
- if Bullet.start?
113
- associations = [join.reflection.name]
114
- if join.reflection.nested?
115
- associations << join.reflection.through_reflection.name
116
- end
117
- associations.each do |association|
118
- Bullet::Detector::Association.add_object_associations(record, association)
119
- Bullet::Detector::NPlusOneQuery.call_association(record, association)
120
- @bullet_eager_loadings[record.class] ||= {}
121
- @bullet_eager_loadings[record.class][record] ||= Set.new
122
- @bullet_eager_loadings[record.class][record] << association
123
- end
124
- end
125
-
126
- result
127
- end
128
- end
129
-
130
- ::ActiveRecord::Associations::CollectionAssociation.class_eval do
131
- # call one to many associations
132
- alias_method :origin_load_target, :load_target
133
- def load_target
134
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start?
135
- origin_load_target
136
- end
137
-
138
- alias_method :origin_include?, :include?
139
- def include?(object)
140
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start?
141
- origin_include?(object)
142
- end
143
- end
144
-
145
- ::ActiveRecord::Associations::HasManyAssociation.class_eval do
146
- alias_method :origin_empty?, :empty?
147
- def empty?
148
- if Bullet.start? && !loaded? && !has_cached_counter?(@reflection)
149
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name)
150
- end
151
- origin_empty?
152
- end
153
- end
154
-
155
- ::ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
156
- alias_method :origin_empty?, :empty?
157
- def empty?
158
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name) if Bullet.start? && !loaded?
159
- origin_empty?
160
- end
161
- end
162
-
163
- ::ActiveRecord::Associations::SingularAssociation.class_eval do
164
- # call has_one and belongs_to associations
165
- alias_method :origin_reader, :reader
166
- def reader(force_reload = false)
167
- result = origin_reader(force_reload)
168
- if Bullet.start?
169
- Bullet::Detector::NPlusOneQuery.call_association(@owner, @reflection.name, inversed: @inversed)
170
- Bullet::Detector::NPlusOneQuery.add_possible_objects(result) unless @inversed
171
- end
172
- result
173
- end
174
- end
175
-
176
- ::ActiveRecord::Associations::HasManyAssociation.class_eval do
177
- alias_method :origin_has_cached_counter?, :has_cached_counter?
178
-
179
- def has_cached_counter?(reflection = reflection())
180
- result = origin_has_cached_counter?(reflection)
181
- Bullet::Detector::CounterCache.add_counter_cache(owner, reflection.name) if Bullet.start? && !result
182
- result
183
- end
184
- end
185
-
186
- ::ActiveRecord::Associations::CollectionProxy.class_eval do
187
- def count(column_name = nil, options = {})
188
- if Bullet.start?
189
- Bullet::Detector::CounterCache.add_counter_cache(proxy_association.owner, proxy_association.reflection.name)
190
- Bullet::Detector::NPlusOneQuery.call_association(proxy_association.owner, proxy_association.reflection.name)
191
- end
192
- super(column_name, options)
193
- end
194
- end
195
- end
196
- end
197
- end