roundhouse_ui 0.2.0 → 0.4.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 46c3254f820732cc242a9bdc774b2361dfadef7b2b1256f149d3d69c07f1be1b
4
- data.tar.gz: 7037fdc482c7d515bd3168754f445ed046d0f26e53340014a29dd2c0591ae9d0
3
+ metadata.gz: 30b9ee99930148d47a69b86e7a4c079231f8a2504a9a24aaa17a625e39e96429
4
+ data.tar.gz: 333e7892afa4690288d35c671249f3fad6c3a19e304f9b4952346fe0f873a9fd
5
5
  SHA512:
6
- metadata.gz: 80c1574f244bf2f4f4782811aa3688029421d6e06079aa693690da97f056b80a019ced7306ba7ee5d0e83122d53401a1135070ba2ea1a63c30a2491a4e1b7e83
7
- data.tar.gz: 28da55ba898c4eabfb2bca7a33f2f3d6524fdfde833ca2d3cf792ce37fd3d3b2720c5a98a27f2a73ed79424019f2339a09423853a14604920c9e0f89230acfac
6
+ metadata.gz: 9391d5af40b1fa0db7de47fab56009cf6d11bb80f2c16d4d65bc4b7b2b376eb7ac7cff595dfe0ebdaa0df401436f811cfc1f9a3d52028aef491c7d5821b0e322
7
+ data.tar.gz: 8099ad548223bbb7d91f13a51b86e62010eb269f446d380c95fa4a416a1f4f95979631e9cbdd5115973bfa98d363ab04f5ef9d1801462a51ce215a5a28f57abe
data/README.md CHANGED
@@ -79,9 +79,17 @@ RoundhouseUi.configure do |c|
79
79
 
80
80
  # Where queue snapshots are stored (default: Redis). Swap for a file/S3 store.
81
81
  # c.snapshot_store = MyS3SnapshotStore.new
82
+
83
+ # Fold sidekiq-failures' `failed` set into the Errors view (see below).
84
+ # No-op unless the sidekiq-failures gem is loaded. Default: off.
85
+ c.show_sidekiq_failures = true
82
86
  end
