rails-profiler 0.19.0 → 0.19.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4e5e84c4946f549552450c0d2d2d2053b273d3bed8dba66fefa17d9c4e5f7f9
4
- data.tar.gz: 6b4be4f2b3116dbdc4270df5a6f860eabbbf709c3fc296ea34f400aaeb26ce83
3
+ metadata.gz: 713e1b01bcd916191e3ae2b89124f4637eeb95d7b9837d350d2ee76a575a2f14
4
+ data.tar.gz: 9f1becf53b9b39145a4ebdf81981793aeb5f0494cce1e6872facdcf11c0a597a
5
5
  SHA512:
6
- metadata.gz: 66136505b84f792418deb7a29d7d77f36a3cc62cf3bdab9d7defc842f9797b39667c92e18984925a14276880a129ef5b42faa644112ab1c917b61e9f11f8b359
7
- data.tar.gz: b4ac6c0b37496721a3960732ab0839224e896aa4390757b36d84595200e6441034649f8829573a1fee30cf9c8216c5275e5faa9b0534bc48fe49a20e738d2bf3
6
+ metadata.gz: 298e772fd273b734126c095e57186e36d7f6fbaf26c7bb7db9899d02a38431231e4ecbde42f0b1a103deac26c29f48a596a065b5dd93f16a900b24d66e69a280
7
+ data.tar.gz: 78f23d51a52d0467e3acf9d912cbcbeebec03cf245632d88c7306d00d7c00645dda2be0a6208f3e58f4c815ea3a9efe0cb4e6783a3c6c4be44b784fd72e35f37
@@ -989,6 +989,7 @@
989
989
  const body = await res.json().catch(() => ({}));
990
990
  throw new Error(body.error ?? `Request failed (${res.status})`);
991
991
  }
992
+ return res.json();
992
993
  }
