good_job 4.11.2 → 4.12.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: 5de7dadf0496603de0b1b38fbb1df12745078c8cb529f615f2495c6ff926caca
4
- data.tar.gz: 94a0e727f0523e64ceb63cec668cd532c25e55cb0257a2f5c4328762aacef6cd
3
+ metadata.gz: 90cb1ad1b70dc5b80b42058a6fb30b88830b204a678e258f920d9e100329895f
4
+ data.tar.gz: 7b3ac07c100a6595d60184b1dd5229383c19edd4e12174e7b58e4d6c51d56597
5
5
  SHA512:
6
- metadata.gz: 44614cb9f776531726c775baf89e8433306c4cad9f477099fb4c752ef6ba6e9504e8001f95ec37db1ce366925da183a530d7c3ba259b070d4b24a6a617379d30
7
- data.tar.gz: d4eac5bbaf7b13f7431fdeb550f4390215df86cd02cb063da24aaab058e5e70dda336aa5543cd71bb20690292d7da8a6ccf5a24c438dbb30b5b99e353fbbe01f
6
+ metadata.gz: ff13e15e5ca242accb229ba2b81c7b66b928b04ad31dd5b8ec5277190ab7237f795cbeb02f3c3ccecb14b7529415963b9f0a3f97811ed8da2dd676e1a2a3918b
7
+ data.tar.gz: 4001a82fa48e08f192f4157ce122ce531868523248bca5ec6ac332edabb243fa18824486a843609097005c74a96a56cd06e992c6b21467596bd59730337ef98a
data/CHANGELOG.md CHANGED
@@ -1,5 +1,49 @@
1
1
  # Changelog
2
2
 
