solid_queue_tui 0.1.1 → 0.1.3

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/exe/sqtui +37 -1
  3. data/lib/solid_queue_tui/actions/discard_job.rb +4 -21
  4. data/lib/solid_queue_tui/actions/discard_scheduled_job.rb +5 -21
  5. data/lib/solid_queue_tui/actions/dispatch_scheduled_job.rb +4 -23
  6. data/lib/solid_queue_tui/actions/enqueue_recurring_task.rb +12 -0
  7. data/lib/solid_queue_tui/actions/retry_job.rb +14 -59
  8. data/lib/solid_queue_tui/actions/toggle_queue_pause.rb +19 -0
  9. data/lib/solid_queue_tui/application.rb +109 -21
  10. data/lib/solid_queue_tui/cli.rb +15 -8
  11. data/lib/solid_queue_tui/components/header.rb +26 -27
  12. data/lib/solid_queue_tui/components/help_bar.rb +1 -0
  13. data/lib/solid_queue_tui/components/job_table.rb +13 -2
  14. data/lib/solid_queue_tui/data/failed_query.rb +37 -91
  15. data/lib/solid_queue_tui/data/jobs_query.rb +119 -121
  16. data/lib/solid_queue_tui/data/processes_query.rb +32 -33
  17. data/lib/solid_queue_tui/data/queues_query.rb +6 -15
  18. data/lib/solid_queue_tui/data/recurring_tasks_query.rb +36 -0
  19. data/lib/solid_queue_tui/data/stats.rb +9 -27
  20. data/lib/solid_queue_tui/formatting_helpers.rb +63 -0
  21. data/lib/solid_queue_tui/railtie.rb +9 -0
  22. data/lib/solid_queue_tui/version.rb +1 -1
  23. data/lib/solid_queue_tui/views/blocked_view.rb +85 -74
  24. data/lib/solid_queue_tui/views/concerns/confirmable.rb +53 -0
  25. data/lib/solid_queue_tui/views/concerns/filterable.rb +128 -0
  26. data/lib/solid_queue_tui/views/concerns/paginatable.rb +79 -0
  27. data/lib/solid_queue_tui/views/dashboard_view.rb +4 -5
  28. data/lib/solid_queue_tui/views/failed_view.rb +65 -179
  29. data/lib/solid_queue_tui/views/finished_view.rb +33 -114
  30. data/lib/solid_queue_tui/views/in_progress_view.rb +85 -69
  31. data/lib/solid_queue_tui/views/job_detail_view.rb +179 -31
  32. data/lib/solid_queue_tui/views/processes_view.rb +2 -24
  33. data/lib/solid_queue_tui/views/queues_view.rb +250 -30
  34. data/lib/solid_queue_tui/views/recurring_tasks_view.rb +155 -0
  35. data/lib/solid_queue_tui/views/scheduled_view.rb +69 -107
  36. data/lib/solid_queue_tui.rb +18 -4
  37. data/lib/tasks/solid_queue_tui.rake +8 -0
  38. metadata +20 -25
  39. data/lib/solid_queue_tui/connection.rb +0 -58
@@ -3,65 +3,70 @@
3
3
  module SolidQueueTui
4
4
  module Views
5
5
  class ScheduledView
6
+ include Filterable
7
+ include Confirmable
8
+ include Paginatable
9
+ include FormattingHelpers
10
+
6
11
  def initialize(tui)
7
12
  @tui = tui
8
- @table_state = RatatuiRuby::TableState.new(nil)
9
- @table_state.select(0)
10
- @selected_row = 0
11
- @jobs = []
12
- @confirm_action = nil
13
+ init_pagination
14
+ init_confirm
15
+ init_filter
13
16
  end
14
17
 
15
18
  def update(jobs:)
16
- @jobs = jobs
17
- @selected_row = @selected_row.clamp(0, [@jobs.size - 1, 0].max)
18
- @table_state.select(@selected_row)
19
+ update_items(jobs)
20
+ end
21
+
22
+ def append(jobs:)
23
+ append_items(jobs)
19
24
  end
20
25
 
21
26
  def render(frame, area)