993
994
  async function resetEnvVar(key) {
994
995
  const res = await fetch(`/_profiler/api/env_vars/reset?key=${encodeURIComponent(key)}`, {
@@ -1005,7 +1006,7 @@
1005
1006
  if (!res.ok) throw new Error(`Request failed (${res.status})`);
1006
1007
  }
1007
1008
  function EnvTab({ envData, readOnly: forceReadOnly = false }) {
1008
- const initial = T2(() => ({ ...envData?.variables ?? {} }), []);
1009
+ const [initial, setInitial] = d2(() => ({ ...envData?.variables ?? {} }));
1009
1010
  const [variables, setVariables] = d2(initial);
1010
1011
  const [overrides, setOverrides] = d2(envData?.overrides ?? {});
1011
1012
  const [total, setTotal] = d2(envData?.total ?? 0);
@@ -1070,6 +1071,7 @@
1070
1071
  setTotal(data.total);
1071
1072
  setOverrides(data.overrides ?? {});
1072
1073
  showFlash("success", `Refreshed \u2014 ${data.total} variables`);
1074
+ return data;
1073
1075
  } catch (e3) {
1074
1076
  showFlash("error", e3.message ?? "Failed to refresh");
1075
1077
  } finally {
@@ -1116,11 +1118,25 @@
1116
1118
  };
1117
1119
  const saveEdit = async () => {
1118
1120
  if (!editingKey) return;
1121
+ const key = editingKey;
1122
+ if (editValue === variables[key]) {
1123
+ setEditingKey(null);
1124
+ return;
1125
+ }
1119
1126
  setSaving(true);
1120
1127
  try {
1121
- await patchEnvVar(editingKey, editValue);
1122
- setVariables((prev) => ({ ...prev, [editingKey]: editValue }));
1123
- showFlash("success", `${editingKey} updated`);
1128
+ const data = await patchEnvVar(key, editValue);
1129
+ setVariables((prev) => ({ ...prev, [key]: editValue }));
1130
+ setOverrides((prev) => {
1131
+ const n2 = { ...prev };
1132
+ if (data.override) {
1133
+ n2[key] = data.override;
1134
+ } else {
1135
+ delete n2[key];
1136
+ }
1137
+ return n2;
1138
+ });
1139
+ showFlash("success", `${key} updated`);
1124
1140
  setEditingKey(null);
1125
1141
  } catch (e3) {
1126
1142
  showFlash("error", e3.message ?? "Failed to update");
@@ -1131,12 +1147,21 @@
1131
1147
  const deleteVar = async (key) => {
1132
1148
  setSaving(true);
1133
1149
  try {
1134
- await patchEnvVar(key, null);
1150
+ const data = await patchEnvVar(key, null);
1135
1151
  setVariables((prev) => {
1136
1152
  const n2 = { ...prev };
1137
1153
  delete n2[key];
1138
1154
  return n2;
1139
1155
  });
1156
+ setOverrides((prev) => {
1157
+ const n2 = { ...prev };
1158
+ if (data.override) {
1159
+ n2[key] = data.override;
1160
+ } else {
1161
+ delete n2[key];
1162
+ }
1163
+ return n2;
1164
+ });
1140
1165
  setUnmaskedKeys((prev) => {
1141
1166
  const n2 = new Set(prev);
1142
1167
  n2.delete(key);
@@ -1154,8 +1179,17 @@
1154
1179
  const next = /^(true|yes)$/i.test(current) ? "false" : "true";
1155
1180
  setSaving(true);
1156
1181
  try {
1157
- await patchEnvVar(key, next);
1182
+ const data = await patchEnvVar(key, next);
1158
1183
  setVariables((prev) => ({ ...prev, [key]: next }));
1184
+ setOverrides((prev) => {
1185
+ const n2 = { ...prev };
1186
+ if (data.override) {
1187
+ n2[key] = data.override;
1188
+ } else {
1189
+ delete n2[key];
1190
+ }
1191
+ return n2;
1192
+ });
1159
1193
  } catch (e3) {
1160
1194
  showFlash("error", e3.message ?? "Failed to update");
1161
1195
  } finally {
@@ -1167,8 +1201,17 @@
1167
1201
  if (!key) return;
1168
1202
  setSaving(true);
1169
1203
  try {
1170
- await patchEnvVar(key, newValue);
1204
+ const data = await patchEnvVar(key, newValue);
1171
1205
  setVariables((prev) => ({ ...prev, [key]: newValue }));
1206
+ setOverrides((prev) => {
1207
+ const n2 = { ...prev };
1208
+ if (data.override) {
1209
+ n2[key] = data.override;
1210
+ } else {
1211
+ delete n2[key];
1212
+ }
1213
+ return n2;
1214
+ });
1172
1215
  setNewKey("");
1173
1216
  setNewValue("");
1174
1217
  showFlash("success", `${key} added`);
@@ -1191,7 +1234,7 @@
1191
1234
  delete n2[key];
1192
1235
  return n2;
1193
1236
  });
1194
- setVariables((prev) => {
1237
+ const updater = (prev) => {
1195
1238
  const n2 = { ...prev };
1196
1239
  if (data.value === null) {
1197
1240
  delete n2[key];
@@ -1199,7 +1242,9 @@
1199
1242
  n2[key] = data.value;
1200
1243
  }
1201
1244
  return n2;
1202
- });
1245
+ };
1246
+ setVariables(updater);
1247
+ setInitial(updater);
1203
1248
  showFlash("success", `${key} reset to original`);
1204
1249
  } catch (e3) {
1205
1250
  showFlash("error", e3.message ?? "Failed to reset");
@@ -1212,7 +1257,8 @@
1212
1257
  try {
1213
1258
  await resetAllEnvVars();
1214
1259
  setOverrides({});
1215
- await refresh();
1260
+ const data = await refresh();
1261
+ if (data) setInitial(data.variables);
1216
1262
  showFlash("success", "All overrides reset");
1217
1263
  } catch (e3) {
1218
1264
  showFlash("error", e3.message ?? "Failed to reset all");
@@ -1449,10 +1495,6 @@
1449
1495
  override.value === "__profiler_deleted__" ? "(deleted)" : override.value,
1450
1496
  " \xB7 original: ",
1451
1497
  override.original ?? "(unset)"
1452
- ] }),
1453
- !override && wasModified && /* @__PURE__ */ u3("div", { style: "font-size:10px;color:#f59e0b;margin-top:2px;font-family:monospace;", children: [
1454
- "was: ",
1455
- initial[key]
1456
1498
  ] })
1457
1499
  ] }) : /* @__PURE__ */ u3(k, { children: [
1458
1500
  /* @__PURE__ */ u3("div", { class: "profiler-flex", style: "align-items:center;gap:4px;", children: [
@@ -1461,7 +1503,9 @@
1461
1503
  "span",
1462
1504
  {
1463
1505
  class: "profiler-text--xs profiler-text--mono",
1464
- style: "word-break:break-all;flex:1;",
1506
+ onClick: () => !readOnly && !isEditing && startEdit(key),
1507
+ title: !readOnly ? "Click to edit" : void 0,
1508
+ style: `word-break:break-all;flex:1;${!readOnly ? "cursor:pointer;" : ""}`,
1465
1509
  children: unmasked ? isQuoted(value) ? /* @__PURE__ */ u3(k, { children: [
1466
1510
  /* @__PURE__ */ u3("span", { style: "color:var(--profiler-text-muted);opacity:0.5;", children: '"' }),
1467
1511
  value,
@@ -1493,28 +1537,24 @@
1493
1537
  override.value === "__profiler_deleted__" ? "(deleted)" : unmasked ? override.value : "\u2022".repeat(Math.min((override.value ?? "").length, 20)),
1494
1538
  " \xB7 original: ",
1495
1539
  override.original == null ? "(unset)" : unmasked ? override.original : "\u2022".repeat(Math.min(override.original.length, 20))
1496
- ] }),
1497
- !override && wasModified && /* @__PURE__ */ u3("div", { style: "font-size:10px;color:#f59e0b;margin-top:2px;font-family:monospace;", children: [
1498
- "was: ",
1499
- unmasked ? isQuoted(initial[key]) ? `"${initial[key]}"` : initial[key] : "\u2022".repeat(Math.min(initial[key].length, 20))
1500
1540
  ] })
1501
1541
  ] }) }),
1502
1542
  !readOnly && /* @__PURE__ */ u3("td", { style: "text-align:right;white-space:nowrap;", children: isEditing ? /* @__PURE__ */ u3(k, { children: [
1503
1543
  /* @__PURE__ */ u3("button", { onClick: saveEdit, disabled: saving, title: "Save", style: "background:none;border:none;cursor:pointer;color:var(--profiler-success,#22c55e);font-size:14px;padding:0 4px;", children: "\u2713" }),
1504
1544
  /* @__PURE__ */ u3("button", { onClick: cancelEdit, disabled: saving, title: "Cancel", style: "background:none;border:none;cursor:pointer;color:var(--profiler-text-muted);font-size:14px;padding:0 4px;", children: "\u2717" })
1505
1545
  ] }) : /* @__PURE__ */ u3(k, { children: [
1506
- /* @__PURE__ */ u3("button", { onClick: () => startEdit(key), disabled: saving, style: "background:none;border:none;cursor:pointer;color:var(--profiler-accent);font-size:11px;padding:0 4px;", children: "Edit" }),
1546
+ /* @__PURE__ */ u3("button", { onClick: () => startEdit(key), disabled: saving, title: "Edit", style: "background:none;border:none;cursor:pointer;color:var(--profiler-accent);font-size:14px;padding:0 4px;", children: "\u270E" }),
1507
1547
  override && /* @__PURE__ */ u3(
1508
1548
  "button",
1509
1549
  {
1510
1550
  onClick: () => resetVar(key),
1511
1551
  disabled: saving,
1512
1552
  title: `Reset to original: ${override.original ?? "(unset)"}`,
1513
- style: "background:none;border:none;cursor:pointer;color:var(--profiler-warning,#f59e0b);font-size:11px;padding:0 4px;",
1514
- children: "\u21A9 Reset"
1553
+ style: "background:none;border:none;cursor:pointer;color:var(--profiler-warning,#f59e0b);font-size:14px;padding:0 4px;",
1554
+ children: "\u21A9"
1515
1555
  }
1516
1556
  ),
1517
- /* @__PURE__ */ u3("button", { onClick: () => deleteVar(key), disabled: saving, style: "background:none;border:none;cursor:pointer;color:var(--profiler-error,#ef4444);font-size:11px;padding:0 4px;", children: "Delete" })
1557
+ /* @__PURE__ */ u3("button", { onClick: () => deleteVar(key), disabled: saving, title: "Delete", style: "background:none;border:none;cursor:pointer;color:var(--profiler-error,#ef4444);font-size:14px;padding:0 4px;", children: "\u2715" })
1518
1558
  ] }) })
1519
1559
  ] }, key);
1520
1560
  })