3
+ ## [v4.12.1](https://github.com/bensheldon/good_job/tree/v4.12.1) (2025-10-16)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.12.0...v4.12.1)
6
+
7
+ **Fixed bugs:**
8
+
9
+ - Improve warning color contrast [\#1679](https://github.com/bensheldon/good_job/pull/1679) ([RDIL](https://github.com/RDIL))
10
+
11
+ **Closed issues:**
12
+
13
+ - batch not showing jobs it enqueued [\#1685](https://github.com/bensheldon/good_job/issues/1685)
14
+ - Recommended method to set up job execution timeout [\#1090](https://github.com/bensheldon/good_job/issues/1090)
15
+
16
+ **Merged pull requests:**
17
+
18
+ - Add CI for Rails 8.1 \(rc\) and use postgres 18 [\#1686](https://github.com/bensheldon/good_job/pull/1686) ([Earlopain](https://github.com/Earlopain))
19
+ - Add warning and alternative to Ruby Timeout example [\#1684](https://github.com/bensheldon/good_job/pull/1684) ([seanpdoyle](https://github.com/seanpdoyle))
20
+
21
+ ## [v4.12.0](https://github.com/bensheldon/good_job/tree/v4.12.0) (2025-09-22)
22
+
23
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.11.2...v4.12.0)
24
+
25
+ **Implemented enhancements:**
26
+
27
+ - Allow `GoodJob::Cli.log_to_stdout =` to be set by the parent application [\#1680](https://github.com/bensheldon/good_job/pull/1680) ([bensheldon](https://github.com/bensheldon))
28
+ - Optimize job\_class scope performance by using column instead of JSON extraction [\#1672](https://github.com/bensheldon/good_job/pull/1672) ([ScotterC](https://github.com/ScotterC))
29
+ - Add state column to cron entries table [\#1652](https://github.com/bensheldon/good_job/pull/1652) ([gregplumbly](https://github.com/gregplumbly))
30
+
31
+ **Fixed bugs:**
32
+
33
+ - Reorganize Dashboard header, ensure favicon svg is encoded; fix double mt-auto [\#1668](https://github.com/bensheldon/good_job/pull/1668) ([bensheldon](https://github.com/bensheldon))
34
+
35
+ **Closed issues:**
36
+
37
+ - GoodJob Cron unique index [\#1678](https://github.com/bensheldon/good_job/issues/1678)
38
+ - params\_job\_class JSON extraction causing performance issues - why not use job\_class column? [\#1671](https://github.com/bensheldon/good_job/issues/1671)
39
+ - Good Job process stalling and CPU at 100% [\#1669](https://github.com/bensheldon/good_job/issues/1669)
40
+
41
+ **Merged pull requests:**
42
+
43
+ - Update sorbet/tapioca [\#1681](https://github.com/bensheldon/good_job/pull/1681) ([bensheldon](https://github.com/bensheldon))
44
+ - Remove obsolete property from tests [\#1676](https://github.com/bensheldon/good_job/pull/1676) ([RDIL](https://github.com/RDIL))
45
+ - Bump actions/checkout from 4 to 5 [\#1673](https://github.com/bensheldon/good_job/pull/1673) ([dependabot[bot]](https://github.com/apps/dependabot))
46
+
3
47
  ## [v4.11.2](https://github.com/bensheldon/good_job/tree/v4.11.2) (2025-08-06)
4
48
 
5
49
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v4.11.1...v4.11.2)
@@ -1024,7 +1068,7 @@
1024
1068
 
1025
1069
  **Fixed bugs:**
1026
1070
 
1027
- - Skip `RecordAlreadyAdvisoryLockedError` during mass-update action [\#1158](https://github.com/bensheldon/good_job/pull/1158) ([jmarsh24](https://github.com/jmarsh24))
1071
+ - Skip `RecordAlreadyAdvisoryLockedError` during mass-update action [\#1158](https://github.com/bensheldon/good_job/pull/1158) ([justinallenmarsh](https://github.com/justinallenmarsh))
1028
1072
 
1029
1073
  **Closed issues:**
1030
1074
 
data/README.md CHANGED
@@ -1057,7 +1057,28 @@ end
1057
1057
 
1058
1058
  ### Timeouts
1059
1059
 
1060
- Job timeouts can be configured with an `around_perform`:
1060
+ Avoid using Ruby's built-in [Timeout](https://github.com/ruby/timeout) mechanism
1061
+ ([1](https://www.mikeperham.com/2015/05/08/timeout-rubys-most-dangerous-api/),
1062
+ [2](https://blog.headius.com/2008/02/rubys-threadraise-threadkill-timeoutrb.html)).
1063
+ Instead, declare either of Active Job's [discard_on](https://api.rubyonrails.org/classes/ActiveJob/Exceptions/ClassMethods.html#method-i-discard_on) or [retry_on](https://api.rubyonrails.org/classes/ActiveJob/Exceptions/ClassMethods.html#method-i-retry_on) to handle
1064
+ the underlying mechanism's timeout exceptions (when available).
1065
+
1066
+ For example, rescue from `Net::OpenTimeout` or `Net::ReadTimeout` and discard
1067
+ the job:
1068
+
1069
+ ```ruby
1070
+ class MyJob < ApplicationJob
1071
+ discard_on Net::OpenTimeout, Net::ReadTimeout
1072
+
1073
+ def perform(uri)
1074
+ Net::HTTP.start(uri.host, uri.port, open_timeout: 3, read_timeout: 3) do |http|
1075
+ http.request(...)
1076
+ end
1077
+ end
1078
+ end
1079
+ ```
1080
+
1081
+ If you have no other choice but to use a Ruby Timeout, it can be configured with an `around_perform`:
1061
1082
 
1062
1083
  ```ruby
1063
1084
  class ApplicationJob < ActiveJob::Base
@@ -21,12 +21,11 @@ module GoodJob
21
21
  }.freeze
22
22
 
23
23
  def status_badge(status)
24
- content_tag :span, status_icon(status, class: "text-white") + t(status, scope: 'good_job.status', count: 1),
25
- class: "badge rounded-pill bg-#{STATUS_COLOR.fetch(status)} d-inline-flex gap-2 ps-1 pe-3 align-items-center"
24
+ content_tag :span, status_icon(status) + t(status, scope: 'good_job.status', count: 1),
25
+ class: "badge rounded-pill text-bg-#{STATUS_COLOR.fetch(status)} d-inline-flex gap-2 ps-1 pe-3 align-items-center"
26
26
  end
27
27
 
28
28
  def status_icon(status, **options)
29
- options[:class] ||= "text-#{STATUS_COLOR.fetch(status)}"
30
29
  icon = render_icon STATUS_ICONS.fetch(status)
31
30
  content_tag :span, icon, **options
32
31
  end
@@ -239,7 +239,7 @@ module GoodJob
239
239
  end
240
240
 
241
241
  def params_job_class
242
- json_string(arel_table['serialized_params'], 'job_class')
242
+ arel_table[:job_class]
243
243
  end
244
244
 
245
245
  def params_execution_count
@@ -250,8 +250,8 @@ module GoodJob
250
250
  )
251
251
  end
252
252
 
253
- def concurrency_key_created_at_index_migrated?
254
- return true if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_concurrency_key_and_created_at)
253
+ def job_class_index_migrated?
254
+ return true if connection.index_name_exists?(:good_jobs, :index_good_jobs_on_job_class)
255
255
 
256
256
  migration_pending_warning!
257
257
  false
@@ -28,7 +28,7 @@
28
28
  </div>
29
29
  <div class="col-4 col-lg-1 text-lg-center">
30
30
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.queue" %></div>
31
- <span class="badge bg-primary text-dark font-monospace"><%= job.queue_name %></span>
31
+ <span class="badge text-bg-primary font-monospace"><%= job.queue_name %></span>
32
32
  </div>
33
33
  <div class="col-4 col-lg-1 text-lg-end">
34
34
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.priority" %></div>
@@ -37,13 +37,13 @@
37
37
  <div class="col-4 col-lg-1 text-lg-end">
38
38
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.labels" %></div>
39
39
  <% job.labels&.each do |label| %>
40
- <span class="badge rounded-pill bg-secondary font-monospace"><%= label %></span>
40
+ <span class="badge rounded-pill text-bg-secondary font-monospace"><%= label %></span>
41
41
  <% end %>
42
42
  </div>
43
43
  <div class="col-4 col-lg-1 text-lg-end">
44
44
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.attempts" %></div>
45
45
  <% if job.error %>
46
- <%= tag.span job.executions_count, class: "badge rounded-pill bg-danger",
46
+ <%= tag.span job.executions_count, class: "badge rounded-pill text-bg-danger",
47
47
  data: {
48
48
  bs_toggle: "popover",
49
49
  bs_trigger: "hover focus click",
@@ -52,7 +52,7 @@
52
52
  }
53
53
  %>
54
54
  <% else %>
55
- <% executions_badge_color = job.executions_count > 1 ? "bg-warning" : "bg-secondary" %>
55
+ <% executions_badge_color = job.executions_count > 1 ? "text-bg-warning" : "text-bg-secondary" %>
56
56
  <span class="badge rounded-pill <%= executions_badge_color %>"><%= job.executions_count %></span>
57
57
  <% end %>
58
58
  </div>
@@ -4,14 +4,15 @@
4
4
 
5
5
  <div class="card my-3">
6
6
  <div class="list-group list-group-flush text-nowrap" role="table">
7
- <header class="list-group-item body-secondary">
7
+ <header class="list-group-item bg-body-tertiary">
8
8
  <div class="row small text-muted text-uppercase align-items-center">
9
- <div class="col-12 col-lg-2"></div>
10
- <div class="col-6 col-lg-2 d-none d-lg-block"><%= t "good_job.models.cron.class" %></div>
11
- <div class="col-6 col-lg-2 d-none d-lg-block"><%= t "good_job.models.cron.schedule" %></div>
12
- <div class="col-6 col-lg-2 d-none d-lg-block"><%= t "good_job.models.cron.next_scheduled" %></div>
13
- <div class="col-6 col-lg-2 d-none d-lg-block"><%= t "good_job.models.cron.last_run" %></div>
14
- <div class="col text-end">
9
+ <div class="col-lg-2"></div>
10
+ <div class="col-lg-2 d-none d-lg-block"><%= t "good_job.models.cron.class" %></div>
11
+ <div class="col-lg-2 d-none d-lg-block"><%= t "good_job.models.cron.schedule" %></div>
12
+ <div class="col-lg-2 d-none d-lg-block"><%= t "good_job.models.cron.next_scheduled" %></div>
13
+ <div class="col-lg-1 d-none d-lg-block"><%= t "good_job.models.cron.last_run" %></div>
14
+ <div class="col-lg-1 d-none d-lg-block"><%= t "good_job.models.cron.status" %></div>
15
+ <div class="col-lg-2 text-end">
15
16
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
16
17
  data: { bs_toggle: "collapse", bs_target: ".cron-entry-properties" },
17
18
  aria: { expanded: false, controls: @cron_entries.map { |cron_entry| "##{dom_id(cron_entry, 'properties')}" }.join(" ") } do %>
@@ -37,13 +38,21 @@
37
38
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.cron.next_scheduled" %></div>
38
39
  <%= relative_time cron_entry.next_at %>
39
40
  </div>
40
- <div class="col-6 col-lg-2 text-wrap small">
41
+ <div class="col-6 col-lg-1 text-wrap small">
41
42
  <% if cron_entry.last_job.present? %>
42
43
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.cron.last_run" %></div>
43
44
  <%= link_to relative_time(cron_entry.last_job_at), cron_entry_path(cron_entry), title: "Job #{cron_entry.last_job.id}" %>
44
45
  <% end %>
45
46
  </div>
46
- <div class="col d-flex gap-3 justify-content-end">
47
+ <div class="col-6 col-lg-1 text-wrap small">
48
+ <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.cron.status" %></div>
49
+ <% if cron_entry.enabled? %>
50
+ <span class="text-success"><%= t "good_job.models.cron.states.active" %></span>
51
+ <% else %>
52
+ <span class="text-muted"><%= t "good_job.models.cron.states.paused" %></span>
53
+ <% end %>
54
+ </div>
55
+ <div class="col-lg-2 d-flex gap-3 justify-content-end">
47
56
  <%= button_to enqueue_cron_entry_path(cron_entry), method: :post, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.cron_entries.actions.enqueue") }, title: t("good_job.cron_entries.actions.enqueue"), data: { confirm: t("good_job.cron_entries.actions.confirm_enqueue") } do %>
48
57
  <%= render_icon "skip_forward" %>
49
58
  <% end %>
@@ -5,7 +5,7 @@
5
5
  <%= tag.div id: dom_id(execution), class: "list-group-item py-3" do %>
6
6
  <div class="row align-items-center text-nowrap">
7
7
  <div class="col-md-5 d-flex gap-2">
8
- <%= tag.span execution.number, class: "badge bg-secondary rounded-pill" %>
8
+ <%= tag.span execution.number, class: "badge text-bg-secondary rounded-pill" %>
9
9
  <%= tag.code link_to(execution.id, "##{dom_id(execution)}", class: "text-muted text-decoration-none small") %>
10
10
  </div>
11
11
  <div class="col-md-2 small">
@@ -77,7 +77,7 @@
77
77
  </div>
78
78
  <div class="col-4 col-lg-1 text-lg-center">
79
79
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.queue" %></div>
80
- <span class="badge bg-primary font-monospace"><%= job.queue_name %></span>
80
+ <span class="badge text-bg-primary font-monospace"><%= job.queue_name %></span>
81
81
  </div>
82
82
  <div class="col-4 col-lg-1 text-lg-end">
83
83
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.priority" %></div>
@@ -86,13 +86,13 @@
86
86
  <div class="col-4 col-lg-1 text-wrap text-lg-end">
87
87
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.labels" %></div>
88
88
  <% job.labels&.each do |label| %>
89
- <span class="badge rounded-pill bg-secondary font-monospace"><%= label %></span>
89
+ <span class="badge rounded-pill text-bg-secondary font-monospace"><%= label %></span>
90
90
  <% end %>
91
91
  </div>
92
92
  <div class="col-4 col-lg-1 text-lg-end">
93
93
  <div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.job.attempts" %></div>
94
94
  <% if job.error %>
95
- <%= tag.span job.executions_count, class: "badge rounded-pill bg-danger",
95
+ <%= tag.span job.executions_count, class: "badge rounded-pill text-bg-danger",
96
96
  data: {
97
97
  bs_toggle: "popover",
98
98
  bs_trigger: "hover focus click",
@@ -101,7 +101,7 @@
101
101
  }
102
102
  %>
103
103
  <% else %>
104
- <% executions_badge_color = job.executions_count > 1 ? "bg-warning" : "bg-secondary" %>
104
+ <% executions_badge_color = job.executions_count > 1 ? "text-bg-warning" : "text-bg-secondary" %>
105
105
  <span class="badge rounded-pill <%= executions_badge_color %>"><%= job.executions_count %></span>
106
106
  <% end %>
107
107
  </div>
@@ -111,7 +111,7 @@
111
111
  <div>
112
112
  <%= status_badge job.status %>
113
113
  <% if job.status == :discarded && job.error_event %>
114
- <div class="text-black text-center">
114
+ <div class="text-center">
115
115
  <small><%= t(job.error_event, scope: 'good_job.error_event') %></small>
116
116
  </div>
117
117
  <% end %>
@@ -14,7 +14,7 @@
14
14
  </div>
15
15
  <div class="col-6 col-md-2">
16
16
  <div class="small text-muted text-uppercase"><%= t "good_job.models.job.queue" %></div>
17
- <div class="badge bg-primary font-monospace my-2">
17
+ <div class="badge text-bg-primary font-monospace my-2">
18
18
  <%= tag.strong @job.queue_name %>
19
19
  </div>
20
20
  </div>
@@ -56,7 +56,7 @@
56
56
  <li class="nav-item">
57
57
  <%= link_to filter.to_params(state: name), class: "nav-link #{'active' if params[:state] == name}" do %>
58
58
  <%= t(name, scope: 'good_job.status') %>
59
- <span data-async-values-target="value" data-async-values-key="<%= name %>" data-async-values-zero-class="bg-secondary" class="badge bg-primary rounded-pill d-none"></span>
59
+ <span data-async-values-target="value" data-async-values-key="<%= name %>" data-async-values-zero-class="text-bg-secondary" class="badge text-bg-primary rounded-pill d-none"></span>
60
60
  <% end %>
61
61
  </li>
62
62
  <% end %>
@@ -18,25 +18,25 @@
18
18
  <li class="nav-item">
19
19
  <%= link_to jobs_path, class: ["nav-link", ("active" if controller_name == 'jobs')] do %>
20
20
  <%= t(".jobs") %>
21
- <span data-async-values-target="value" data-async-values-key="jobs_count" class="badge bg-secondary rounded-pill d-none"></span>
21
+ <span data-async-values-target="value" data-async-values-key="jobs_count" class="badge text-bg-secondary rounded-pill d-none"></span>
22
22
  <% end %>
23
23
  </li>
24
24
  <li class="nav-item">
25
25
  <%= link_to batches_path, class: ["nav-link", ("active" if controller_name == 'batches')] do %>
26
26
  <%= t ".batches" %>
27
- <span data-async-values-target="value" data-async-values-key="batches_count" class="badge bg-secondary rounded-pill d-none"></span>
27
+ <span data-async-values-target="value" data-async-values-key="batches_count" class="badge text-bg-secondary rounded-pill d-none"></span>
28
28
  <% end %>
29
29
  </li>
30
30
  <li class="nav-item">
31
31
  <%= link_to cron_entries_path, class: ["nav-link", ("active" if controller_name == 'cron_entries')] do %>
32
32
  <%= t(".cron_schedules") %>
33
- <span data-async-values-target="value" data-async-values-key="cron_entries_count" class="badge bg-secondary rounded-pill d-none"></span>
33
+ <span data-async-values-target="value" data-async-values-key="cron_entries_count" class="badge text-bg-secondary rounded-pill d-none"></span>
34
34
  <% end %>
35
35
  </li>
36
36
  <li class="nav-item">
37
37
  <%= link_to processes_path, class: ["nav-link", ("active" if controller_name == 'processes')] do %>
38
38
  <%= t(".processes") %>
39
- <span data-async-values-target="value" data-async-values-key="processes_count" data-async-values-zero-class="bg-danger" class="badge bg-secondary rounded-pill d-none"></span>
39
+ <span data-async-values-target="value" data-async-values-key="processes_count" data-async-values-zero-class="text-bg-danger" class="badge text-bg-secondary rounded-pill d-none"></span>
40
40
  <% end %>
41
41
  </li>
42
42
  <li class="nav-item">
@@ -47,13 +47,13 @@
47
47
  <li class="nav-item">
48
48
  <%= link_to pauses_path, class: ["nav-link", ("active" if controller_name == 'pauses')] do %>
49
49
  <%= t(".pauses") %>
50
- <span data-async-values-target="value" data-async-values-key="pauses_count" data-async-values-zero-class="d-none" class="badge bg-warning rounded-pill d-none"></span>
50
+ <span data-async-values-target="value" data-async-values-key="pauses_count" data-async-values-zero-class="d-none" class="badge text-bg-warning rounded-pill d-none"></span>
51
51
  <% end %>
52
52
  </li>
53
53
  <li class="nav-item">
54
54
  <%= link_to cleaner_index_path, class: ["nav-link", ("active" if controller_name == 'cleaner')] do %>
55
55
  <%= t(".cleaner") %>
56
- <span data-async-values-target="value" data-async-values-key="discarded_count" class="badge bg-secondary rounded-pill d-none"></span>
56
+ <span data-async-values-target="value" data-async-values-key="discarded_count" class="badge text-bg-secondary rounded-pill d-none"></span>
57
57
  <% end %>
58
58
  </li>
59
59
  </ul>
@@ -1,4 +1,4 @@
1
- <nav class="navbar p-1 shadow-sm mt-auto border-bottom text-muted small" id="secondary-navbar" data-live-poll-region="secondary-navbar">
1
+ <nav class="navbar p-1 shadow-sm border-bottom text-muted small" id="secondary-navbar" data-live-poll-region="secondary-navbar">
2
2
  <div class="container-fluid">
3
3
  <div class="flex-fill d-none d-sm-block ">
4
4
  <%= t(".inspiration") %>
@@ -1,25 +1,21 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="<%= I18n.locale %>" data-bs-theme="auto">
3
3
  <head>
4
- <title>Good Job Dashboard</title>
5
4
  <meta charset="utf-8">
6
5
  <meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
7
- <link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%2210 0 100 100%22><text y=%22.90em%22 font-size=%2290%22>👍</text></svg>">
8
- <%= csrf_meta_tags %>
9
- <%= csp_meta_tag %>
10
6
 
11
7
  <%# Bootstrap Color Modes
12
8
  "It is suggested to include the JavaScript at the top of your page
13
9
  to reduce potential screen flickering during reloading of your site."
14
10
  https://getbootstrap.com/docs/5.3/customize/color-modes/#javascript
15
11
  %>
16
- <script nonce="<%= content_security_policy_nonce %>">
12
+ <script nonce="<%= content_security_policy_nonce %>">
17
13
  let theme = localStorage.getItem('good_job-theme');
18
14
  if (!["light", "dark"].includes(theme)) {
19
15
  theme = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
20
16
  }
21
17
  document.documentElement.setAttribute('data-bs-theme', theme);
22
- </script>
18
+ </script>
23
19
 
24
20
  <%# Do not use asset tag helpers to avoid paths being overriden by config.asset_host %>
25
21
  <%= tag.link rel: "stylesheet", href: frontend_static_path(:bootstrap, format: :css, locale: nil), nonce: content_security_policy_nonce %>
@@ -31,6 +27,11 @@
31
27
  <% importmaps = GoodJob::FrontendsController.js_modules.keys.index_with { |module_name| frontend_module_path(module_name, format: :js, locale: nil) } %>
32
28
  <%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %>
33
29
  <%= tag.script "", type: "module", nonce: content_security_policy_nonce do %> import "application"; <% end %>
30
+
31
+ <title>Good Job Dashboard</title>
32
+ <%= tag.link rel: "icon", href: 'data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="10 0 100 100"><text y=".90em" font-size="90">👍</text></svg>' %>
33
+ <%= csrf_meta_tags %>
34
+ <%= csp_meta_tag %>
34
35
  </head>
35
36
  <body>
36
37
  <div class="d-flex flex-column min-vh-100">
@@ -191,6 +191,10 @@ de:
191
191
  last_run: Letzter Lauf
192
192
  next_scheduled: Als nächstes geplant
193
193
  schedule: Zeitplan
194
+ states:
195
+ active: Aktiv
196
+ paused: Pausiert
197
+ status: Status
194
198
  job:
195
199
  arguments: Argumente
196
200
  attempts: Versuche
@@ -191,6 +191,10 @@ en:
191
191
  last_run: Last run
192
192
  next_scheduled: Next scheduled
193
193
  schedule: Schedule
194
+ states:
195
+ active: Active
196
+ paused: Paused
197
+ status: Status
194
198
  job:
195
199
  arguments: Arguments
196
200
  attempts: Attempts
@@ -191,6 +191,10 @@ es:
191
191
  last_run: Última ejecución
192
192
  next_scheduled: Próxima ejecución
193
193
  schedule: Cronograma
194
+ states:
195
+ active: Activo
196
+ paused: Pausado
197
+ status: Estado
194
198
  job:
195
199
  arguments: Argumentos
196
200
  attempts: Intentos
@@ -191,6 +191,10 @@ fr:
191
191
  last_run: Dernière exécution
192
192
  next_scheduled: Prochaine exécution
193
193
  schedule: Planification
194
+ states:
195
+ active: Actif
196
+ paused: En pause
197
+ status: État
194
198
  job:
195
199
  arguments: Paramètres
196
200
  attempts: Tentatives
@@ -191,6 +191,10 @@ it:
191
191
  last_run: Ultima esecuzione
192
192
  next_scheduled: Prossima pianificazione
193
193
  schedule: Pianificazione
194
+ states:
195
+ active: Attivo
196
+ paused: In pausa
197
+ status: Stato
194
198
  job:
195
199
  arguments: Argomenti
196
200
  attempts: Tentativi
@@ -191,6 +191,10 @@ ja:
191
191
  last_run: 前回実行日時
192
192
  next_scheduled: 次回予定日時
193
193
  schedule: スケジュール
194
+ states:
195
+ active: アクティブ
196
+ paused: 一時停止
197
+ status: 状態
194
198
  job:
195
199
  arguments: 引数
196
200
  attempts: 試行回数
@@ -191,6 +191,10 @@ ko:
191
191
  last_run: 마지막 실행 일시
192
192
  next_scheduled: 다음 예정 일시
193
193
  schedule: 스케줄
194
+ states:
195
+ active: 활성
196
+ paused: 일시정지
197
+ status: 상태
194
198
  job:
195
199
  arguments: 인수
196
200
  attempts: 시도 횟수
@@ -191,6 +191,10 @@ nl:
191
191
  last_run: Laatste ronde
192
192
  next_scheduled: Volgende gepland
193
193
  schedule: Schema
194
+ states:
195
+ active: Actief
196
+ paused: Gepauzeerd
197
+ status: Status
194
198
  job:
195
199
  arguments: Argumenten
196
200
  attempts: Pogingen
@@ -191,6 +191,10 @@ pt-BR:
191
191
  last_run: Última execução
192
192
  next_scheduled: Próximo agendamento
193
193
  schedule: Agendamento
194
+ states:
195
+ active: Ativo
196
+ paused: Pausado
197
+ status: Estado
194
198
  job:
195
199
  arguments: Argumentos
196
200
  attempts: Tentativas
@@ -217,6 +217,10 @@ ru:
217
217
  last_run: Последний запуск
218
218
  next_scheduled: Следующий по расписанию
219
219
  schedule: Расписание
220
+ states:
221
+ active: Активно
222
+ paused: Приостановлено
223
+ status: Состояние
220
224
  job:
221
225
  arguments: Параметры
222
226
  attempts: Попытки
@@ -191,6 +191,10 @@ tr:
191
191
  last_run: Son çalıştırma
192
192
  next_scheduled: Sonraki planlama
193
193
  schedule: Program
194
+ states:
195
+ active: Aktif
196
+ paused: Duraklatıldı
197
+ status: Durum
194
198
  job:
195
199
  arguments: Argümanlar
196
200
  attempts: Denemeler
@@ -217,6 +217,10 @@ uk:
217
217
  last_run: Останній запуск
218
218
  next_scheduled: Наступне заплановане
219
219
  schedule: Розклад
220
+ states:
221
+ active: Активний
222
+ paused: Призупинено
223
+ status: Стан
220
224
  job:
221
225
  arguments: Аргументи
222
226
  attempts: Спроби
data/exe/good_job CHANGED
@@ -4,5 +4,4 @@
4
4
  require 'good_job/cli'
5
5
 
6
6
  GoodJob::CLI.within_exe = true
7
- GoodJob::CLI.log_to_stdout = true
8
7
  GoodJob::CLI.start(ARGV)
@@ -92,6 +92,7 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
92
92
  where: "finished_at IS NULL", name: :index_good_job_jobs_for_candidate_lookup
93
93
  add_index :good_jobs, [:batch_id], where: "batch_id IS NOT NULL"
94
94
  add_index :good_jobs, [:batch_callback_id], where: "batch_callback_id IS NOT NULL"
95
+ add_index :good_jobs, :job_class, name: :index_good_jobs_on_job_class
95
96
  add_index :good_jobs, :labels, using: :gin, where: "(labels IS NOT NULL)", name: :index_good_jobs_on_labels
96
97
 
97
98
  add_index :good_job_executions, [:active_job_id, :created_at], name: :index_good_job_executions_on_active_job_id_and_created_at
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddIndexGoodJobsJobClass < ActiveRecord::Migration<%= migration_version %>
4
+ disable_ddl_transaction!
5
+
6
+ def change
7
+ reversible do |dir|
8
+ dir.up do
9
+ # Ensure this incremental update migration is idempotent
10
+ # with monolithic install migration.
11
+ return if connection.index_exists? :good_jobs, :job_class
12
+ end
13
+ end
14
+
15
+ add_index :good_jobs, :job_class, algorithm: :concurrently
16
+ end
17
+ end
data/lib/good_job/cli.rb CHANGED
@@ -29,10 +29,15 @@ module GoodJob
29
29
  attr_accessor :within_exe
30
30
  alias within_exe? within_exe
31
31
 
32
- # Whether to log to STDOUT
32
+ # Whether the CLI's default logger should log to STDOUT.
33
33
  # @return [Boolean, nil]
34
34
  attr_accessor :log_to_stdout
35
- alias log_to_stdout? log_to_stdout
35
+
36
+ # Whether the CLI's default logger logs to STDOUT.
37
+ # @return [Boolean, nil]
38
+ def log_to_stdout?
39
+ GoodJob::CLI.log_to_stdout.nil? ? GoodJob::CLI.within_exe? : GoodJob::CLI.log_to_stdout
40
+ end
36
41
 
37
42
  # @!visibility private
38
43
  def exit_on_failure?
@@ -2,7 +2,7 @@
2
2
 
3
3
  module GoodJob
4
4
  # GoodJob gem version.
5
- VERSION = '4.11.2'
5
+ VERSION = '4.12.1'
6
6
 
7
7
  # GoodJob version as Gem::Version object
8
8
  GEM_VERSION = Gem::Version.new(VERSION)
data/lib/good_job.rb CHANGED
@@ -290,7 +290,7 @@ module GoodJob
290
290
  # For use in tests/CI to validate GoodJob is up-to-date.
291
291
  # @return [Boolean]
292
292
  def self.migrated?
293
- GoodJob::Job.concurrency_key_created_at_index_migrated?
293
+ GoodJob::Job.job_class_index_migrated?
294
294
  end
295
295
 
296
296
  # Pause job execution for a given queue or job class.
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: good_job
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.11.2
4
+ version: 4.12.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Sheldon
@@ -349,6 +349,7 @@ files:
349
349
  - lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb
350
350
  - lib/generators/good_job/templates/update/migrations/02_add_jobs_finished_at_to_good_job_batches.rb.erb
351
351
  - lib/generators/good_job/templates/update/migrations/03_add_index_good_jobs_concurrency_key_created_at.rb.erb
352
+ - lib/generators/good_job/templates/update/migrations/04_add_index_good_jobs_job_class.rb.erb
352
353
  - lib/generators/good_job/update_generator.rb
353
354
  - lib/good_job.rb
354
355
  - lib/good_job/active_job_extensions/batches.rb
@@ -421,7 +422,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
421
422
  - !ruby/object:Gem::Version
422
423
  version: '0'
423
424
  requirements: []
424
- rubygems_version: 3.6.9
425
+ rubygems_version: 3.7.2
425
426
  specification_version: 4
426
427
  summary: A multithreaded, Postgres-based ActiveJob backend for Ruby on Rails
427
428
  test_files: []