83
87
  ```
84
88
 
89
+ Every option is independent and has a safe default — set only what you need.
90
+ `read_only`, `allow_job_editing`, and `show_sidekiq_failures` default to `false`;
91
+ `redact_args` to `[]`; `observability` to a no-op adapter; `snapshot_store` to Redis.
92
+
85
93
  ## Pausing queues
86
94
 
87
95
  Pause/resume is pure OSS. To make a pause actually stop a queue from being worked,
@@ -99,6 +107,27 @@ all of Sidekiq's weighting/ordering. Until it's installed, the Queues page recor
99
107
  but **warns that they aren't enforced** (worker and web are separate processes, so
100
108
  Roundhouse detects whether a fetcher has reported in).
101
109
 
110
+ > ⚠️ **Sidekiq Pro/Enterprise users:** super_fetch / reliable fetch sets its own
111
+ > `fetch_class`, and Sidekiq allows only one. Installing `RoundhouseUi::Fetch` would
112
+ > **replace reliable fetch and lose its crash-recovery guarantees** — don't. On those
113
+ > tiers, leave the fetch strategy out; pause/resume stays inert and the UI says so.
114
+
115
+ ## Surfacing sidekiq-failures
116
+
117
+ If you use [`sidekiq-failures`](https://github.com/mhfs/sidekiq-failures), failures it
118
+ records live in their own Redis set — which Roundhouse doesn't read by default. Jobs with
119
+ `retry: false` are the common case: they fail, get recorded there, but never enter the
120
+ retry or dead sets, so they're invisible in Roundhouse. Opt in to fold them into the
121
+ grouped **Errors** view:
122
+
123
+ ```ruby
124
+ # config/initializers/roundhouse.rb
125
+ RoundhouseUi.configure { |c| c.show_sidekiq_failures = true }
126
+ ```
127
+
128
+ It's a no-op unless `sidekiq-failures` is loaded. Failures appear in **Errors** grouped by
129
+ job class + error (not yet as an individual-job list with per-row actions).
130
+
102
131
  ## Cancelling jobs
103
132
 
104
133
  Cancellation is cooperative — Ruby can't safely kill a running thread. Install the
@@ -18,7 +18,7 @@ module RoundhouseUi
18
18
  scanned = 0
19
19
  truncated = false
20
20
 
21
- { "retry" => Sidekiq::RetrySet.new, "dead" => Sidekiq::DeadSet.new }.each do |source, set|
21
+ sources.each do |source, set|
22
22
  set.each do |entry|
23
23
  scanned += 1
24
24
  if scanned > SCAN_LIMIT
@@ -35,6 +35,17 @@ module RoundhouseUi
35
35
  [ list, scanned, truncated ]
36
36
  end
37
37
 
38
+ # Sidekiq's native sets, plus the sidekiq-failures `failed` set when opted in
39
+ # and that gem is loaded. Its FailureSet is a Sidekiq::JobSet, so it iterates
40
+ # exactly like the others — no special-casing in the aggregation above.
41
+ def sources
42
+ sets = { "retry" => Sidekiq::RetrySet.new, "dead" => Sidekiq::DeadSet.new }
43
+ if RoundhouseUi.show_sidekiq_failures && defined?(Sidekiq::Failures::FailureSet)
44
+ sets["failed"] = Sidekiq::Failures::FailureSet.new
45
+ end
46
+ sets
47
+ end
48
+
38
49
  def record(groups, source, entry)
39
50
  error = entry.item["error_class"] || "UnknownError"
40
51
  group = (groups["#{entry.klass}|#{error}"] ||= {
@@ -46,6 +46,8 @@
46
46
  .rh-badge.crit { background:rgba(230,106,96,.16); color:var(--crit); }
47
47
 
48
48
  .rh-main { padding:22px 30px 70px; max-width:1180px; }
49
+ :root[data-width="full"] .rh-main { max-width:none; }
50
+ .rh-iconbtn.is-on { color:var(--text); border-color:var(--accent); }
49
51
  .rh-top { display:flex; align-items:center; gap:14px; margin-bottom:22px; }
50
52
  .rh-top h1 { font-size:21px; font-weight:650; letter-spacing:-.02em; margin:0; }
51
53
  .rh-crumb { color:var(--faint); font-size:13px; }
@@ -148,7 +150,7 @@
148
150
  </style>
149
151
  <%# Apply the saved theme before first paint so it persists across navigations with no flash. %>
150
152
  <script nonce="<%= content_nonce %>">
151
- (function () { try { var t = localStorage.getItem("rh-theme"); if (t) document.documentElement.setAttribute("data-theme", t); } catch (e) {} })();
153
+ (function () { try { var t = localStorage.getItem("rh-theme"); if (t) document.documentElement.setAttribute("data-theme", t); var w = localStorage.getItem("rh-width"); if (w) document.documentElement.setAttribute("data-width", w); } catch (e) {} })();
152
154
  </script>
153
155
  <%# Turbo Drive: link navigation without full-page reloads (kills the flicker). %>
154
156
  <script src="<%= turbo_js_path %>"></script>
@@ -212,6 +214,7 @@
212
214
  }
213
215
  function startOnce() { if (started) return; started = true; poll(); setInterval(poll, 2500); }
214
216
  function syncTheme() { var b = document.getElementById("rh-theme"); if (b) b.textContent = document.documentElement.getAttribute("data-theme") === "light" ? "☀" : "☾"; }
217
+ function syncWidth() { var b = document.getElementById("rh-width"); if (b) b.classList.toggle("is-on", document.documentElement.getAttribute("data-width") === "full"); }
215
218
 
216
219
  // The sidebar is turbo-permanent (so its badges don't flicker), so we move
217
220
  // the active-nav highlight ourselves on each navigation.
@@ -233,6 +236,14 @@
233
236
  try { localStorage.setItem("rh-theme", next); } catch (_) {}
234
237
  syncTheme();
235
238
  });
239
+ // width toggle (compact 1180px ⟷ full) — same persistence pattern as theme
240
+ document.addEventListener("click", function (e) {
241
+ var b = e.target.closest && e.target.closest("#rh-width"); if (!b) return;
242
+ var next = document.documentElement.getAttribute("data-width") === "full" ? "compact" : "full";
243
+ document.documentElement.setAttribute("data-width", next);
244
+ try { localStorage.setItem("rh-width", next); } catch (_) {}
245
+ syncWidth();
246
+ });
236
247
  // command palette (⌘K)
237
248
  var CMDS = [
238
249
  { label: "Dashboard", path: "<%= root_path %>", icon: "▦" },
@@ -249,7 +260,8 @@
249
260
  { label: "Snapshots", path: "<%= snapshots_path %>", icon: "⛁" },
250
261
  { label: "Audit log", path: "<%= audit_log_path %>", icon: "☷" },
251
262
  <% if RoundhouseUi.allow_job_editing %>{ label: "Enqueue job", path: "<%= new_job_path %>", icon: "+" },<% end %>
252
- { label: "Toggle theme", action: "theme", icon: "◐" }
263
+ { label: "Toggle theme", action: "theme", icon: "◐" },
264
+ { label: "Toggle full width", action: "width", icon: "⟷" }
253
265
  ];
254
266
  var palSel = 0, palFiltered = [];
255
267
  function palEl() { return document.getElementById("rh-palette"); }
@@ -280,6 +292,7 @@
280
292
  function palRun(c) {
281
293
  palClose();
282
294
  if (c.action === "theme") { var b = document.getElementById("rh-theme"); if (b) b.click(); return; }
295
+ if (c.action === "width") { var wb = document.getElementById("rh-width"); if (wb) wb.click(); return; }
283
296
  if (c.path) { window.Turbo ? Turbo.visit(c.path) : (location.href = c.path); }
284
297
  }
285
298
  function palOpen() { var p = palEl(), i = palInputEl(); if (!p || !i) return; p.hidden = false; i.value = ""; palSel = 0; palRender(); i.focus(); }
@@ -299,8 +312,8 @@
299
312
  else if (e.key === "Enter") { e.preventDefault(); if (palFiltered[palSel]) palRun(palFiltered[palSel]); }
300
313
  });
301
314
 
302
- document.addEventListener("turbo:load", function () { startOnce(); syncTheme(); setActiveNav(); draw(); });
303
- document.addEventListener("DOMContentLoaded", function () { startOnce(); syncTheme(); setActiveNav(); });
315
+ document.addEventListener("turbo:load", function () { startOnce(); syncTheme(); syncWidth(); setActiveNav(); draw(); });
316
+ document.addEventListener("DOMContentLoaded", function () { startOnce(); syncTheme(); syncWidth(); setActiveNav(); });
304
317
  document.addEventListener("visibilitychange", function () { if (!document.hidden) poll(); });
305
318
  })();
306
319
  </script>
@@ -345,6 +358,7 @@
345
358
  <span class="rh-live"><span class="rh-dot"></span> live · <span class="num" data-stat="rate">—</span>/min</span>
346
359
  <% if RoundhouseUi.read_only %><span class="rh-ro">read-only</span><% end %>
347
360
  <button class="rh-kbd" id="rh-palette-open" type="button" title="Command palette (⌘K)">⌘K</button>
361
+ <button class="rh-iconbtn" id="rh-width" type="button" title="Toggle full width" aria-label="Toggle full width">⟷</button>
348
362
  <button class="rh-iconbtn" id="rh-theme" type="button" title="Toggle theme" aria-label="Toggle theme">☾</button>
349
363
  </header>
350
364
 
@@ -1,3 +1,3 @@
1
1
  module RoundhouseUi
2
- VERSION = "0.2.0"
2
+ VERSION = "0.4.0"
3
3
  end
data/lib/roundhouse_ui.rb CHANGED
@@ -51,6 +51,12 @@ module RoundhouseUi
51
51
  # e.g. RoundhouseUi.redact_args = %w[password token secret]. Default: none.
52
52
  attr_accessor :redact_args
53
53
 
54
+ # Opt-in: fold failures recorded by the `sidekiq-failures` gem (its `failed`
55
+ # sorted set) into the grouped Errors view. Off by default, and a no-op
56
+ # unless sidekiq-failures is loaded. Jobs with `retry: false` never enter
57
+ # Sidekiq's retry/dead sets, so this is the only way to surface them here.
58
+ attr_accessor :show_sidekiq_failures
59
+
54
60
  # Configure in an initializer:
55
61
  #
56
62
  # RoundhouseUi.configure do |c|
@@ -70,4 +76,5 @@ module RoundhouseUi
70
76
  self.read_only = false
71
77
  self.allow_job_editing = false
72
78
  self.redact_args = []
79
+ self.show_sidekiq_failures = false
73
80
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: roundhouse_ui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - R.J. Robinson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-06-29 00:00:00.000000000 Z
11
+ date: 2026-06-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails