rails-profiler 0.19.2 → 0.21.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 +4 -4
- data/app/assets/builds/profiler-toolbar.js +75 -55
- data/app/assets/builds/profiler.css +94 -6
- data/app/assets/builds/profiler.js +40 -4
- data/app/views/layouts/profiler/application.html.erb +1 -0
- data/app/views/layouts/profiler/embedded.html.erb +1 -0
- data/lib/profiler/job_profiler.rb +1 -0
- data/lib/profiler/mcp/tools/get_profile_detail.rb +7 -0
- data/lib/profiler/mcp/tools/query_jobs.rb +4 -1
- data/lib/profiler/middleware/profiler_middleware.rb +1 -0
- data/lib/profiler/middleware/toolbar_injector.rb +169 -62
- data/lib/profiler/models/profile.rb +4 -1
- data/lib/profiler/storage/sqlite_store.rb +11 -2
- data/lib/profiler/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 80a34504e3cb67a077d5f46265cbc265cbf7704f89ad06f90e5ab5df22651a70
|
|
4
|
+
data.tar.gz: d23a6d0d9f107611d66dfe34987447efde5f74f2562efd146d43d910fe431c6a
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 668d228ede43471fc71a5d65d6498bfd00606865f7cad69dd23e03f79a4e84a2ba5f34dbc2d9e07809df4319946055464b7b8e615ee29e14c8cfafdef10ffc4a
|
|
7
|
+
data.tar.gz: 16926d1a141c64cc72110698cd21e4d5c26d6e9022cd17ad6ba5ba3112aa98ce1917db9d005c112fac62190790433c06b24f1d9a85648edabf679664eb89b30d
|
|
@@ -68,8 +68,8 @@
|
|
|
68
68
|
}
|
|
69
69
|
}
|
|
70
70
|
function P(n2, l3, u4, t3, i3, r3, o3, e3, f4, c3, s3) {
|
|
71
|
-
var a3, h3, y3, d3, w3, g2,
|
|
72
|
-
for (f4 = A(u4, l3, m3, f4, b), a3 = 0; a3 < b; a3++) null != (y3 = u4.__k[a3]) && (h3 = -1 != y3.__i && m3[y3.__i] || p, y3.__i = a3, g2 = z(n2, y3, h3, i3, r3, o3, e3, f4, c3, s3), d3 = y3.__e, y3.ref && h3.ref != y3.ref && (h3.ref && D(h3.ref, null, y3), s3.push(y3.ref, y3.__c || d3, y3)), null == w3 && null != d3 && (w3 = d3), (
|
|
71
|
+
var a3, h3, y3, d3, w3, g2, _3, m3 = t3 && t3.__k || v, b = l3.length;
|
|
72
|
+
for (f4 = A(u4, l3, m3, f4, b), a3 = 0; a3 < b; a3++) null != (y3 = u4.__k[a3]) && (h3 = -1 != y3.__i && m3[y3.__i] || p, y3.__i = a3, g2 = z(n2, y3, h3, i3, r3, o3, e3, f4, c3, s3), d3 = y3.__e, y3.ref && h3.ref != y3.ref && (h3.ref && D(h3.ref, null, y3), s3.push(y3.ref, y3.__c || d3, y3)), null == w3 && null != d3 && (w3 = d3), (_3 = !!(4 & y3.__u)) || h3.__k === y3.__k ? f4 = H(y3, f4, n2, _3) : "function" == typeof y3.type && void 0 !== g2 ? f4 = g2 : d3 && (f4 = d3.nextSibling), y3.__u &= -7);
|
|
73
73
|
return u4.__e = w3, f4;
|
|
74
74
|
}
|
|
75
75
|
function A(n2, l3, u4, t3, i3) {
|
|
@@ -130,11 +130,11 @@
|
|
|
130
130
|
};
|
|
131
131
|
}
|
|
132
132
|
function z(n2, u4, t3, i3, r3, o3, e3, f4, c3, s3) {
|
|
133
|
-
var a3, h3, p3, y3,
|
|
133
|
+
var a3, h3, p3, y3, _3, m3, b, S2, C3, M2, $2, I2, A3, H2, L, T3 = u4.type;
|
|
134
134
|
if (void 0 !== u4.constructor) return null;
|
|
135
135
|
128 & t3.__u && (c3 = !!(32 & t3.__u), o3 = [f4 = u4.__e = t3.__e]), (a3 = l.__b) && a3(u4);
|
|
136
136
|
n: if ("function" == typeof T3) try {
|
|
137
|
-
if (S2 = u4.props, C3 = T3.prototype && T3.prototype.render, M2 = (a3 = T3.contextType) && i3[a3.__c], $2 = a3 ? M2 ? M2.props.value : a3.__ : i3, t3.__c ? b = (h3 = u4.__c = t3.__c).__ = h3.__E : (C3 ? u4.__c = h3 = new T3(S2, $2) : (u4.__c = h3 = new x(S2, $2), h3.constructor = T3, h3.render = G), M2 && M2.sub(h3), h3.state || (h3.state = {}), h3.__n = i3, p3 = h3.__d = true, h3.__h = [], h3._sb = []), C3 && null == h3.__s && (h3.__s = h3.state), C3 && null != T3.getDerivedStateFromProps && (h3.__s == h3.state && (h3.__s = w({}, h3.__s)), w(h3.__s, T3.getDerivedStateFromProps(S2, h3.__s))), y3 = h3.props,
|
|
137
|
+
if (S2 = u4.props, C3 = T3.prototype && T3.prototype.render, M2 = (a3 = T3.contextType) && i3[a3.__c], $2 = a3 ? M2 ? M2.props.value : a3.__ : i3, t3.__c ? b = (h3 = u4.__c = t3.__c).__ = h3.__E : (C3 ? u4.__c = h3 = new T3(S2, $2) : (u4.__c = h3 = new x(S2, $2), h3.constructor = T3, h3.render = G), M2 && M2.sub(h3), h3.state || (h3.state = {}), h3.__n = i3, p3 = h3.__d = true, h3.__h = [], h3._sb = []), C3 && null == h3.__s && (h3.__s = h3.state), C3 && null != T3.getDerivedStateFromProps && (h3.__s == h3.state && (h3.__s = w({}, h3.__s)), w(h3.__s, T3.getDerivedStateFromProps(S2, h3.__s))), y3 = h3.props, _3 = h3.state, h3.__v = u4, p3) C3 && null == T3.getDerivedStateFromProps && null != h3.componentWillMount && h3.componentWillMount(), C3 && null != h3.componentDidMount && h3.__h.push(h3.componentDidMount);
|
|
138
138
|
else {
|
|
139
139
|
if (C3 && null == T3.getDerivedStateFromProps && S2 !== y3 && null != h3.componentWillReceiveProps && h3.componentWillReceiveProps(S2, $2), u4.__v == t3.__v || !h3.__e && null != h3.shouldComponentUpdate && false === h3.shouldComponentUpdate(S2, h3.__s, $2)) {
|
|
140
140
|
u4.__v != t3.__v && (h3.props = S2, h3.state = h3.__s, h3.__d = false), u4.__e = t3.__e, u4.__k = t3.__k, u4.__k.some(function(n3) {
|
|
@@ -143,14 +143,14 @@
|
|
|
143
143
|
break n;
|
|
144
144
|
}
|
|
145
145
|
null != h3.componentWillUpdate && h3.componentWillUpdate(S2, h3.__s, $2), C3 && null != h3.componentDidUpdate && h3.__h.push(function() {
|
|
146
|
-
h3.componentDidUpdate(y3,
|
|
146
|
+
h3.componentDidUpdate(y3, _3, m3);
|
|
147
147
|
});
|
|
148
148
|
}
|
|
149
149
|
if (h3.context = $2, h3.props = S2, h3.__P = n2, h3.__e = false, I2 = l.__r, A3 = 0, C3) h3.state = h3.__s, h3.__d = false, I2 && I2(u4), a3 = h3.render(h3.props, h3.state, h3.context), v.push.apply(h3.__h, h3._sb), h3._sb = [];
|
|
150
150
|
else do {
|
|
151
151
|
h3.__d = false, I2 && I2(u4), a3 = h3.render(h3.props, h3.state, h3.context), h3.state = h3.__s;
|
|
152
152
|
} while (h3.__d && ++A3 < 25);
|
|
153
|
-
h3.state = h3.__s, null != h3.getChildContext && (i3 = w(w({}, i3), h3.getChildContext())), C3 && !p3 && null != h3.getSnapshotBeforeUpdate && (m3 = h3.getSnapshotBeforeUpdate(y3,
|
|
153
|
+
h3.state = h3.__s, null != h3.getChildContext && (i3 = w(w({}, i3), h3.getChildContext())), C3 && !p3 && null != h3.getSnapshotBeforeUpdate && (m3 = h3.getSnapshotBeforeUpdate(y3, _3)), H2 = null != a3 && a3.type === k && null == a3.key ? q(a3.props.children) : a3, f4 = P(n2, d(H2) ? H2 : [H2], u4, t3, i3, r3, o3, e3, f4, c3, s3), h3.base = u4.__e, u4.__u &= -161, h3.__h.length && e3.push(h3), b && (h3.__E = h3.__ = null);
|
|
154
154
|
} catch (n3) {
|
|
155
155
|
if (u4.__v = null, c3 || null != o3) if (n3.then) {
|
|
156
156
|
for (u4.__u |= c3 ? 160 : 128; f4 && 8 == f4.nodeType && f4.nextSibling; ) f4 = f4.nextSibling;
|
|
@@ -184,7 +184,7 @@
|
|
|
184
184
|
return "object" != typeof n2 || null == n2 || n2.__b > 0 ? n2 : d(n2) ? n2.map(q) : w({}, n2);
|
|
185
185
|
}
|
|
186
186
|
function B(u4, t3, i3, r3, o3, e3, f4, c3, s3) {
|
|
187
|
-
var a3, h3, v3, y3, w3,
|
|
187
|
+
var a3, h3, v3, y3, w3, _3, m3, b = i3.props || p, k3 = t3.props, x2 = t3.type;
|
|
188
188
|
if ("svg" == x2 ? o3 = "http://www.w3.org/2000/svg" : "math" == x2 ? o3 = "http://www.w3.org/1998/Math/MathML" : o3 || (o3 = "http://www.w3.org/1999/xhtml"), null != e3) {
|
|
189
189
|
for (a3 = 0; a3 < e3.length; a3++) if ((w3 = e3[a3]) && "setAttribute" in w3 == !!x2 && (x2 ? w3.localName == x2 : 3 == w3.nodeType)) {
|
|
190
190
|
u4 = w3, e3[a3] = null;
|
|
@@ -199,10 +199,10 @@
|
|
|
199
199
|
else {
|
|
200
200
|
if (e3 = e3 && n.call(u4.childNodes), !c3 && null != e3) for (b = {}, a3 = 0; a3 < u4.attributes.length; a3++) b[(w3 = u4.attributes[a3]).name] = w3.value;
|
|
201
201
|
for (a3 in b) w3 = b[a3], "dangerouslySetInnerHTML" == a3 ? v3 = w3 : "children" == a3 || a3 in k3 || "value" == a3 && "defaultValue" in k3 || "checked" == a3 && "defaultChecked" in k3 || F(u4, a3, null, w3, o3);
|
|
202
|
-
for (a3 in k3) w3 = k3[a3], "children" == a3 ? y3 = w3 : "dangerouslySetInnerHTML" == a3 ? h3 = w3 : "value" == a3 ?
|
|
202
|
+
for (a3 in k3) w3 = k3[a3], "children" == a3 ? y3 = w3 : "dangerouslySetInnerHTML" == a3 ? h3 = w3 : "value" == a3 ? _3 = w3 : "checked" == a3 ? m3 = w3 : c3 && "function" != typeof w3 || b[a3] === w3 || F(u4, a3, w3, b[a3], o3);
|
|
203
203
|
if (h3) c3 || v3 && (h3.__html == v3.__html || h3.__html == u4.innerHTML) || (u4.innerHTML = h3.__html), t3.__k = [];
|
|
204
204
|
else if (v3 && (u4.innerHTML = ""), P("template" == t3.type ? u4.content : u4, d(y3) ? y3 : [y3], t3, i3, r3, "foreignObject" == x2 ? "http://www.w3.org/1999/xhtml" : o3, e3, f4, e3 ? e3[0] : i3.__k && S(i3, 0), c3, s3), null != e3) for (a3 = e3.length; a3--; ) g(e3[a3]);
|
|
205
|
-
c3 || (a3 = "value", "progress" == x2 && null ==
|
|
205
|
+
c3 || (a3 = "value", "progress" == x2 && null == _3 ? u4.removeAttribute("value") : null != _3 && (_3 !== u4[a3] || "progress" == x2 && !_3 || "option" == x2 && _3 != b[a3]) && F(u4, a3, _3, b[a3], o3), a3 = "checked", null != m3 && m3 != u4[a3] && F(u4, a3, m3, b[a3], o3));
|
|
206
206
|
}
|
|
207
207
|
return u4;
|
|
208
208
|
}
|
|
@@ -314,6 +314,10 @@
|
|
|
314
314
|
var i3 = p2(t2++, 3);
|
|
315
315
|
!c2.__s && C2(i3.__H, u4) && (i3.__ = n2, i3.u = u4, r2.__H.__h.push(i3));
|
|
316
316
|
}
|
|
317
|
+
function _2(n2, u4) {
|
|
318
|
+
var i3 = p2(t2++, 4);
|
|
319
|
+
!c2.__s && C2(i3.__H, u4) && (i3.__ = n2, i3.u = u4, r2.__h.push(i3));
|
|
320
|
+
}
|
|
317
321
|
function A2(n2) {
|
|
318
322
|
return o2 = 5, T2(function() {
|
|
319
323
|
return { current: n2 };
|
|
@@ -427,7 +431,7 @@
|
|
|
427
431
|
}
|
|
428
432
|
hideTimer.current = setTimeout(() => setVisible(false), 150);
|
|
429
433
|
};
|
|
430
|
-
|
|
434
|
+
_2(() => {
|
|
431
435
|
if (visible && panelRef.current) {
|
|
432
436
|
const el = panelRef.current;
|
|
433
437
|
el.style.left = "50%";
|
|
@@ -453,8 +457,7 @@
|
|
|
453
457
|
"div",
|
|
454
458
|
{
|
|
455
459
|
ref: panelRef,
|
|
456
|
-
class: `profiler-toolbar-panel${panelLarge ? " profiler-toolbar-panel-large" : ""}`,
|
|
457
|
-
style: { display: visible ? "block" : "none" },
|
|
460
|
+
class: `profiler-toolbar-panel${panelLarge ? " profiler-toolbar-panel-large" : ""}${visible ? " is-visible" : ""}`,
|
|
458
461
|
onMouseEnter: show,
|
|
459
462
|
onMouseLeave: hide,
|
|
460
463
|
children: panel
|
|
@@ -1048,7 +1051,7 @@
|
|
|
1048
1051
|
const dbClass = (dbData?.slow_queries ?? 0) > 0 ? "profiler-text--error" : "profiler-text--success";
|
|
1049
1052
|
const ajaxClass = (ajaxData?.total_requests ?? 0) > 20 ? "profiler-text--error" : "profiler-text--success";
|
|
1050
1053
|
const cacheClass = (cacheData?.hit_rate ?? 0) > 80 ? "profiler-text--success" : "profiler-text--warning";
|
|
1051
|
-
return /* @__PURE__ */ u3("div", { class: "profiler-toolbar-container", children: [
|
|
1054
|
+
return /* @__PURE__ */ u3(k, { children: /* @__PURE__ */ u3("div", { class: "profiler-toolbar-container", children: [
|
|
1052
1055
|
requestData && /* @__PURE__ */ u3(k, { children: [
|
|
1053
1056
|
/* @__PURE__ */ u3(
|
|
1054
1057
|
ToolbarItem,
|
|
@@ -1291,67 +1294,84 @@
|
|
|
1291
1294
|
]
|
|
1292
1295
|
}
|
|
1293
1296
|
),
|
|
1294
|
-
/* @__PURE__ */ u3("a", { href: "/_profiler", class: "profiler-toolbar-item", children: "\u2B21 Profiler" })
|
|
1295
|
-
] });
|
|
1297
|
+
/* @__PURE__ */ u3("a", { href: "/_profiler", class: "profiler-toolbar-item profiler-toolbar-logo", children: "\u2B21 Profiler" })
|
|
1298
|
+
] }) });
|
|
1296
1299
|
}
|
|
1297
1300
|
|
|
1298
1301
|
// app/assets/typescript/profiler/toolbar-bundle.tsx
|
|
1299
|
-
|
|
1302
|
+
var ANIM_MS = 280;
|
|
1303
|
+
function applyTheme(elements) {
|
|
1300
1304
|
const stored = localStorage.getItem("profiler-theme");
|
|
1301
1305
|
const theme = stored === "light" ? "light" : stored === "dark" ? "dark" : window.matchMedia("(prefers-color-scheme: light)").matches ? "light" : "dark";
|
|
1302
|
-
el.setAttribute("data-theme", theme);
|
|
1306
|
+
elements.forEach((el) => el.setAttribute("data-theme", theme));
|
|
1307
|
+
}
|
|
1308
|
+
function ToolbarMount({ token }) {
|
|
1309
|
+
const [profile, setProfile] = d2(null);
|
|
1310
|
+
y2(() => {
|
|
1311
|
+
const load = () => {
|
|
1312
|
+
fetch(`/_profiler/api/toolbar/${token}`).then((res) => res.json()).then((data) => {
|
|
1313
|
+
if (data.profile) setProfile(data.profile);
|
|
1314
|
+
}).catch((err) => console.debug("Profiler toolbar load failed:", err));
|
|
1315
|
+
};
|
|
1316
|
+
window.__PROFILER_REFRESH_TOOLBAR__ = load;
|
|
1317
|
+
load();
|
|
1318
|
+
}, [token]);
|
|
1319
|
+
if (!profile) return null;
|
|
1320
|
+
return /* @__PURE__ */ u3(ToolbarApp, { profile, token });
|
|
1303
1321
|
}
|
|
1304
1322
|
function mountToolbar() {
|
|
1305
1323
|
const el = document.getElementById("profiler-toolbar");
|
|
1306
|
-
|
|
1324
|
+
const toggleEl = document.getElementById("profiler-toolbar-toggle");
|
|
1325
|
+
if (!el || !toggleEl) return;
|
|
1307
1326
|
const token = el.dataset.token;
|
|
1308
1327
|
if (!token) return;
|
|
1309
|
-
|
|
1328
|
+
const themeEls = [el, toggleEl];
|
|
1329
|
+
applyTheme(themeEls);
|
|
1310
1330
|
window.addEventListener("storage", (e3) => {
|
|
1311
|
-
if (e3.key === "profiler-theme") applyTheme(
|
|
1331
|
+
if (e3.key === "profiler-theme") applyTheme(themeEls);
|
|
1312
1332
|
});
|
|
1313
1333
|
window.addEventListener("profiler:theme-change", ((e3) => {
|
|
1314
|
-
if (e3.detail?.theme)
|
|
1334
|
+
if (e3.detail?.theme) themeEls.forEach((elem) => elem.setAttribute("data-theme", e3.detail.theme));
|
|
1315
1335
|
}));
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1336
|
+
let isCollapsed = localStorage.getItem("profiler-toolbar-collapsed") === "true";
|
|
1337
|
+
toggleEl.dataset.collapsed = String(isCollapsed);
|
|
1338
|
+
if (isCollapsed && !el.style.transform) {
|
|
1339
|
+
el.style.animation = "none";
|
|
1340
|
+
el.style.transform = "translateX(calc(100% + 44px))";
|
|
1341
|
+
}
|
|
1342
|
+
if (!isCollapsed) {
|
|
1343
|
+
setTimeout(() => {
|
|
1344
|
+
el.style.animation = "none";
|
|
1345
|
+
}, 400);
|
|
1346
|
+
}
|
|
1347
|
+
function toggleCollapse() {
|
|
1348
|
+
const next = !isCollapsed;
|
|
1349
|
+
isCollapsed = next;
|
|
1350
|
+
localStorage.setItem("profiler-toolbar-collapsed", String(next));
|
|
1351
|
+
toggleEl.dataset.collapsed = String(next);
|
|
1352
|
+
if (next) {
|
|
1353
|
+
el.style.transition = `transform ${ANIM_MS}ms cubic-bezier(0.4,0,0.2,1)`;
|
|
1354
|
+
el.style.transform = "translateX(calc(100% + 44px))";
|
|
1355
|
+
} else {
|
|
1356
|
+
el.style.transition = "none";
|
|
1357
|
+
el.style.transform = "translateX(calc(100% + 44px))";
|
|
1358
|
+
void el.offsetWidth;
|
|
1359
|
+
el.style.transition = `transform ${ANIM_MS}ms cubic-bezier(0.4,0,0.2,1)`;
|
|
1360
|
+
el.style.transform = "translateX(0)";
|
|
1361
|
+
setTimeout(() => {
|
|
1362
|
+
el.style.transform = "";
|
|
1363
|
+
el.style.transition = "";
|
|
1364
|
+
}, ANIM_MS + 50);
|
|
1365
|
+
}
|
|
1366
|
+
}
|
|
1367
|
+
toggleEl.addEventListener("click", toggleCollapse);
|
|
1327
1368
|
document.addEventListener("keydown", (e3) => {
|
|
1328
1369
|
if (e3.altKey && e3.key === "p") {
|
|
1329
1370
|
e3.preventDefault();
|
|
1330
|
-
|
|
1331
|
-
if (hidden2) {
|
|
1332
|
-
el.dataset.hidden = "false";
|
|
1333
|
-
el.style.transform = "translateY(0)";
|
|
1334
|
-
el.style.opacity = "1";
|
|
1335
|
-
localStorage.setItem("profiler-toolbar-hidden", "false");
|
|
1336
|
-
} else {
|
|
1337
|
-
el.dataset.hidden = "true";
|
|
1338
|
-
el.style.transform = "translateY(100%)";
|
|
1339
|
-
el.style.opacity = "0";
|
|
1340
|
-
localStorage.setItem("profiler-toolbar-hidden", "true");
|
|
1341
|
-
}
|
|
1342
|
-
}
|
|
1343
|
-
if (e3.key === "Escape") {
|
|
1344
|
-
el.dataset.hidden = "true";
|
|
1345
|
-
el.style.transform = "translateY(100%)";
|
|
1346
|
-
el.style.opacity = "0";
|
|
1371
|
+
toggleCollapse();
|
|
1347
1372
|
}
|
|
1348
1373
|
});
|
|
1349
|
-
|
|
1350
|
-
if (hidden) {
|
|
1351
|
-
el.dataset.hidden = "true";
|
|
1352
|
-
el.style.transform = "translateY(100%)";
|
|
1353
|
-
el.style.opacity = "0";
|
|
1354
|
-
}
|
|
1374
|
+
J(/* @__PURE__ */ u3(ToolbarMount, { token }), el);
|
|
1355
1375
|
}
|
|
1356
1376
|
if (document.readyState === "loading") {
|
|
1357
1377
|
document.addEventListener("DOMContentLoaded", mountToolbar);
|
|
@@ -521,6 +521,34 @@ tr:hover .btn-row-delete {
|
|
|
521
521
|
border: 1px solid var(--profiler-border);
|
|
522
522
|
}
|
|
523
523
|
|
|
524
|
+
.profiler-version-badge {
|
|
525
|
+
display: inline-flex;
|
|
526
|
+
align-items: center;
|
|
527
|
+
padding: 1px 6px;
|
|
528
|
+
border-radius: var(--profiler-radius-sm);
|
|
529
|
+
font-family: var(--profiler-font-mono);
|
|
530
|
+
font-size: var(--profiler-text-xs);
|
|
531
|
+
color: var(--profiler-text-muted);
|
|
532
|
+
background: var(--profiler-bg-lighter);
|
|
533
|
+
border: 1px solid var(--profiler-border);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.profiler-version-warn {
|
|
537
|
+
margin-left: 6px;
|
|
538
|
+
cursor: help;
|
|
539
|
+
font-size: var(--profiler-text-xs);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
.profiler-version-mismatch {
|
|
543
|
+
margin-top: var(--profiler-space-2);
|
|
544
|
+
padding: var(--profiler-space-2) var(--profiler-space-3);
|
|
545
|
+
border-radius: var(--profiler-radius-sm);
|
|
546
|
+
font-size: var(--profiler-text-sm);
|
|
547
|
+
background: var(--profiler-warning-bg);
|
|
548
|
+
color: var(--profiler-warning);
|
|
549
|
+
border: 1px solid rgba(251, 146, 60, 0.3);
|
|
550
|
+
}
|
|
551
|
+
|
|
524
552
|
@keyframes fadeInDown {
|
|
525
553
|
from {
|
|
526
554
|
opacity: 0;
|
|
@@ -1367,6 +1395,26 @@ tr:hover .btn-row-delete {
|
|
|
1367
1395
|
color: var(--profiler-text-muted);
|
|
1368
1396
|
}
|
|
1369
1397
|
|
|
1398
|
+
@keyframes pfPop {
|
|
1399
|
+
0% {
|
|
1400
|
+
transform: scaleX(1);
|
|
1401
|
+
}
|
|
1402
|
+
15% {
|
|
1403
|
+
transform: scaleX(1.18);
|
|
1404
|
+
}
|
|
1405
|
+
40% {
|
|
1406
|
+
transform: scaleX(0.87);
|
|
1407
|
+
}
|
|
1408
|
+
65% {
|
|
1409
|
+
transform: scaleX(1.08);
|
|
1410
|
+
}
|
|
1411
|
+
85% {
|
|
1412
|
+
transform: scaleX(0.97);
|
|
1413
|
+
}
|
|
1414
|
+
100% {
|
|
1415
|
+
transform: scaleX(1);
|
|
1416
|
+
}
|
|
1417
|
+
}
|
|
1370
1418
|
#profiler-toolbar {
|
|
1371
1419
|
position: fixed;
|
|
1372
1420
|
bottom: 0;
|
|
@@ -1381,6 +1429,7 @@ tr:hover .btn-row-delete {
|
|
|
1381
1429
|
font-family: var(--profiler-font-mono);
|
|
1382
1430
|
font-size: 11px;
|
|
1383
1431
|
animation: toolbarIn 300ms cubic-bezier(0.4, 0, 0.2, 1) both;
|
|
1432
|
+
transition: left 280ms cubic-bezier(0.4, 0, 0.2, 1);
|
|
1384
1433
|
}
|
|
1385
1434
|
#profiler-toolbar::before {
|
|
1386
1435
|
content: "";
|
|
@@ -1436,12 +1485,10 @@ tr:hover .btn-row-delete {
|
|
|
1436
1485
|
background: var(--profiler-accent);
|
|
1437
1486
|
transform: scaleX(0);
|
|
1438
1487
|
transform-origin: left;
|
|
1439
|
-
transition: transform
|
|
1488
|
+
transition: transform 280ms cubic-bezier(0.34, 1.4, 0.64, 1);
|
|
1440
1489
|
}
|
|
1441
1490
|
.profiler-toolbar-item:last-child {
|
|
1442
1491
|
border-right: none;
|
|
1443
|
-
margin-left: auto;
|
|
1444
|
-
padding-left: 20px;
|
|
1445
1492
|
}
|
|
1446
1493
|
.profiler-toolbar-item:hover {
|
|
1447
1494
|
color: var(--profiler-text);
|
|
@@ -1451,12 +1498,30 @@ tr:hover .btn-row-delete {
|
|
|
1451
1498
|
transform: scaleX(1);
|
|
1452
1499
|
}
|
|
1453
1500
|
|
|
1501
|
+
.profiler-toolbar-collapse-btn {
|
|
1502
|
+
margin-left: auto;
|
|
1503
|
+
padding: 0 14px;
|
|
1504
|
+
border-left: 1px solid var(--profiler-border);
|
|
1505
|
+
font-size: 10px;
|
|
1506
|
+
color: var(--profiler-text-muted);
|
|
1507
|
+
cursor: pointer;
|
|
1508
|
+
}
|
|
1509
|
+
.profiler-toolbar-collapse-btn:hover {
|
|
1510
|
+
color: var(--profiler-text);
|
|
1511
|
+
background: var(--profiler-accent-bg);
|
|
1512
|
+
animation: pfPop 380ms ease-out both;
|
|
1513
|
+
}
|
|
1514
|
+
|
|
1454
1515
|
a.profiler-toolbar-item {
|
|
1455
1516
|
cursor: pointer;
|
|
1456
1517
|
font-family: var(--profiler-font-mono);
|
|
1457
1518
|
font-size: 11px;
|
|
1458
1519
|
}
|
|
1459
1520
|
|
|
1521
|
+
.profiler-toolbar-logo {
|
|
1522
|
+
border-right: 1px solid var(--profiler-border) !important;
|
|
1523
|
+
}
|
|
1524
|
+
|
|
1460
1525
|
.profiler-text--success {
|
|
1461
1526
|
color: var(--profiler-success) !important;
|
|
1462
1527
|
}
|
|
@@ -1477,19 +1542,22 @@ a.profiler-toolbar-item {
|
|
|
1477
1542
|
color: var(--profiler-accent) !important;
|
|
1478
1543
|
}
|
|
1479
1544
|
|
|
1480
|
-
|
|
1545
|
+
.profiler-toolbar-item.profiler-text--error::after {
|
|
1481
1546
|
background: var(--profiler-error);
|
|
1482
1547
|
}
|
|
1483
1548
|
|
|
1484
|
-
|
|
1549
|
+
.profiler-toolbar-item.profiler-text--warning::after {
|
|
1485
1550
|
background: var(--profiler-warning);
|
|
1486
1551
|
}
|
|
1487
1552
|
|
|
1553
|
+
.profiler-toolbar-item.profiler-text--success::after {
|
|
1554
|
+
background: var(--profiler-success);
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1488
1557
|
.profiler-toolbar-hoverable {
|
|
1489
1558
|
position: relative;
|
|
1490
1559
|
}
|
|
1491
1560
|
.profiler-toolbar-hoverable .profiler-toolbar-panel {
|
|
1492
|
-
display: none;
|
|
1493
1561
|
position: absolute;
|
|
1494
1562
|
bottom: calc(100% + 12px);
|
|
1495
1563
|
left: 50%;
|
|
@@ -1502,6 +1570,16 @@ a.profiler-toolbar-item.profiler-text--warning::after {
|
|
|
1502
1570
|
box-shadow: var(--profiler-shadow-xl), 0 0 0 1px var(--profiler-border-accent);
|
|
1503
1571
|
z-index: var(--profiler-z-panel);
|
|
1504
1572
|
overflow: hidden;
|
|
1573
|
+
opacity: 0;
|
|
1574
|
+
visibility: hidden;
|
|
1575
|
+
pointer-events: none;
|
|
1576
|
+
transition: opacity 160ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s linear 160ms;
|
|
1577
|
+
}
|
|
1578
|
+
.profiler-toolbar-hoverable .profiler-toolbar-panel.is-visible {
|
|
1579
|
+
opacity: 1;
|
|
1580
|
+
visibility: visible;
|
|
1581
|
+
pointer-events: auto;
|
|
1582
|
+
transition: opacity 160ms cubic-bezier(0.4, 0, 0.2, 1), visibility 0s;
|
|
1505
1583
|
}
|
|
1506
1584
|
.profiler-toolbar-hoverable .profiler-toolbar-panel::before {
|
|
1507
1585
|
content: "";
|
|
@@ -1646,6 +1724,16 @@ a.profiler-toolbar-item.profiler-text--warning::after {
|
|
|
1646
1724
|
margin-top: 6px;
|
|
1647
1725
|
}
|
|
1648
1726
|
|
|
1727
|
+
#profiler-toolbar[data-collapsed=true] .profiler-toolbar-item:not(.profiler-toolbar-collapse-btn) {
|
|
1728
|
+
display: none;
|
|
1729
|
+
}
|
|
1730
|
+
#profiler-toolbar[data-collapsed=true] .profiler-toolbar-collapse-btn {
|
|
1731
|
+
margin-left: 0;
|
|
1732
|
+
}
|
|
1733
|
+
#profiler-toolbar[data-collapsed=true]::before {
|
|
1734
|
+
opacity: 0;
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1649
1737
|
.container > table {
|
|
1650
1738
|
width: 100%;
|
|
1651
1739
|
border-collapse: collapse;
|
|
@@ -396,6 +396,11 @@
|
|
|
396
396
|
return "function" == typeof t3 ? t3(n2) : t3;
|
|
397
397
|
}
|
|
398
398
|
|
|
399
|
+
// app/assets/typescript/profiler/dashboard/utils.ts
|
|
400
|
+
function getGemVersion() {
|
|
401
|
+
return document.querySelector('meta[name="profiler-version"]')?.content ?? "";
|
|
402
|
+
}
|
|
403
|
+
|
|
399
404
|
// app/assets/typescript/profiler/components/dashboard/tabs/shared/utils.ts
|
|
400
405
|
function methodBadge(method) {
|
|
401
406
|
const map = { GET: "info", POST: "success", PUT: "warning", PATCH: "warning", DELETE: "error" };
|
|
@@ -1635,6 +1640,7 @@
|
|
|
1635
1640
|
{ key: "has_exception", label: "Has exception" }
|
|
1636
1641
|
];
|
|
1637
1642
|
function ProfileList() {
|
|
1643
|
+
const currentVersion = getGemVersion();
|
|
1638
1644
|
const params = new URLSearchParams(window.location.search);
|
|
1639
1645
|
const initialSection = () => {
|
|
1640
1646
|
const s3 = params.get("section");
|
|
@@ -1982,7 +1988,11 @@
|
|
|
1982
1988
|
/* @__PURE__ */ u3("span", { class: "h1-emoji", children: "\u{1F50D}" }),
|
|
1983
1989
|
" Rails Profiler"
|
|
1984
1990
|
] }),
|
|
1985
|
-
/* @__PURE__ */ u3("p", { children: "Recent profiled requests and jobs" })
|
|
1991
|
+
/* @__PURE__ */ u3("p", { children: "Recent profiled requests and jobs" }),
|
|
1992
|
+
currentVersion && /* @__PURE__ */ u3("span", { class: "profiler-version-badge", children: [
|
|
1993
|
+
"v",
|
|
1994
|
+
currentVersion
|
|
1995
|
+
] })
|
|
1986
1996
|
] }),
|
|
1987
1997
|
/* @__PURE__ */ u3("div", { class: "profiler-panel profiler-mb-6", children: [
|
|
1988
1998
|
/* @__PURE__ */ u3("div", { class: "tabs", children: [
|
|
@@ -2092,7 +2102,10 @@
|
|
|
2092
2102
|
/* @__PURE__ */ u3("tbody", { children: sortedProfiles.map((p3) => /* @__PURE__ */ u3("tr", { children: [
|
|
2093
2103
|
/* @__PURE__ */ u3("td", { children: formatTime(p3.started_at) }),
|
|
2094
2104
|
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: methodClass(p3.method), children: p3.method }) }),
|
|
2095
|
-
/* @__PURE__ */ u3("td", { children:
|
|
2105
|
+
/* @__PURE__ */ u3("td", { children: [
|
|
2106
|
+
/* @__PURE__ */ u3("a", { href: `${BASE}/profiles/${p3.token}`, children: p3.path }),
|
|
2107
|
+
p3.gem_version && p3.gem_version !== currentVersion && /* @__PURE__ */ u3("span", { class: "profiler-version-warn", title: `Captur\xE9 avec v${p3.gem_version} (actuel : v${currentVersion})`, children: "\u26A0\uFE0F" })
|
|
2108
|
+
] }),
|
|
2096
2109
|
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: durationClass(p3.duration), children: [
|
|
2097
2110
|
p3.duration.toFixed(2),
|
|
2098
2111
|
" ms"
|
|
@@ -2169,7 +2182,10 @@
|
|
|
2169
2182
|
const isFailed = p3.status === 500;
|
|
2170
2183
|
return /* @__PURE__ */ u3("tr", { children: [
|
|
2171
2184
|
/* @__PURE__ */ u3("td", { children: formatTime(p3.started_at) }),
|
|
2172
|
-
/* @__PURE__ */ u3("td", { children:
|
|
2185
|
+
/* @__PURE__ */ u3("td", { children: [
|
|
2186
|
+
/* @__PURE__ */ u3("a", { href: `${BASE}/profiles/${p3.token}`, children: p3.path }),
|
|
2187
|
+
p3.gem_version && p3.gem_version !== currentVersion && /* @__PURE__ */ u3("span", { class: "profiler-version-warn", title: `Captur\xE9 avec v${p3.gem_version} (actuel : v${currentVersion})`, children: "\u26A0\uFE0F" })
|
|
2188
|
+
] }),
|
|
2173
2189
|
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: "profiler-text--xs profiler-text--mono", children: jobData?.queue || "-" }) }),
|
|
2174
2190
|
/* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: durationClass(p3.duration), children: [
|
|
2175
2191
|
p3.duration.toFixed(2),
|
|
@@ -4482,7 +4498,17 @@
|
|
|
4482
4498
|
" MB"
|
|
4483
4499
|
] })
|
|
4484
4500
|
] }),
|
|
4485
|
-
/* @__PURE__ */ u3("span", { style: "color:var(--profiler-text-muted)", children: new Date(profile.started_at).toLocaleString("en", { hour12: false, month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" }) })
|
|
4501
|
+
/* @__PURE__ */ u3("span", { style: "color:var(--profiler-text-muted)", children: new Date(profile.started_at).toLocaleString("en", { hour12: false, month: "short", day: "numeric", hour: "2-digit", minute: "2-digit", second: "2-digit" }) }),
|
|
4502
|
+
profile.gem_version && /* @__PURE__ */ u3("span", { class: "profiler-version-badge", children: [
|
|
4503
|
+
"v",
|
|
4504
|
+
profile.gem_version
|
|
4505
|
+
] })
|
|
4506
|
+
] }),
|
|
4507
|
+
profile.gem_version && profile.gem_version !== getGemVersion() && /* @__PURE__ */ u3("div", { class: "profiler-version-mismatch", children: [
|
|
4508
|
+
"\u26A0\uFE0F Profil captur\xE9 avec la version ",
|
|
4509
|
+
/* @__PURE__ */ u3("strong", { children: profile.gem_version }),
|
|
4510
|
+
" \u2014 version actuelle : ",
|
|
4511
|
+
/* @__PURE__ */ u3("strong", { children: getGemVersion() })
|
|
4486
4512
|
] })
|
|
4487
4513
|
] }),
|
|
4488
4514
|
/* @__PURE__ */ u3("div", { class: "profiler-panel profiler-mb-6", children: [
|
|
@@ -4628,8 +4654,18 @@
|
|
|
4628
4654
|
(profile.memory / 1024 / 1024).toFixed(2),
|
|
4629
4655
|
" MB"
|
|
4630
4656
|
] })
|
|
4657
|
+
] }),
|
|
4658
|
+
profile.gem_version && /* @__PURE__ */ u3("span", { class: "profiler-version-badge", children: [
|
|
4659
|
+
"v",
|
|
4660
|
+
profile.gem_version
|
|
4631
4661
|
] })
|
|
4632
4662
|
] }),
|
|
4663
|
+
profile.gem_version && profile.gem_version !== getGemVersion() && /* @__PURE__ */ u3("div", { class: "profiler-version-mismatch", children: [
|
|
4664
|
+
"\u26A0\uFE0F Profil captur\xE9 avec la version ",
|
|
4665
|
+
/* @__PURE__ */ u3("strong", { children: profile.gem_version }),
|
|
4666
|
+
" \u2014 version actuelle : ",
|
|
4667
|
+
/* @__PURE__ */ u3("strong", { children: getGemVersion() })
|
|
4668
|
+
] }),
|
|
4633
4669
|
parent && /* @__PURE__ */ u3("div", { class: "profiler-flex profiler-flex--gap-2 profiler-mt-2 profiler-text--sm", children: [
|
|
4634
4670
|
/* @__PURE__ */ u3("span", { style: "color:var(--profiler-text-muted)", children: "Triggered by:" }),
|
|
4635
4671
|
parent.profile_type === "http" ? /* @__PURE__ */ u3("span", { children: [
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<%= csrf_meta_tags %>
|
|
7
7
|
<%= csp_meta_tag %>
|
|
8
|
+
<meta name="profiler-version" content="<%= Profiler::VERSION %>">
|
|
8
9
|
|
|
9
10
|
<link rel="stylesheet" href="/_profiler/assets/profiler.css">
|
|
10
11
|
<script src="/_profiler/assets/profiler.js" defer></script>
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
6
|
<%= csrf_meta_tags %>
|
|
7
7
|
<%= csp_meta_tag %>
|
|
8
|
+
<meta name="profiler-version" content="<%= Profiler::VERSION %>">
|
|
8
9
|
|
|
9
10
|
<link rel="stylesheet" href="/_profiler/assets/profiler.css">
|
|
10
11
|
<script src="/_profiler/assets/profiler.js" defer></script>
|
|
@@ -76,6 +76,13 @@ module Profiler
|
|
|
76
76
|
lines << "**Duration:** #{profile.duration.round(2)} ms"
|
|
77
77
|
lines << "**Memory:** #{(profile.memory / 1024.0 / 1024.0).round(2)} MB" if profile.memory
|
|
78
78
|
lines << "**Time:** #{profile.started_at}"
|
|
79
|
+
if profile.gem_version
|
|
80
|
+
if profile.gem_version != Profiler::VERSION
|
|
81
|
+
lines << "**Gem Version:** #{profile.gem_version} ⚠️ (current: #{Profiler::VERSION})"
|
|
82
|
+
else
|
|
83
|
+
lines << "**Gem Version:** #{profile.gem_version}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
79
86
|
lines << "**Parent Token:** #{profile.parent_token}" if profile.parent_token
|
|
80
87
|
lines << ""
|
|
81
88
|
lines
|
|
@@ -4,7 +4,7 @@ module Profiler
|
|
|
4
4
|
module MCP
|
|
5
5
|
module Tools
|
|
6
6
|
class QueryJobs
|
|
7
|
-
ALL_FIELDS = %w[time job_class queue status duration token parent_token].freeze
|
|
7
|
+
ALL_FIELDS = %w[time job_class queue status duration gem_version token parent_token].freeze
|
|
8
8
|
|
|
9
9
|
def self.call(params)
|
|
10
10
|
limit = params["limit"]&.to_i || 20
|
|
@@ -64,6 +64,9 @@ module Profiler
|
|
|
64
64
|
when "queue" then job_data["queue"] || "-"
|
|
65
65
|
when "status" then job_data["status"] || "-"
|
|
66
66
|
when "duration" then "#{profile.duration.round(2)}ms"
|
|
67
|
+
when "gem_version"
|
|
68
|
+
v = profile.gem_version || "-"
|
|
69
|
+
v != Profiler::VERSION ? "#{v} ⚠️" : v
|
|
67
70
|
when "token" then profile.token.to_s
|
|
68
71
|
when "parent_token" then profile.parent_token || "-"
|
|
69
72
|
end
|
|
@@ -62,9 +62,11 @@ module Profiler
|
|
|
62
62
|
def toolbar_html
|
|
63
63
|
<<~HTML
|
|
64
64
|
#{ajax_interceptor_script}
|
|
65
|
-
<div id="profiler-toolbar" data-token="#{@token}"></div>
|
|
66
|
-
<script src="/_profiler/assets/profiler-toolbar.js" defer#{nonce_attr}></script>
|
|
67
65
|
<style>#{toolbar_styles}</style>
|
|
66
|
+
<div id="profiler-toolbar" class="profiler-root" data-token="#{@token}"></div>
|
|
67
|
+
<button id="profiler-toolbar-toggle" class="profiler-root" title="Toggle profiler (Alt+P)"><span class="profiler-toggle-icon">▶</span></button>
|
|
68
|
+
<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>
|
|
69
|
+
<script src="/_profiler/assets/profiler-toolbar.js" defer#{nonce_attr}></script>
|
|
68
70
|
HTML
|
|
69
71
|
end
|
|
70
72
|
|
|
@@ -76,39 +78,28 @@ module Profiler
|
|
|
76
78
|
# Variables are defined on #profiler-toolbar to avoid polluting the host app.
|
|
77
79
|
def toolbar_styles
|
|
78
80
|
<<~'CSS'
|
|
79
|
-
|
|
81
|
+
.profiler-root {
|
|
80
82
|
--pf-bg: #080b10;
|
|
81
83
|
--pf-surface: #0d1117;
|
|
82
84
|
--pf-raised: #131920;
|
|
83
85
|
--pf-text: #eef2f7;
|
|
84
|
-
--pf-muted: #
|
|
86
|
+
--pf-muted: #4d6170;
|
|
85
87
|
--pf-amber: #f59e0b;
|
|
86
88
|
--pf-amber-h: #fbbf24;
|
|
87
|
-
--pf-amber-bg: rgba(245,158,11,.
|
|
88
|
-
--pf-amber-glow: rgba(245,158,11,.
|
|
89
|
+
--pf-amber-bg: rgba(245,158,11,.08);
|
|
90
|
+
--pf-amber-glow: rgba(245,158,11,.2);
|
|
89
91
|
--pf-success: #22c55e;
|
|
90
92
|
--pf-warning: #fb923c;
|
|
91
93
|
--pf-error: #f87171;
|
|
92
94
|
--pf-info: #60a5fa;
|
|
93
|
-
--pf-border: rgba(255,255,255,.
|
|
94
|
-
--pf-border-s: rgba(255,255,255,.
|
|
95
|
+
--pf-border: rgba(255,255,255,.06);
|
|
96
|
+
--pf-border-s: rgba(255,255,255,.11);
|
|
95
97
|
--pf-mono: 'JetBrains Mono','SF Mono','Fira Code',monospace;
|
|
96
|
-
--pf-tf:
|
|
97
|
-
--pf-tb:
|
|
98
|
-
|
|
99
|
-
position: fixed;
|
|
100
|
-
bottom: 0; left: 0; right: 0;
|
|
101
|
-
height: 44px;
|
|
102
|
-
background: var(--pf-bg);
|
|
103
|
-
border-top: 1px solid var(--pf-border-s);
|
|
104
|
-
z-index: 999999;
|
|
105
|
-
font-family: var(--pf-mono);
|
|
106
|
-
font-size: 11px;
|
|
107
|
-
color: var(--pf-muted);
|
|
108
|
-
animation: pfIn 300ms cubic-bezier(.4,0,.2,1) both;
|
|
98
|
+
--pf-tf: 140ms cubic-bezier(.4,0,.2,1);
|
|
99
|
+
--pf-tb: 240ms cubic-bezier(.4,0,.2,1);
|
|
109
100
|
}
|
|
110
|
-
/* ── Light theme
|
|
111
|
-
|
|
101
|
+
/* ── Light theme ─────────────────────────────────────────────────── */
|
|
102
|
+
.profiler-root[data-theme="light"] {
|
|
112
103
|
--pf-bg: #f5f3ef;
|
|
113
104
|
--pf-surface: #edeae3;
|
|
114
105
|
--pf-raised: #e3ded5;
|
|
@@ -116,39 +107,63 @@ module Profiler
|
|
|
116
107
|
--pf-muted: #8a7a6e;
|
|
117
108
|
--pf-amber: #b45309;
|
|
118
109
|
--pf-amber-h: #92400e;
|
|
119
|
-
--pf-amber-bg: rgba(180,83,9,.
|
|
120
|
-
--pf-amber-glow: rgba(180,83,9,.
|
|
110
|
+
--pf-amber-bg: rgba(180,83,9,.07);
|
|
111
|
+
--pf-amber-glow: rgba(180,83,9,.18);
|
|
121
112
|
--pf-success: #15803d;
|
|
122
113
|
--pf-warning: #c2410c;
|
|
123
114
|
--pf-error: #dc2626;
|
|
124
115
|
--pf-info: #1d4ed8;
|
|
125
|
-
--pf-border: rgba(28,20,16,.
|
|
126
|
-
--pf-border-s: rgba(28,20,16,.
|
|
116
|
+
--pf-border: rgba(28,20,16,.08);
|
|
117
|
+
--pf-border-s: rgba(28,20,16,.18);
|
|
127
118
|
}
|
|
128
|
-
|
|
119
|
+
#profiler-toolbar {
|
|
120
|
+
position: fixed;
|
|
121
|
+
bottom: 0; left: 0; right: 44px;
|
|
122
|
+
height: 44px;
|
|
123
|
+
background: linear-gradient(180deg, var(--pf-surface) 0%, var(--pf-bg) 100%);
|
|
124
|
+
border-top: 1px solid var(--pf-border-s);
|
|
125
|
+
z-index: 999999;
|
|
126
|
+
font-family: var(--pf-mono);
|
|
127
|
+
font-size: 11px;
|
|
128
|
+
color: var(--pf-muted);
|
|
129
|
+
animation: pfIn 320ms cubic-bezier(.4,0,.2,1) both;
|
|
130
|
+
}
|
|
131
|
+
/* ── Slide-in animation ──────────────────────────────────────────── */
|
|
129
132
|
@keyframes pfIn {
|
|
130
133
|
from { transform: translateY(100%); opacity: 0; }
|
|
131
|
-
to { transform: translateY(0);
|
|
134
|
+
to { transform: translateY(0); opacity: 1; }
|
|
135
|
+
}
|
|
136
|
+
@keyframes pfPop {
|
|
137
|
+
0% { transform: scaleX(1); }
|
|
138
|
+
15% { transform: scaleX(1.18); }
|
|
139
|
+
40% { transform: scaleX(0.87); }
|
|
140
|
+
65% { transform: scaleX(1.08); }
|
|
141
|
+
85% { transform: scaleX(0.97); }
|
|
142
|
+
100% { transform: scaleX(1); }
|
|
132
143
|
}
|
|
144
|
+
/* ── Amber accent line ───────────────────────────────────────────── */
|
|
133
145
|
#profiler-toolbar::before {
|
|
134
146
|
content: '';
|
|
135
147
|
position: absolute;
|
|
136
148
|
top: -1px; left: 0; right: 0;
|
|
137
149
|
height: 1px;
|
|
138
|
-
background: linear-gradient(90deg,transparent 0
|
|
139
|
-
opacity: .
|
|
150
|
+
background: linear-gradient(90deg, transparent 0%, #f59e0b 25%, #fbbf24 50%, #f59e0b 75%, transparent 100%);
|
|
151
|
+
opacity: .55;
|
|
152
|
+
transition: opacity 280ms;
|
|
140
153
|
}
|
|
154
|
+
/* ── Container ───────────────────────────────────────────────────── */
|
|
141
155
|
#profiler-toolbar .profiler-toolbar-container {
|
|
142
156
|
display: flex;
|
|
143
157
|
align-items: stretch;
|
|
144
158
|
height: 100%;
|
|
145
159
|
}
|
|
160
|
+
/* ── Items ───────────────────────────────────────────────────────── */
|
|
146
161
|
#profiler-toolbar .profiler-toolbar-item {
|
|
147
162
|
position: relative;
|
|
148
163
|
display: inline-flex;
|
|
149
164
|
align-items: center;
|
|
150
165
|
gap: 5px;
|
|
151
|
-
padding: 0
|
|
166
|
+
padding: 0 13px;
|
|
152
167
|
color: var(--pf-muted);
|
|
153
168
|
text-decoration: none;
|
|
154
169
|
white-space: nowrap;
|
|
@@ -159,7 +174,9 @@ module Profiler
|
|
|
159
174
|
font-family: var(--pf-mono);
|
|
160
175
|
font-size: 11px;
|
|
161
176
|
transition: color var(--pf-tf), background var(--pf-tf);
|
|
177
|
+
cursor: default;
|
|
162
178
|
}
|
|
179
|
+
/* Amber bottom indicator — springy easing */
|
|
163
180
|
#profiler-toolbar .profiler-toolbar-item::after {
|
|
164
181
|
content: '';
|
|
165
182
|
position: absolute;
|
|
@@ -168,52 +185,114 @@ module Profiler
|
|
|
168
185
|
background: var(--pf-amber);
|
|
169
186
|
transform: scaleX(0);
|
|
170
187
|
transform-origin: left;
|
|
171
|
-
transition: transform
|
|
188
|
+
transition: transform 280ms cubic-bezier(.34,1.4,.64,1);
|
|
172
189
|
}
|
|
173
|
-
|
|
174
|
-
#profiler-toolbar a.profiler-toolbar-item
|
|
190
|
+
/* Hover: all interactive items */
|
|
191
|
+
#profiler-toolbar a.profiler-toolbar-item,
|
|
192
|
+
#profiler-toolbar .profiler-toolbar-hoverable { cursor: pointer; }
|
|
193
|
+
|
|
194
|
+
#profiler-toolbar a.profiler-toolbar-item:hover,
|
|
195
|
+
#profiler-toolbar .profiler-toolbar-hoverable:hover {
|
|
175
196
|
color: var(--pf-text);
|
|
176
197
|
background: var(--pf-amber-bg);
|
|
177
198
|
}
|
|
178
|
-
#profiler-toolbar a.profiler-toolbar-item:hover::after
|
|
199
|
+
#profiler-toolbar a.profiler-toolbar-item:hover::after,
|
|
200
|
+
#profiler-toolbar .profiler-toolbar-hoverable:hover::after { transform: scaleX(1); }
|
|
201
|
+
|
|
202
|
+
/* Severity underline colors */
|
|
203
|
+
#profiler-toolbar .profiler-text--error::after { background: var(--pf-error); }
|
|
204
|
+
#profiler-toolbar .profiler-text--warning::after { background: var(--pf-warning); }
|
|
205
|
+
#profiler-toolbar .profiler-text--success::after { background: var(--pf-success); }
|
|
206
|
+
|
|
207
|
+
/* Semantic colors */
|
|
179
208
|
#profiler-toolbar .profiler-text--success { color: var(--pf-success) !important; }
|
|
180
209
|
#profiler-toolbar .profiler-text--warning { color: var(--pf-warning) !important; }
|
|
181
210
|
#profiler-toolbar .profiler-text--error { color: var(--pf-error) !important; }
|
|
182
211
|
#profiler-toolbar .profiler-text--accent { color: var(--pf-amber) !important; }
|
|
183
212
|
#profiler-toolbar .profiler-text--muted { color: var(--pf-muted) !important; }
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
#profiler-toolbar .profiler-toolbar-
|
|
187
|
-
border-right:
|
|
188
|
-
margin-left: auto;
|
|
213
|
+
|
|
214
|
+
/* Logo link */
|
|
215
|
+
#profiler-toolbar .profiler-toolbar-logo {
|
|
216
|
+
border-right: 1px solid var(--pf-border) !important;
|
|
189
217
|
color: var(--pf-amber);
|
|
190
|
-
padding-left: 20px;
|
|
191
218
|
font-weight: 600;
|
|
219
|
+
letter-spacing: .02em;
|
|
220
|
+
}
|
|
221
|
+
#profiler-toolbar .profiler-toolbar-logo:hover { color: var(--pf-amber-h); }
|
|
222
|
+
|
|
223
|
+
/* ── Toggle button (fixed, always visible) ───────────────────────── */
|
|
224
|
+
#profiler-toolbar-toggle {
|
|
225
|
+
position: fixed;
|
|
226
|
+
bottom: 0; right: 0;
|
|
227
|
+
width: 44px; height: 44px;
|
|
228
|
+
display: flex; align-items: center; justify-content: center;
|
|
229
|
+
cursor: pointer;
|
|
230
|
+
background: linear-gradient(180deg, var(--pf-surface) 0%, var(--pf-bg) 100%);
|
|
231
|
+
border: none;
|
|
232
|
+
border-top: 1px solid var(--pf-border-s);
|
|
233
|
+
border-left: 1px solid var(--pf-border);
|
|
234
|
+
color: var(--pf-muted);
|
|
235
|
+
font-family: var(--pf-mono);
|
|
236
|
+
font-size: 13px;
|
|
237
|
+
z-index: 1000000;
|
|
238
|
+
border-radius: 0;
|
|
239
|
+
transition: color var(--pf-tf), background var(--pf-tf);
|
|
240
|
+
}
|
|
241
|
+
#profiler-toolbar-toggle:hover {
|
|
242
|
+
color: var(--pf-amber);
|
|
243
|
+
animation: pfPop 380ms ease-out both;
|
|
244
|
+
transform: none;
|
|
192
245
|
}
|
|
193
|
-
|
|
246
|
+
.profiler-toggle-icon {
|
|
247
|
+
display: inline-block;
|
|
248
|
+
transition: transform 280ms cubic-bezier(.4,0,.2,1);
|
|
249
|
+
}
|
|
250
|
+
#profiler-toolbar-toggle[data-collapsed="true"] .profiler-toggle-icon {
|
|
251
|
+
transform: scaleX(-1);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
/* ── Hoverable wrapper ───────────────────────────────────────────── */
|
|
194
255
|
#profiler-toolbar .profiler-toolbar-hoverable { position: relative; }
|
|
256
|
+
|
|
257
|
+
/* ── Panel (CSS-driven visibility) ───────────────────────────────── */
|
|
195
258
|
#profiler-toolbar .profiler-toolbar-panel {
|
|
196
|
-
display: none;
|
|
197
259
|
position: absolute;
|
|
198
|
-
bottom: calc(100% +
|
|
260
|
+
bottom: calc(100% + 12px);
|
|
199
261
|
left: 50%;
|
|
200
262
|
transform: translateX(-50%);
|
|
201
263
|
min-width: 300px;
|
|
202
264
|
max-width: 420px;
|
|
203
265
|
background: var(--pf-surface);
|
|
204
266
|
border: 1px solid var(--pf-border-s);
|
|
205
|
-
border-radius:
|
|
206
|
-
box-shadow: 0
|
|
267
|
+
border-radius: 10px;
|
|
268
|
+
box-shadow: 0 12px 40px rgba(0,0,0,.7), 0 0 0 1px var(--pf-amber-glow);
|
|
207
269
|
z-index: 1000000;
|
|
208
270
|
overflow: hidden;
|
|
271
|
+
/* Hidden state */
|
|
272
|
+
opacity: 0;
|
|
273
|
+
visibility: hidden;
|
|
274
|
+
pointer-events: none;
|
|
275
|
+
transition:
|
|
276
|
+
opacity 160ms cubic-bezier(.4,0,.2,1),
|
|
277
|
+
visibility 0s linear 160ms;
|
|
209
278
|
}
|
|
279
|
+
#profiler-toolbar .profiler-toolbar-panel.is-visible {
|
|
280
|
+
opacity: 1;
|
|
281
|
+
visibility: visible;
|
|
282
|
+
pointer-events: auto;
|
|
283
|
+
transition:
|
|
284
|
+
opacity 160ms cubic-bezier(.4,0,.2,1),
|
|
285
|
+
visibility 0s;
|
|
286
|
+
}
|
|
287
|
+
/* Amber top bar on panel */
|
|
210
288
|
#profiler-toolbar .profiler-toolbar-panel::before {
|
|
211
289
|
content: '';
|
|
212
290
|
position: absolute;
|
|
213
291
|
top: 0; left: 0; right: 0;
|
|
214
292
|
height: 2px;
|
|
215
|
-
background: linear-gradient(90deg
|
|
293
|
+
background: linear-gradient(90deg, #f59e0b, #fbbf24);
|
|
216
294
|
}
|
|
295
|
+
/* Arrow */
|
|
217
296
|
#profiler-toolbar .profiler-toolbar-panel::after {
|
|
218
297
|
content: '';
|
|
219
298
|
position: absolute;
|
|
@@ -223,11 +302,13 @@ module Profiler
|
|
|
223
302
|
border-top-color: var(--pf-border-s);
|
|
224
303
|
}
|
|
225
304
|
#profiler-toolbar .profiler-toolbar-panel-large { min-width: 420px; max-width: 560px; }
|
|
305
|
+
|
|
306
|
+
/* ── Panel header ────────────────────────────────────────────────── */
|
|
226
307
|
#profiler-toolbar .profiler-toolbar-panel-header {
|
|
227
308
|
display: flex;
|
|
228
309
|
align-items: center;
|
|
229
310
|
justify-content: space-between;
|
|
230
|
-
padding: 10px 14px
|
|
311
|
+
padding: 10px 14px 9px;
|
|
231
312
|
font-size: 10px;
|
|
232
313
|
font-weight: 700;
|
|
233
314
|
letter-spacing: .12em;
|
|
@@ -244,6 +325,8 @@ module Profiler
|
|
|
244
325
|
letter-spacing: 0;
|
|
245
326
|
font-size: 10px;
|
|
246
327
|
}
|
|
328
|
+
|
|
329
|
+
/* ── Panel content ───────────────────────────────────────────────── */
|
|
247
330
|
#profiler-toolbar .profiler-toolbar-panel-content {
|
|
248
331
|
padding: 8px 14px 12px;
|
|
249
332
|
max-height: 380px;
|
|
@@ -258,16 +341,22 @@ module Profiler
|
|
|
258
341
|
background: var(--pf-border-s);
|
|
259
342
|
border-radius: 99px;
|
|
260
343
|
}
|
|
344
|
+
|
|
345
|
+
/* ── Panel rows ──────────────────────────────────────────────────── */
|
|
261
346
|
#profiler-toolbar .profiler-toolbar-panel-row {
|
|
262
347
|
display: flex;
|
|
263
348
|
justify-content: space-between;
|
|
264
349
|
align-items: baseline;
|
|
265
|
-
padding:
|
|
350
|
+
padding: 6px 0;
|
|
266
351
|
border-bottom: 1px solid var(--pf-border);
|
|
267
352
|
gap: 12px;
|
|
268
353
|
}
|
|
269
354
|
#profiler-toolbar .profiler-toolbar-panel-row:last-child { border-bottom: none; }
|
|
270
|
-
#profiler-toolbar .profiler-toolbar-panel-row span {
|
|
355
|
+
#profiler-toolbar .profiler-toolbar-panel-row span {
|
|
356
|
+
color: var(--pf-muted);
|
|
357
|
+
font-size: 10px;
|
|
358
|
+
flex-shrink: 0;
|
|
359
|
+
}
|
|
271
360
|
#profiler-toolbar .profiler-toolbar-panel-row strong {
|
|
272
361
|
color: var(--pf-text);
|
|
273
362
|
font-weight: 600;
|
|
@@ -275,6 +364,8 @@ module Profiler
|
|
|
275
364
|
font-variant-numeric: tabular-nums;
|
|
276
365
|
word-break: break-all;
|
|
277
366
|
}
|
|
367
|
+
|
|
368
|
+
/* ── Section header ──────────────────────────────────────────────── */
|
|
278
369
|
#profiler-toolbar .profiler-section__header {
|
|
279
370
|
font-size: 9px;
|
|
280
371
|
font-weight: 700;
|
|
@@ -282,22 +373,26 @@ module Profiler
|
|
|
282
373
|
text-transform: uppercase;
|
|
283
374
|
color: var(--pf-amber);
|
|
284
375
|
padding: 10px 0 4px;
|
|
285
|
-
border-bottom: 1px solid rgba(245,158,11,.
|
|
376
|
+
border-bottom: 1px solid rgba(245,158,11,.18);
|
|
286
377
|
margin-bottom: 6px;
|
|
287
378
|
font-family: var(--pf-mono);
|
|
288
379
|
}
|
|
289
380
|
#profiler-toolbar .profiler-section__header:first-child { padding-top: 4px; }
|
|
381
|
+
|
|
382
|
+
/* ── Query cards ─────────────────────────────────────────────────── */
|
|
290
383
|
#profiler-toolbar .profiler-toolbar-panel-query {
|
|
291
384
|
padding: 7px 10px;
|
|
292
385
|
background: var(--pf-raised);
|
|
293
386
|
border: 1px solid var(--pf-border);
|
|
294
|
-
border-radius:
|
|
387
|
+
border-radius: 5px;
|
|
295
388
|
margin-bottom: 5px;
|
|
389
|
+
transition: border-color var(--pf-tf);
|
|
296
390
|
}
|
|
297
391
|
#profiler-toolbar .profiler-toolbar-panel-query:last-child { margin-bottom: 0; }
|
|
392
|
+
#profiler-toolbar .profiler-toolbar-panel-query:hover { border-color: var(--pf-border-s); }
|
|
298
393
|
#profiler-toolbar .profiler-toolbar-panel-query-slow {
|
|
299
394
|
border-left: 2px solid var(--pf-error);
|
|
300
|
-
background: rgba(248,113,113,.
|
|
395
|
+
background: rgba(248,113,113,.05);
|
|
301
396
|
}
|
|
302
397
|
#profiler-toolbar .profiler-toolbar-panel-query code {
|
|
303
398
|
display: block;
|
|
@@ -313,6 +408,8 @@ module Profiler
|
|
|
313
408
|
padding: 0;
|
|
314
409
|
border: none;
|
|
315
410
|
}
|
|
411
|
+
|
|
412
|
+
/* ── More hint ───────────────────────────────────────────────────── */
|
|
316
413
|
#profiler-toolbar .profiler-more {
|
|
317
414
|
text-align: center;
|
|
318
415
|
padding: 7px;
|
|
@@ -322,13 +419,17 @@ module Profiler
|
|
|
322
419
|
border-top: 1px dashed var(--pf-border);
|
|
323
420
|
margin-top: 6px;
|
|
324
421
|
}
|
|
422
|
+
|
|
423
|
+
/* ── Ajax cards ──────────────────────────────────────────────────── */
|
|
325
424
|
#profiler-toolbar .profiler-ajax-card {
|
|
326
425
|
background: var(--pf-raised);
|
|
327
426
|
border: 1px solid var(--pf-border);
|
|
328
|
-
border-radius:
|
|
427
|
+
border-radius: 5px;
|
|
329
428
|
padding: 6px 8px;
|
|
330
429
|
margin-bottom: 4px;
|
|
430
|
+
transition: border-color var(--pf-tf);
|
|
331
431
|
}
|
|
432
|
+
#profiler-toolbar .profiler-ajax-card:hover { border-color: var(--pf-border-s); }
|
|
332
433
|
#profiler-toolbar .profiler-ajax-card--success { border-left: 2px solid var(--pf-success); }
|
|
333
434
|
#profiler-toolbar .profiler-ajax-card--error { border-left: 2px solid var(--pf-error); }
|
|
334
435
|
#profiler-toolbar .profiler-ajax-card__row {
|
|
@@ -337,10 +438,12 @@ module Profiler
|
|
|
337
438
|
align-items: center;
|
|
338
439
|
gap: 8px;
|
|
339
440
|
}
|
|
441
|
+
|
|
442
|
+
/* ── Dump cards ──────────────────────────────────────────────────── */
|
|
340
443
|
#profiler-toolbar .profiler-dump-card {
|
|
341
444
|
background: var(--pf-raised);
|
|
342
445
|
border: 1px solid var(--pf-border);
|
|
343
|
-
border-radius:
|
|
446
|
+
border-radius: 5px;
|
|
344
447
|
padding: 6px 8px;
|
|
345
448
|
margin-bottom: 4px;
|
|
346
449
|
}
|
|
@@ -349,11 +452,13 @@ module Profiler
|
|
|
349
452
|
justify-content: space-between;
|
|
350
453
|
margin-bottom: 4px;
|
|
351
454
|
}
|
|
455
|
+
|
|
456
|
+
/* ── Badges ──────────────────────────────────────────────────────── */
|
|
352
457
|
#profiler-toolbar .badge {
|
|
353
458
|
display: inline-flex;
|
|
354
459
|
align-items: center;
|
|
355
|
-
padding: 1px
|
|
356
|
-
border-radius:
|
|
460
|
+
padding: 1px 6px;
|
|
461
|
+
border-radius: 4px;
|
|
357
462
|
font-size: 9px;
|
|
358
463
|
font-weight: 700;
|
|
359
464
|
letter-spacing: .05em;
|
|
@@ -361,10 +466,12 @@ module Profiler
|
|
|
361
466
|
background: var(--pf-raised);
|
|
362
467
|
color: var(--pf-muted);
|
|
363
468
|
}
|
|
364
|
-
#profiler-toolbar .badge-info { background: rgba(96,165,250,.
|
|
365
|
-
#profiler-toolbar .badge-success { background: rgba(34,197,94,.
|
|
366
|
-
#profiler-toolbar .badge-warning { background: rgba(251,146,60,.
|
|
367
|
-
#profiler-toolbar .badge-error { background: rgba(248,113,113,.
|
|
469
|
+
#profiler-toolbar .badge-info { background: rgba(96,165,250,.1); color: var(--pf-info); }
|
|
470
|
+
#profiler-toolbar .badge-success { background: rgba(34,197,94,.1); color: var(--pf-success); }
|
|
471
|
+
#profiler-toolbar .badge-warning { background: rgba(251,146,60,.1); color: var(--pf-warning); }
|
|
472
|
+
#profiler-toolbar .badge-error { background: rgba(248,113,113,.1); color: var(--pf-error); }
|
|
473
|
+
|
|
474
|
+
/* ── Utilities ───────────────────────────────────────────────────── */
|
|
368
475
|
#profiler-toolbar .profiler-flex { display: flex; }
|
|
369
476
|
#profiler-toolbar .profiler-flex--between { justify-content: space-between; }
|
|
370
477
|
#profiler-toolbar .profiler-flex--gap-2 { gap: 8px; }
|
|
@@ -13,7 +13,8 @@ module Profiler
|
|
|
13
13
|
:response_headers, :collectors_data, :collectors_metadata,
|
|
14
14
|
:parent_token, :is_ajax, :profile_type,
|
|
15
15
|
:request_body, :request_body_encoding,
|
|
16
|
-
:response_body, :response_body_encoding
|
|
16
|
+
:response_body, :response_body_encoding,
|
|
17
|
+
:gem_version
|
|
17
18
|
|
|
18
19
|
def initialize(request = nil)
|
|
19
20
|
@token = SecureRandom.hex(16)
|
|
@@ -76,6 +77,7 @@ module Profiler
|
|
|
76
77
|
|
|
77
78
|
{
|
|
78
79
|
profile_type: @profile_type,
|
|
80
|
+
gem_version: @gem_version,
|
|
79
81
|
token: @token,
|
|
80
82
|
path: @path,
|
|
81
83
|
method: @method,
|
|
@@ -127,6 +129,7 @@ module Profiler
|
|
|
127
129
|
profile.parent_token = data[:parent_token]
|
|
128
130
|
profile.is_ajax = data[:is_ajax] || false
|
|
129
131
|
profile.profile_type = data[:profile_type] || "http"
|
|
132
|
+
profile.gem_version = data[:gem_version]
|
|
130
133
|
|
|
131
134
|
# Convert collectors_data keys to strings recursively for consistency
|
|
132
135
|
profile.collectors_data = (data[:collectors_data] || {}).transform_keys(&:to_s).transform_values do |value|
|
|
@@ -51,17 +51,18 @@ module Profiler
|
|
|
51
51
|
@db.execute(
|
|
52
52
|
<<~SQL,
|
|
53
53
|
INSERT OR REPLACE INTO profiler_profiles (
|
|
54
|
-
token, profile_type, path, method, status, duration, memory,
|
|
54
|
+
token, profile_type, gem_version, path, method, status, duration, memory,
|
|
55
55
|
started_at, finished_at, parent_token, is_ajax,
|
|
56
56
|
tabs, params, headers, response_headers, collectors_meta
|
|
57
57
|
) VALUES (
|
|
58
|
-
:token, :profile_type, :path, :method, :status, :duration, :memory,
|
|
58
|
+
:token, :profile_type, :gem_version, :path, :method, :status, :duration, :memory,
|
|
59
59
|
:started_at, :finished_at, :parent_token, :is_ajax,
|
|
60
60
|
:tabs, :params, :headers, :response_headers, :collectors_meta
|
|
61
61
|
)
|
|
62
62
|
SQL
|
|
63
63
|
token: token,
|
|
64
64
|
profile_type: data[:profile_type] || "http",
|
|
65
|
+
gem_version: data[:gem_version],
|
|
65
66
|
path: data[:path],
|
|
66
67
|
method: data[:method],
|
|
67
68
|
status: data[:status],
|
|
@@ -176,6 +177,7 @@ module Profiler
|
|
|
176
177
|
CREATE TABLE IF NOT EXISTS profiler_profiles (
|
|
177
178
|
token TEXT PRIMARY KEY,
|
|
178
179
|
profile_type TEXT NOT NULL DEFAULT 'http',
|
|
180
|
+
gem_version TEXT,
|
|
179
181
|
path TEXT,
|
|
180
182
|
method TEXT,
|
|
181
183
|
status INTEGER,
|
|
@@ -202,6 +204,12 @@ module Profiler
|
|
|
202
204
|
CREATE INDEX IF NOT EXISTS idx_profiler_profile_type
|
|
203
205
|
ON profiler_profiles(profile_type);
|
|
204
206
|
SQL
|
|
207
|
+
|
|
208
|
+
begin
|
|
209
|
+
@db.execute("ALTER TABLE profiler_profiles ADD COLUMN gem_version TEXT")
|
|
210
|
+
rescue SQLite3::Exception
|
|
211
|
+
# column already exists
|
|
212
|
+
end
|
|
205
213
|
end
|
|
206
214
|
|
|
207
215
|
def row_to_profile(row, load_blobs: true)
|
|
@@ -223,6 +231,7 @@ module Profiler
|
|
|
223
231
|
Models::Profile.from_hash(
|
|
224
232
|
token: row["token"],
|
|
225
233
|
profile_type: row["profile_type"],
|
|
234
|
+
gem_version: row["gem_version"],
|
|
226
235
|
path: row["path"],
|
|
227
236
|
method: row["method"],
|
|
228
237
|
status: row["status"],
|
data/lib/profiler/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rails-profiler
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.21.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Sébastien Duplessy
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-05-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|