bullet 8.3.0 → 8.3.1

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: ce3dc25a18b75ec4068eae5386c39a96ac90775b2b410efd64f9993790205b83
4
- data.tar.gz: a777865c06b2b8102a834db3d8a943bd41b2aadf92a91ecbc23f466dcc2bdea6
3
+ metadata.gz: d5c5aff8226198c501ed0c0a3e9d71138bd896f9956cf072b0f92fef79b125c7
4
+ data.tar.gz: 90209fe95da7ad96e110cfdabb96a91a9c403ef9e5824df2877aeab20239f020
5
5
  SHA512:
6
- metadata.gz: 643e6e91d5cb5a3d1de379b738451e82a3e9e7691f2a78a0d7721bac47deaff49181ac93f54cc8fd3390c34732d2bfdea0f4f0701bfb509e0fa9af789f41c519
7
- data.tar.gz: 70982dc3a2e6cfe721ee126dd138f0c3ff9b0401d5376db35bc88f7245fcb6364f00ebae0e11401658a36e28964d7806d90f6eff15dc498b7e4aecdb32969884
6
+ metadata.gz: 98dbc38b0e5f77f61617e75bab2b92838bbead761421681791711c917f61282e6ae29fe70fabe645ef8685344a879535fb4634451ff1bb35d560b0fd8a9c3a12
7
+ data.tar.gz: 49f2ebf88e0d99386ebd35a82403ece35167e9ac253013244f9ded4fe76de6b14ea070741847b6048b90ca1e15b6765430a56a180d8f6e6961383be035d08b38
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  ## Unreleased
2
2
 
3
+ ## 8.3.1 (10/24/2026)
4
+
5
+ * Avoid redefining the `footer_position` getter
6
+ * Avoid the CGI loading warning on Ruby 4
7
+ * Keep request state separate for sibling fibers on one thread
8
+
3
9
  ## 8.3.0 (10/10/2026)
4
10
 
5
11
  * Add support for ActiveRecord 8.2
@@ -15,7 +21,7 @@
15
21
 
16
22
  ## 8.1.3 (06/02/2026)
17
23
 
18
- * Handle inversed polymorphic belongs_to false positives
24
+ * Handle inversed polymorphic `belongs_to` false positives
19
25
 
20
26
  ## 8.1.2 (05/25/2026)
21
27
 
@@ -36,7 +36,7 @@ module Bullet
36
36
  # that the objects may cause N+1 query.
37
37
  # e.g. { Post => ["Post:1", "Post:2"] }
38
38
  def possible_objects
39
- Thread.current.thread_variable_get(:bullet_possible_objects)
39
+ Bullet.state[:bullet_possible_objects]
40
40
  end
41
41
 
42
42
  # impossible_objects keep the class to objects relationships
@@ -45,7 +45,7 @@ module Bullet
45
45
  # if find collection returns only one object, then the object is impossible object,
46
46
  # impossible_objects are used to avoid treating 1+1 query to N+1 query.
47
47
  def impossible_objects
48
- Thread.current.thread_variable_get(:bullet_impossible_objects)
48
+ Bullet.state[:bullet_impossible_objects]
49
49
  end
50
50
 
51
51
  private
@@ -56,7 +56,7 @@ module Bullet
56
56
  # the object_associations keep all associations that may be or may no be
57
57
  # unpreload associations or unused preload associations.
58
58
  def object_associations
59
- Thread.current.thread_variable_get(:bullet_object_associations)
59
+ Bullet.state[:bullet_object_associations]
60
60
  end
61
61
 
62
62
  # call_object_associations keep the object relationships
@@ -64,27 +64,27 @@ module Bullet
64
64
  # e.g. { "Post:1" => [:comments] }
65
65
  # they are used to detect unused preload associations.
66
66
  def call_object_associations
67
- Thread.current.thread_variable_get(:bullet_call_object_associations)
67
+ Bullet.state[:bullet_call_object_associations]
68
68
  end
69
69
 
70
70
  # inversed_objects keeps object relationships
71
71
  # that association is inversed.
72
72
  # e.g. { "Comment:1" => ["post"] }
73
73
  def inversed_objects
74
- Thread.current.thread_variable_get(:bullet_inversed_objects)
74
+ Bullet.state[:bullet_inversed_objects]
75
75
  end
76
76
 
77
77
  # eager_loadings keep the object relationships
78
78
  # that the associations are preloaded by find :include.
