bullet 8.0.2 → 8.0.7
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 +24 -0
- data/README.md +7 -0
- 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/detector/n_plus_one_query.rb +13 -3
- data/lib/bullet/rack.rb +55 -28
- data/lib/bullet/registry/base.rb +5 -1
- data/lib/bullet/version.rb +1 -1
- data/lib/bullet.rb +2 -2
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7ed7921469ed264816680bf580bd37cc8e9033287d90a86c65bb4d486fb789f3
|
4
|
+
data.tar.gz: a0c0ca44bc16b3673b3c7ea14f03a12dee3c73fc72084ee959ef19839bbd4f68
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 354d9bd9b4d126ef45edf063184e7c9d612aa243caa7c2044a67f665f625a40e7c23ca61efcfe70193823689499bb6bb868022647770c8ed9dba60f9dff3e26f
|
7
|
+
data.tar.gz: f19a948e75f612a65143fb069559c4691d12a73bb8cd0b45699b4b80e372565d39ed0c193eabcdc4d8e289cefaa3f0845f380d2509b4d6f69a32d57392339840
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,29 @@
|
|
1
1
|
## Next Release
|
2
2
|
|
3
|
+
## 8.0.7 (05/15/2025)
|
4
|
+
|
5
|
+
* Try to insert `Bullet::Rack` properly
|
6
|
+
|
7
|
+
## 8.0.6 (05/07/2025)
|
8
|
+
|
9
|
+
* Add CSP nonce for footer styles as well
|
10
|
+
* Add support for OpenTelemetry reporting
|
11
|
+
|
12
|
+
## 8.0.5 (04/21/2025)
|
13
|
+
|
14
|
+
* Properly insert ContentSecurityPolicy middleware
|
15
|
+
* Properly parse query string
|
16
|
+
|
17
|
+
## 8.0.4 (04/18/2025)
|
18
|
+
|
19
|
+
* Insert bullet middleware before `ContentSecurityPolicy`
|
20
|
+
* Support url query `skip_html_injection=true`
|
21
|
+
* Mark object as impossible after updating inversed
|
22
|
+
|
23
|
+
## 8.0.3 (04/04/2025)
|
24
|
+
|
25
|
+
* Update non persisted `inversed_objects`
|
26
|
+
|
3
27
|
## 8.0.2 (04/02/2025)
|
4
28
|
|
5
29
|
* Do not cache `bullet_key` if object is not persisted
|
data/README.md
CHANGED
@@ -74,6 +74,7 @@ config.after_initialize do
|
|
74
74
|
Bullet.stacktrace_includes = [ 'your_gem', 'your_middleware' ]
|
75
75
|
Bullet.stacktrace_excludes = [ 'their_gem', 'their_middleware', ['my_file.rb', 'my_method'], ['my_file.rb', 16..20] ]
|
76
76
|
Bullet.slack = { webhook_url: 'http://some.slack.url', channel: '#default', username: 'notifier' }
|
77
|
+
Bullet.opentelemetry = true
|
77
78
|
end
|
78
79
|
```
|
79
80
|
|
@@ -100,6 +101,7 @@ The code above will enable all of the Bullet notification systems:
|
|
100
101
|
Each item can be a string (match substring), a regex, or an array where the first item is a path to match, and the second
|
101
102
|
item is a line number, a Range of line numbers, or a (bare) method name, to exclude only particular lines in a file.
|
102
103
|
* `Bullet.slack`: add notifications to slack
|
104
|
+
* `Bullet.opentelemetry`: add notifications to OpenTelemetry
|
103
105
|
* `Bullet.raise`: raise errors, useful for making your specs fail unless they have optimized queries
|
104
106
|
* `Bullet.always_append_html_body`: always append the html body even if no notifications are present. Note: `console` or `add_footer` must also be true. Useful for Single Page Applications where the initial page load might not have any notifications present.
|
105
107
|
* `Bullet.skip_user_in_notification`: exclude the OS user (`whoami`) from notifications.
|
@@ -192,6 +194,11 @@ see [https://github.com/flyerhzm/uniform_notifier](https://github.com/flyerhzm/u
|
|
192
194
|
|
193
195
|
Growl support is dropped from uniform_notifier 1.16.0, if you still want it, please use uniform_notifier 1.15.0.
|
194
196
|
|
197
|
+
## URL query control
|
198
|
+
|
199
|
+
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,
|
200
|
+
e.g. `http://localhost:3000/posts?skip_html_injection=true`
|
201
|
+
|
195
202
|
## Important
|
196
203
|
|
197
204
|
If you find Bullet does not work for you, *please disable your browser's cache*.
|
@@ -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
|
@@ -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/rack.rb
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'rack/request'
|
4
|
+
require 'json'
|
5
|
+
require 'cgi'
|
6
|
+
|
3
7
|
module Bullet
|
4
8
|
class Rack
|
5
9
|
include Dependency
|
6
10
|
|
7
|
-
NONCE_MATCHER = /script-src .*'nonce-(?<nonce>[A-Za-z0-9+\/]+={0,2})'/
|
11
|
+
NONCE_MATCHER = /(script|style)-src .*'nonce-(?<nonce>[A-Za-z0-9+\/]+={0,2})'/
|
8
12
|
|
9
13
|
def initialize(app)
|
10
14
|
@app = app
|
@@ -19,12 +23,13 @@ module Bullet
|
|
19
23
|
response_body = nil
|
20
24
|
|
21
25
|
if Bullet.notification? || Bullet.always_append_html_body
|
22
|
-
|
26
|
+
request = ::Rack::Request.new(env)
|
27
|
+
if Bullet.inject_into_page? && !skip_html_injection?(request) && !file?(headers) && !sse?(headers) && !empty?(response) && status == 200
|
23
28
|
if html_request?(headers, response)
|
24
29
|
response_body = response_body(response)
|
25
30
|
|
26
31
|
with_security_policy_nonce(headers) do |nonce|
|
27
|
-
response_body = append_to_html_body(response_body, footer_note) if Bullet.add_footer
|
32
|
+
response_body = append_to_html_body(response_body, footer_note(nonce)) if Bullet.add_footer
|
28
33
|
response_body = append_to_html_body(response_body, Bullet.gather_inline_notifications)
|
29
34
|
if Bullet.add_footer && !Bullet.skip_http_headers
|
30
35
|
response_body = append_to_html_body(response_body, xhr_script(nonce))
|
@@ -65,16 +70,48 @@ module Bullet
|
|
65
70
|
end
|
66
71
|
end
|
67
72
|
|
68
|
-
def footer_note
|
69
|
-
|
73
|
+
def footer_note(nonce = nil)
|
74
|
+
%(<details id="bullet-footer" data-is-bullet-footer><summary>Bullet Warnings</summary><div>#{Bullet.footer_info.uniq.join('<br>')}#{footer_console_message(nonce)}</div>#{footer_style(nonce)}</details>)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Make footer styles work with ContentSecurityPolicy style-src as self
|
78
|
+
def footer_style(nonce = nil)
|
79
|
+
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 summary {font-weight: 600; padding: 2px 8px;}
|
82
|
+
details#bullet-footer div {padding: 8px; border-top: 1px solid #9b1c1c;}
|
83
|
+
CSS
|
84
|
+
if nonce
|
85
|
+
%(<style type="text/css" nonce="#{nonce}">#{css}</style>)
|
86
|
+
else
|
87
|
+
%(<style type="text/css">#{css}</style>)
|
88
|
+
end
|
70
89
|
end
|
71
90
|
|
72
91
|
def set_header(headers, header_name, header_array)
|
73
92
|
# Many proxy applications such as Nginx and AWS ELB limit
|
74
93
|
# the size a header to 8KB, so truncate the list of reports to
|
75
94
|
# be under that limit
|
76
|
-
header_array.pop while header_array.
|
77
|
-
headers[header_name] = header_array
|
95
|
+
header_array.pop while JSON.generate(header_array).length > 8 * 1024
|
96
|
+
headers[header_name] = JSON.generate(header_array)
|
97
|
+
end
|
98
|
+
|
99
|
+
def skip_html_injection?(request)
|
100
|
+
query_string = request.env['QUERY_STRING']
|
101
|
+
return false if query_string.nil? || query_string.empty?
|
102
|
+
|
103
|
+
params = simple_parse_query_string(query_string)
|
104
|
+
params['skip_html_injection'] == 'true'
|
105
|
+
end
|
106
|
+
|
107
|
+
# Simple query string parser
|
108
|
+
def simple_parse_query_string(query_string)
|
109
|
+
params = {}
|
110
|
+
query_string.split('&').each do |pair|
|
111
|
+
key, value = pair.split('=', 2).map { |s| CGI.unescape(s) }
|
112
|
+
params[key] = value if key && !key.empty?
|
113
|
+
end
|
114
|
+
params
|
78
115
|
end
|
79
116
|
|
80
117
|
def file?(headers)
|
@@ -99,28 +136,18 @@ module Bullet
|
|
99
136
|
|
100
137
|
private
|
101
138
|
|
102
|
-
def
|
103
|
-
<<~EOF
|
104
|
-
id="bullet-footer" data-is-bullet-footer
|
105
|
-
style="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;"
|
106
|
-
EOF
|
107
|
-
end
|
108
|
-
|
109
|
-
def summary_attributes
|
110
|
-
<<~EOF
|
111
|
-
style="font-weight: 600; padding: 2px 8px"
|
112
|
-
EOF
|
113
|
-
end
|
114
|
-
|
115
|
-
def footer_content_attributes
|
116
|
-
<<~EOF
|
117
|
-
style="padding: 8px; border-top: 1px solid #9b1c1c;"
|
118
|
-
EOF
|
119
|
-
end
|
120
|
-
|
121
|
-
def footer_console_message
|
139
|
+
def footer_console_message(nonce = nil)
|
122
140
|
if Bullet.console_enabled?
|
123
|
-
|
141
|
+
footer = %(<br/><span id="console-message">See 'Uniform Notifier' in JS Console for Stacktrace</span>)
|
142
|
+
css = "details#bullet-footer #console-message {font-style: italic;}"
|
143
|
+
style =
|
144
|
+
if nonce
|
145
|
+
%(<style type="text/css" nonce="#{nonce}">#{css}</style>)
|
146
|
+
else
|
147
|
+
%(<style type="text/css">#{css}</style>)
|
148
|
+
end
|
149
|
+
|
150
|
+
footer + style
|
124
151
|
end
|
125
152
|
end
|
126
153
|
|
data/lib/bullet/registry/base.rb
CHANGED
data/lib/bullet/version.rb
CHANGED
data/lib/bullet.rb
CHANGED
@@ -23,8 +23,8 @@ module Bullet
|
|
23
23
|
|
24
24
|
if defined?(Rails::Railtie)
|
25
25
|
class BulletRailtie < Rails::Railtie
|
26
|
-
initializer 'bullet.
|
27
|
-
if defined?(ActionDispatch::ContentSecurityPolicy::Middleware) && Rails.application.config.content_security_policy
|
26
|
+
initializer 'bullet.add_middleware' do |app|
|
27
|
+
if defined?(ActionDispatch::ContentSecurityPolicy::Middleware) && Rails.application.config.content_security_policy && !app.config.api_only
|
28
28
|
app.middleware.insert_before ActionDispatch::ContentSecurityPolicy::Middleware, Bullet::Rack
|
29
29
|
else
|
30
30
|
app.middleware.use Bullet::Rack
|
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.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Richard Huang
|
8
8
|
bindir: bin
|
9
9
|
cert_chain: []
|
10
|
-
date:
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: activesupport
|
@@ -112,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
112
|
- !ruby/object:Gem::Version
|
113
113
|
version: 1.3.6
|
114
114
|
requirements: []
|
115
|
-
rubygems_version: 3.6.
|
115
|
+
rubygems_version: 3.6.7
|
116
116
|
specification_version: 4
|
117
117
|
summary: help to kill N+1 queries and unused eager loading.
|
118
118
|
test_files: []
|