22
- if @confirm_action
23
- content_area, confirm_area = @tui.layout_split(
27
+ if confirm_mode?
28
+ render_table(frame, area)
29
+ render_confirm_popup(frame, area)
30
+ elsif filter_mode?
31
+ filter_area, content_area = @tui.layout_split(
24
32
  area,
25
33
  direction: :vertical,
26
34
  constraints: [
27
- @tui.constraint_fill(1),
28
- @tui.constraint_length(3)
35
+ @tui.constraint_length(3),
36
+ @tui.constraint_fill(1)
29
37
  ]
30
38
  )
39
+ render_filter_input(frame, filter_area)
31
40
  render_table(frame, content_area)
32
- render_confirm(frame, confirm_area)
33
41
  else
34
42
  render_table(frame, area)
35
43
  end
36
44
  end
37
45
 
38
46
  def handle_input(event)
39
- if @confirm_action
47
+ if confirm_mode?
40
48
  handle_confirm_input(event)
49
+ elsif filter_mode?
50
+ handle_filter_input(event)
41
51
  else
42
52
  handle_normal_input(event)
43
53
  end
44
54
  end
45
55
 
46
- def selected_item
47
- return nil if @jobs.empty? || @selected_row >= @jobs.size
48
- @jobs[@selected_row]
49
- end
50
-
51
56
  def capturing_input?
52
- !!@confirm_action
57
+ filter_mode? || confirm_mode?
53
58
  end
54
59
 
55
60
  def bindings
56
- if @confirm_action
57
- [
58
- { key: "y", action: "Confirm" },
59
- { key: "n/Esc", action: "Cancel" }
60
- ]
61
+ if confirm_mode?
62
+ confirm_bindings
63
+ elsif filter_mode?
64
+ filter_bindings
61
65
  else
62
66
  [
63
67
  { key: "j/k", action: "Navigate" },
64
68
  { key: "Enter", action: "Detail" },
69
+ { key: "/", action: "Filter" },
65
70
  { key: "N", action: "Run Now" },
66
71
  { key: "D", action: "Discard" },
67
72
  { key: "G/g", action: "Bottom/Top" }
@@ -69,7 +74,9 @@ module SolidQueueTui
69
74
  end
70
75
  end
71
76
 
72
- def breadcrumb = "scheduled"
77
+ def breadcrumb
78
+ @filters.empty? ? "scheduled" : "scheduled:filtered"
79
+ end
73
80
 
74
81
  private
75
82
 
@@ -78,7 +85,9 @@ module SolidQueueTui
78
85
  in { type: :key, code: "j" } | { type: :key, code: "up" }
79
86
  move_selection(-1)
80
87
  in { type: :key, code: "k" } | { type: :key, code: "down" }
81
- move_selection(1)
88
+ result = move_selection(1)
89
+ return :load_more if result == :load_more
90
+ nil
82
91
  in { type: :key, code: "g" }
83
92
  jump_to_top
84
93
  in { type: :key, code: "G" }
@@ -89,51 +98,40 @@ module SolidQueueTui
89
98
  in { type: :key, code: "D" }
90
99
  @confirm_action = :discard if selected_item
91
100
  nil
92
- else
93
- nil
94
- end
95
- end
96
-
97
- def handle_confirm_input(event)
98
- case event
99
- in { type: :key, code: "y" }
100
- action = @confirm_action
101
- @confirm_action = nil
102
- case action
103
- when :run_now
104
- item = selected_item
105
- return nil unless item
106
- Actions::DispatchScheduledJob.call(item.id)
107
- :refresh
108
- when :discard
109
- item = selected_item
110
- return nil unless item
111
- Actions::DiscardScheduledJob.call(item.id)
112
- :refresh
113
- end
114
- in { type: :key, code: "n" } | { type: :key, code: "esc" }
115
- @confirm_action = nil
101
+ in { type: :key, code: "/" }
102
+ enter_filter_mode
116
103
  nil
104
+ in { type: :key, code: "esc" }
105
+ clear_filter
117
106
  else
118
107
  nil
119
108
  end
120
109
  end
121
110
 
122
- def move_selection(delta)
123
- return if @jobs.empty?
124
- @selected_row = (@selected_row + delta).clamp(0, @jobs.size - 1)
125
- @table_state.select(@selected_row)
126
- end
127
-
128
- def jump_to_top
129
- @selected_row = 0
130
- @table_state.select(0)
111
+ def confirm_message
112
+ case @confirm_action
113
+ when :run_now
114
+ job = selected_item
115
+ "Run job ##{job&.id} (#{job&.class_name}) now? [y/n]"
116
+ when :discard
117
+ job = selected_item
118
+ "Discard job ##{job&.id} (#{job&.class_name})? This cannot be undone. [y/n]"
119
+ end
131
120
  end
132
121
 
133
- def jump_to_bottom
134
- return if @jobs.empty?
135
- @selected_row = @jobs.size - 1
136
- @table_state.select(@selected_row)
122
+ def execute_confirm_action(action)
123
+ case action
124
+ when :run_now
125
+ item = selected_item
126
+ return nil unless item
127
+ Actions::DispatchScheduledJob.call(item.id)
128
+ :refresh
129
+ when :discard
130
+ item = selected_item
131
+ return nil unless item
132
+ Actions::DiscardScheduledJob.call(item.id)
133
+ :refresh
134
+ end
137
135
  end
138
136
 
139
137
  def render_table(frame, area)
@@ -143,73 +141,37 @@ module SolidQueueTui
143
141
  { key: :queue_name, label: "QUEUE", width: 14 },
144
142
  { key: :priority, label: "PRI", width: 5 },
145
143
  { key: :scheduled_at, label: "SCHEDULED AT", width: 20 },
144
+ { key: :status, label: "STATUS", width: 10, color_by: :status },
146
145
  { key: :created_at, label: "CREATED", width: 12 }
147
146
  ]
148
147
 
149
- rows = @jobs.map do |job|
148
+ now = Time.now.utc
149
+ rows = items.map do |job|
150
+ delayed = job.scheduled_at && job.scheduled_at < now
150
151
  {
151
152
  id: job.id,
152
153
  class_name: job.class_name,
153
154
  queue_name: job.queue_name,
154
155
  priority: job.priority,
155
156
  scheduled_at: format_time(job.scheduled_at),
157
+ status: delayed ? "DELAYED" : "pending",
156
158
  created_at: time_ago(job.created_at)
157
159
  }
158
160
  end
159
161
 
160
162
  table = Components::JobTable.new(
161
163
  @tui,
162
- title: "Scheduled Jobs",
164
+ title: filter_title("Scheduled Jobs"),
163
165
  columns: columns,
164
166
  rows: rows,
165
167
  selected_row: @selected_row,
168
+ total_count: @total_count,
166
169
  empty_message: "No scheduled jobs"
167
170
  )
168
171
 
169
172
  table.render(frame, area, @table_state)
170
173
  end
171
174
 
172
- def render_confirm(frame, area)
173
- message = case @confirm_action
174
- when :run_now
175
- job = selected_item
176
- "Run job ##{job&.id} (#{job&.class_name}) now? [y/n]"
177
- when :discard
178
- job = selected_item
179
- "Discard job ##{job&.id} (#{job&.class_name})? This cannot be undone. [y/n]"
180
- end
181
-
182
- frame.render_widget(
183
- @tui.paragraph(
184
- text: " #{message}",
185
- style: @tui.style(fg: :yellow, modifiers: [:bold]),
186
- block: @tui.block(
187
- title: " Confirm ",
188
- title_style: @tui.style(fg: :red, modifiers: [:bold]),
189
- borders: [:all],
190
- border_type: :rounded,
191
- border_style: @tui.style(fg: :red)
192
- )
193
- ),
194
- area
195
- )
196
- end
197
-
198
- def format_time(time)
199
- return "n/a" unless time
200
- time.strftime("%Y-%m-%d %H:%M:%S")
201
- end
202
-
203
- def time_ago(time)
204
- return "n/a" unless time
205
- seconds = (Time.now.utc - time).to_i
206
- case seconds
207
- when 0..59 then "#{seconds}s ago"
208
- when 60..3599 then "#{seconds / 60}m ago"
209
- when 3600..86399 then "#{seconds / 3600}h ago"
210
- else "#{seconds / 86400}d ago"
211
- end
212
- end
213
175
  end
214
176
  end
215
177
  end
@@ -1,10 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "json"
4
- require "time"
5
-
6
3
  require_relative "solid_queue_tui/version"
7
- require_relative "solid_queue_tui/connection"
4
+ require_relative "solid_queue_tui/formatting_helpers"
8
5
 
9
6
  # Data layer
10
7
  require_relative "solid_queue_tui/data/stats"
@@ -12,18 +9,26 @@ require_relative "solid_queue_tui/data/jobs_query"
12
9
  require_relative "solid_queue_tui/data/queues_query"
13
10
  require_relative "solid_queue_tui/data/processes_query"
14
11
  require_relative "solid_queue_tui/data/failed_query"
12
+ require_relative "solid_queue_tui/data/recurring_tasks_query"
15
13
 
16
14
  # Actions
17
15
  require_relative "solid_queue_tui/actions/retry_job"
18
16
  require_relative "solid_queue_tui/actions/discard_job"
19
17
  require_relative "solid_queue_tui/actions/dispatch_scheduled_job"
20
18
  require_relative "solid_queue_tui/actions/discard_scheduled_job"
19
+ require_relative "solid_queue_tui/actions/toggle_queue_pause"
20
+ require_relative "solid_queue_tui/actions/enqueue_recurring_task"
21
21
 
22
22
  # Components
23
23
  require_relative "solid_queue_tui/components/header"
24
24
  require_relative "solid_queue_tui/components/job_table"
25
25
  require_relative "solid_queue_tui/components/help_bar"
26
26
 
27
+ # View concerns
28
+ require_relative "solid_queue_tui/views/concerns/filterable"
29
+ require_relative "solid_queue_tui/views/concerns/confirmable"
30
+ require_relative "solid_queue_tui/views/concerns/paginatable"
31
+
27
32
  # Views
28
33
  require_relative "solid_queue_tui/views/dashboard_view"
29
34
  require_relative "solid_queue_tui/views/queues_view"
@@ -32,6 +37,7 @@ require_relative "solid_queue_tui/views/in_progress_view"
32
37
  require_relative "solid_queue_tui/views/blocked_view"
33
38
  require_relative "solid_queue_tui/views/scheduled_view"
34
39
  require_relative "solid_queue_tui/views/finished_view"
40
+ require_relative "solid_queue_tui/views/recurring_tasks_view"
35
41
  require_relative "solid_queue_tui/views/processes_view"
36
42
  require_relative "solid_queue_tui/views/job_detail_view"
37
43
 
@@ -43,4 +49,12 @@ require_relative "solid_queue_tui/application"
43
49
  require_relative "solid_queue_tui/cli"
44
50
 
45
51
  module SolidQueueTui
52
+ @refresh_interval = 200
53
+ @page_size = 100
54
+
55
+ class << self
56
+ attr_accessor :refresh_interval, :page_size
57
+ end
46
58
  end
59
+
60
+ require_relative "solid_queue_tui/railtie" if defined?(Rails::Railtie)
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ namespace :solid_queue_tui do
4
+ desc "Launch the Solid Queue TUI dashboard"
5
+ task start: :environment do
6
+ SolidQueueTui::CLI.run([])
7
+ end
8
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solid_queue_tui
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shiva Reddy
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-02-16 00:00:00.000000000 Z
11
+ date: 2026-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: ratatui_ruby
@@ -25,47 +25,33 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
- name: activerecord
28
+ name: solid_queue
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '7.0'
33
+ version: '1.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '7.0'
40
+ version: '1.0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: sqlite3
42
+ name: railties
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :development
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
- - !ruby/object:Gem::Dependency
56
- name: pg
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :development
47
+ version: '7.1'
48
+ type: :runtime
63
49
  prerelease: false
64
50
  version_requirements: !ruby/object:Gem::Requirement
65
51
  requirements:
66
52
  - - ">="
67
53
  - !ruby/object:Gem::Version
68
- version: '0'
54
+ version: '7.1'
69
55
  - !ruby/object:Gem::Dependency
70
56
  name: rake
71
57
  requirement: !ruby/object:Gem::Requirement
@@ -108,21 +94,28 @@ files:
108
94
  - lib/solid_queue_tui/actions/discard_job.rb
109
95
  - lib/solid_queue_tui/actions/discard_scheduled_job.rb
110
96
  - lib/solid_queue_tui/actions/dispatch_scheduled_job.rb
97
+ - lib/solid_queue_tui/actions/enqueue_recurring_task.rb
111
98
  - lib/solid_queue_tui/actions/retry_job.rb
99
+ - lib/solid_queue_tui/actions/toggle_queue_pause.rb
112
100
  - lib/solid_queue_tui/application.rb
113
101
  - lib/solid_queue_tui/cli.rb
114
102
  - lib/solid_queue_tui/components/header.rb
115
103
  - lib/solid_queue_tui/components/help_bar.rb
116
104
  - lib/solid_queue_tui/components/job_table.rb
117
- - lib/solid_queue_tui/connection.rb
118
105
  - lib/solid_queue_tui/data/failed_query.rb
119
106
  - lib/solid_queue_tui/data/jobs_query.rb
120
107
  - lib/solid_queue_tui/data/processes_query.rb
121
108
  - lib/solid_queue_tui/data/queues_query.rb
109
+ - lib/solid_queue_tui/data/recurring_tasks_query.rb
122
110
  - lib/solid_queue_tui/data/stats.rb
123
111
  - lib/solid_queue_tui/dev_reloader.rb
112
+ - lib/solid_queue_tui/formatting_helpers.rb
113
+ - lib/solid_queue_tui/railtie.rb
124
114
  - lib/solid_queue_tui/version.rb
125
115
  - lib/solid_queue_tui/views/blocked_view.rb
116
+ - lib/solid_queue_tui/views/concerns/confirmable.rb
117
+ - lib/solid_queue_tui/views/concerns/filterable.rb
118
+ - lib/solid_queue_tui/views/concerns/paginatable.rb
126
119
  - lib/solid_queue_tui/views/dashboard_view.rb
127
120
  - lib/solid_queue_tui/views/failed_view.rb
128
121
  - lib/solid_queue_tui/views/finished_view.rb
@@ -130,7 +123,9 @@ files:
130
123
  - lib/solid_queue_tui/views/job_detail_view.rb
131
124
  - lib/solid_queue_tui/views/processes_view.rb
132
125
  - lib/solid_queue_tui/views/queues_view.rb
126
+ - lib/solid_queue_tui/views/recurring_tasks_view.rb
133
127
  - lib/solid_queue_tui/views/scheduled_view.rb
128
+ - lib/tasks/solid_queue_tui.rake
134
129
  homepage: https://github.com/shivareddy-Aluri/solid-queue-tui
135
130
  licenses:
136
131
  - MIT
@@ -143,7 +138,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
143
138
  requirements:
144
139
  - - ">="
145
140
  - !ruby/object:Gem::Version
146
- version: 3.2.0
141
+ version: 3.2.9
147
142
  required_rubygems_version: !ruby/object:Gem::Requirement
148
143
  requirements:
149
144
  - - ">="
@@ -1,58 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "active_record"
4
- require "yaml"
5
-
6
- module SolidQueueTui
7
- class Connection
8
- CONFIG_FILE = "config/solid_tui.yml"
9
-
10
- # Loads config, establishes the DB connection, and returns the parsed config hash.
11
- def self.establish!
12
- config = load_config
13
-
14
- url = config["database_url"]
15
- raise ConnectionError, <<~MSG unless url
16
- No database_url found in #{CONFIG_FILE}.
17
-
18
- Create #{CONFIG_FILE} with:
19
- database_url: postgres://user:pass@localhost/myapp_queue
20
- refresh: 2
21
- MSG
22
-
23
- ActiveRecord::Base.establish_connection(url)
24
- verify_solid_queue_tables!
25
-
26
- config
27
- end
28
-
29
- def self.load_config
30
- path = File.join(Dir.pwd, CONFIG_FILE)
31
-
32
- unless File.exist?(path)
33
- raise ConnectionError, <<~MSG
34
- Config file not found: #{CONFIG_FILE}
35
-
36
- Create #{CONFIG_FILE} with:
37
- database_url: postgres://user:pass@localhost/myapp_queue
38
- refresh: 2
39
- MSG
40
- end
41
-
42
- YAML.safe_load(File.read(path), permitted_classes: [Symbol]) || {}
43
- end
44
-
45
- def self.verify_solid_queue_tables!
46
- conn = ActiveRecord::Base.connection
47
- required = %w[solid_queue_jobs solid_queue_ready_executions]
48
- missing = required.reject { |t| conn.table_exists?(t) }
49
-
50
- unless missing.empty?
51
- raise ConnectionError, "Missing Solid Queue tables: #{missing.join(', ')}. " \
52
- "Is this a Solid Queue database?"
53
- end
54
- end
55
-
56
- class ConnectionError < StandardError; end
57
- end
58
- end