79
79
  # e.g. { ["Post:1", "Post:2"] => [:comments, :user] }
80
80
  def eager_loadings
81
- Thread.current.thread_variable_get(:bullet_eager_loadings)
81
+ Bullet.state[:bullet_eager_loadings]
82
82
  end
83
83
 
84
84
  # call_stacks keeps stacktraces where querie-objects were called from.
85
85
  # e.g. { 'Object:111' => [SomeProject/app/controllers/...] }
86
86
  def call_stacks
87
- Thread.current.thread_variable_get(:bullet_call_stacks)
87
+ Bullet.state[:bullet_call_stacks]
88
88
  end
89
89
  end
90
90
  end
@@ -46,11 +46,11 @@ module Bullet
46
46
  end
47
47
 
48
48
  def possible_objects
49
- Thread.current.thread_variable_get(:bullet_counter_possible_objects)
49
+ Bullet.state[:bullet_counter_possible_objects]
50
50
  end
51
51
 
52
52
  def impossible_objects
53
- Thread.current.thread_variable_get(:bullet_counter_impossible_objects)
53
+ Bullet.state[:bullet_counter_impossible_objects]
54
54
  end
55
55
 
56
56
  private
data/lib/bullet/rack.rb CHANGED
@@ -2,7 +2,12 @@
2
2
 
3
3
  require 'rack/request'
4
4
  require 'json'
5
- require 'cgi'
5
+
6
+ if RUBY_VERSION.to_i >= 4
7
+ require 'cgi/escape'
8
+ else
9
+ require 'cgi'
10
+ end
6
11
 
7
12
  module Bullet
8
13
  class Rack
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Bullet
4
- VERSION = '8.3.0'
4
+ VERSION = '8.3.1'
5
5
  end
data/lib/bullet.rb CHANGED
@@ -39,10 +39,10 @@ module Bullet
39
39
  :counter_cache_enable,
40
40
  :stacktrace_includes,
41
41
  :stacktrace_excludes,
42
- :skip_html_injection
42
+ :skip_html_injection,
43
+ :footer_position
43
44
  attr_reader :safelist
44
45
  attr_accessor :add_footer,
45
- :footer_position,
46
46
  :orm_patches_applied,
47
47
  :skip_http_headers,
48
48
  :always_append_html_body,
@@ -149,56 +149,71 @@ module Bullet
149
149
  puts "[Bullet][#{title}] #{message}" if ENV['BULLET_DEBUG'] == 'true'
150
150
  end
151
151
 
152
+ # Fiber storage reaches child fibers; start_request gives each request its own, and the owner check keeps it off new threads.
153
+ def state
154
+ unless Fiber.respond_to?(:[])
155
+ return Thread.current.thread_variable_get(:bullet_state) || Thread.current.thread_variable_set(:bullet_state, {})
156
+ end
157
+
158
+ current = Fiber[:bullet_state]
159
+ return current if current && current[:owner].equal?(Thread.current)
160
+
161
+ Fiber[:bullet_state] = { owner: Thread.current }
162
+ end
163
+
152
164
  def start_request
153
- Thread.current.thread_variable_set(:bullet_start, true)
154
- Thread.current.thread_variable_set(:bullet_notification_collector, Bullet::NotificationCollector.new)
155
-
156
- Thread.current.thread_variable_set(:bullet_object_associations, Bullet::Registry::Base.new)
157
- Thread.current.thread_variable_set(:bullet_call_object_associations, Bullet::Registry::Base.new)
158
- Thread.current.thread_variable_set(:bullet_possible_objects, Bullet::Registry::Object.new)
159
- Thread.current.thread_variable_set(:bullet_impossible_objects, Bullet::Registry::Object.new)
160
- Thread.current.thread_variable_set(:bullet_inversed_objects, Bullet::Registry::Base.new)
161
- Thread.current.thread_variable_set(:bullet_eager_loadings, Bullet::Registry::Association.new)
162
- Thread.current.thread_variable_set(:bullet_call_stacks, Bullet::Registry::CallStack.new)
163
-
164
- unless Thread.current.thread_variable_get(:bullet_counter_possible_objects)
165
- Thread.current.thread_variable_set(:bullet_counter_possible_objects, Bullet::Registry::Object.new)
165
+ if Fiber.respond_to?(:[])
166
+ Fiber[:bullet_state] = state.slice(:owner)
167
+ end
168
+ Bullet.state[:bullet_start] = true
169
+ Bullet.state[:bullet_notification_collector] = Bullet::NotificationCollector.new
170
+
171
+ Bullet.state[:bullet_object_associations] = Bullet::Registry::Base.new
172
+ Bullet.state[:bullet_call_object_associations] = Bullet::Registry::Base.new
173
+ Bullet.state[:bullet_possible_objects] = Bullet::Registry::Object.new
174
+ Bullet.state[:bullet_impossible_objects] = Bullet::Registry::Object.new
175
+ Bullet.state[:bullet_inversed_objects] = Bullet::Registry::Base.new
176
+ Bullet.state[:bullet_eager_loadings] = Bullet::Registry::Association.new
177
+ Bullet.state[:bullet_call_stacks] = Bullet::Registry::CallStack.new
178
+
179
+ unless Bullet.state[:bullet_counter_possible_objects]
180
+ Bullet.state[:bullet_counter_possible_objects] = Bullet::Registry::Object.new
166
181
  end
