rails-profiler 0.34.6 → 0.35.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: 0637e842e7e8cce996b450d94201b5a117bc6797fe095a488ea9f0b9b54c504f
4
- data.tar.gz: 5949fcd0e376266ebfff2cc698b4964c4af39f85ae83832e905c0909354844dc
3
+ metadata.gz: deae1ffa2327cdd3603f1451a376444412a495cd544920c4ff7b91b53275a169
4
+ data.tar.gz: 661c22ece7d423f81a12f283c5daafcca147b1bf8661ffa1a96ea7a42d962451
5
5
  SHA512:
6
- metadata.gz: a5834f8092fa5c9346fb578444a32e43ffb1ebe669ece132b8214d1400257a6dabcea588b3a81125924c40f64fd98aa410e92523500c829b7af06f128be42997
7
- data.tar.gz: 4728ac7e13f397cf2439a079f63430d74056a9bf126c7cfb29001fcde35003b076b76426b600465a246c2a177f660600f9ae4e2deef445273cfb43464d09b398
6
+ metadata.gz: 668ccb4d779af6e835701b59df419eaf91daed287ccc3087dd45a74ad9e7e6895bc7c37c36d152e06d1f3ddbcc68181b539c1eb1dea98e3a5421c1bb35eac2b3
7
+ data.tar.gz: 3511797c44acfea198604714c262eb8555a983f7e8392abc6788041dd9e18ff5ce29cb4814d6f64a364c6a921c89790a33e1bf3dc4eef085aa6bbaa877c7e8ad
data/CHANGELOG.md CHANGED
@@ -19,6 +19,35 @@ every commit of every tag interval is accounted for one way or the other.
19
19
 
20
20
  ## [Unreleased]
21
21
 
22
+ ## [0.35.1] - 2026-10-17
23
+
24
+ <!-- stamped -->
25
+
26
+ ### Fixed
27
+
28
+ - **Release:** The version a merge publishes is now read from the subject line of each commit only,
29
+ never from its body, and `BREAKING-CHANGE:` (with a hyphen) publishes a major version just as
30
+ `BREAKING CHANGE:` already did.
31
+ - **Canary:** A prerelease is now numbered after the version its branch is going to publish, not
32
+ after the last release, so RubyGems no longer sorts a recent canary as older than the release it
33
+ precedes.
34
+
35
+ ### Security
36
+
37
+ - **Release:** `rake release` and its subtasks now refuse to run from a developer machine and point
38
+ to the `release` CI job, the only publisher. `rake build` and `rake install` keep working.
39
+ - **CI:** The `canary` job no longer publishes a prerelease on every push to a `feature/`,
40
+ `bugfix/`, `hotfix/`, `fix/` or `breaking/` branch; it is now manual and publishes only when
41
+ someone plays it.
42
+
43
+ ## [0.35.0] - 2026-10-17
44
+
45
+ <!-- stamped -->
46
+
47
+ ### Added
48
+
49
+ - The dashboard pages now carry the Chrome extension's icon as their favicon, served at `/_profiler/assets/icon.png`.
50
+
22
51
  ## [0.34.6] - 2026-10-16
23
52
 
24
53
  <!-- stamped -->
@@ -4098,7 +4098,7 @@
4098
4098
  panel: /* @__PURE__ */ u3(RoutesPanel, { routesData }),
4099
4099
  children: [
4100
4100
  /* @__PURE__ */ u3("span", { class: "profiler-text--muted profiler-text--xs", children: "ROUTE" }),
4101
- /* @__PURE__ */ u3("span", { class: "profiler-text--xs profiler-text--mono", children: routesData.matched?.pattern ?? "\u2014" })
4101
+ /* @__PURE__ */ u3("span", { class: "profiler-text--xs profiler-text--mono", children: routesData.matched?.pattern ?? "-" })
4102
4102
  ]
4103
4103
  }
4104
4104
  ),
@@ -4033,7 +4033,7 @@
4033
4033
  return "warning";
4034
4034
  }
