bullet 8.0.1 → 8.0.4
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 +4 -4
- data/CHANGELOG.md +14 -0
- data/README.md +30 -5
- data/lib/bullet/active_record4.rb +4 -1
- data/lib/bullet/active_record41.rb +4 -1
- data/lib/bullet/active_record42.rb +4 -1
- data/lib/bullet/active_record5.rb +1 -0
- data/lib/bullet/active_record52.rb +1 -0
- data/lib/bullet/active_record60.rb +1 -0
- data/lib/bullet/active_record61.rb +1 -0
- data/lib/bullet/active_record70.rb +1 -0
- data/lib/bullet/active_record71.rb +1 -0
- data/lib/bullet/active_record72.rb +1 -0
- data/lib/bullet/active_record80.rb +1 -0
- data/lib/bullet/dependency.rb +6 -0
- data/lib/bullet/detector/n_plus_one_query.rb +13 -3
- data/lib/bullet/ext/object.rb +8 -5
- data/lib/bullet/mongoid9x.rb +72 -0
- data/lib/bullet/rack.rb +22 -3
- data/lib/bullet/registry/base.rb +5 -1
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +5 -5
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a61908a92c8bc22bba69f9ea56946f1b6b2abd320e392e7539a3a6024b939b0c
|
4
|
+
data.tar.gz: 1fc844bc72f0e9e4c046590ef3f981180ce0beb5e32fab2217592d21e3456705
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 456c8c81a574243cda8ad3a3bb728b0ae97f169434e1608158a72967b8f9a6e7153939fcb2ce7c9d9e363921e918871e6a8b3ed7c374a45bc910170a4cb3e779
|
7
|
+
data.tar.gz: 3ebfc6724a13326f9d2286506727edb5d47b66b01253fbc48a2407d9e7f1c6c3f8ac700d7405024fcc86766a9fd65d170d0eea0678ffd1af3c60dd90b0c82b06
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,19 @@
|
|
1
1
|
## Next Release
|
2
2
|
|
3
|
+
## 8.0.4 (04/18/2024)
|
4
|
+
|
5
|
+
* Insert bullet middleware before `ContentSecurityPolicy`
|
6
|
+
* Support url query `skip_html_injection=true`
|
7
|
+
* Mark object as impossible after updating inversed
|
8
|
+
|
9
|
+
## 8.0.3 (04/04/2025)
|
10
|
+
|
11
|
+
* Update non persisted `inversed_objects`
|
12
|
+
|
13
|
+
## 8.0.2 (04/02/2025)
|
14
|
+
|
15
|
+
* Do not cache `bullet_key` if object is not persisted
|
16
|
+
|
3
17
|
## 8.0.1 (02/10/2025)
|
4
18
|
|
5
19
|
* Update benchmark to use sqlite
|
data/README.md
CHANGED
@@ -121,8 +121,6 @@ Bullet.unused_eager_loading_enable = false
|
|
121
121
|
Bullet.counter_cache_enable = false
|
122
122
|
```
|
123
123
|
|
124
|
-
Note: When calling `Bullet.enable`, all other detectors are reset to their defaults (`true`) and need reconfiguring.
|
125
|
-
|
126
124
|
## Safe list
|
127
125
|
|
128
126
|
Sometimes Bullet may notify you of query problems you don't care to fix, or
|
@@ -194,6 +192,11 @@ see [https://github.com/flyerhzm/uniform_notifier](https://github.com/flyerhzm/u
|
|
194
192
|
|
195
193
|
Growl support is dropped from uniform_notifier 1.16.0, if you still want it, please use uniform_notifier 1.15.0.
|
196
194
|
|
195
|
+
## URL query control
|
196
|
+
|
197
|
+
You can add the URL query parameter `skip_html_injection` to make the current HTML request behave as if `Bullet.skip_html_injection` is enabled,
|
198
|
+
e.g. `http://localhost:3000/posts?skip_html_injection=true`
|
199
|
+
|
197
200
|
## Important
|
198
201
|
|
199
202
|
If you find Bullet does not work for you, *please disable your browser's cache*.
|
@@ -240,7 +243,7 @@ If your application generates a Content-Security-Policy via a separate middlewar
|
|
240
243
|
|
241
244
|
### Run in tests
|
242
245
|
|
243
|
-
First you need to enable Bullet in test environment.
|
246
|
+
First you need to enable Bullet in the test environment.
|
244
247
|
|
245
248
|
```ruby
|
246
249
|
# config/environments/test.rb
|
@@ -251,11 +254,13 @@ config.after_initialize do
|
|
251
254
|
end
|
252
255
|
```
|
253
256
|
|
254
|
-
Then wrap each test in Bullet api.
|
257
|
+
Then wrap each test in the Bullet api.
|
258
|
+
|
259
|
+
With RSpec:
|
255
260
|
|
256
261
|
```ruby
|
257
262
|
# spec/rails_helper.rb
|
258
|
-
|
263
|
+
RSpec.configure do |config|
|
259
264
|
config.before(:each) do
|
260
265
|
Bullet.start_request
|
261
266
|
end
|
@@ -267,6 +272,26 @@ if Bullet.enable?
|
|
267
272
|
end
|
268
273
|
```
|
269
274
|
|
275
|
+
With Minitest:
|
276
|
+
|
277
|
+
```ruby
|
278
|
+
# test/test_helper.rb
|
279
|
+
module ActiveSupport
|
280
|
+
class TestCase
|
281
|
+
def before_setup
|
282
|
+
Bullet.start_request
|
283
|
+
super
|
284
|
+
end
|
285
|
+
|
286
|
+
def after_teardown
|
287
|
+
super
|
288
|
+
Bullet.perform_out_of_channel_notifications if Bullet.notification?
|
289
|
+
Bullet.end_request
|
290
|
+
end
|
291
|
+
end
|
292
|
+
end
|
293
|
+
```
|
294
|
+
|
270
295
|
## Debug Mode
|
271
296
|
|
272
297
|
Bullet outputs some details info, to enable debug mode, set
|
@@ -49,7 +49,10 @@ module Bullet
|
|
49
49
|
|
50
50
|
::ActiveRecord::Persistence.class_eval do
|
51
51
|
def _create_record_with_bullet(*args)
|
52
|
-
_create_record_without_bullet(*args).tap
|
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
|
53
56
|
end
|
54
57
|
alias_method_chain :_create_record, :bullet
|
55
58
|
end
|
@@ -52,7 +52,10 @@ module Bullet
|
|
52
52
|
|
53
53
|
::ActiveRecord::Persistence.class_eval do
|
54
54
|
def _create_record_with_bullet(*args)
|
55
|
-
_create_record_without_bullet(*args).tap
|
55
|
+
_create_record_without_bullet(*args).tap do
|
56
|
+
Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
|
57
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
58
|
+
end
|
56
59
|
end
|
57
60
|
alias_method_chain :_create_record, :bullet
|
58
61
|
end
|
@@ -45,7 +45,10 @@ module Bullet
|
|
45
45
|
|
46
46
|
::ActiveRecord::Persistence.class_eval do
|
47
47
|
def _create_record_with_bullet(*args)
|
48
|
-
_create_record_without_bullet(*args).tap
|
48
|
+
_create_record_without_bullet(*args).tap do
|
49
|
+
Bullet::Detector::NPlusOneQuery.update_inversed_object(self)
|
50
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(self)
|
51
|
+
end
|
49
52
|
end
|
50
53
|
alias_method_chain :_create_record, :bullet
|
51
54
|
end
|
data/lib/bullet/dependency.rb
CHANGED
@@ -56,6 +56,8 @@ module Bullet
|
|
56
56
|
'mongoid7x'
|
57
57
|
elsif mongoid8x?
|
58
58
|
'mongoid8x'
|
59
|
+
elsif mongoid9x?
|
60
|
+
'mongoid9x'
|
59
61
|
else
|
60
62
|
raise "Bullet does not support mongoid #{::Mongoid::VERSION} yet"
|
61
63
|
end
|
@@ -149,5 +151,9 @@ module Bullet
|
|
149
151
|
def mongoid8x?
|
150
152
|
mongoid? && ::Mongoid::VERSION =~ /\A8/
|
151
153
|
end
|
154
|
+
|
155
|
+
def mongoid9x?
|
156
|
+
mongoid? && ::Mongoid::VERSION =~ /\A9/
|
157
|
+
end
|
152
158
|
end
|
153
159
|
end
|
@@ -67,13 +67,23 @@ module Bullet
|
|
67
67
|
def add_inversed_object(object, association)
|
68
68
|
return unless Bullet.start?
|
69
69
|
return unless Bullet.n_plus_one_query_enable?
|
70
|
-
return unless object.bullet_primary_key_value
|
71
70
|
|
71
|
+
object_key = object.bullet_primary_key_value ? object.bullet_key : object.object_id
|
72
72
|
Bullet.debug(
|
73
73
|
'Detector::NPlusOneQuery#add_inversed_object',
|
74
|
-
"object: #{
|
74
|
+
"object: #{object_key}, association: #{association}"
|
75
75
|
)
|
76
|
-
inversed_objects.add
|
76
|
+
inversed_objects.add object_key, association
|
77
|
+
end
|
78
|
+
|
79
|
+
def update_inversed_object(object)
|
80
|
+
if inversed_objects&.key?(object.object_id)
|
81
|
+
Bullet.debug(
|
82
|
+
'Detector::NPlusOneQuery#update_inversed_object',
|
83
|
+
"object from #{object.object_id} to #{object.bullet_key}"
|
84
|
+
)
|
85
|
+
inversed_objects.add(object.bullet_key, inversed_objects[object.object_id].to_a)
|
86
|
+
end
|
77
87
|
end
|
78
88
|
|
79
89
|
# decide whether the object.associations is unpreloaded or not.
|
data/lib/bullet/ext/object.rb
CHANGED
@@ -7,17 +7,20 @@ module Bullet
|
|
7
7
|
attr_writer :bullet_key, :bullet_primary_key_value
|
8
8
|
|
9
9
|
def bullet_key
|
10
|
+
return "#{self.class}:" if respond_to?(:persisted?) && !persisted?
|
11
|
+
|
10
12
|
@bullet_key ||= "#{self.class}:#{bullet_primary_key_value}"
|
11
13
|
end
|
12
14
|
|
13
15
|
def bullet_primary_key_value
|
14
|
-
|
15
|
-
return if respond_to?(:persisted?) && !persisted?
|
16
|
+
return if respond_to?(:persisted?) && !persisted?
|
16
17
|
|
17
|
-
|
18
|
+
@bullet_primary_key_value ||=
|
19
|
+
begin
|
20
|
+
primary_key = self.class.try(:primary_keys) || self.class.try(:primary_key) || :id
|
18
21
|
|
19
|
-
|
20
|
-
|
22
|
+
bullet_join_potential_composite_primary_key(primary_key)
|
23
|
+
end
|
21
24
|
end
|
22
25
|
|
23
26
|
private
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Bullet
|
4
|
+
module Mongoid
|
5
|
+
def self.enable
|
6
|
+
require 'mongoid'
|
7
|
+
require 'rubygems'
|
8
|
+
::Mongoid::Contextual::Mongo.class_eval do
|
9
|
+
alias_method :origin_first, :first
|
10
|
+
alias_method :origin_last, :last
|
11
|
+
alias_method :origin_each, :each
|
12
|
+
alias_method :origin_eager_load, :eager_load
|
13
|
+
|
14
|
+
%i[first last].each do |context|
|
15
|
+
default = Gem::Version.new(::Mongoid::VERSION) >= Gem::Version.new('7.5') ? nil : {}
|
16
|
+
define_method(context) do |opts = default|
|
17
|
+
result = send(:"origin_#{context}", opts)
|
18
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(result) if result
|
19
|
+
result
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def each(&_block)
|
24
|
+
return to_enum unless block_given?
|
25
|
+
|
26
|
+
first_document = nil
|
27
|
+
document_count = 0
|
28
|
+
|
29
|
+
origin_each do |document|
|
30
|
+
document_count += 1
|
31
|
+
|
32
|
+
if document_count == 1
|
33
|
+
first_document = document
|
34
|
+
elsif document_count == 2
|
35
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects([first_document, document])
|
36
|
+
yield(first_document)
|
37
|
+
first_document = nil
|
38
|
+
yield(document)
|
39
|
+
else
|
40
|
+
Bullet::Detector::NPlusOneQuery.add_possible_objects(document)
|
41
|
+
yield(document)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if document_count == 1
|
46
|
+
Bullet::Detector::NPlusOneQuery.add_impossible_object(first_document)
|
47
|
+
yield(first_document)
|
48
|
+
end
|
49
|
+
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def eager_load(docs)
|
54
|
+
associations = criteria.inclusions.map(&:name)
|
55
|
+
docs.each { |doc| Bullet::Detector::NPlusOneQuery.add_object_associations(doc, associations) }
|
56
|
+
Bullet::Detector::UnusedEagerLoading.add_eager_loadings(docs, associations)
|
57
|
+
origin_eager_load(docs)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
::Mongoid::Association::Accessors.class_eval do
|
62
|
+
alias_method :origin_get_relation, :get_relation
|
63
|
+
|
64
|
+
def get_relation(name, association, object, reload = false)
|
65
|
+
result = origin_get_relation(name, association, object, reload)
|
66
|
+
Bullet::Detector::NPlusOneQuery.call_association(self, name) unless association.embedded?
|
67
|
+
result
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/bullet/rack.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'rack/request'
|
4
|
+
require 'json'
|
5
|
+
|
3
6
|
module Bullet
|
4
7
|
class Rack
|
5
8
|
include Dependency
|
@@ -19,7 +22,8 @@ module Bullet
|
|
19
22
|
response_body = nil
|
20
23
|
|
21
24
|
if Bullet.notification? || Bullet.always_append_html_body
|
22
|
-
|
25
|
+
request = ::Rack::Request.new(env)
|
26
|
+
if Bullet.inject_into_page? && !skip_html_injection?(request) && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
|
23
27
|
if html_request?(headers, response)
|
24
28
|
response_body = response_body(response)
|
25
29
|
|
@@ -73,8 +77,23 @@ module Bullet
|
|
73
77
|
# Many proxy applications such as Nginx and AWS ELB limit
|
74
78
|
# the size a header to 8KB, so truncate the list of reports to
|
75
79
|
# be under that limit
|
76
|
-
header_array.pop while header_array.
|
77
|
-
headers[header_name] = header_array
|
80
|
+
header_array.pop while JSON.generate(header_array).length > 8 * 1024
|
81
|
+
headers[header_name] = JSON.generate(header_array)
|
82
|
+
end
|
83
|
+
|
84
|
+
def skip_html_injection?(request)
|
85
|
+
query_string = request.env['QUERY_STRING']
|
86
|
+
return false if query_string.nil? || query_string.empty?
|
87
|
+
|
88
|
+
if defined?(Rack::QueryParser)
|
89
|
+
parser = Rack::QueryParser.new
|
90
|
+
params = parser.parse_nested_query(query_string)
|
91
|
+
else
|
92
|
+
# compatible with rack 1.x,
|
93
|
+
# remove it after dropping rails 4.2 suppport
|
94
|
+
params = Rack::Utils.parse_nested_query(query_string)
|
95
|
+
end
|
96
|
+
params['skip_html_injection'] == 'true'
|
78
97
|
end
|
79
98
|
|
80
99
|
def file?(headers)
|
data/lib/bullet/registry/base.rb
CHANGED
data/lib/bullet/version.rb
CHANGED
data/lib/bullet.rb
CHANGED
@@ -24,7 +24,7 @@ module Bullet
|
|
24
24
|
if defined?(Rails::Railtie)
|
25
25
|
class BulletRailtie < Rails::Railtie
|
26
26
|
initializer 'bullet.configure_rails_initialization' do |app|
|
27
|
-
if defined?(ActionDispatch::ContentSecurityPolicy::Middleware)
|
27
|
+
if defined?(ActionDispatch::ContentSecurityPolicy::Middleware)
|
28
28
|
app.middleware.insert_before ActionDispatch::ContentSecurityPolicy::Middleware, Bullet::Rack
|
29
29
|
else
|
30
30
|
app.middleware.use Bullet::Rack
|
@@ -64,7 +64,7 @@ module Bullet
|
|
64
64
|
].freeze
|
65
65
|
|
66
66
|
def enable=(enable)
|
67
|
-
@enable =
|
67
|
+
@enable = enable
|
68
68
|
|
69
69
|
if enable?
|
70
70
|
reset_safelist
|
@@ -90,15 +90,15 @@ module Bullet
|
|
90
90
|
end
|
91
91
|
|
92
92
|
def n_plus_one_query_enable?
|
93
|
-
enable? &&
|
93
|
+
enable? && (@n_plus_one_query_enable.nil? ? true : @n_plus_one_query_enable)
|
94
94
|
end
|
95
95
|
|
96
96
|
def unused_eager_loading_enable?
|
97
|
-
enable? &&
|
97
|
+
enable? && (@unused_eager_loading_enable.nil? ? true : @unused_eager_loading_enable)
|
98
98
|
end
|
99
99
|
|
100
100
|
def counter_cache_enable?
|
101
|
-
enable? &&
|
101
|
+
enable? && (@counter_cache_enable.nil? ? true : @counter_cache_enable)
|
102
102
|
end
|
103
103
|
|
104
104
|
def stacktrace_includes
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bullet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 8.0.
|
4
|
+
version: 8.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date: 2025-
|
10
|
+
date: 2025-04-18 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activesupport
|
@@ -75,6 +75,7 @@ files:
|
|
75
75
|
- lib/bullet/mongoid6x.rb
|
76
76
|
- lib/bullet/mongoid7x.rb
|
77
77
|
- lib/bullet/mongoid8x.rb
|
78
|
+
- lib/bullet/mongoid9x.rb
|
78
79
|
- lib/bullet/notification.rb
|
79
80
|
- lib/bullet/notification/base.rb
|
80
81
|
- lib/bullet/notification/counter_cache.rb
|