167
182
 
168
- unless Thread.current.thread_variable_get(:bullet_counter_impossible_objects)
169
- Thread.current.thread_variable_set(:bullet_counter_impossible_objects, Bullet::Registry::Object.new)
183
+ unless Bullet.state[:bullet_counter_impossible_objects]
184
+ Bullet.state[:bullet_counter_impossible_objects] = Bullet::Registry::Object.new
170
185
  end
171
186
  end
172
187
 
173
188
  def end_request
174
- Thread.current.thread_variable_set(:bullet_start, nil)
175
- Thread.current.thread_variable_set(:bullet_notification_collector, nil)
189
+ Bullet.state[:bullet_start] = nil
190
+ Bullet.state[:bullet_notification_collector] = nil
176
191
 
177
- Thread.current.thread_variable_set(:bullet_object_associations, nil)
178
- Thread.current.thread_variable_set(:bullet_call_object_associations, nil)
179
- Thread.current.thread_variable_set(:bullet_possible_objects, nil)
180
- Thread.current.thread_variable_set(:bullet_impossible_objects, nil)
181
- Thread.current.thread_variable_set(:bullet_inversed_objects, nil)
182
- Thread.current.thread_variable_set(:bullet_eager_loadings, nil)
192
+ Bullet.state[:bullet_object_associations] = nil
193
+ Bullet.state[:bullet_call_object_associations] = nil
194
+ Bullet.state[:bullet_possible_objects] = nil
195
+ Bullet.state[:bullet_impossible_objects] = nil
196
+ Bullet.state[:bullet_inversed_objects] = nil
197
+ Bullet.state[:bullet_eager_loadings] = nil
183
198
 
184
- Thread.current.thread_variable_set(:bullet_counter_possible_objects, nil)
185
- Thread.current.thread_variable_set(:bullet_counter_impossible_objects, nil)
199
+ Bullet.state[:bullet_counter_possible_objects] = nil
200
+ Bullet.state[:bullet_counter_impossible_objects] = nil
186
201
  end
187
202
 
188
203
  def start?
189
- enable? && Thread.current.thread_variable_get(:bullet_start)
204
+ enable? && Bullet.state[:bullet_start]
190
205
  end
191
206
 
192
207
  def pause
193
- Thread.current.thread_variable_set(:bullet_start, false)
208
+ Bullet.state[:bullet_start] = false
194
209
  end
195
210
 
196
211
  def resume
197
- Thread.current.thread_variable_set(:bullet_start, true)
212
+ Bullet.state[:bullet_start] = true
198
213
  end
199
214
 
200
215
  def paused?
201
- enable? && !Thread.current.thread_variable_get(:bullet_start)
216
+ enable? && !Bullet.state[:bullet_start]
202
217
  end
203
218
 
204
219
  def skip
@@ -210,7 +225,7 @@ module Bullet
210
225
  end
211
226
 
212
227
  def notification_collector
213
- Thread.current.thread_variable_get(:bullet_notification_collector)
228
+ Bullet.state[:bullet_notification_collector]
214
229
  end
215
230
 
216
231
  def notification?
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.3.0
4
+ version: 8.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Richard Huang
@@ -135,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
135
  - !ruby/object:Gem::Version
136
136
  version: 1.3.6
137
137
  requirements: []
138
- rubygems_version: 4.0.6
138
+ rubygems_version: 4.0.20
139
139
  specification_version: 4
140
140
  summary: help to kill N+1 queries and unused eager loading.
141
141
  test_files: []