good_job 2.13.0 → 2.14.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/CHANGELOG.md +53 -1
- data/engine/app/assets/good_job/modules/application.js +14 -0
- data/engine/app/assets/good_job/modules/charts.js +29 -0
- data/engine/app/assets/good_job/modules/checkbox_toggle.js +51 -0
- data/engine/app/assets/good_job/modules/document_ready.js +7 -0
- data/engine/app/assets/good_job/modules/poller.js +93 -0
- data/engine/app/assets/good_job/modules/toasts.js +8 -0
- data/engine/app/assets/good_job/scripts.js +3 -0
- data/engine/app/assets/{style.css → good_job/style.css} +4 -0
- data/engine/app/assets/{vendor → good_job/vendor}/bootstrap/bootstrap.bundle.min.js +0 -0
- data/engine/app/assets/{vendor → good_job/vendor}/bootstrap/bootstrap.min.css +0 -0
- data/engine/app/assets/{vendor → good_job/vendor}/chartjs/chart.min.js +0 -0
- data/engine/app/assets/good_job/vendor/es_module_shims.js +1 -0
- data/engine/app/assets/{vendor → good_job/vendor}/rails_ujs.js +0 -0
- data/engine/app/controllers/good_job/assets_controller.rb +23 -6
- data/engine/app/controllers/good_job/executions_controller.rb +1 -5
- data/engine/app/controllers/good_job/jobs_controller.rb +45 -2
- data/engine/app/filters/good_job/base_filter.rb +3 -0
- data/engine/app/filters/good_job/jobs_filter.rb +5 -2
- data/engine/app/helpers/good_job/application_helper.rb +6 -0
- data/engine/app/views/good_job/executions/_table.erb +1 -1
- data/engine/app/views/good_job/jobs/_table.erb +101 -62
- data/engine/app/views/good_job/jobs/index.html.erb +1 -1
- data/engine/app/views/good_job/shared/_alert.erb +20 -13
- data/engine/app/views/good_job/shared/_navbar.erb +1 -4
- data/engine/app/views/layouts/good_job/application.html.erb +11 -8
- data/engine/config/locales/en.yml +0 -1
- data/engine/config/locales/es.yml +0 -1
- data/engine/config/locales/nl.yml +0 -1
- data/engine/config/locales/ru.yml +0 -1
- data/engine/config/routes.rb +10 -3
- data/lib/good_job/active_job_job.rb +9 -7
- data/lib/good_job/lockable.rb +10 -0
- data/lib/good_job/version.rb +1 -1
- metadata +15 -10
- data/engine/app/assets/scripts.js +0 -133
- data/engine/app/filters/good_job/executions_filter.rb +0 -41
- data/engine/app/views/good_job/executions/index.html.erb +0 -15
@@ -1,72 +1,111 @@
|
|
1
1
|
<div class="my-3" data-gj-poll-replace id="jobs-table">
|
2
2
|
<div class="table-responsive">
|
3
|
-
|
4
|
-
<
|
5
|
-
<
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
<%= link_to job_path(job.id) do %>
|
29
|
-
<code><%= job.id %></code>
|
3
|
+
<%= form_with(url: mass_update_jobs_path, method: :put, local: true, data: { "checkbox-toggle": "job_ids" }) do |form| %>
|
4
|
+
<table class="table table-hover table-sm mb-0">
|
5
|
+
<thead>
|
6
|
+
<tr>
|
7
|
+
<th><%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %></th>
|
8
|
+
<th>ActiveJob ID</th>
|
9
|
+
<th>State</th>
|
10
|
+
<th>Job Class</th>
|
11
|
+
<th>Queue</th>
|
12
|
+
<th>Scheduled At</th>
|
13
|
+
<th>Executions</th>
|
14
|
+
<th>Error</th>
|
15
|
+
<th>
|
16
|
+
ActiveJob Params
|
17
|
+
<%= tag.button "Toggle", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
18
|
+
data: { bs_toggle: "collapse", bs_target: ".job-params" },
|
19
|
+
aria: { expanded: false, controls: jobs.map { |job| "##{dom_id(job, "params")}" }.join(" ") }
|
20
|
+
%>
|
21
|
+
</th>
|
22
|
+
<th>
|
23
|
+
Actions<br>
|
24
|
+
|
25
|
+
<div class="d-inline text-nowrap">
|
26
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'reschedule', class: 'btn btn-sm btn-outline-primary', title: "Reschedule all", data: { confirm: "Confirm reschedule all", disable: true } do %>
|
27
|
+
<%= render_icon "skip_forward" %> All
|
30
28
|
<% end %>
|
31
|
-
</td>
|
32
|
-
<td><%= status_badge(job.status) %></td>
|
33
|
-
<td><%= job.job_class %></td>
|
34
|
-
<td><%= job.queue_name %></td>
|
35
|
-
<td><%= relative_time(job.scheduled_at || job.created_at) %></td>
|
36
|
-
<td><%= job.executions_count %></td>
|
37
|
-
<td class="text-break"><%= truncate(job.recent_error, length: 1_000) %></td>
|
38
|
-
<td>
|
39
|
-
<%= tag.button "Preview", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
40
|
-
data: { bs_toggle: "collapse", bs_target: "##{dom_id(job, 'params')}" },
|
41
|
-
aria: { expanded: false, controls: dom_id(job, "params") }
|
42
|
-
%>
|
43
|
-
<%= tag.pre JSON.pretty_generate(job.serialized_params), id: dom_id(job, "params"), class: "collapse job-params" %>
|
44
|
-
</td>
|
45
|
-
<td>
|
46
|
-
<div class="text-nowrap">
|
47
|
-
<% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
|
48
|
-
<%= button_to reschedule_job_path(job.id), method: :put, class: "btn btn-sm #{job_reschedulable ? 'btn-outline-primary' : 'btn-outline-secondary'}", form_class: "d-inline-block", disabled: !job_reschedulable, aria: { label: "Reschedule job" }, title: "Reschedule job", data: { confirm: "Confirm reschedule" } do %>
|
49
|
-
<%= render "good_job/shared/icons/skip_forward" %>
|
50
|
-
<% end %>
|
51
29
|
|
52
|
-
|
53
|
-
<%=
|
54
|
-
|
55
|
-
<% end %>
|
30
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'discard', class: 'btn btn-sm btn-outline-primary', title: "Discard all", data: { confirm: "Confirm discard all", disable: true } do %>
|
31
|
+
<%= render_icon "stop" %> All
|
32
|
+
<% end %>
|
56
33
|
|
57
|
-
|
58
|
-
|
34
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'retry', class: 'btn btn-sm btn-outline-primary', title: "Retry all", data: { confirm: "Confirm retry all", disable: true } do %>
|
35
|
+
<%= render_icon "arrow_clockwise" %> All
|
36
|
+
<% end %>
|
37
|
+
</div>
|
38
|
+
</tr>
|
39
|
+
<tr class="d-none" data-checkbox-toggle-show="job_ids">
|
40
|
+
<td class="text-center table-warning" colspan="10">
|
41
|
+
<% all_jobs_count = local_assigns[:all_jobs_count] %>
|
42
|
+
<label>
|
43
|
+
<%= check_box_tag "all_job_ids", 1, false, disabled: true, data: { "checkbox-toggle-show": "job_ids"} %>
|
44
|
+
Apply to all <%= all_jobs_count.present? ? number_with_delimiter(all_jobs_count) : "" %> <%= "job".pluralize(all_jobs_count || 99) %>.
|
45
|
+
<em>This could be a lot.</em>
|
46
|
+
</label>
|
47
|
+
</td>
|
48
|
+
</tr>
|
49
|
+
</thead>
|
50
|
+
<tbody>
|
51
|
+
<% if jobs.present? %>
|
52
|
+
<% jobs.each do |job| %>
|
53
|
+
<tr class="<%= dom_class(job) %>" id="<%= dom_id(job) %>">
|
54
|
+
<td><%= check_box_tag 'job_ids[]', job.id, false, data: { "checkbox-toggle-each": "job_ids" } %></td>
|
55
|
+
<td>
|
56
|
+
<%= link_to job_path(job.id) do %>
|
57
|
+
<code><%= job.id %></code>
|
59
58
|
<% end %>
|
60
|
-
</
|
61
|
-
|
59
|
+
</td>
|
60
|
+
<td><%= status_badge(job.status) %></td>
|
61
|
+
<td><%= job.job_class %></td>
|
62
|
+
<td><%= job.queue_name %></td>
|
63
|
+
<td><%= relative_time(job.scheduled_at || job.created_at) %></td>
|
64
|
+
<td><%= job.executions_count %></td>
|
65
|
+
<td class="text-break"><%= truncate(job.recent_error, length: 1_000) %></td>
|
66
|
+
<td>
|
67
|
+
<%= tag.button "Preview", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
68
|
+
data: { bs_toggle: "collapse", bs_target: "##{dom_id(job, 'params')}" },
|
69
|
+
aria: { expanded: false, controls: dom_id(job, "params") }
|
70
|
+
%>
|
71
|
+
<%= tag.pre JSON.pretty_generate(job.serialized_params), id: dom_id(job, "params"), class: "collapse job-params" %>
|
72
|
+
</td>
|
73
|
+
<td>
|
74
|
+
<div class="text-nowrap">
|
75
|
+
<% if job.status.in? [:scheduled, :retried, :queued] %>
|
76
|
+
<%= link_to reschedule_job_path(job.id), method: :put, class: "btn btn-sm btn-outline-primary", title: "Reschedule job", data: { confirm: "Confirm reschedule", disable: true } do %>
|
77
|
+
<%= render_icon "skip_forward" %>
|
78
|
+
<% end %>
|
79
|
+
<% else %>
|
80
|
+
<button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "skip_forward" %></button>
|
81
|
+
<% end %>
|
82
|
+
|
83
|
+
<% if job.status.in? [:scheduled, :retried, :queued] %>
|
84
|
+
<%= link_to discard_job_path(job.id), method: :put, class: "btn btn-sm btn-outline-primary", title: "Discard job", data: { confirm: "Confirm discard", disable: true } do %>
|
85
|
+
<%= render_icon "stop" %>
|
86
|
+
<% end %>
|
87
|
+
<% else %>
|
88
|
+
<button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "stop" %></button>
|
89
|
+
<% end %>
|
90
|
+
|
91
|
+
<% if job.status == :discarded %>
|
92
|
+
<%= link_to retry_job_path(job.id), method: :put, class: "btn btn-sm btn-outline-primary", title: "Retry job", data: { confirm: "Confirm retry", disable: true } do %>
|
93
|
+
<%= render_icon "arrow_clockwise" %>
|
94
|
+
<% end %>
|
95
|
+
<% else %>
|
96
|
+
<button class="btn btn-sm btn-outline-secondary" disabled><%= render_icon "arrow_clockwise" %></button>
|
97
|
+
<% end %>
|
98
|
+
</div>
|
99
|
+
</td>
|
100
|
+
</tr>
|
101
|
+
<% end %>
|
102
|
+
<% else %>
|
103
|
+
<tr>
|
104
|
+
<td colspan="10" class="py-2 text-center text-muted">No jobs found.</td>
|
62
105
|
</tr>
|
63
106
|
<% end %>
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
</tr>
|
68
|
-
<% end %>
|
69
|
-
</tbody>
|
70
|
-
</table>
|
107
|
+
</tbody>
|
108
|
+
</table>
|
109
|
+
<% end %>
|
71
110
|
</div>
|
72
111
|
</div>
|
@@ -1,6 +1,6 @@
|
|
1
1
|
<%= render 'good_job/shared/filter', title: "Jobs", filter: @filter %>
|
2
2
|
<%= render 'good_job/shared/chart', chart_data: GoodJob::ScheduledByQueueChart.new(@filter).data %>
|
3
|
-
<%= render 'good_job/jobs/table', jobs: @filter.records %>
|
3
|
+
<%= render 'good_job/jobs/table', jobs: @filter.records, all_jobs_count: @filter.filtered_query_count %>
|
4
4
|
|
5
5
|
<% if @filter.records.present? %>
|
6
6
|
<nav aria-label="Job pagination" class="mt-3" data-gj-poll-replace id="jobs-pagination">
|
@@ -1,13 +1,20 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
1
|
+
<div class="toast-container position-fixed p-3 start-50 translate-middle-x">
|
2
|
+
<% if notice %>
|
3
|
+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
4
|
+
<div class="toast-body d-flex align-items-center gap-2">
|
5
|
+
<%= render "good_job/shared/icons/check", class: "flex-shrink-0 text-success" %>
|
6
|
+
<div class="flex-fill"><%= notice %></div>
|
7
|
+
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
<% end %>
|
11
|
+
<% if alert %>
|
12
|
+
<div class="toast" role="alert" aria-live="assertive" aria-atomic="true">
|
13
|
+
<div class="toast-body d-flex align-items-center gap-2">
|
14
|
+
<%= render "good_job/shared/icons/exclamation", class: "flex-shrink-0 text-danger" %>
|
15
|
+
<div class="flex-fill"><%= alert %></div>
|
16
|
+
<button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="Close"></button>
|
17
|
+
</div>
|
18
|
+
</div>
|
19
|
+
<% end %>
|
20
|
+
</div>
|
@@ -7,9 +7,6 @@
|
|
7
7
|
|
8
8
|
<div class="collapse navbar-collapse" id="navbarSupportedContent">
|
9
9
|
<ul class="navbar-nav me-auto">
|
10
|
-
<li class="nav-item">
|
11
|
-
<%= link_to t(".executions"), root_path, class: ["nav-link", ("active" if controller_name == 'executions')] %>
|
12
|
-
</li>
|
13
10
|
<li class="nav-item">
|
14
11
|
<%= link_to t(".jobs"), jobs_path, class: ["nav-link", ("active" if controller_name == 'jobs')] %>
|
15
12
|
</li>
|
@@ -22,7 +19,7 @@
|
|
22
19
|
</ul>
|
23
20
|
<div class="nav-item pe-2">
|
24
21
|
<div class="form-check">
|
25
|
-
<input type="checkbox" id="toggle-poll" name="toggle-poll"
|
22
|
+
<input type="checkbox" id="toggle-poll" name="toggle-poll" <%= 'checked' if params[:poll].present? %>>
|
26
23
|
<label for="toggle-poll"><%= t(".live_poll") %></label>
|
27
24
|
</div>
|
28
25
|
</div>
|
@@ -7,21 +7,24 @@
|
|
7
7
|
<%= csrf_meta_tags %>
|
8
8
|
<%= csp_meta_tag %>
|
9
9
|
|
10
|
-
<%#
|
11
|
-
<%=
|
12
|
-
<%=
|
10
|
+
<%# Do not use asset tag helpers to avoid paths being overriden by config.asset_host %>
|
11
|
+
<%= tag.link rel: "stylesheet", media: "screen", href: bootstrap_path(format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
|
12
|
+
<%= tag.link rel: "stylesheet", media: "screen", href: style_path(format: :css, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
|
13
13
|
|
14
|
-
<%=
|
15
|
-
<%=
|
16
|
-
<%=
|
14
|
+
<%= tag.script "", src: bootstrap_path(format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
|
15
|
+
<%= tag.script "", src: chartjs_path(format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
|
16
|
+
<%= tag.script "", src: rails_ujs_path(format: :js, v: GoodJob::VERSION, locale: nil), nonce: content_security_policy_nonce %>
|
17
17
|
|
18
|
-
<%=
|
18
|
+
<%= tag.script "", src: es_module_shims_path(format: :js, v: GoodJob::VERSION, locale: nil), async: true, nonce: content_security_policy_nonce %>
|
19
|
+
<% importmaps = { imports: GoodJob::AssetsController.js_modules.keys.each_with_object({}) { |module_name, imports| imports[module_name] = modules_path(module_name, format: :js, locale: nil, v: GoodJob::VERSION) } } %>
|
20
|
+
<%= tag.script importmaps.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce %>
|
21
|
+
<%= tag.script "", src: scripts_path(format: :js, v: GoodJob::VERSION, locale: nil), type: "module", nonce: content_security_policy_nonce %>
|
19
22
|
</head>
|
20
23
|
<body>
|
21
24
|
<div class="d-flex flex-column min-vh-100">
|
22
25
|
<%= render "good_job/shared/navbar" %>
|
23
26
|
|
24
|
-
<div class="container-fluid flex-grow-1">
|
27
|
+
<div class="container-fluid flex-grow-1 relative">
|
25
28
|
<%= render "good_job/shared/alert" %>
|
26
29
|
|
27
30
|
<%= yield %>
|
data/engine/config/routes.rb
CHANGED
@@ -1,10 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
GoodJob::Engine.routes.draw do
|
3
|
-
root to: '
|
3
|
+
root to: redirect(path: 'jobs')
|
4
4
|
|
5
5
|
resources :executions, only: %i[destroy]
|
6
6
|
|
7
7
|
resources :jobs, only: %i[index show] do
|
8
|
+
collection do
|
9
|
+
get :mass_update, to: redirect(path: 'jobs')
|
10
|
+
put :mass_update
|
11
|
+
end
|
12
|
+
|
8
13
|
member do
|
9
14
|
put :discard
|
10
15
|
put :reschedule
|
@@ -20,7 +25,7 @@ GoodJob::Engine.routes.draw do
|
|
20
25
|
|
21
26
|
resources :processes, only: %i[index]
|
22
27
|
|
23
|
-
scope controller: :assets do
|
28
|
+
scope :assets, controller: :assets do
|
24
29
|
constraints(format: :css) do
|
25
30
|
get :bootstrap, action: :bootstrap_css
|
26
31
|
get :style, action: :style_css
|
@@ -28,8 +33,10 @@ GoodJob::Engine.routes.draw do
|
|
28
33
|
|
29
34
|
constraints(format: :js) do
|
30
35
|
get :bootstrap, action: :bootstrap_js
|
31
|
-
get :rails_ujs, action: :rails_ujs_js
|
32
36
|
get :chartjs, action: :chartjs_js
|
37
|
+
get :rails_ujs, action: :rails_ujs_js
|
38
|
+
get :es_module_shims, action: :es_module_shims_js
|
39
|
+
get "modules/:module", action: :modules_js, as: :modules
|
33
40
|
get :scripts, action: :scripts_js
|
34
41
|
end
|
35
42
|
end
|
@@ -148,7 +148,7 @@ module GoodJob
|
|
148
148
|
# Tests whether the job is being executed right now.
|
149
149
|
# @return [Boolean]
|
150
150
|
def running?
|
151
|
-
# Avoid N+1 Query: `.
|
151
|
+
# Avoid N+1 Query: `.includes_advisory_locks`
|
152
152
|
if has_attribute?(:locktype)
|
153
153
|
self['locktype'].present?
|
154
154
|
else
|
@@ -157,7 +157,7 @@ module GoodJob
|
|
157
157
|
end
|
158
158
|
|
159
159
|
# Retry a job that has errored and been discarded.
|
160
|
-
# This action will create a new
|
160
|
+
# This action will create a new {Execution} record for the job.
|
161
161
|
# @return [ActiveJob::Base]
|
162
162
|
def retry_job
|
163
163
|
with_advisory_lock do
|
@@ -165,7 +165,7 @@ module GoodJob
|
|
165
165
|
active_job = execution.active_job
|
166
166
|
|
167
167
|
raise AdapterNotGoodJobError unless active_job.class.queue_adapter.is_a? GoodJob::Adapter
|
168
|
-
raise ActionForStateMismatchError
|
168
|
+
raise ActionForStateMismatchError if execution.finished_at.blank? || execution.error.blank?
|
169
169
|
|
170
170
|
# Update the executions count because the previous execution will not have been preserved
|
171
171
|
# Do not update `exception_executions` because that comes from rescue_from's arguments
|
@@ -176,7 +176,7 @@ module GoodJob
|
|
176
176
|
current_thread.execution = execution
|
177
177
|
|
178
178
|
execution.class.transaction(joinable: false, requires_new: true) do
|
179
|
-
new_active_job = active_job.retry_job(wait: 0, error: error)
|
179
|
+
new_active_job = active_job.retry_job(wait: 0, error: execution.error)
|
180
180
|
execution.save
|
181
181
|
end
|
182
182
|
end
|
@@ -189,11 +189,11 @@ module GoodJob
|
|
189
189
|
# @return [void]
|
190
190
|
def discard_job(message)
|
191
191
|
with_advisory_lock do
|
192
|
-
raise ActionForStateMismatchError unless status.in? [:scheduled, :queued, :retried]
|
193
|
-
|
194
192
|
execution = head_execution(reload: true)
|
195
193
|
active_job = execution.active_job
|
196
194
|
|
195
|
+
raise ActionForStateMismatchError if execution.finished_at.present?
|
196
|
+
|
197
197
|
job_error = GoodJob::ActiveJobJob::DiscardJobError.new(message)
|
198
198
|
|
199
199
|
update_execution = proc do
|
@@ -216,7 +216,9 @@ module GoodJob
|
|
216
216
|
# @return [void]
|
217
217
|
def reschedule_job(scheduled_at = Time.current)
|
218
218
|
with_advisory_lock do
|
219
|
-
|
219
|
+
execution = head_execution(reload: true)
|
220
|
+
|
221
|
+
raise ActionForStateMismatchError if execution.finished_at.present?
|
220
222
|
|
221
223
|
execution = head_execution(reload: true)
|
222
224
|
execution.update(scheduled_at: scheduled_at)
|
data/lib/good_job/lockable.rb
CHANGED
@@ -82,6 +82,16 @@ module GoodJob
|
|
82
82
|
joins(sanitize_sql_for_conditions([join_sql, { table_name: table_name }]))
|
83
83
|
end)
|
84
84
|
|
85
|
+
# Joins the current query with Postgres's +pg_locks+ table AND SELECTs the resulting columns
|
86
|
+
# @!method joins_advisory_locks(column: _advisory_lockable_column)
|
87
|
+
# @!scope class
|
88
|
+
# @param column [String, Symbol] column values to Advisory Lock against
|
89
|
+
# @return [ActiveRecord::Relation]
|
90
|
+
scope :includes_advisory_locks, (lambda do |column: _advisory_lockable_column|
|
91
|
+
owns_advisory_lock_sql = "#{connection.quote_table_name('pg_locks')}.#{connection.quote_column_name('pid')} = pg_backend_pid() AS owns_advisory_lock"
|
92
|
+
joins_advisory_locks(column: column).select("#{quoted_table_name}.*, #{connection.quote_table_name('pg_locks')}.locktype, #{owns_advisory_lock_sql}")
|
93
|
+
end)
|
94
|
+
|
85
95
|
# Find records that do not have an advisory lock on them.
|
86
96
|
# @!method advisory_unlocked(column: _advisory_lockable_column)
|
87
97
|
# @!scope class
|
data/lib/good_job/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: good_job
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.14.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ben Sheldon
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -360,12 +360,19 @@ files:
|
|
360
360
|
- CHANGELOG.md
|
361
361
|
- LICENSE.txt
|
362
362
|
- README.md
|
363
|
-
- engine/app/assets/
|
364
|
-
- engine/app/assets/
|
365
|
-
- engine/app/assets/
|
366
|
-
- engine/app/assets/
|
367
|
-
- engine/app/assets/
|
368
|
-
- engine/app/assets/
|
363
|
+
- engine/app/assets/good_job/modules/application.js
|
364
|
+
- engine/app/assets/good_job/modules/charts.js
|
365
|
+
- engine/app/assets/good_job/modules/checkbox_toggle.js
|
366
|
+
- engine/app/assets/good_job/modules/document_ready.js
|
367
|
+
- engine/app/assets/good_job/modules/poller.js
|
368
|
+
- engine/app/assets/good_job/modules/toasts.js
|
369
|
+
- engine/app/assets/good_job/scripts.js
|
370
|
+
- engine/app/assets/good_job/style.css
|
371
|
+
- engine/app/assets/good_job/vendor/bootstrap/bootstrap.bundle.min.js
|
372
|
+
- engine/app/assets/good_job/vendor/bootstrap/bootstrap.min.css
|
373
|
+
- engine/app/assets/good_job/vendor/chartjs/chart.min.js
|
374
|
+
- engine/app/assets/good_job/vendor/es_module_shims.js
|
375
|
+
- engine/app/assets/good_job/vendor/rails_ujs.js
|
369
376
|
- engine/app/charts/good_job/scheduled_by_queue_chart.rb
|
370
377
|
- engine/app/controllers/good_job/application_controller.rb
|
371
378
|
- engine/app/controllers/good_job/assets_controller.rb
|
@@ -374,13 +381,11 @@ files:
|
|
374
381
|
- engine/app/controllers/good_job/jobs_controller.rb
|
375
382
|
- engine/app/controllers/good_job/processes_controller.rb
|
376
383
|
- engine/app/filters/good_job/base_filter.rb
|
377
|
-
- engine/app/filters/good_job/executions_filter.rb
|
378
384
|
- engine/app/filters/good_job/jobs_filter.rb
|
379
385
|
- engine/app/helpers/good_job/application_helper.rb
|
380
386
|
- engine/app/views/good_job/cron_entries/index.html.erb
|
381
387
|
- engine/app/views/good_job/cron_entries/show.html.erb
|
382
388
|
- engine/app/views/good_job/executions/_table.erb
|
383
|
-
- engine/app/views/good_job/executions/index.html.erb
|
384
389
|
- engine/app/views/good_job/jobs/_table.erb
|
385
390
|
- engine/app/views/good_job/jobs/index.html.erb
|
386
391
|
- engine/app/views/good_job/jobs/show.html.erb
|
@@ -1,133 +0,0 @@
|
|
1
|
-
/*jshint esversion: 6, strict: false */
|
2
|
-
const GOOD_JOB_DEFAULT_POLL_INTERVAL_SECONDS = 30;
|
3
|
-
const GOOD_JOB_MINIMUM_POLL_INTERVAL = 1000;
|
4
|
-
|
5
|
-
const GoodJob = {
|
6
|
-
// Register functions to execute when the DOM is ready
|
7
|
-
ready: (callback) => {
|
8
|
-
if (document.readyState !== "loading") {
|
9
|
-
callback();
|
10
|
-
} else {
|
11
|
-
document.addEventListener("DOMContentLoaded", callback);
|
12
|
-
}
|
13
|
-
},
|
14
|
-
|
15
|
-
init: () => {
|
16
|
-
GoodJob.updateSettings();
|
17
|
-
GoodJob.addListeners();
|
18
|
-
GoodJob.pollUpdates();
|
19
|
-
GoodJob.renderCharts(true);
|
20
|
-
},
|
21
|
-
|
22
|
-
addListeners: () => {
|
23
|
-
const gjActionEls = document.querySelectorAll('[data-gj-action]');
|
24
|
-
|
25
|
-
for (let i = 0; i < gjActionEls.length; i++) {
|
26
|
-
const el = gjActionEls[i];
|
27
|
-
const [eventName, func] = el.dataset.gjAction.split('#');
|
28
|
-
|
29
|
-
el.addEventListener(eventName, GoodJob[func]);
|
30
|
-
}
|
31
|
-
},
|
32
|
-
|
33
|
-
updateSettings: () => {
|
34
|
-
const queryString = window.location.search;
|
35
|
-
const urlParams = new URLSearchParams(queryString);
|
36
|
-
|
37
|
-
// live poll interval and enablement
|
38
|
-
if (urlParams.has('poll')) {
|
39
|
-
const parsedInterval = (parseInt(urlParams.get('poll')) || GOOD_JOB_DEFAULT_POLL_INTERVAL_SECONDS) * 1000;
|
40
|
-
GoodJob.pollInterval = Math.max(parsedInterval, GOOD_JOB_MINIMUM_POLL_INTERVAL);
|
41
|
-
GoodJob.setStorage('pollInterval', GoodJob.pollInterval);
|
42
|
-
|
43
|
-
GoodJob.pollEnabled = true;
|
44
|
-
} else {
|
45
|
-
GoodJob.pollInterval = GoodJob.getStorage('pollInterval') || (GOOD_JOB_DEFAULT_POLL_INTERVAL_SECONDS * 1000);
|
46
|
-
GoodJob.pollEnabled = GoodJob.getStorage('pollEnabled') || false;
|
47
|
-
}
|
48
|
-
|
49
|
-
document.getElementById('toggle-poll').checked = GoodJob.pollEnabled;
|
50
|
-
},
|
51
|
-
|
52
|
-
togglePoll: (ev) => {
|
53
|
-
GoodJob.pollEnabled = ev.currentTarget.checked;
|
54
|
-
GoodJob.setStorage('pollEnabled', GoodJob.pollEnabled);
|
55
|
-
},
|
56
|
-
|
57
|
-
pollUpdates: () => {
|
58
|
-
setTimeout(() => {
|
59
|
-
if (GoodJob.pollEnabled === true) {
|
60
|
-
fetch(window.location.href)
|
61
|
-
.then(resp => resp.text())
|
62
|
-
.then(GoodJob.updateContent)
|
63
|
-
.finally(GoodJob.pollUpdates);
|
64
|
-
} else {
|
65
|
-
GoodJob.pollUpdates();
|
66
|
-
}
|
67
|
-
}, GoodJob.pollInterval);
|
68
|
-
},
|
69
|
-
|
70
|
-
updateContent: (newContent) => {
|
71
|
-
const domParser = new DOMParser();
|
72
|
-
const parsedDOM = domParser.parseFromString(newContent, "text/html");
|
73
|
-
|
74
|
-
const newElements = parsedDOM.querySelectorAll('[data-gj-poll-replace]');
|
75
|
-
|
76
|
-
for (let i = 0; i < newElements.length; i++) {
|
77
|
-
const newEl = newElements[i];
|
78
|
-
const oldEl = document.getElementById(newEl.id);
|
79
|
-
|
80
|
-
if (oldEl) {
|
81
|
-
oldEl.replaceWith(newEl);
|
82
|
-
}
|
83
|
-
}
|
84
|
-
|
85
|
-
GoodJob.renderCharts(false);
|
86
|
-
},
|
87
|
-
|
88
|
-
renderCharts: (animate) => {
|
89
|
-
const charts = document.querySelectorAll('.chart');
|
90
|
-
|
91
|
-
for (let i = 0; i < charts.length; i++) {
|
92
|
-
const chartEl = charts[i];
|
93
|
-
const chartData = JSON.parse(chartEl.dataset.json);
|
94
|
-
|
95
|
-
const ctx = chartEl.getContext('2d');
|
96
|
-
const chart = new Chart(ctx, {
|
97
|
-
type: 'line',
|
98
|
-
data: {
|
99
|
-
labels: chartData.labels,
|
100
|
-
datasets: chartData.datasets
|
101
|
-
},
|
102
|
-
options: {
|
103
|
-
animation: animate,
|
104
|
-
responsive: true,
|
105
|
-
maintainAspectRatio: false,
|
106
|
-
scales: {
|
107
|
-
y: {
|
108
|
-
beginAtZero: true
|
109
|
-
}
|
110
|
-
}
|
111
|
-
}
|
112
|
-
});
|
113
|
-
}
|
114
|
-
},
|
115
|
-
|
116
|
-
getStorage: (key) => {
|
117
|
-
const value = localStorage.getItem('good_job-' + key);
|
118
|
-
|
119
|
-
if (value === 'true') {
|
120
|
-
return true;
|
121
|
-
} else if (value === 'false') {
|
122
|
-
return false;
|
123
|
-
} else {
|
124
|
-
return value;
|
125
|
-
}
|
126
|
-
},
|
127
|
-
|
128
|
-
setStorage: (key, value) => {
|
129
|
-
localStorage.setItem('good_job-' + key, value);
|
130
|
-
}
|
131
|
-
};
|
132
|
-
|
133
|
-
GoodJob.ready(GoodJob.init);
|