bullet 8.1.3 → 8.3.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: ce3dc25a18b75ec4068eae5386c39a96ac90775b2b410efd64f9993790205b83
4
+ data.tar.gz: a777865c06b2b8102a834db3d8a943bd41b2aadf92a91ecbc23f466dcc2bdea6
5
5
  SHA512:
6
- metadata.gz: 5b24c5aea3986b30399e0456268bdb00462540324b745aa4ccc451f77b0e4d78222fec5c4fb781b72114f5f497c12069ad5daf3b3678ae674ae7c58d688d824a
7
- data.tar.gz: 449c1c448f20f778053ec7ea032956f7446c2a14c1ea11293f3bc13cf7cf0d794beecf46dfb8052d58c644f35679a5d671f7def8b6d3b6f8eafd0a4689f7b565
6
+ metadata.gz: 643e6e91d5cb5a3d1de379b738451e82a3e9e7691f2a78a0d7721bac47deaff49181ac93f54cc8fd3390c34732d2bfdea0f4f0701bfb509e0fa9af789f41c519
7
+ data.tar.gz: 70982dc3a2e6cfe721ee126dd138f0c3ff9b0401d5376db35bc88f7245fcb6364f00ebae0e11401658a36e28964d7806d90f6eff15dc498b7e4aecdb32969884
data/CHANGELOG.md CHANGED
@@ -1,5 +1,18 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 8.3.0 (10/10/2026)
4
+
5
+ * Add support for ActiveRecord 8.2
6
+
7
+ ## 8.2.0 (08/29/2026)
8
+
9
+ * Add thread-safe `Bullet.pause`, `Bullet.resume`, `Bullet.paused?`, and `Bullet.skip` APIs
10
+ * Add configurable footer positioning
11
+ * Fix unused eager loading false positives when N+1 detection is disabled
12
+ * Fix unused eager loading false positives for conditional `has_many :through` associations
13
+ * Fix Ruby 4.0 compatibility issues
14
+ * Drop support for ActiveRecord versions older than 7 and Mongoid versions older than 8
15
+
3
16
  ## 8.1.3 (06/02/2026)
4
17
 
5
18
  * 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
 
@@ -16,8 +16,8 @@ module Bullet
16
16
  require 'active_record'
17
17
  ::ActiveRecord::Base.extend(
18
18
  Module.new do
19
- def find_by_sql(sql, binds = [], preparable: nil, &block)
20
- result = super
19
+ def find_by_sql(*args, **kwargs, &block)
20
+ result = super(*args, **kwargs, &block)
21
21
  if Bullet.start?
22
22
  if result.is_a? Array
23
23
  if result.size > 1
@@ -61,25 +61,32 @@ module Bullet
61
61
  end
62
62
  )
63
63
 
64
- ::ActiveRecord::Associations::Preloader.prepend(
64
+ ::ActiveRecord::Associations::Preloader::Batch.prepend(
65
65
  Module.new do
66
- def preloaders_for_one(association, records, scope, polymorphic_parent)
66
+ def call
67
67
  if Bullet.start?
68
- records.compact!
69
- if records.first.class.name !~ /^HABTM_/
70
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, association) }
71
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, association)
68
+ @preloaders.each do |preloader|
69
+ preloader.records.each { |record|
70
+ Bullet::Detector::Association.add_object_associations(record, preloader.associations)
71
+ }
72
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(preloader.records, preloader.associations)
72
73
  end
73
74
  end
74
75
  super
75
76
  end
77
+ end
78
+ )
76
79
 
