sktop 0.1.6 → 0.2.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/lib/sktop/cli.rb +77 -0
- data/lib/sktop/display.rb +111 -9
- data/lib/sktop/job_actions.rb +75 -0
- data/lib/sktop/stats_collector.rb +120 -0
- data/lib/sktop/version.rb +4 -1
- data/lib/sktop.rb +21 -0
- 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: d1e0891fc2a69763f5784c7cc96fc69103d14362aa67a08b82b6c13aa578985b
|
|
4
|
+
data.tar.gz: fea7b7284c37d666516d42c2bcc97e0f6b3db4cbb53628e2e3b35c6521c44845
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 59b4378e2dae18d69bcbd9c3a6492ff0bcad4d830ad9d0c893fa0b9f70f6845d87b6652dcc850d7a02efaea10c00343203d8b495c8eded6318a9b6c648e1fb5e
|
|
7
|
+
data.tar.gz: eb54c39df9aa1ec70bcb719b11a7ab0aac88970586345793e890b67dda9f753f207e8f7e5ea0923153118b66c59952f60ff46309e28da281e2bc4f87cbd48c4a
|
data/lib/sktop/cli.rb
CHANGED
|
@@ -7,7 +7,18 @@ require "io/console"
|
|
|
7
7
|
require "io/wait"
|
|
8
8
|
|
|
9
9
|
module Sktop
|
|
10
|
+
# Command-line interface for the Sktop Sidekiq monitor.
|
|
11
|
+
# Handles argument parsing, Redis configuration, and the main event loop.
|
|
12
|
+
#
|
|
13
|
+
# @example Basic usage
|
|
14
|
+
# Sktop::CLI.new.run
|
|
15
|
+
#
|
|
16
|
+
# @example With custom arguments
|
|
17
|
+
# Sktop::CLI.new(["-r", "redis://myhost:6379/1", "-q"]).run
|
|
10
18
|
class CLI
|
|
19
|
+
# Create a new CLI instance.
|
|
20
|
+
#
|
|
21
|
+
# @param args [Array<String>] command-line arguments (defaults to ARGV)
|
|
11
22
|
def initialize(args = ARGV)
|
|
12
23
|
@args = args
|
|
13
24
|
@options = {
|
|
@@ -20,6 +31,11 @@ module Sktop
|
|
|
20
31
|
@running = true
|
|
21
32
|
end
|
|
22
33
|
|
|
34
|
+
# Run the CLI application.
|
|
35
|
+
# Parses options, configures Redis, and starts the TUI.
|
|
36
|
+
#
|
|
37
|
+
# @return [void]
|
|
38
|
+
# @raise [SystemExit] on connection errors or interrupts
|
|
23
39
|
def run
|
|
24
40
|
parse_options!
|
|
25
41
|
configure_redis
|
|
@@ -39,6 +55,10 @@ module Sktop
|
|
|
39
55
|
exit 1
|
|
40
56
|
end
|
|
41
57
|
|
|
58
|
+
# Gracefully shutdown the application.
|
|
59
|
+
# Restores terminal state and shows the cursor.
|
|
60
|
+
#
|
|
61
|
+
# @return [void]
|
|
42
62
|
def shutdown
|
|
43
63
|
@running = false
|
|
44
64
|
print "\e[?25h" # Show cursor
|
|
@@ -48,6 +68,9 @@ module Sktop
|
|
|
48
68
|
|
|
49
69
|
private
|
|
50
70
|
|
|
71
|
+
# Parse command-line options and populate @options hash.
|
|
72
|
+
# @return [void]
|
|
73
|
+
# @api private
|
|
51
74
|
def parse_options!
|
|
52
75
|
parser = OptionParser.new do |opts|
|
|
53
76
|
opts.banner = "Usage: sktop [options]"
|
|
@@ -117,6 +140,9 @@ module Sktop
|
|
|
117
140
|
parser.parse!(@args)
|
|
118
141
|
end
|
|
119
142
|
|
|
143
|
+
# Configure Sidekiq's Redis connection from CLI options.
|
|
144
|
+
# @return [void]
|
|
145
|
+
# @api private
|
|
120
146
|
def configure_redis
|
|
121
147
|
Sidekiq.configure_client do |config|
|
|
122
148
|
if @options[:namespace]
|
|
@@ -130,6 +156,10 @@ module Sktop
|
|
|
130
156
|
end
|
|
131
157
|
end
|
|
132
158
|
|
|
159
|
+
# Start the main TUI loop with background data fetching.
|
|
160
|
+
# Handles both one-shot mode and interactive auto-refresh mode.
|
|
161
|
+
# @return [void]
|
|
162
|
+
# @api private
|
|
133
163
|
def start_watcher
|
|
134
164
|
collector = StatsCollector.new
|
|
135
165
|
@display = Display.new
|
|
@@ -267,6 +297,10 @@ module Sktop
|
|
|
267
297
|
end
|
|
268
298
|
end
|
|
269
299
|
|
|
300
|
+
# Render the display from the cached data snapshot.
|
|
301
|
+
# Shows loading screen if no data is available yet.
|
|
302
|
+
# @return [void]
|
|
303
|
+
# @api private
|
|
270
304
|
def render_cached_data
|
|
271
305
|
data = @data_mutex.synchronize { @cached_data }
|
|
272
306
|
if data
|
|
@@ -276,6 +310,13 @@ module Sktop
|
|
|
276
310
|
end
|
|
277
311
|
end
|
|
278
312
|
|
|
313
|
+
# Handle a keyboard input event.
|
|
314
|
+
# Routes to appropriate view or action handler.
|
|
315
|
+
#
|
|
316
|
+
# @param key [String] the key character pressed
|
|
317
|
+
# @param stdin [IO] the stdin IO object for reading escape sequences
|
|
318
|
+
# @return [void]
|
|
319
|
+
# @api private
|
|
279
320
|
def handle_keypress(key, stdin)
|
|
280
321
|
case key
|
|
281
322
|
when 'q', 'Q'
|
|
@@ -336,6 +377,10 @@ module Sktop
|
|
|
336
377
|
end
|
|
337
378
|
end
|
|
338
379
|
|
|
380
|
+
# Handle Ctrl+R to retry the selected job.
|
|
381
|
+
# Only works in retries and dead views.
|
|
382
|
+
# @return [void]
|
|
383
|
+
# @api private
|
|
339
384
|
def handle_retry_action
|
|
340
385
|
return unless [:retries, :dead].include?(@display.current_view)
|
|
341
386
|
|
|
@@ -375,6 +420,10 @@ module Sktop
|
|
|
375
420
|
end
|
|
376
421
|
end
|
|
377
422
|
|
|
423
|
+
# Handle Ctrl+X to delete the selected job.
|
|
424
|
+
# Works in retries, dead, and queue_jobs views.
|
|
425
|
+
# @return [void]
|
|
426
|
+
# @api private
|
|
378
427
|
def handle_delete_action
|
|
379
428
|
# Handle queue_jobs view separately
|
|
380
429
|
if @display.current_view == :queue_jobs
|
|
@@ -420,6 +469,10 @@ module Sktop
|
|
|
420
469
|
end
|
|
421
470
|
end
|
|
422
471
|
|
|
472
|
+
# Handle Alt+R to retry all jobs in the current view.
|
|
473
|
+
# Only works in retries and dead views.
|
|
474
|
+
# @return [void]
|
|
475
|
+
# @api private
|
|
423
476
|
def handle_retry_all_action
|
|
424
477
|
return unless [:retries, :dead].include?(@display.current_view)
|
|
425
478
|
|
|
@@ -433,6 +486,10 @@ module Sktop
|
|
|
433
486
|
end
|
|
434
487
|
end
|
|
435
488
|
|
|
489
|
+
# Handle Alt+X to delete all jobs in the current view.
|
|
490
|
+
# Only works in retries and dead views.
|
|
491
|
+
# @return [void]
|
|
492
|
+
# @api private
|
|
436
493
|
def handle_delete_all_action
|
|
437
494
|
return unless [:retries, :dead].include?(@display.current_view)
|
|
438
495
|
|
|
@@ -446,6 +503,10 @@ module Sktop
|
|
|
446
503
|
end
|
|
447
504
|
end
|
|
448
505
|
|
|
506
|
+
# Handle Ctrl+Q to quiet the selected Sidekiq process.
|
|
507
|
+
# Only works in processes view.
|
|
508
|
+
# @return [void]
|
|
509
|
+
# @api private
|
|
449
510
|
def handle_quiet_process_action
|
|
450
511
|
return unless @display.current_view == :processes
|
|
451
512
|
|
|
@@ -483,6 +544,10 @@ module Sktop
|
|
|
483
544
|
end
|
|
484
545
|
end
|
|
485
546
|
|
|
547
|
+
# Handle Ctrl+K to stop/kill the selected Sidekiq process.
|
|
548
|
+
# Only works in processes view.
|
|
549
|
+
# @return [void]
|
|
550
|
+
# @api private
|
|
486
551
|
def handle_stop_process_action
|
|
487
552
|
return unless @display.current_view == :processes
|
|
488
553
|
|
|
@@ -520,6 +585,10 @@ module Sktop
|
|
|
520
585
|
end
|
|
521
586
|
end
|
|
522
587
|
|
|
588
|
+
# Handle Enter key to view jobs in the selected queue.
|
|
589
|
+
# Only works in queues view.
|
|
590
|
+
# @return [void]
|
|
591
|
+
# @api private
|
|
523
592
|
def handle_enter_action
|
|
524
593
|
return unless @display.current_view == :queues
|
|
525
594
|
|
|
@@ -563,6 +632,10 @@ module Sktop
|
|
|
563
632
|
end
|
|
564
633
|
end
|
|
565
634
|
|
|
635
|
+
# Handle Escape key to go back or return to main view.
|
|
636
|
+
# From queue_jobs returns to queues, otherwise returns to main.
|
|
637
|
+
# @return [void]
|
|
638
|
+
# @api private
|
|
566
639
|
def handle_escape_action
|
|
567
640
|
if @display.current_view == :queue_jobs
|
|
568
641
|
# Go back to queues view
|
|
@@ -574,6 +647,10 @@ module Sktop
|
|
|
574
647
|
end
|
|
575
648
|
end
|
|
576
649
|
|
|
650
|
+
# Handle Ctrl+X to delete a job from the current queue.
|
|
651
|
+
# Only works in queue_jobs view.
|
|
652
|
+
# @return [void]
|
|
653
|
+
# @api private
|
|
577
654
|
def handle_delete_queue_job_action
|
|
578
655
|
return unless @display.current_view == :queue_jobs
|
|
579
656
|
|
data/lib/sktop/display.rb
CHANGED
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Sktop
|
|
4
|
+
# Handles all terminal display rendering for the Sktop TUI.
|
|
5
|
+
# Manages views, scrolling, selection, and ANSI-colored output.
|
|
6
|
+
#
|
|
7
|
+
# @example Basic usage
|
|
8
|
+
# display = Sktop::Display.new
|
|
9
|
+
# display.current_view = :queues
|
|
10
|
+
# display.render(collector)
|
|
11
|
+
#
|
|
12
|
+
# @example With cached data for auto-refresh
|
|
13
|
+
# display = Sktop::Display.new
|
|
14
|
+
# display.render_refresh_from_cache(data_hash)
|
|
4
15
|
class Display
|
|
16
|
+
# @return [Symbol] the current view (:main, :queues, :processes, :workers, :retries, :scheduled, :dead, :queue_jobs)
|
|
17
|
+
# @return [Symbol] the connection status (:connecting, :connected, :updating, :error)
|
|
18
|
+
# @return [Time, nil] the timestamp of the last successful data update
|
|
5
19
|
attr_accessor :current_view, :connection_status, :last_update
|
|
6
20
|
|
|
21
|
+
# Create a new Display instance with default settings.
|
|
7
22
|
def initialize
|
|
8
23
|
@pastel = Pastel.new
|
|
9
24
|
@cursor = TTY::Cursor
|
|
@@ -18,16 +33,23 @@ module Sktop
|
|
|
18
33
|
@selected_queue = nil # Track which queue is being viewed in queue_jobs view
|
|
19
34
|
end
|
|
20
35
|
|
|
36
|
+
# @return [String, nil] the name of the currently selected queue (for queue_jobs view)
|
|
21
37
|
attr_accessor :selected_queue
|
|
22
38
|
|
|
39
|
+
# Scroll the current view up by one line.
|
|
40
|
+
# @return [Integer] the new scroll offset
|
|
23
41
|
def scroll_up
|
|
24
42
|
@scroll_offsets[@current_view] = [@scroll_offsets[@current_view] - 1, 0].max
|
|
25
43
|
end
|
|
26
44
|
|
|
45
|
+
# Scroll the current view down by one line.
|
|
46
|
+
# @return [Integer] the new scroll offset
|
|
27
47
|
def scroll_down
|
|
28
48
|
@scroll_offsets[@current_view] += 1
|
|
29
49
|
end
|
|
30
50
|
|
|
51
|
+
# Move selection up in selectable views, or scroll up in non-selectable views.
|
|
52
|
+
# @return [Integer] the new selected index or scroll offset
|
|
31
53
|
def select_up
|
|
32
54
|
if selectable_view?
|
|
33
55
|
@selected_index[@current_view] = [@selected_index[@current_view] - 1, 0].max
|
|
@@ -36,6 +58,8 @@ module Sktop
|
|
|
36
58
|
end
|
|
37
59
|
end
|
|
38
60
|
|
|
61
|
+
# Move selection down in selectable views, or scroll down in non-selectable views.
|
|
62
|
+
# @return [Integer] the new selected index or scroll offset
|
|
39
63
|
def select_down
|
|
40
64
|
if selectable_view?
|
|
41
65
|
@selected_index[@current_view] += 1
|
|
@@ -44,10 +68,15 @@ module Sktop
|
|
|
44
68
|
end
|
|
45
69
|
end
|
|
46
70
|
|
|
71
|
+
# Check if the current view supports item selection.
|
|
72
|
+
# @return [Boolean] true if the view supports selection
|
|
47
73
|
def selectable_view?
|
|
48
74
|
[:queues, :queue_jobs, :processes, :retries, :dead].include?(@current_view)
|
|
49
75
|
end
|
|
50
76
|
|
|
77
|
+
# Move up by one page in the current view.
|
|
78
|
+
# @param page_size [Integer, nil] custom page size (defaults to terminal height - 8)
|
|
79
|
+
# @return [Integer] the new selected index or scroll offset
|
|
51
80
|
def page_up(page_size = nil)
|
|
52
81
|
page_size ||= default_page_size
|
|
53
82
|
if selectable_view?
|
|
@@ -57,6 +86,9 @@ module Sktop
|
|
|
57
86
|
end
|
|
58
87
|
end
|
|
59
88
|
|
|
89
|
+
# Move down by one page in the current view.
|
|
90
|
+
# @param page_size [Integer, nil] custom page size (defaults to terminal height - 8)
|
|
91
|
+
# @return [Integer] the new selected index or scroll offset
|
|
60
92
|
def page_down(page_size = nil)
|
|
61
93
|
page_size ||= default_page_size
|
|
62
94
|
if selectable_view?
|
|
@@ -66,51 +98,77 @@ module Sktop
|
|
|
66
98
|
end
|
|
67
99
|
end
|
|
68
100
|
|
|
101
|
+
# Calculate the default page size based on terminal height.
|
|
102
|
+
# @return [Integer] the page size (minimum 5)
|
|
69
103
|
def default_page_size
|
|
70
104
|
# Use terminal height minus header/footer overhead as page size
|
|
71
105
|
[terminal_height - 8, 5].max
|
|
72
106
|
end
|
|
73
107
|
|
|
108
|
+
# Get the currently selected index for the current view.
|
|
109
|
+
# @return [Integer] the selected index (0-based)
|
|
74
110
|
def selected_index
|
|
75
111
|
@selected_index[@current_view]
|
|
76
112
|
end
|
|
77
113
|
|
|
114
|
+
# Set a temporary status message to display.
|
|
115
|
+
# The message will be shown for approximately 3 seconds.
|
|
116
|
+
# @param message [String] the status message to display
|
|
117
|
+
# @return [void]
|
|
78
118
|
def set_status(message)
|
|
79
119
|
@status_message = message
|
|
80
120
|
@status_time = Time.now
|
|
81
121
|
end
|
|
82
122
|
|
|
123
|
+
# Reset the scroll offset for the current view to zero.
|
|
124
|
+
# @return [Integer] 0
|
|
83
125
|
def reset_scroll
|
|
84
126
|
@scroll_offsets[@current_view] = 0
|
|
85
127
|
end
|
|
86
128
|
|
|
129
|
+
# Set the current view, preserving scroll position.
|
|
130
|
+
# @param view [Symbol] the view to switch to
|
|
131
|
+
# @return [Symbol] the new current view
|
|
87
132
|
def current_view=(view)
|
|
88
133
|
@current_view = view
|
|
89
134
|
# Don't reset scroll when switching views - preserve position
|
|
90
135
|
end
|
|
91
136
|
|
|
137
|
+
# Ordered list of views for cycling with arrow keys.
|
|
138
|
+
# @return [Array<Symbol>] the view order
|
|
92
139
|
VIEW_ORDER = [:main, :queues, :processes, :workers, :retries, :scheduled, :dead].freeze
|
|
93
140
|
|
|
141
|
+
# Switch to the next view in the cycle.
|
|
142
|
+
# @return [Symbol] the new current view
|
|
94
143
|
def next_view
|
|
95
144
|
current_idx = VIEW_ORDER.index(@current_view) || 0
|
|
96
145
|
@current_view = VIEW_ORDER[(current_idx + 1) % VIEW_ORDER.length]
|
|
97
146
|
end
|
|
98
147
|
|
|
148
|
+
# Switch to the previous view in the cycle.
|
|
149
|
+
# @return [Symbol] the new current view
|
|
99
150
|
def previous_view
|
|
100
151
|
current_idx = VIEW_ORDER.index(@current_view) || 0
|
|
101
152
|
@current_view = VIEW_ORDER[(current_idx - 1) % VIEW_ORDER.length]
|
|
102
153
|
end
|
|
103
154
|
|
|
155
|
+
# Reset cursor to top-left and hide it.
|
|
156
|
+
# @return [void]
|
|
104
157
|
def reset_cursor
|
|
105
158
|
print @cursor.move_to(0, 0)
|
|
106
159
|
print @cursor.hide
|
|
107
160
|
$stdout.flush
|
|
108
161
|
end
|
|
109
162
|
|
|
163
|
+
# Show the cursor (call on exit).
|
|
164
|
+
# @return [void]
|
|
110
165
|
def show_cursor
|
|
111
166
|
print @cursor.show
|
|
112
167
|
end
|
|
113
168
|
|
|
169
|
+
# Update the cached terminal size from TTY::Screen.
|
|
170
|
+
# Call this before entering raw mode or when terminal resizes.
|
|
171
|
+
# @return [Array<Integer>, nil] [height, width] or nil if unchanged
|
|
114
172
|
def update_terminal_size
|
|
115
173
|
# Force refresh of terminal size using TTY::Screen
|
|
116
174
|
height = TTY::Screen.height
|
|
@@ -120,16 +178,26 @@ module Sktop
|
|
|
120
178
|
end
|
|
121
179
|
end
|
|
122
180
|
|
|
181
|
+
# Render the current view as a string (for one-shot mode).
|
|
182
|
+
# @param collector [StatsCollector, CachedData] the data source
|
|
183
|
+
# @return [String] the rendered output
|
|
123
184
|
def render(collector)
|
|
124
185
|
content_parts = build_output(collector)
|
|
125
186
|
content_parts.reject { |p| p == :footer }.map(&:to_s).join("\n")
|
|
126
187
|
end
|
|
127
188
|
|
|
189
|
+
# Render the current view directly to the terminal with screen clearing.
|
|
190
|
+
# @param collector [StatsCollector] the data source
|
|
191
|
+
# @return [void]
|
|
128
192
|
def render_refresh(collector)
|
|
129
193
|
content = build_output(collector)
|
|
130
194
|
render_with_overwrite(content)
|
|
131
195
|
end
|
|
132
196
|
|
|
197
|
+
# Render the current view from cached data hash.
|
|
198
|
+
# Updates connection status and last update time.
|
|
199
|
+
# @param data [Hash] cached data hash with :overview, :queues, :processes, etc.
|
|
200
|
+
# @return [void]
|
|
133
201
|
def render_refresh_from_cache(data)
|
|
134
202
|
@connection_status = :connected
|
|
135
203
|
@last_update = Time.now
|
|
@@ -138,6 +206,8 @@ module Sktop
|
|
|
138
206
|
render_with_overwrite(content)
|
|
139
207
|
end
|
|
140
208
|
|
|
209
|
+
# Render a loading screen while waiting for initial data.
|
|
210
|
+
# @return [void]
|
|
141
211
|
def render_loading
|
|
142
212
|
lines = []
|
|
143
213
|
lines << header_bar
|
|
@@ -149,40 +219,58 @@ module Sktop
|
|
|
149
219
|
render_with_overwrite(lines)
|
|
150
220
|
end
|
|
151
221
|
|
|
152
|
-
#
|
|
222
|
+
# Wrapper class to make a cached data hash act like a StatsCollector.
|
|
223
|
+
# Provides the same interface methods that Display expects.
|
|
224
|
+
#
|
|
225
|
+
# @example
|
|
226
|
+
# cached = CachedData.new({ overview: {...}, queues: [...] })
|
|
227
|
+
# cached.overview # => {...}
|
|
153
228
|
class CachedData
|
|
229
|
+
# Create a new CachedData wrapper.
|
|
230
|
+
# @param data [Hash] the raw data hash
|
|
154
231
|
def initialize(data)
|
|
155
232
|
@data = data
|
|
156
233
|
end
|
|
157
234
|
|
|
235
|
+
# @return [Hash] overview statistics
|
|
158
236
|
def overview
|
|
159
237
|
@data[:overview]
|
|
160
238
|
end
|
|
161
239
|
|
|
240
|
+
# @return [Array<Hash>] queue information
|
|
162
241
|
def queues
|
|
163
242
|
@data[:queues]
|
|
164
243
|
end
|
|
165
244
|
|
|
245
|
+
# @return [Array<Hash>] process information
|
|
166
246
|
def processes
|
|
167
247
|
@data[:processes]
|
|
168
248
|
end
|
|
169
249
|
|
|
250
|
+
# @return [Array<Hash>] worker information
|
|
170
251
|
def workers
|
|
171
252
|
@data[:workers]
|
|
172
253
|
end
|
|
173
254
|
|
|
255
|
+
# @param limit [Integer] maximum jobs to return
|
|
256
|
+
# @return [Array<Hash>] retry job information
|
|
174
257
|
def retry_jobs(limit: 50)
|
|
175
258
|
@data[:retry_jobs]&.first(limit) || []
|
|
176
259
|
end
|
|
177
260
|
|
|
261
|
+
# @param limit [Integer] maximum jobs to return
|
|
262
|
+
# @return [Array<Hash>] scheduled job information
|
|
178
263
|
def scheduled_jobs(limit: 50)
|
|
179
264
|
@data[:scheduled_jobs]&.first(limit) || []
|
|
180
265
|
end
|
|
181
266
|
|
|
267
|
+
# @param limit [Integer] maximum jobs to return
|
|
268
|
+
# @return [Array<Hash>] dead job information
|
|
182
269
|
def dead_jobs(limit: 50)
|
|
183
270
|
@data[:dead_jobs]&.first(limit) || []
|
|
184
271
|
end
|
|
185
272
|
|
|
273
|
+
# @return [Array<Hash>] cached queue jobs data
|
|
186
274
|
def queue_jobs_cache
|
|
187
275
|
@data[:queue_jobs] || []
|
|
188
276
|
end
|
|
@@ -264,8 +352,10 @@ module Sktop
|
|
|
264
352
|
lines = []
|
|
265
353
|
lines << header_bar
|
|
266
354
|
lines << ""
|
|
267
|
-
|
|
268
|
-
|
|
355
|
+
stats_meters(collector.overview, collector.processes).each_line(chomp: true) { |l| lines << l }
|
|
356
|
+
lines << ""
|
|
357
|
+
# Calculate available rows: height - header(1) - blank(1) - stats(6) - blank(1) - section(1) - table_header(1) - footer(1) = height - 12
|
|
358
|
+
max_rows = terminal_height - 12
|
|
269
359
|
queues_selectable(collector.queues, max_rows).each_line(chomp: true) { |l| lines << l }
|
|
270
360
|
lines << :footer
|
|
271
361
|
lines
|
|
@@ -275,7 +365,9 @@ module Sktop
|
|
|
275
365
|
lines = []
|
|
276
366
|
lines << header_bar
|
|
277
367
|
lines << ""
|
|
278
|
-
|
|
368
|
+
stats_meters(collector.overview, collector.processes).each_line(chomp: true) { |l| lines << l }
|
|
369
|
+
lines << ""
|
|
370
|
+
max_rows = terminal_height - 12
|
|
279
371
|
jobs = collector.respond_to?(:queue_jobs_cache) ? collector.queue_jobs_cache : []
|
|
280
372
|
queue_jobs_selectable(jobs, max_rows).each_line(chomp: true) { |l| lines << l }
|
|
281
373
|
lines << :footer
|
|
@@ -286,7 +378,9 @@ module Sktop
|
|
|
286
378
|
lines = []
|
|
287
379
|
lines << header_bar
|
|
288
380
|
lines << ""
|
|
289
|
-
|
|
381
|
+
stats_meters(collector.overview, collector.processes).each_line(chomp: true) { |l| lines << l }
|
|
382
|
+
lines << ""
|
|
383
|
+
max_rows = terminal_height - 12
|
|
290
384
|
processes_selectable(collector.processes, max_rows).each_line(chomp: true) { |l| lines << l }
|
|
291
385
|
lines << :footer
|
|
292
386
|
lines
|
|
@@ -296,7 +390,9 @@ module Sktop
|
|
|
296
390
|
lines = []
|
|
297
391
|
lines << header_bar
|
|
298
392
|
lines << ""
|
|
299
|
-
|
|
393
|
+
stats_meters(collector.overview, collector.processes).each_line(chomp: true) { |l| lines << l }
|
|
394
|
+
lines << ""
|
|
395
|
+
max_rows = terminal_height - 12
|
|
300
396
|
workers_section(collector.workers, max_rows: max_rows, scrollable: true).each_line(chomp: true) { |l| lines << l }
|
|
301
397
|
lines << :footer
|
|
302
398
|
lines
|
|
@@ -306,7 +402,9 @@ module Sktop
|
|
|
306
402
|
lines = []
|
|
307
403
|
lines << header_bar
|
|
308
404
|
lines << ""
|
|
309
|
-
|
|
405
|
+
stats_meters(collector.overview, collector.processes).each_line(chomp: true) { |l| lines << l }
|
|
406
|
+
lines << ""
|
|
407
|
+
max_rows = terminal_height - 12
|
|
310
408
|
retries_scrollable(collector.retry_jobs(limit: 500), max_rows).each_line(chomp: true) { |l| lines << l }
|
|
311
409
|
lines << :footer
|
|
312
410
|
lines
|
|
@@ -316,7 +414,9 @@ module Sktop
|
|
|
316
414
|
lines = []
|
|
317
415
|
lines << header_bar
|
|
318
416
|
lines << ""
|
|
319
|
-
|
|
417
|
+
stats_meters(collector.overview, collector.processes).each_line(chomp: true) { |l| lines << l }
|
|
418
|
+
lines << ""
|
|
419
|
+
max_rows = terminal_height - 12
|
|
320
420
|
scheduled_scrollable(collector.scheduled_jobs(limit: 500), max_rows).each_line(chomp: true) { |l| lines << l }
|
|
321
421
|
lines << :footer
|
|
322
422
|
lines
|
|
@@ -326,7 +426,9 @@ module Sktop
|
|
|
326
426
|
lines = []
|
|
327
427
|
lines << header_bar
|
|
328
428
|
lines << ""
|
|
329
|
-
|
|
429
|
+
stats_meters(collector.overview, collector.processes).each_line(chomp: true) { |l| lines << l }
|
|
430
|
+
lines << ""
|
|
431
|
+
max_rows = terminal_height - 12
|
|
330
432
|
dead_scrollable(collector.dead_jobs(limit: 500), max_rows).each_line(chomp: true) { |l| lines << l }
|
|
331
433
|
lines << :footer
|
|
332
434
|
lines
|
data/lib/sktop/job_actions.rb
CHANGED
|
@@ -1,8 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Sktop
|
|
4
|
+
# Provides methods for performing actions on Sidekiq jobs and processes.
|
|
5
|
+
# All methods are class methods and can be called directly on the module.
|
|
6
|
+
#
|
|
7
|
+
# @example Retry a failed job
|
|
8
|
+
# Sktop::JobActions.retry_job("abc123", :retry)
|
|
9
|
+
#
|
|
10
|
+
# @example Delete a dead job
|
|
11
|
+
# Sktop::JobActions.delete_job("abc123", :dead)
|
|
12
|
+
#
|
|
13
|
+
# @example Quiet a Sidekiq process
|
|
14
|
+
# Sktop::JobActions.quiet_process("myhost:12345:abc")
|
|
4
15
|
module JobActions
|
|
5
16
|
class << self
|
|
17
|
+
# Retry a specific job from the retry or dead queue.
|
|
18
|
+
#
|
|
19
|
+
# @param jid [String] the job ID to retry
|
|
20
|
+
# @param source [Symbol] the source queue (:retry or :dead)
|
|
21
|
+
# @return [Boolean] true if the job was successfully retried
|
|
22
|
+
# @raise [RuntimeError] if the job is not found
|
|
23
|
+
#
|
|
24
|
+
# @example Retry a job from the retry queue
|
|
25
|
+
# Sktop::JobActions.retry_job("abc123def456", :retry)
|
|
6
26
|
def retry_job(jid, source)
|
|
7
27
|
job = find_job(jid, source)
|
|
8
28
|
raise "Job not found (JID: #{jid})" unless job
|
|
@@ -11,6 +31,12 @@ module Sktop
|
|
|
11
31
|
true
|
|
12
32
|
end
|
|
13
33
|
|
|
34
|
+
# Delete a specific job from the retry or dead queue.
|
|
35
|
+
#
|
|
36
|
+
# @param jid [String] the job ID to delete
|
|
37
|
+
# @param source [Symbol] the source queue (:retry or :dead)
|
|
38
|
+
# @return [Boolean] true if the job was successfully deleted
|
|
39
|
+
# @raise [RuntimeError] if the job is not found
|
|
14
40
|
def delete_job(jid, source)
|
|
15
41
|
job = find_job(jid, source)
|
|
16
42
|
raise "Job not found (JID: #{jid})" unless job
|
|
@@ -19,6 +45,12 @@ module Sktop
|
|
|
19
45
|
true
|
|
20
46
|
end
|
|
21
47
|
|
|
48
|
+
# Kill a specific job, moving it to the dead queue.
|
|
49
|
+
#
|
|
50
|
+
# @param jid [String] the job ID to kill
|
|
51
|
+
# @param source [Symbol] the source queue (:retry or :scheduled)
|
|
52
|
+
# @return [Boolean] true if the job was successfully killed
|
|
53
|
+
# @raise [RuntimeError] if the job is not found
|
|
22
54
|
def kill_job(jid, source)
|
|
23
55
|
job = find_job(jid, source)
|
|
24
56
|
raise "Job not found (JID: #{jid})" unless job
|
|
@@ -27,6 +59,10 @@ module Sktop
|
|
|
27
59
|
true
|
|
28
60
|
end
|
|
29
61
|
|
|
62
|
+
# Retry all jobs in a sorted set (retry or dead queue).
|
|
63
|
+
#
|
|
64
|
+
# @param source [Symbol] the source queue (:retry or :dead)
|
|
65
|
+
# @return [Integer] the number of jobs retried
|
|
30
66
|
def retry_all(source)
|
|
31
67
|
set = get_set(source)
|
|
32
68
|
count = set.size
|
|
@@ -34,6 +70,10 @@ module Sktop
|
|
|
34
70
|
count
|
|
35
71
|
end
|
|
36
72
|
|
|
73
|
+
# Delete all jobs from a sorted set (retry or dead queue).
|
|
74
|
+
#
|
|
75
|
+
# @param source [Symbol] the source queue (:retry or :dead)
|
|
76
|
+
# @return [Integer] the number of jobs deleted
|
|
37
77
|
def delete_all(source)
|
|
38
78
|
set = get_set(source)
|
|
39
79
|
count = set.size
|
|
@@ -41,6 +81,12 @@ module Sktop
|
|
|
41
81
|
count
|
|
42
82
|
end
|
|
43
83
|
|
|
84
|
+
# Delete a specific job from a named queue.
|
|
85
|
+
#
|
|
86
|
+
# @param queue_name [String] the name of the queue
|
|
87
|
+
# @param jid [String] the job ID to delete
|
|
88
|
+
# @return [Boolean] true if the job was successfully deleted
|
|
89
|
+
# @raise [RuntimeError] if the job is not found in the queue
|
|
44
90
|
def delete_queue_job(queue_name, jid)
|
|
45
91
|
queue = Sidekiq::Queue.new(queue_name)
|
|
46
92
|
job = queue.find { |j| j.jid == jid }
|
|
@@ -50,6 +96,12 @@ module Sktop
|
|
|
50
96
|
true
|
|
51
97
|
end
|
|
52
98
|
|
|
99
|
+
# Send the QUIET signal to a Sidekiq process.
|
|
100
|
+
# A quieted process stops fetching new jobs but finishes current work.
|
|
101
|
+
#
|
|
102
|
+
# @param identity [String] the process identity (e.g., "hostname:pid:tag")
|
|
103
|
+
# @return [Boolean] true if the signal was sent successfully
|
|
104
|
+
# @raise [RuntimeError] if the process is not found
|
|
53
105
|
def quiet_process(identity)
|
|
54
106
|
process = find_process(identity)
|
|
55
107
|
raise "Process not found (identity: #{identity})" unless process
|
|
@@ -58,6 +110,12 @@ module Sktop
|
|
|
58
110
|
true
|
|
59
111
|
end
|
|
60
112
|
|
|
113
|
+
# Send the STOP signal to a Sidekiq process.
|
|
114
|
+
# This initiates a graceful shutdown of the process.
|
|
115
|
+
#
|
|
116
|
+
# @param identity [String] the process identity (e.g., "hostname:pid:tag")
|
|
117
|
+
# @return [Boolean] true if the signal was sent successfully
|
|
118
|
+
# @raise [RuntimeError] if the process is not found
|
|
61
119
|
def stop_process(identity)
|
|
62
120
|
process = find_process(identity)
|
|
63
121
|
raise "Process not found (identity: #{identity})" unless process
|
|
@@ -68,6 +126,12 @@ module Sktop
|
|
|
68
126
|
|
|
69
127
|
private
|
|
70
128
|
|
|
129
|
+
# Get the appropriate Sidekiq sorted set for a given source.
|
|
130
|
+
#
|
|
131
|
+
# @param source [Symbol] the source type (:retry, :dead, or :scheduled)
|
|
132
|
+
# @return [Sidekiq::RetrySet, Sidekiq::DeadSet, Sidekiq::ScheduledSet]
|
|
133
|
+
# @raise [RuntimeError] if the source is unknown
|
|
134
|
+
# @api private
|
|
71
135
|
def get_set(source)
|
|
72
136
|
case source
|
|
73
137
|
when :retry
|
|
@@ -81,6 +145,12 @@ module Sktop
|
|
|
81
145
|
end
|
|
82
146
|
end
|
|
83
147
|
|
|
148
|
+
# Find a job by JID in a sorted set.
|
|
149
|
+
#
|
|
150
|
+
# @param jid [String] the job ID to find
|
|
151
|
+
# @param source [Symbol] the source type (:retry, :dead, or :scheduled)
|
|
152
|
+
# @return [Sidekiq::SortedEntry, nil] the job entry or nil if not found
|
|
153
|
+
# @api private
|
|
84
154
|
def find_job(jid, source)
|
|
85
155
|
set = get_set(source)
|
|
86
156
|
|
|
@@ -98,6 +168,11 @@ module Sktop
|
|
|
98
168
|
nil
|
|
99
169
|
end
|
|
100
170
|
|
|
171
|
+
# Find a Sidekiq process by its identity string.
|
|
172
|
+
#
|
|
173
|
+
# @param identity [String] the process identity
|
|
174
|
+
# @return [Sidekiq::Process, nil] the process or nil if not found
|
|
175
|
+
# @api private
|
|
101
176
|
def find_process(identity)
|
|
102
177
|
Sidekiq::ProcessSet.new.find { |p| p["identity"] == identity }
|
|
103
178
|
end
|
|
@@ -1,16 +1,48 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Sktop
|
|
4
|
+
# Collects statistics and data from Sidekiq for display in the TUI.
|
|
5
|
+
# Wraps the Sidekiq API to provide a consistent interface for querying
|
|
6
|
+
# queues, processes, workers, and job data.
|
|
7
|
+
#
|
|
8
|
+
# @example Basic usage
|
|
9
|
+
# collector = Sktop::StatsCollector.new
|
|
10
|
+
# puts collector.overview[:processed]
|
|
11
|
+
# puts collector.queues.length
|
|
12
|
+
#
|
|
13
|
+
# @example Refreshing data
|
|
14
|
+
# collector = Sktop::StatsCollector.new
|
|
15
|
+
# loop do
|
|
16
|
+
# collector.refresh!
|
|
17
|
+
# display_stats(collector.overview)
|
|
18
|
+
# sleep 2
|
|
19
|
+
# end
|
|
4
20
|
class StatsCollector
|
|
21
|
+
# Create a new StatsCollector instance.
|
|
22
|
+
# Initializes with fresh statistics from Sidekiq.
|
|
5
23
|
def initialize
|
|
6
24
|
@stats = Sidekiq::Stats.new
|
|
7
25
|
end
|
|
8
26
|
|
|
27
|
+
# Refresh the cached statistics from Sidekiq.
|
|
28
|
+
# Call this method periodically to get updated data.
|
|
29
|
+
#
|
|
30
|
+
# @return [self] the collector instance for method chaining
|
|
9
31
|
def refresh!
|
|
10
32
|
@stats = Sidekiq::Stats.new
|
|
11
33
|
self
|
|
12
34
|
end
|
|
13
35
|
|
|
36
|
+
# Get an overview of Sidekiq statistics.
|
|
37
|
+
#
|
|
38
|
+
# @return [Hash] overview statistics
|
|
39
|
+
# @option return [Integer] :processed total jobs processed
|
|
40
|
+
# @option return [Integer] :failed total jobs failed
|
|
41
|
+
# @option return [Integer] :scheduled_size jobs in scheduled queue
|
|
42
|
+
# @option return [Integer] :retry_size jobs in retry queue
|
|
43
|
+
# @option return [Integer] :dead_size jobs in dead queue
|
|
44
|
+
# @option return [Integer] :enqueued total jobs across all queues
|
|
45
|
+
# @option return [Float] :default_queue_latency latency of default queue in seconds
|
|
14
46
|
def overview
|
|
15
47
|
{
|
|
16
48
|
processed: @stats.processed,
|
|
@@ -23,6 +55,13 @@ module Sktop
|
|
|
23
55
|
}
|
|
24
56
|
end
|
|
25
57
|
|
|
58
|
+
# Get information about all Sidekiq queues.
|
|
59
|
+
#
|
|
60
|
+
# @return [Array<Hash>] array of queue information hashes
|
|
61
|
+
# @option return [String] :name queue name
|
|
62
|
+
# @option return [Integer] :size number of jobs in queue
|
|
63
|
+
# @option return [Float] :latency queue latency in seconds
|
|
64
|
+
# @option return [Boolean] :paused whether the queue is paused
|
|
26
65
|
def queues
|
|
27
66
|
Sidekiq::Queue.all.map do |queue|
|
|
28
67
|
{
|
|
@@ -34,6 +73,21 @@ module Sktop
|
|
|
34
73
|
end
|
|
35
74
|
end
|
|
36
75
|
|
|
76
|
+
# Get information about all running Sidekiq processes.
|
|
77
|
+
#
|
|
78
|
+
# @return [Array<Hash>] array of process information hashes
|
|
79
|
+
# @option return [String] :identity unique process identifier
|
|
80
|
+
# @option return [String] :hostname the host running the process
|
|
81
|
+
# @option return [Integer] :pid process ID
|
|
82
|
+
# @option return [String, nil] :tag optional process tag
|
|
83
|
+
# @option return [Time] :started_at when the process started
|
|
84
|
+
# @option return [Integer] :concurrency number of worker threads
|
|
85
|
+
# @option return [Integer] :busy number of threads currently processing
|
|
86
|
+
# @option return [Array<String>] :queues queues this process listens to
|
|
87
|
+
# @option return [Array<String>] :labels process labels
|
|
88
|
+
# @option return [Boolean] :quiet whether the process is quieted
|
|
89
|
+
# @option return [Boolean] :stopping whether the process is stopping
|
|
90
|
+
# @option return [Integer, nil] :rss memory usage in KB
|
|
37
91
|
def processes
|
|
38
92
|
Sidekiq::ProcessSet.new.map do |process|
|
|
39
93
|
# Use direct hash access for status flags - the method accessors
|
|
@@ -61,12 +115,30 @@ module Sktop
|
|
|
61
115
|
end
|
|
62
116
|
end
|
|
63
117
|
|
|
118
|
+
# Get information about all currently running workers.
|
|
119
|
+
#
|
|
120
|
+
# @return [Array<Hash>] array of worker information hashes
|
|
121
|
+
# @option return [String] :process_id the process identity
|
|
122
|
+
# @option return [String] :thread_id the thread identifier
|
|
123
|
+
# @option return [String] :queue the queue being processed
|
|
124
|
+
# @option return [String] :class the job class name
|
|
125
|
+
# @option return [Array] :args the job arguments
|
|
126
|
+
# @option return [Time] :run_at when the job started
|
|
127
|
+
# @option return [Float] :elapsed seconds since job started
|
|
64
128
|
def workers
|
|
65
129
|
Sidekiq::Workers.new.map do |process_id, thread_id, work|
|
|
66
130
|
extract_worker_info(process_id, thread_id, work)
|
|
67
131
|
end
|
|
68
132
|
end
|
|
69
133
|
|
|
134
|
+
# Extract worker information from Sidekiq's Workers API.
|
|
135
|
+
# Handles both Sidekiq 6.x (hash-based) and 7.x (Work object) formats.
|
|
136
|
+
#
|
|
137
|
+
# @param process_id [String] the process identity
|
|
138
|
+
# @param thread_id [String] the thread identifier
|
|
139
|
+
# @param work [Hash, Sidekiq::Work] the work data (format depends on Sidekiq version)
|
|
140
|
+
# @return [Hash] normalized worker information
|
|
141
|
+
# @api private
|
|
70
142
|
def extract_worker_info(process_id, thread_id, work)
|
|
71
143
|
# Sidekiq 7+ returns Work objects, older versions return hashes
|
|
72
144
|
if work.is_a?(Hash)
|
|
@@ -108,6 +180,16 @@ module Sktop
|
|
|
108
180
|
end
|
|
109
181
|
end
|
|
110
182
|
|
|
183
|
+
# Get jobs from a specific queue.
|
|
184
|
+
#
|
|
185
|
+
# @param queue_name [String] the name of the queue
|
|
186
|
+
# @param limit [Integer] maximum number of jobs to return (default: 100)
|
|
187
|
+
# @return [Array<Hash>] array of job information hashes
|
|
188
|
+
# @option return [String] :jid the job ID
|
|
189
|
+
# @option return [String] :class the job class name
|
|
190
|
+
# @option return [Array] :args the job arguments
|
|
191
|
+
# @option return [Time, nil] :enqueued_at when the job was enqueued
|
|
192
|
+
# @option return [Time, nil] :created_at when the job was created
|
|
111
193
|
def queue_jobs(queue_name, limit: 100)
|
|
112
194
|
queue = Sidekiq::Queue.new(queue_name)
|
|
113
195
|
queue.first(limit).map do |job|
|
|
@@ -121,6 +203,15 @@ module Sktop
|
|
|
121
203
|
end
|
|
122
204
|
end
|
|
123
205
|
|
|
206
|
+
# Get jobs from the scheduled queue.
|
|
207
|
+
#
|
|
208
|
+
# @param limit [Integer] maximum number of jobs to return (default: 10)
|
|
209
|
+
# @return [Array<Hash>] array of scheduled job information hashes
|
|
210
|
+
# @option return [String] :class the job class name
|
|
211
|
+
# @option return [String] :queue the target queue
|
|
212
|
+
# @option return [Array] :args the job arguments
|
|
213
|
+
# @option return [Time] :scheduled_at when the job is scheduled to run
|
|
214
|
+
# @option return [Time, nil] :created_at when the job was created
|
|
124
215
|
def scheduled_jobs(limit: 10)
|
|
125
216
|
Sidekiq::ScheduledSet.new.first(limit).map do |job|
|
|
126
217
|
{
|
|
@@ -133,6 +224,18 @@ module Sktop
|
|
|
133
224
|
end
|
|
134
225
|
end
|
|
135
226
|
|
|
227
|
+
# Get jobs from the retry queue.
|
|
228
|
+
#
|
|
229
|
+
# @param limit [Integer] maximum number of jobs to return (default: 10)
|
|
230
|
+
# @return [Array<Hash>] array of retry job information hashes
|
|
231
|
+
# @option return [String] :jid the job ID
|
|
232
|
+
# @option return [String] :class the job class name
|
|
233
|
+
# @option return [String] :queue the original queue
|
|
234
|
+
# @option return [Array] :args the job arguments
|
|
235
|
+
# @option return [Time, nil] :failed_at when the job failed
|
|
236
|
+
# @option return [Integer] :retry_count number of retry attempts
|
|
237
|
+
# @option return [String] :error_class the exception class name
|
|
238
|
+
# @option return [String] :error_message the exception message
|
|
136
239
|
def retry_jobs(limit: 10)
|
|
137
240
|
Sidekiq::RetrySet.new.first(limit).map do |job|
|
|
138
241
|
{
|
|
@@ -148,6 +251,17 @@ module Sktop
|
|
|
148
251
|
end
|
|
149
252
|
end
|
|
150
253
|
|
|
254
|
+
# Get jobs from the dead (morgue) queue.
|
|
255
|
+
#
|
|
256
|
+
# @param limit [Integer] maximum number of jobs to return (default: 10)
|
|
257
|
+
# @return [Array<Hash>] array of dead job information hashes
|
|
258
|
+
# @option return [String] :jid the job ID
|
|
259
|
+
# @option return [String] :class the job class name
|
|
260
|
+
# @option return [String] :queue the original queue
|
|
261
|
+
# @option return [Array] :args the job arguments
|
|
262
|
+
# @option return [Time, nil] :failed_at when the job failed
|
|
263
|
+
# @option return [String] :error_class the exception class name
|
|
264
|
+
# @option return [String] :error_message the exception message
|
|
151
265
|
def dead_jobs(limit: 10)
|
|
152
266
|
Sidekiq::DeadSet.new.first(limit).map do |job|
|
|
153
267
|
{
|
|
@@ -162,6 +276,12 @@ module Sktop
|
|
|
162
276
|
end
|
|
163
277
|
end
|
|
164
278
|
|
|
279
|
+
# Get historical statistics for processed and failed jobs.
|
|
280
|
+
#
|
|
281
|
+
# @param days [Integer] number of days of history to retrieve (default: 7)
|
|
282
|
+
# @return [Hash] history data
|
|
283
|
+
# @option return [Hash] :processed daily processed counts keyed by date
|
|
284
|
+
# @option return [Hash] :failed daily failed counts keyed by date
|
|
165
285
|
def history(days: 7)
|
|
166
286
|
stats_history = Sidekiq::Stats::History.new(days)
|
|
167
287
|
{
|
data/lib/sktop/version.rb
CHANGED
data/lib/sktop.rb
CHANGED
|
@@ -13,10 +13,31 @@ require_relative "sktop/job_actions"
|
|
|
13
13
|
require_relative "sktop/display"
|
|
14
14
|
require_relative "sktop/cli"
|
|
15
15
|
|
|
16
|
+
# Sktop is a terminal-based Sidekiq monitoring tool similar to htop.
|
|
17
|
+
# It provides real-time visibility into Sidekiq processes, queues,
|
|
18
|
+
# workers, and job status with an interactive TUI.
|
|
19
|
+
#
|
|
20
|
+
# @example Running from command line
|
|
21
|
+
# sktop -r redis://localhost:6379/0
|
|
22
|
+
#
|
|
23
|
+
# @example Using in Ruby code
|
|
24
|
+
# Sktop.configure_redis(url: "redis://localhost:6379/0")
|
|
25
|
+
# Sktop::CLI.new.run
|
|
26
|
+
#
|
|
27
|
+
# @see https://github.com/jamez01/sktop
|
|
16
28
|
module Sktop
|
|
29
|
+
# Base error class for all Sktop errors
|
|
17
30
|
class Error < StandardError; end
|
|
18
31
|
|
|
19
32
|
class << self
|
|
33
|
+
# Configure the Redis connection for Sidekiq client mode.
|
|
34
|
+
#
|
|
35
|
+
# @param url [String, nil] Redis connection URL (e.g., "redis://localhost:6379/0")
|
|
36
|
+
# @param namespace [String, nil] Redis namespace for Sidekiq keys (currently unused)
|
|
37
|
+
# @return [void]
|
|
38
|
+
#
|
|
39
|
+
# @example Configure with a custom Redis URL
|
|
40
|
+
# Sktop.configure_redis(url: "redis://myredis:6379/1")
|
|
20
41
|
def configure_redis(url: nil, namespace: nil)
|
|
21
42
|
options = {}
|
|
22
43
|
options[:url] = url if url
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sktop
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- James
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-01-
|
|
11
|
+
date: 2026-01-30 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: sidekiq
|