@@ -22,13 +22,18 @@ module Profiler
22
22
  value = params[:value]
23
23
 
24
24
  if value.nil? || value.to_s.empty?
25
- ENV.delete(key)
26
25
  Profiler.env_override_store.delete(key)
27
- render json: { key: key, value: nil, deleted: true }
26
+ ENV.delete(key)
27
+ render json: { key: key, value: nil, deleted: true, override: Profiler.env_override_store.all_overrides[key] }
28
28
  else
29
+ current_original = Profiler.env_override_store.all_overrides.dig(key, "original")
30
+ if current_original == value.to_s
31
+ Profiler.env_override_store.reset(key)
32
+ else
33
+ Profiler.env_override_store.set(key, value.to_s)
34
+ end
29
35
  ENV[key] = value.to_s
30
- Profiler.env_override_store.set(key, value.to_s)
31
- render json: { key: key, value: ENV[key] }
36
+ render json: { key: key, value: ENV[key], override: Profiler.env_override_store.all_overrides[key] }
32
37
  end
33
38
  end
34
39
 
@@ -14,6 +14,11 @@ module Profiler
14
14
  def call(env)
15
15
  return @app.call(env) unless should_profile?(env)
16
16
 
17
+ status = nil
18
+ headers = nil
19
+ body = nil
20
+ collectors = nil
21
+
17
22
  profile = Models::Profile.new(build_request(env))