77
- def preloaders_for_reflection(reflection, records, scope)
80
+ ::ActiveRecord::Associations::Preloader::Branch.prepend(
81
+ Module.new do
82
+ def preloaders_for_reflection(reflection, reflection_records)
78
83
  if Bullet.start?
79
- records.compact!
80
- if records.first.class.name !~ /^HABTM_/
81
- records.each { |record| Bullet::Detector::Association.add_object_associations(record, reflection.name) }
82
- Bullet::Detector::UnusedEagerLoading.add_eager_loadings(records, reflection.name)
84
+ reflection_records.compact!
85
+ if reflection_records.first.class.name !~ /^HABTM_/
86
+ reflection_records.each { |record|
87
+ Bullet::Detector::Association.add_object_associations(record, reflection.name)
88
+ }
89
+ Bullet::Detector::UnusedEagerLoading.add_eager_loadings(reflection_records, reflection.name)
83
90
  end
84
91
  end
85
92
  super
@@ -89,25 +96,41 @@ module Bullet
89
96
 
90
97
  ::ActiveRecord::Associations::Preloader::ThroughAssociation.prepend(
91
98
  Module.new do
92
- def preloaded_records
93
- if Bullet.start? && !defined?(@preloaded_records)
94
- source_preloaders.each do |source_preloader|
95
- reflection_name = source_preloader.send(:reflection).name
96
- source_preloader.send(:owners).each do |owner|
99
+ def source_preloaders
100
+ if Bullet.start? && !defined?(@source_preloaders)
101
+ preloaders = super
102
+ preloaders.each do |preloader|
103
+ reflection_name = preloader.send(:reflection).name
104
+ preloader.send(:owners).each do |owner|
97
105
  Bullet::Detector::NPlusOneQuery.call_association(owner, reflection_name)
98
106
  end
99
107
  end
108
+ else
109
+ super
110
+ end
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
100
124
  end
101
- super
102
125
  end
103
126
  end
104
127
  )
105
128
 
106
129
  ::ActiveRecord::Associations::JoinDependency.prepend(
107
130
  Module.new do
108
- def instantiate(result_set, &block)
131
+ def instantiate(*args, **kwargs, &block)
109
132
  @bullet_eager_loadings = {}
110
- records = super
133
+ records = super(*args, **kwargs, &block)
111
134
 
112
135
  if Bullet.start?
113
136
  @bullet_eager_loadings.each do |_klazz, eager_loadings_hash|
@@ -121,7 +144,8 @@ module Bullet
121
144
  records
122
145
  end
123
146
 
124
- def construct(ar_parent, parent, row, seen, model_cache)
147
+ def construct(*args, **kwargs, &block)
148
+ ar_parent, parent, row = args
125
149
  if Bullet.start?
126
150
  unless ar_parent.nil?
127
151
  parent.children.each do |node|
@@ -144,12 +168,13 @@ module Bullet
144
168
  end
145
169
  end
146
170
 
147
- super
171
+ super(*args, **kwargs, &block)
148
172
  end
149
173
 
150
174
  # call join associations
151
- def construct_model(record, node, row, model_cache, id)
152
- result = super
175
+ def construct_model(*args, **kwargs, &block)
176
+ record, node = args
177
+ result = super(*args, **kwargs, &block)
153
178
 
154
179
  if Bullet.start?
155
180
  associations = [node.reflection.name]
@@ -178,6 +203,13 @@ module Bullet
178
203
  end
179
204
  super
180
205
  end
206
+
207
+ def inversed_from_queries(record)
208
+ if Bullet.start? && inversable?(record)
209
+ Bullet::Detector::NPlusOneQuery.add_inversed_object(owner, reflection.name)
210
+ end
211
+ super
212
+ end
181
213
  end
182
214
  )
183
215
 
@@ -200,11 +232,7 @@ module Bullet
200
232
  end
201
233
  end
202
234
  end
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
235
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
208
236
  if records.first.class.name !~ /^HABTM_/
209
237
  if records.size > 1
210
238
  Bullet::Detector::NPlusOneQuery.add_possible_objects(records)
@@ -235,31 +263,28 @@ module Bullet
235
263
  ::ActiveRecord::Associations::SingularAssociation.prepend(
236
264
  Module.new do
237
265
  # call has_one and belongs_to associations
238
- def target
239
- result = super()
266
+ def reader
267
+ result = super
240
268
 
241
269
  if Bullet.start?
242
270
  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
271
+ if is_a? ::ActiveRecord::Associations::ThroughAssociation
272
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.through_reflection.name)
273
+ association = owner.association(reflection.through_reflection.name)
274
+ Array.wrap(association.target).each do |through_record|
275
+ Bullet::Detector::NPlusOneQuery.call_association(through_record, source_reflection.name)
276
+ end
250
277
 
251
- if reflection.through_reflection != through_reflection
252
- Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
253
- end
278
+ if reflection.through_reflection != through_reflection
279
+ Bullet::Detector::NPlusOneQuery.call_association(owner, through_reflection.name)
254
280
  end
255
281
  end
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
282
+ Bullet::Detector::NPlusOneQuery.call_association(owner, reflection.name)
283
+
284
+ if Bullet::Detector::NPlusOneQuery.impossible?(owner)
285
+ Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
286
+ else
287
+ Bullet::Detector::NPlusOneQuery.add_possible_objects(result) if result
263
288
  end
264
289
  end
265
290
  end
@@ -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?
@@ -39,6 +25,8 @@ module Bullet
39
25
  'active_record80'
40
26
  elsif active_record81?
41
27
  'active_record81'
28
+ elsif active_record82?
29
+ 'active_record82'
42
30
  else
43
31
  raise "Bullet does not support active_record #{::ActiveRecord::VERSION::STRING} yet"
44
32
  end
@@ -66,16 +54,8 @@ module Bullet
66
54
  end
67
55
  end
68
56
 
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
57
+ def active_record6_or_older?
58
+ active_record? && ::ActiveRecord::VERSION::MAJOR < 7
79
59
  end
80
60
 
81
61
  def active_record7?
@@ -86,38 +66,6 @@ module Bullet
86
66
  active_record? && ::ActiveRecord::VERSION::MAJOR == 8
87
67
  end
88
68
 
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
69
  def active_record70?
122
70
  active_record7? && ::ActiveRecord::VERSION::MINOR == 0
123
71
  end
@@ -138,6 +86,10 @@ module Bullet
138
86
  active_record8? && ::ActiveRecord::VERSION::MINOR == 1
139
87
  end
140
88
 
89
+ def active_record82?
90
+ active_record8? && ::ActiveRecord::VERSION::MINOR == 2
91
+ end
92
+
141
93
  def mongoid4x?
142
94
  mongoid? && ::Mongoid::VERSION =~ /\A4/
143
95
  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.3.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