4035
4035
  function formatBytes(bytes) {
4036
- if (bytes < 0) return "\u2014";
4036
+ if (bytes < 0) return "-";
4037
4037
  if (bytes < 1024) return `${bytes} B`;
4038
4038
  if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
4039
4039
  return `${(bytes / (1024 * 1024)).toFixed(2)} MB`;
@@ -4682,7 +4682,7 @@
4682
4682
  setVariables(data.variables);
4683
4683
  setTotal(data.total);
4684
4684
  setOverrides(data.overrides ?? {});
4685
- showFlash("success", `Refreshed \u2014 ${data.total} variables`);
4685
+ showFlash("success", `Refreshed - ${data.total} variables`);
4686
4686
  return data;
4687
4687
  } catch (e3) {
4688
4688
  showFlash("error", e3.message ?? "Failed to refresh");
@@ -4956,10 +4956,10 @@
4956
4956
  ] })
4957
4957
  ] }),
4958
4958
  forceReadOnly ? /* @__PURE__ */ u3("div", { style: "background:var(--profiler-bg-subtle,rgba(0,0,0,0.03));border:1px solid var(--profiler-border);border-radius:6px;padding:8px 12px;margin-bottom:16px;font-size:12px;color:var(--profiler-text-muted);", children: [
4959
- "Snapshot taken at request time \u2014 modify env vars in ",
4959
+ "Snapshot taken at request time - modify env vars in ",
4960
4960
  /* @__PURE__ */ u3("a", { href: "/_profiler?section=env", style: "color:var(--profiler-accent);", children: "/_profiler?section=env" }),
4961
4961
  "."
4962
- ] }) : /* @__PURE__ */ u3("div", { style: "background:var(--profiler-warning-bg,rgba(245,158,11,0.1));border:1px solid var(--profiler-warning,#f59e0b);border-radius:6px;padding:8px 12px;margin-bottom:16px;font-size:12px;color:var(--profiler-warning,#f59e0b);", children: "\u26A0 Changes affect the current process only \u2014 for development use." }),
4962
+ ] }) : /* @__PURE__ */ u3("div", { style: "background:var(--profiler-warning-bg,rgba(245,158,11,0.1));border:1px solid var(--profiler-warning,#f59e0b);border-radius:6px;padding:8px 12px;margin-bottom:16px;font-size:12px;color:var(--profiler-warning,#f59e0b);", children: "\u26A0 Changes affect the current process only - for development use." }),
4963
4963
  flash && /* @__PURE__ */ u3("div", { style: `background:${flash.type === "success" ? "var(--profiler-success-bg,rgba(34,197,94,0.1))" : "var(--profiler-error-bg,rgba(239,68,68,0.08))"};border:1px solid ${flash.type === "success" ? "var(--profiler-success,#22c55e)" : "var(--profiler-error,#ef4444)"};border-radius:6px;padding:6px 12px;margin-bottom:12px;font-size:12px;color:${flash.type === "success" ? "var(--profiler-success,#22c55e)" : "var(--profiler-error,#ef4444)"};`, children: [
4964
4964
  flash.type === "success" ? "\u2713" : "\u2717",
4965
4965
  " ",
@@ -6100,7 +6100,7 @@
6100
6100
  p3.duration.toFixed(2),
6101
6101
  " ms"
6102
6102
  ] }) }),
6103
- /* @__PURE__ */ u3("td", { children: p3.collectors_data?.database?.total_queries ?? "\u2014" }),
6103
+ /* @__PURE__ */ u3("td", { children: p3.collectors_data?.database?.total_queries ?? "-" }),
6104
6104
  /* @__PURE__ */ u3("td", { children: formatAllocations(allocatedObjects(p3)) }),
6105
6105
  /* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: statusClass(p3.status), children: p3.status }) }),
6106
6106
  /* @__PURE__ */ u3("td", { class: "profiler-text--xs profiler-text--mono profiler-text--muted", children: /* @__PURE__ */ u3("button", { class: "token-copy", onClick: () => copyToken(p3.token), title: "Copy full token", children: copiedToken === p3.token ? "\u2713" : p3.token.substring(0, 8) + "\u2026" }) }),
@@ -6263,7 +6263,7 @@
6263
6263
  p3.duration.toFixed(2),
6264
6264
  " ms"
6265
6265
  ] }) }),