18
23
  Profiler::CurrentContext.token = profile.token
19
24
 
@@ -23,14 +28,12 @@ module Profiler
23
28
  # Store profile in env for collectors
24
29
  env["profiler.profile"] = profile
25
30
 
26
- # Create and subscribe collectors
27
31
  collectors = create_collectors(profile)
28
32
  env["profiler.collectors"] = collectors
29
33
 
30
34
  # Measure memory before
31
35
  memory_before = current_memory if Profiler.configuration.track_memory
32
36
 
33
- # Process request
34
37
  status, headers, body = @app.call(env)
35
38
 
36
39
  # Measure memory after
@@ -39,14 +42,11 @@ module Profiler
39
42
  profile.memory = memory_after - memory_before
40
43
  end
41
44
 
42
- # Collect and buffer response body (avoids double-reading by ToolbarInjector)
43
45
  body_content = collect_body(body)
44
46
  body = [body_content]
45
47
 
46
- # Finish profile
47
48
  profile.finish(status, headers)
48
49
 
49
- # Store request and response bodies
50
50
  profile.set_bodies(
51
51
  request_body: req_body_raw,
52
52
  response_body: body_content,
@@ -54,7 +54,6 @@ module Profiler
54
54
  resp_content_type: (headers["content-type"] || headers["Content-Type"]).to_s
55
55
  )
56
56
 
57
- # Collect data from all collectors
58
57
  collectors.each do |collector|
59
58
  begin
60
59
  collector.collect if collector.respond_to?(:collect)
@@ -64,14 +63,11 @@ module Profiler
64
63
  end
65
64
  end
66
65
 
67
- # Store profile
68
66
  Profiler.storage.save(profile.token, profile)
69
67
  Profiler::CurrentContext.clear
70
68
 
71
- # Add profiler token header
72
69
  headers["X-Profiler-Token"] = profile.token
73
70
 
74
- # Inject toolbar if HTML response
75
71
  if html_response?(headers)
76
72
  nonce = env['action_dispatch.content_security_policy_nonce']
77
73
  body = ToolbarInjector.new(body, profile.token, nonce).inject
@@ -80,7 +76,9 @@ module Profiler
80
76
  [status, headers, body]
81
77
  rescue => e
82
78
  warn "Profiler error: #{e.message}\n#{e.backtrace.join("\n")}"
83
- @app.call(env)
79
+ collectors&.each { |c| c.unsubscribe if c.respond_to?(:unsubscribe) }
80
+ Profiler::CurrentContext.clear
81
+ status ? [status, headers, body || []] : @app.call(env)
84
82
  end
85
83
 
86
84
  private
@@ -19,6 +19,9 @@ module Profiler
19
19
 
20
20
  @db = SQLite3::Database.new(db_path.to_s)
21
21
  @db.results_as_hash = true
22
+ @db.busy_timeout = 5000
23
+ @db.execute("PRAGMA journal_mode=WAL")
24
+ @db.execute("PRAGMA synchronous=NORMAL")
22
25
 
23
26
  @blob_store = BlobStore.new(blob_path.to_s)
24
27
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Profiler
4
- VERSION = "0.19.0"
4
+ VERSION = "0.19.1"
5
5
  end
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.19.0
4
+ version: 0.19.1
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-04-15 00:00:00.000000000 Z
11
+ date: 2026-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails