rails-profiler 0.35.2 → 0.35.3
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 +22 -0
- data/app/assets/builds/profiler-toolbar.js +2 -4454
- data/app/assets/builds/profiler.css +1 -3546
- data/app/assets/builds/profiler.js +14 -9699
- data/app/controllers/profiler/assets_controller.rb +14 -5
- data/app/views/layouts/profiler/application.html.erb +2 -2
- data/app/views/layouts/profiler/embedded.html.erb +6 -4
- data/lib/profiler/assets.rb +61 -0
- data/lib/profiler/middleware/toolbar_injector.rb +22 -10
- data/lib/profiler/version.rb +1 -1
- data/lib/profiler.rb +1 -0
- metadata +2 -1
|
@@ -10,16 +10,21 @@ module Profiler
|
|
|
10
10
|
|
|
11
11
|
BUILD_MISSING = "Profiler assets are not built (%s is missing). Run `yarn build:full` in the profiler gem."
|
|
12
12
|
|
|
13
|
+
# A year: the URL a page uses carries the digest of the content (?v=), so a new version of
|
|
14
|
+
# the gem is a new URL. Without the digest, or with an old one, the file is only kept an
|
|
15
|
+
# hour, as it may be replaced under the same URL.
|
|
16
|
+
VERSIONED_CACHE_CONTROL = "public, max-age=#{365 * 24 * 3600}, immutable".freeze
|
|
17
|
+
|
|
13
18
|
def toolbar_js
|
|
14
|
-
serve
|
|
19
|
+
serve "profiler-toolbar.js", "application/javascript"
|
|
15
20
|
end
|
|
16
21
|
|
|
17
22
|
def main_js
|
|
18
|
-
serve
|
|
23
|
+
serve "profiler.js", "application/javascript"
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
def main_css
|
|
22
|
-
serve
|
|
27
|
+
serve "profiler.css", "text/css"
|
|
23
28
|
end
|
|
24
29
|
|
|
25
30
|
def icon
|
|
@@ -30,8 +35,12 @@ module Profiler
|
|
|
30
35
|
private
|
|
31
36
|
|
|
32
37
|
def serve(name, content_type)
|
|
33
|
-
body =
|
|
34
|
-
|
|
38
|
+
body = Profiler::Assets.read(name)
|
|
39
|
+
if Profiler::Assets.current?(name, params[:v])
|
|
40
|
+
response.headers["Cache-Control"] = VERSIONED_CACHE_CONTROL
|
|
41
|
+
else
|
|
42
|
+
expires_in 1.hour, public: true
|
|
43
|
+
end
|
|
35
44
|
render plain: body, content_type: content_type
|
|
36
45
|
rescue Errno::ENOENT
|
|
37
46
|
render plain: format(BUILD_MISSING, name), status: :not_found
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
<meta name="profiler-name" content="<%= Profiler.configuration.resolved_name %>">
|
|
11
11
|
<meta name="profiler-is-master" content="<%= Profiler.configuration.master? %>">
|
|
12
12
|
|
|
13
|
-
<link rel="stylesheet" href="
|
|
14
|
-
<script src="
|
|
13
|
+
<link rel="stylesheet" href="<%= Profiler::Assets.url("profiler.css") %>">
|
|
14
|
+
<script src="<%= Profiler::Assets.url("profiler.js") %>" defer></script>
|
|
15
15
|
</head>
|
|
16
16
|
|
|
17
17
|
<body>
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
<% nonce = content_security_policy_nonce %>
|
|
2
|
+
<% nonce_attr = nonce ? %( nonce="#{ERB::Util.html_escape(nonce)}").html_safe : "" %>
|
|
1
3
|
<!DOCTYPE html>
|
|
2
4
|
<html data-theme="<%= params[:theme].presence&.then { |t| %w[light dark].include?(t) ? t : 'dark' } || 'dark' %>">
|
|
3
5
|
<head>
|
|
@@ -8,16 +10,16 @@
|
|
|
8
10
|
<%= csp_meta_tag %>
|
|
9
11
|
<meta name="profiler-version" content="<%= Profiler.version %>">
|
|
10
12
|
|
|
11
|
-
<link rel="stylesheet" href="
|
|
12
|
-
<script src="
|
|
13
|
+
<link rel="stylesheet" href="<%= Profiler::Assets.url("profiler.css") %>">
|
|
14
|
+
<script src="<%= Profiler::Assets.url("profiler.js") %>" defer></script>
|
|
13
15
|
|
|
14
|
-
<style
|
|
16
|
+
<style<%= nonce_attr %>>
|
|
15
17
|
/* Embedded overrides, tighter layout, no page chrome */
|
|
16
18
|
body { overflow-y: auto; }
|
|
17
19
|
.container { padding: 16px 20px; }
|
|
18
20
|
</style>
|
|
19
21
|
|
|
20
|
-
<script
|
|
22
|
+
<script<%= nonce_attr %>>
|
|
21
23
|
// Receive theme changes from the Chrome extension panel via postMessage.
|
|
22
24
|
// The extension sends: { type: 'profiler:set-theme', theme: 'light' | 'dark' }
|
|
23
25
|
window.addEventListener('message', function(e) {
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "digest"
|
|
4
|
+
|
|
5
|
+
module Profiler
|
|
6
|
+
# The gem's built front end files. Each is read once and kept until the file changes on disk
|
|
7
|
+
# (a rebuild in development), and named by a digest of its content so that a browser that
|
|
8
|
+
# cached one never keeps it past a new version of the gem.
|
|
9
|
+
module Assets
|
|
10
|
+
NAMES = %w[profiler.js profiler.css profiler-toolbar.js].freeze
|
|
11
|
+
URL_PREFIX = "/_profiler/assets"
|
|
12
|
+
|
|
13
|
+
Entry = Struct.new(:mtime, :content, :digest)
|
|
14
|
+
|
|
15
|
+
@entries = {}
|
|
16
|
+
@mutex = Mutex.new
|
|
17
|
+
|
|
18
|
+
class << self
|
|
19
|
+
# Where the build writes the files; the gem's own, never the application's.
|
|
20
|
+
def builds_dir
|
|
21
|
+
File.expand_path("../../app/assets/builds", __dir__)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# The file's content, or raises Errno::ENOENT when the assets are not built.
|
|
25
|
+
def read(name)
|
|
26
|
+
entry(name).content
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Short digest of the content; the gem version when the file is not built.
|
|
30
|
+
def digest(name)
|
|
31
|
+
entry(name).digest
|
|
32
|
+
rescue Errno::ENOENT
|
|
33
|
+
Digest::SHA256.hexdigest(Profiler::VERSION)[0, 12]
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# The URL a page uses for the asset, carrying its digest.
|
|
37
|
+
def url(name)
|
|
38
|
+
"#{URL_PREFIX}/#{name}?v=#{digest(name)}"
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def current?(name, version)
|
|
42
|
+
version.is_a?(String) && !version.empty? && version == digest(name)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def entry(name)
|
|
48
|
+
raise ArgumentError, "unknown asset: #{name}" unless NAMES.include?(name)
|
|
49
|
+
|
|
50
|
+
path = File.join(builds_dir, name)
|
|
51
|
+
mtime = File.mtime(path)
|
|
52
|
+
cached = @mutex.synchronize { @entries[name] }
|
|
53
|
+
return cached if cached && cached.mtime == mtime
|
|
54
|
+
|
|
55
|
+
content = File.read(path)
|
|
56
|
+
fresh = Entry.new(mtime, content, Digest::SHA256.hexdigest(content)[0, 12])
|
|
57
|
+
@mutex.synchronize { @entries[name] = fresh }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
@@ -38,6 +38,24 @@ module Profiler
|
|
|
38
38
|
)*+
|
|
39
39
|
}xi
|
|
40
40
|
|
|
41
|
+
INTERCEPTOR_PATH = File.expand_path("../../../app/assets/javascript/profiler-ajax-interceptor.js", __dir__)
|
|
42
|
+
|
|
43
|
+
class << self
|
|
44
|
+
# The script is the same for every request of a version of the gem: read once. A failed
|
|
45
|
+
# read is not kept, so that the next request tries again.
|
|
46
|
+
def ajax_interceptor_code
|
|
47
|
+
@ajax_interceptor_code ||= File.read(INTERCEPTOR_PATH).freeze
|
|
48
|
+
rescue => e
|
|
49
|
+
Profiler.log_error("ToolbarInjector: could not load the AJAX interceptor script", e)
|
|
50
|
+
""
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
# The toolbar CSS is a constant of the class too, built once.
|
|
54
|
+
def toolbar_styles
|
|
55
|
+
@toolbar_styles ||= build_toolbar_styles
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
41
59
|
def initialize(body, token, nonce = nil)
|
|
42
60
|
@body = body
|
|
43
61
|
@token = token
|
|
@@ -139,13 +157,7 @@ module Profiler
|
|
|
139
157
|
end
|
|
140
158
|
|
|
141
159
|
def ajax_interceptor_code
|
|
142
|
-
|
|
143
|
-
path = File.join(__dir__, "..", "..", "..", "app", "assets", "javascript", "profiler-ajax-interceptor.js")
|
|
144
|
-
File.read(path)
|
|
145
|
-
rescue => e
|
|
146
|
-
Profiler.log_error("ToolbarInjector: could not load the AJAX interceptor script", e)
|
|
147
|
-
""
|
|
148
|
-
end
|
|
160
|
+
self.class.ajax_interceptor_code
|
|
149
161
|
end
|
|
150
162
|
|
|
151
163
|
# The middleware hands over the page it has already read: an Array of parts, or a String.
|
|
@@ -157,11 +169,11 @@ module Profiler
|
|
|
157
169
|
def toolbar_html
|
|
158
170
|
<<~HTML
|
|
159
171
|
#{ajax_interceptor_script}
|
|
160
|
-
<style>#{toolbar_styles}</style>
|
|
172
|
+
<style#{nonce_attr}>#{self.class.toolbar_styles}</style>
|
|
161
173
|
<div id="profiler-toolbar" class="profiler-root" data-token="#{ERB::Util.html_escape(@token)}"></div>
|
|
162
174
|
<button id="profiler-toolbar-toggle" class="profiler-root" title="Toggle profiler (Alt+P)"><span class="profiler-toggle-icon">▶</span></button>
|
|
163
175
|
<script#{nonce_attr}>(function(){var c=localStorage.getItem('profiler-toolbar-collapsed')==='true',t=localStorage.getItem('profiler-theme'),theme=t==='light'?'light':t==='dark'?'dark':(window.matchMedia('(prefers-color-scheme:light)').matches?'light':'dark'),el=document.getElementById('profiler-toolbar'),tog=document.getElementById('profiler-toolbar-toggle');if(c){el.style.cssText='animation:none!important;transform:translateX(calc(100% + 44px))';tog.dataset.collapsed='true';}el.setAttribute('data-theme',theme);tog.setAttribute('data-theme',theme);})();</script>
|
|
164
|
-
<script src="
|
|
176
|
+
<script src="#{Profiler::Assets.url("profiler-toolbar.js")}" defer#{nonce_attr}></script>
|
|
165
177
|
HTML
|
|
166
178
|
end
|
|
167
179
|
|
|
@@ -176,7 +188,7 @@ module Profiler
|
|
|
176
188
|
|
|
177
189
|
# Thermal design system, self-contained CSS for the injected toolbar.
|
|
178
190
|
# Variables are defined on #profiler-toolbar to avoid polluting the host app.
|
|
179
|
-
def
|
|
191
|
+
def self.build_toolbar_styles
|
|
180
192
|
<<~'CSS'
|
|
181
193
|
.profiler-root {
|
|
182
194
|
--pf-bg: #080b10;
|
data/lib/profiler/version.rb
CHANGED
data/lib/profiler.rb
CHANGED
|
@@ -273,6 +273,7 @@ require_relative "profiler/collectors/mailer_collector"
|
|
|
273
273
|
require_relative "profiler/storage/token"
|
|
274
274
|
require_relative "profiler/storage/private_files"
|
|
275
275
|
require_relative "profiler/storage/unavailable"
|
|
276
|
+
require_relative "profiler/assets"
|
|
276
277
|
require_relative "profiler/ajax_data"
|
|
277
278
|
require_relative "profiler/env_override_store"
|
|
278
279
|
require_relative "profiler/instrumentation/thread_context_propagation"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-profiler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.35.
|
|
4
|
+
version: 0.35.3
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sébastien Duplessy
|
|
@@ -145,6 +145,7 @@ files:
|
|
|
145
145
|
- lib/profiler.rb
|
|
146
146
|
- lib/profiler/ajax_data.rb
|
|
147
147
|
- lib/profiler/allocation_counter.rb
|
|
148
|
+
- lib/profiler/assets.rb
|
|
148
149
|
- lib/profiler/body_content.rb
|
|
149
150
|
- lib/profiler/boot_env.rb
|
|
150
151
|
- lib/profiler/cluster/master_client.rb
|