6266
- /* @__PURE__ */ u3("td", { children: p3.collectors_data?.database?.total_queries ?? "\u2014" }),
6266
+ /* @__PURE__ */ u3("td", { children: p3.collectors_data?.database?.total_queries ?? "-" }),
6267
6267
  /* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: isError ? "badge-error" : "badge-success", children: isError ? "\u2717 Error" : "\u2713 OK" }) }),
6268
6268
  /* @__PURE__ */ u3("td", { class: "profiler-text--xs profiler-text--mono profiler-text--muted", children: /* @__PURE__ */ u3("button", { class: "token-copy", onClick: () => copyToken(p3.token), title: "Copy full token", children: copiedToken === p3.token ? "\u2713" : p3.token.substring(0, 8) + "\u2026" }) }),
6269
6269
  /* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("button", { class: "btn-row-delete", onClick: () => deleteConsole2(p3.token), title: "Delete", children: "\xD7" }) })
@@ -6346,7 +6346,7 @@
6346
6346
  p3.duration.toFixed(2),
6347
6347
  " ms"
6348
6348
  ] }) }),
6349
- /* @__PURE__ */ u3("td", { children: p3.collectors_data?.database?.total_queries ?? "\u2014" }),
6349
+ /* @__PURE__ */ u3("td", { children: p3.collectors_data?.database?.total_queries ?? "-" }),
6350
6350
  /* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("span", { class: status === "failed" ? "badge-error" : status === "pending" ? "badge-warning" : "badge-success", children: status === "failed" ? "\u2717 Failed" : status === "pending" ? "\u23F8 Pending" : "\u2713 Passed" }) }),
6351
6351
  /* @__PURE__ */ u3("td", { class: "profiler-text--xs profiler-text--mono profiler-text--muted", children: /* @__PURE__ */ u3("button", { class: "token-copy", onClick: () => copyToken(p3.token), title: "Copy full token", children: copiedToken === p3.token ? "\u2713" : p3.token.substring(0, 8) + "\u2026" }) }),
6352
6352
  /* @__PURE__ */ u3("td", { children: /* @__PURE__ */ u3("button", { class: "btn-row-delete", onClick: () => deleteTest2(p3.token), title: "Delete", children: "\xD7" }) })
@@ -6741,7 +6741,7 @@
6741
6741
  /* @__PURE__ */ u3("span", { class: "profiler-alert-banner__icon", children: "\u26A0\uFE0F" }),
6742
6742
  /* @__PURE__ */ u3("div", { style: "flex:1", children: [
6743
6743
  /* @__PURE__ */ u3("strong", { children: "Potential N+1 detected" }),
6744
- " \u2014 ",
6744
+ " - ",
6745
6745
  n1Groups.length,
6746
6746
  " pattern",
6747
6747
  n1Groups.length > 1 ? "s" : "",
@@ -7814,7 +7814,7 @@
7814
7814
  class: `profiler-fn-profiling__mode-btn${mode === m3 ? " profiler-fn-profiling__mode-btn--active" : ""}`,
7815
7815
  onClick: () => mode !== m3 && onModeChange(m3),
7816
7816
  disabled: modeUpdating,
7817
- title: m3 === "full" ? "Exhaustive TracePoint \u2014 timing + allocations + memory bytes" : "StackProf sampling \u2014 very low overhead",
7817
+ title: m3 === "full" ? "Exhaustive TracePoint - timing + allocations + memory bytes" : "StackProf sampling - very low overhead",
7818
7818
  children: m3.charAt(0).toUpperCase() + m3.slice(1)
7819
7819
  },
7820
7820
  m3
@@ -7864,7 +7864,7 @@
7864
7864
  ": statistical sampling with the stackprof gem, to add to the application's Gemfile; very low overhead (<1%), on by default once stackprof is installed.",
7865
7865
  " ",
7866
7866
  /* @__PURE__ */ u3("strong", { children: "Full" }),
7867
- ": exhaustive TracePoint tracing with memory bytes \u2014 significant overhead."
7867
+ ": exhaustive TracePoint tracing with memory bytes - significant overhead."
7868
7868
  ] }),
7869
7869
  enabled && stackprofMissing && /* @__PURE__ */ u3("p", { class: "profiler-fn-profiling__hint", children: [
7870
7870
  "No data: sampling needs the ",
@@ -7884,7 +7884,7 @@
7884
7884
  data.frame_cap_reached && /* @__PURE__ */ u3("div", { class: "profiler-fn-profiling__cap-warning", children: [
7885
7885
  "\u26A0 Frame cap reached (",
7886
7886
  data.max_frames,
7887
- ' frames) \u2014 call tree is truncated. Increase "Max frames" to capture more.'
7887
+ ' frames) - call tree is truncated. Increase "Max frames" to capture more.'
7888
7888
  ] }),
7889
7889
  /* @__PURE__ */ u3("div", { class: "profiler-flamegraph__stats", style: { marginTop: "0.75rem" }, children: [
7890
7890
  /* @__PURE__ */ u3("div", { class: "stat-item", children: [
@@ -8581,8 +8581,8 @@
8581
8581
  filtered.map((route, i3) => /* @__PURE__ */ u3("div", { class: `profiler-flex-table__row${route.matched ? " profiler-flex-table__row--matched" : ""}`, children: [
8582
8582
  /* @__PURE__ */ u3("span", { class: "profiler-flex-table__cell--fixed", children: /* @__PURE__ */ u3(VerbBadge, { verb: route.verb }) }),
8583
8583
  /* @__PURE__ */ u3("span", { class: "profiler-flex-table__cell profiler-text--mono profiler-text--xs", title: route.pattern, children: route.pattern }),
8584
- /* @__PURE__ */ u3("span", { class: "profiler-flex-table__cell profiler-flex-table__cell--muted profiler-text--mono profiler-text--xs", title: route.name ? `${route.name}_path` : "", children: route.name ? `${route.name}_path` : "\u2014" }),
8585
- /* @__PURE__ */ u3("span", { class: "profiler-flex-table__cell profiler-flex-table__cell--muted profiler-text--mono profiler-text--xs", title: route.controller_action ?? "", children: route.controller_action ?? "\u2014" })
8584
+ /* @__PURE__ */ u3("span", { class: "profiler-flex-table__cell profiler-flex-table__cell--muted profiler-text--mono profiler-text--xs", title: route.name ? `${route.name}_path` : "", children: route.name ? `${route.name}_path` : "-" }),
8585
+ /* @__PURE__ */ u3("span", { class: "profiler-flex-table__cell profiler-flex-table__cell--muted profiler-text--mono profiler-text--xs", title: route.controller_action ?? "", children: route.controller_action ?? "-" })
8586
8586
  ] }, i3))
8587
8587
  ] })
8588
8588
  ] });
@@ -8692,7 +8692,7 @@
8692
8692
  const hasText = !!email.body_text;
8693
8693
  if (!email.body_captured) {
8694
8694
  return /* @__PURE__ */ u3("div", { class: "profiler-mt-3 profiler-text--xs profiler-text--muted", children: [
8695
- "Body not captured \u2014 enable ",
8695
+ "Body not captured - enable ",
8696
8696
  /* @__PURE__ */ u3("code", { children: "config.capture_mail_body = true" }),
8697
8697
  " in your profiler initializer"
8698
8698
  ] });
@@ -8778,7 +8778,7 @@
8778
8778
  ] }),
8779
8779
  /* @__PURE__ */ u3("div", { class: "profiler-kv-row", children: [
8780
8780
  /* @__PURE__ */ u3("span", { children: "Render" }),
8781
- /* @__PURE__ */ u3("span", { class: "profiler-query-card__duration", children: email.duration_ms != null ? `${email.duration_ms} ms` : "\u2014" })
8781
+ /* @__PURE__ */ u3("span", { class: "profiler-query-card__duration", children: email.duration_ms != null ? `${email.duration_ms} ms` : "-" })
8782
8782
  ] }),
8783
8783
  email.delivery_ms != null && /* @__PURE__ */ u3("div", { class: "profiler-kv-row", children: [
8784
8784
  /* @__PURE__ */ u3("span", { children: "Delivery" }),
@@ -8810,7 +8810,7 @@
8810
8810
  email.action
8811
8811
  ] }),
8812
8812
  email.subject && /* @__PURE__ */ u3("span", { class: "profiler-text--muted", children: [
8813
- " \u2014 ",
8813
+ " - ",
8814
8814
  email.subject.slice(0, 60),
8815
8815
  email.subject.length > 60 ? "\u2026" : ""
8816
8816
  ] })
@@ -8891,7 +8891,7 @@
8891
8891
  /* @__PURE__ */ u3("span", { class: "profiler-alert-banner__icon", children: "\u26A0\uFE0F" }),
8892
8892
  /* @__PURE__ */ u3("div", { style: { flex: 1 }, children: [
8893
8893
  /* @__PURE__ */ u3("strong", { children: "Send loop detected" }),
8894
- " \u2014 ",
8894
+ " - ",
8895
8895
  mailerData.loop_warnings.length,
8896
8896
  " pattern",
8897
8897
  mailerData.loop_warnings.length > 1 ? "s" : "",
@@ -8900,7 +8900,7 @@
8900
8900
  ] }),
8901
8901
  mailerData.truncated && /* @__PURE__ */ u3("div", { class: "profiler-alert-banner profiler-alert-banner--warning profiler-mb-4", children: [
8902
8902
  /* @__PURE__ */ u3("span", { class: "profiler-alert-banner__icon", children: "\u26A0\uFE0F" }),
8903
- /* @__PURE__ */ u3("span", { children: "Showing first 50 emails only \u2014 additional emails were truncated" })
8903
+ /* @__PURE__ */ u3("span", { children: "Showing first 50 emails only - additional emails were truncated" })
8904
8904
  ] }),
8905
8905
  mailerData.emails.length > 0 && /* @__PURE__ */ u3(k, { children: [
8906
8906
  /* @__PURE__ */ u3("h3", { class: "profiler-text--lg profiler-mt-4 profiler-mb-3", children: [
@@ -8912,7 +8912,7 @@
8912
8912
  mailerData.queued && mailerData.queued.length > 0 && /* @__PURE__ */ u3(k, { children: [
8913
8913
  /* @__PURE__ */ u3("h3", { class: "profiler-text--lg profiler-mt-4 profiler-mb-2", children: [
8914
8914
  "Queued",
8915
- /* @__PURE__ */ u3("span", { class: "profiler-text--muted profiler-text--sm", style: { fontWeight: "normal", marginLeft: "8px" }, children: "deliver_later \u2014 will be sent in a background job" })
8915
+ /* @__PURE__ */ u3("span", { class: "profiler-text--muted profiler-text--sm", style: { fontWeight: "normal", marginLeft: "8px" }, children: "deliver_later - will be sent in a background job" })
8916
8916
  ] }),
8917
8917
  mailerData.queued.map((email, i3) => /* @__PURE__ */ u3(QueuedRow, { email }, `q-${i3}`))
8918
8918
  ] }),
@@ -8979,7 +8979,7 @@
8979
8979
  profile.gem_version && profile.gem_version !== getGemVersion() && /* @__PURE__ */ u3("div", { class: "profiler-version-mismatch", children: [
8980
8980
  "\u26A0\uFE0F Profil captur\xE9 avec la version ",
8981
8981
  /* @__PURE__ */ u3("strong", { children: profile.gem_version }),
8982
- " \u2014 version actuelle : ",
8982
+ " - version actuelle : ",
8983
8983
  /* @__PURE__ */ u3("strong", { children: getGemVersion() })
8984
8984
  ] })
8985
8985
  ] }),
@@ -9133,7 +9133,7 @@
9133
9133
  profile.gem_version && profile.gem_version !== getGemVersion() && /* @__PURE__ */ u3("div", { class: "profiler-version-mismatch", children: [
9134
9134
  "\u26A0\uFE0F Profil captur\xE9 avec la version ",
9135
9135
  /* @__PURE__ */ u3("strong", { children: profile.gem_version }),
9136
- " \u2014 version actuelle : ",
9136
+ " - version actuelle : ",
9137
9137
  /* @__PURE__ */ u3("strong", { children: getGemVersion() })
9138
9138
  ] }),
9139
9139
  parent && /* @__PURE__ */ u3("div", { class: "profiler-flex profiler-flex--gap-2 profiler-mt-2 profiler-text--sm", children: [
@@ -9262,7 +9262,7 @@
9262
9262
  profile.gem_version && profile.gem_version !== getGemVersion() && /* @__PURE__ */ u3("div", { class: "profiler-version-mismatch", children: [
9263
9263
  "\u26A0\uFE0F Profil captur\xE9 avec la version ",
9264
9264
  /* @__PURE__ */ u3("strong", { children: profile.gem_version }),
9265
- " \u2014 version actuelle : ",
9265
+ " - version actuelle : ",
9266
9266
  /* @__PURE__ */ u3("strong", { children: getGemVersion() })
9267
9267
  ] })
9268
9268
  ] }),
Binary file
@@ -27,7 +27,7 @@ module Profiler
27
27
  name = params[:name].to_s
28
28
  entry = Profiler.slave_registry.heartbeat(name)
29
29
  if entry.nil?
30
- render json: { error: "Unknown slave — please re-register" }, status: :not_found
30
+ render json: { error: "Unknown slave - please re-register" }, status: :not_found
31
31
  else
32
32
  render json: { ok: true }
33
33
  end
@@ -22,6 +22,11 @@ module Profiler
22
22
  serve("profiler.css", "text/css")
23
23
  end
24
24
 
25
+ def icon
26
+ expires_in 1.day, public: true
27
+ send_file Profiler::Engine.root.join("app", "assets", "images", "icon.png"), type: "image/png", disposition: "inline"
28
+ end
29
+
25
30
  private
26
31
 
27
32
  def serve(name, content_type)
@@ -83,13 +83,13 @@ module Profiler
83
83
  # Sets @resolved_storage to the storage that owns the profile so that
84
84
  # secondary lookups (child jobs, parent, AJAX) go to the same node.
85
85
  def resolve_profile(token)
86
- # Local storage — fast path, covers master-owned profiles
86
+ # Local storage, fast path, covers master-owned profiles
87
87
  if (profile = Profiler.storage.load(token))
88
88
  @resolved_storage = Profiler.storage
89
89
  return profile
90
90
  end
91
91
 
92
- # Cache hit — skip fan-out if we already know which slave has this token
92
+ # Cache hit, skip fan-out if we already know which slave has this token
93
93
  if (cached_slave = Profiler.token_cache.fetch(token))
94
94
  entry = Profiler.slave_registry.all.find { |e| e[:name] == cached_slave }
95
95
  if entry && entry[:status] == "online"
@@ -2,6 +2,7 @@
2
2
  <html>
3
3
  <head>
4
4
  <title>Rails Profiler</title>
5
+ <link rel="icon" type="image/png" href="/_profiler/assets/icon.png">
5
6
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
7
  <%= csrf_meta_tags %>
7
8
  <%= csp_meta_tag %>
@@ -2,6 +2,7 @@
2
2
  <html data-theme="<%= params[:theme].presence&.then { |t| %w[light dark].include?(t) ? t : 'dark' } || 'dark' %>">
3
3
  <head>
4
4
  <title>Rails Profiler</title>
5
+ <link rel="icon" type="image/png" href="/_profiler/assets/icon.png">
5
6
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
7
  <%= csrf_meta_tags %>
7
8
  <%= csp_meta_tag %>
@@ -11,7 +12,7 @@
11
12
  <script src="/_profiler/assets/profiler.js" defer></script>
12
13
 
13
14
  <style>
14
- /* Embedded overrides — tighter layout, no page chrome */
15
+ /* Embedded overrides, tighter layout, no page chrome */
15
16
  body { overflow-y: auto; }
16
17
  .container { padding: 16px 20px; }
17
18
  </style>
data/config/routes.rb CHANGED
@@ -26,6 +26,7 @@ Profiler::Engine.routes.draw do
26
26
  get "assets/profiler-toolbar.js", to: "assets#toolbar_js"
27
27
  get "assets/profiler.js", to: "assets#main_js"
28
28
  get "assets/profiler.css", to: "assets#main_css"
29
+ get "assets/icon.png", to: "assets#icon"
29
30
 
30
31
  get "test_runner", to: "test_runner#index"
31
32
 
@@ -173,7 +173,7 @@ module Profiler
173
173
  app_root = app_root_path
174
174
 
175
175
  # Partition frames: app frames vs GC frame
176
- # Exclude Ruby meta-frames (<main>, <class:Foo>, #<Class:0x...> generated templates) — noise
176
+ # Exclude Ruby meta-frames (<main>, <class:Foo>, #<Class:0x...> generated templates), noise
177
177
  gc_frame_ids = frames_meta.each_with_object(Set.new) { |(id, f), s| s << id if f[:name] == GC_FRAME_NAME }
178
178
  app_frame_ids = frames_meta.each_with_object(Set.new) do |(id, f), s|
179
179
  s << id if f[:file]&.start_with?(app_root) && !f[:name].to_s.match?(/\A[<#]/)
@@ -310,7 +310,7 @@ module Profiler
310
310
  @loop_warnings << {
311
311
  key: key,
312
312
  count: count,
313
- message: "#{key} was called #{count} times in a single request — possible send loop"
313
+ message: "#{key} was called #{count} times in a single request - possible send loop"
314
314
  }
315
315
  end
316
316
  end
@@ -39,7 +39,7 @@ module Profiler
39
39
  def toolbar_summary
40
40
  data = panel_content
41
41
  matched = data[:matched]
42
- { text: matched ? matched[:pattern] : "\u2014", color: "blue" }
42
+ { text: matched ? matched[:pattern] : "-", color: "blue" }
43
43
  end
44
44
  end
45
45
  end
@@ -33,7 +33,7 @@ module Profiler
33
33
  end
34
34
 
35
35
  def rack_app
36
- # Memoized at class level — survives route reloads in development
36
+ # Memoized at class level, survives route reloads in development
37
37
  @rack_app ||= lambda do |env|
38
38
  HttpGuard.call(env) || instance.http_transport.handle_request(Rack::Request.new(env))
39
39
  end
@@ -230,7 +230,7 @@ module Profiler
230
230
  ),
231
231
  define_tool(
232
232
  name: "get_profile_mailers",
233
- description: "Get detailed mailer activity for a profile: delivered emails, errors, and queued deliveries — including email bodies when capture_mail_body is enabled.",
233
+ description: "Get detailed mailer activity for a profile: delivered emails, errors, and queued deliveries - including email bodies when capture_mail_body is enabled.",
234
234
  input_schema: {
235
235
  properties: {
236
236
  token: { type: "string", description: "Profile token, or 'latest' for the most recent profile (required)" },
@@ -33,7 +33,7 @@ module Profiler
33
33
  data = Profiler::ExplainRunner.run(token, query_index.to_i)
34
34
 
35
35
  lines = []
36
- lines << "# EXPLAIN ANALYZE — Query ##{query_index}"
36
+ lines << "# EXPLAIN ANALYZE - Query ##{query_index}"
37
37
  lines << "Adapter: `#{data[:adapter]}`\n"
38
38
 
39
39
  if data[:format] == "json"
@@ -313,7 +313,7 @@ module Profiler
313
313
  if view_data["views"] && !view_data["views"].empty?
314
314
  lines << "### Templates"
315
315
  view_data["views"].each do |view|
316
- lines << "- `#{view['identifier']}` — #{view['duration'].round(2)} ms"
316
+ lines << "- `#{view['identifier']}` - #{view['duration'].round(2)} ms"
317
317
  end
318
318
  lines << ""
319
319
  end
@@ -321,7 +321,7 @@ module Profiler
321
321
  if view_data["partials"] && !view_data["partials"].empty?
322
322
  lines << "### Partials"
323
323
  view_data["partials"].each do |partial|
324
- lines << "- `#{partial['identifier']}` — #{partial['duration'].round(2)} ms"
324
+ lines << "- `#{partial['identifier']}` - #{partial['duration'].round(2)} ms"
325
325
  end
326
326
  lines << ""
327
327
  end
@@ -343,7 +343,7 @@ module Profiler
343
343
  lines << "### Cache Reads"
344
344
  cache_data["reads"].each do |op|
345
345
  hit_label = op["hit"] ? "HIT" : "MISS"
346
- lines << "- [#{hit_label}] `#{op['key']}` — #{op['duration'].round(2)} ms"
346
+ lines << "- [#{hit_label}] `#{op['key']}` - #{op['duration'].round(2)} ms"
347
347
  end
348
348
  lines << ""
349
349
  end
@@ -351,7 +351,7 @@ module Profiler
351
351
  if cache_data["writes"] && !cache_data["writes"].empty?
352
352
  lines << "### Cache Writes"
353
353
  cache_data["writes"].each do |op|
354
- lines << "- `#{op['key']}` — #{op['duration'].round(2)} ms"
354
+ lines << "- `#{op['key']}` - #{op['duration'].round(2)} ms"
355
355
  end
356
356
  lines << ""
357
357
  end
@@ -359,7 +359,7 @@ module Profiler
359
359
  if cache_data["deletes"] && !cache_data["deletes"].empty?
360
360
  lines << "### Cache Deletes"
361
361
  cache_data["deletes"].each do |op|
362
- lines << "- `#{op['key']}` — #{op['duration'].round(2)} ms"
362
+ lines << "- `#{op['key']}` - #{op['duration'].round(2)} ms"
363
363
  end
364
364
  lines << ""
365
365
  end
@@ -378,7 +378,7 @@ module Profiler
378
378
  if ajax_data["requests"] && !ajax_data["requests"].empty?
379
379
  lines << "### Request List"
380
380
  ajax_data["requests"].each do |req|
381
- lines << "- **#{req['method']} #{req['path']}** — #{req['status']} — #{req['duration'].round(2)} ms (token: #{req['token']})"
381
+ lines << "- **#{req['method']} #{req['path']}** - #{req['status']} - #{req['duration'].round(2)} ms (token: #{req['token']})"
382
382
  end
383
383
  lines << ""
384
384
  end
@@ -402,7 +402,7 @@ module Profiler
402
402
  http_data["requests"].each_with_index do |req, index|
403
403
  flag = req["duration"] >= threshold ? " [SLOW]" : ""
404
404
  err = req["status"] >= 400 || req["status"] == 0 ? " [ERROR]" : ""
405
- lines << "\n**#{index + 1}. #{req['method']} #{req['url']}** — #{req['status'] == 0 ? 'error' : req['status']} — #{req['duration'].round(2)} ms#{flag}#{err}"
405
+ lines << "\n**#{index + 1}. #{req['method']} #{req['url']}** - #{req['status'] == 0 ? 'error' : req['status']} - #{req['duration'].round(2)} ms#{flag}#{err}"
406
406
 
407
407
  if req["request_body"] && !req["request_body"].empty?
408
408
  lines << "**Request Body:**"
@@ -143,7 +143,7 @@ module Profiler
143
143
 
144
144
  if timed_out
145
145
  lines << ""
146
- lines << "> ⚠ Timed out — run is still in progress. Use run ID `#{run.id}` to check later."
146
+ lines << "> ⚠ Timed out - run is still in progress. Use run ID `#{run.id}` to check later."
147
147
  end
148
148
 
149
149
  lines << "\n## Output (last #{output.length} chars)"
@@ -174,7 +174,7 @@ module Profiler
174
174
  @nonce ? " nonce=\"#{ERB::Util.html_escape(@nonce)}\"" : ""
175
175
  end
176
176
 
177
- # Thermal design system — self-contained CSS for the injected toolbar.
177
+ # Thermal design system, self-contained CSS for the injected toolbar.
178
178
  # Variables are defined on #profiler-toolbar to avoid polluting the host app.
179
179
  def toolbar_styles
180
180
  <<~'CSS'
@@ -278,7 +278,7 @@ module Profiler
278
278
  transition: color var(--pf-tf), background var(--pf-tf);
279
279
  cursor: default;
280
280
  }
281
- /* Amber bottom indicator — springy easing */
281
+ /* Amber bottom indicator, springy easing */
282
282
  #profiler-toolbar .profiler-toolbar-item::after {
283
283
  content: '';
284
284
  position: absolute;
@@ -117,7 +117,7 @@ module Profiler
117
117
  end
118
118
 
119
119
  initializer "profiler.start_cluster_client" do
120
- # Check slave? inside on_load — the app's own initializers (config/initializers/profiler.rb)
120
+ # Check slave? inside on_load, the app's own initializers (config/initializers/profiler.rb)
121
121
  # set master_url AFTER railtie initializers run, so the check must happen after_initialize.
122
122
  ActiveSupport.on_load(:after_initialize) do
123
123
  next unless Profiler.configuration.enabled
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Profiler
4
- VERSION = "0.34.6"
4
+ VERSION = "0.35.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.34.6
4
+ version: 0.35.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-10-16 00:00:00.000000000 Z
11
+ date: 2026-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -115,6 +115,7 @@ files:
115
115
  - app/assets/builds/profiler-toolbar.js
116
116
  - app/assets/builds/profiler.css
117
117
  - app/assets/builds/profiler.js
118
+ - app/assets/images/icon.png
118
119
  - app/assets/javascript/profiler-ajax-interceptor.js
119
120
  - app/controllers/profiler/api/ajax_controller.rb
120
121
  - app/controllers/profiler/api/cluster_controller.rb