good_job 4.13.0 → 4.13.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 +4 -4
- data/CHANGELOG.md +17 -0
- data/app/controllers/good_job/batches_controller.rb +1 -1
- data/app/controllers/good_job/cron_entries_controller.rb +3 -3
- data/app/controllers/good_job/frontends_controller.rb +15 -10
- data/app/controllers/good_job/jobs_controller.rb +7 -7
- data/app/controllers/good_job/pauses_controller.rb +2 -2
- data/app/frontend/good_job/application.js +23 -9
- data/app/frontend/good_job/modules/form_controller.js +11 -0
- data/app/frontend/good_job/vendor/turbo.js +34 -0
- data/app/models/good_job/batch.rb +2 -0
- data/app/models/good_job/batch_record.rb +2 -0
- data/app/models/good_job/job.rb +2 -2
- data/app/models/good_job/process.rb +2 -0
- data/app/models/good_job/setting.rb +2 -0
- data/app/views/good_job/batches/_jobs.erb +4 -4
- data/app/views/good_job/batches/_table.erb +2 -2
- data/app/views/good_job/batches/show.html.erb +1 -1
- data/app/views/good_job/cron_entries/index.html.erb +2 -2
- data/app/views/good_job/jobs/_table.erb +12 -9
- data/app/views/good_job/jobs/show.html.erb +4 -4
- data/app/views/good_job/pauses/_group.html.erb +1 -1
- data/app/views/good_job/pauses/_pause.html.erb +1 -1
- data/app/views/good_job/shared/_filter.erb +4 -14
- data/app/views/good_job/shared/_navbar.erb +6 -6
- data/app/views/layouts/good_job/application.html.erb +10 -8
- data/config/brakeman.ignore +25 -25
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +1 -1
- data/lib/generators/good_job/templates/update/migrations/05_add_index_good_jobs_finished_at_for_cleanup.rb.erb +17 -0
- data/lib/generators/good_job/templates/update/migrations/06_remove_extraneous_finished_at_index.rb.erb +17 -0
- data/lib/good_job/version.rb +1 -1
- data/lib/good_job.rb +1 -1
- metadata +5 -3
- data/app/frontend/good_job/modules/document_ready.js +0 -7
- data/app/frontend/good_job/vendor/rails_ujs.js +0 -7
|
@@ -68,26 +68,26 @@
|
|
|
68
68
|
<ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
|
|
69
69
|
<li>
|
|
70
70
|
<% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
|
|
71
|
-
<%=
|
|
71
|
+
<%= button_to reschedule_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_reschedulable}", title: t(".actions.reschedule"), data: { turbo_confirm: t(".actions.confirm_reschedule") } do %>
|
|
72
72
|
<%= render_icon "skip_forward" %>
|
|
73
73
|
<%= t "good_job.actions.reschedule" %>
|
|
74
74
|
<% end %>
|
|
75
75
|
</li>
|
|
76
76
|
<li>
|
|
77
77
|
<% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
|
|
78
|
-
<%=
|
|
78
|
+
<%= button_to discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_discardable}", title: t(".actions.discard"), data: { turbo_confirm: t(".actions.confirm_discard") } do %>
|
|
79
79
|
<%= render_icon "stop" %>
|
|
80
80
|
<%= t "good_job.actions.discard" %>
|
|
81
81
|
<% end %>
|
|
82
82
|
</li>
|
|
83
83
|
<li>
|
|
84
|
-
<%=
|
|
84
|
+
<%= button_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: t(".actions.retry"), data: { turbo_confirm: t(".actions.confirm_retry") } do %>
|
|
85
85
|
<%= render_icon "arrow_clockwise" %>
|
|
86
86
|
<%= t "good_job.actions.retry" %>
|
|
87
87
|
<% end %>
|
|
88
88
|
</li>
|
|
89
89
|
<li>
|
|
90
|
-
<%=
|
|
90
|
+
<%= button_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.finished?}", title: t(".actions.destroy"), data: { turbo_confirm: t(".actions.confirm_destroy") } do %>
|
|
91
91
|
<%= render_icon "trash" %>
|
|
92
92
|
<%= t "good_job.actions.destroy" %>
|
|
93
93
|
<% end %>
|
|
@@ -58,9 +58,9 @@
|
|
|
58
58
|
<div class="d-lg-none small text-muted mt-1"><%= t "good_job.models.batch.jobs" %></div>
|
|
59
59
|
<%= batch.jobs.size %>
|
|
60
60
|
</div>
|
|
61
|
-
<div class="col text-end">
|
|
61
|
+
<div class="col text-end d-flex flex-row justify-content-end">
|
|
62
62
|
<% if batch.discarded? %>
|
|
63
|
-
<%=
|
|
63
|
+
<%= button_to retry_batch_path(batch), method: :put, class: "btn btn-sm btn-outline-primary", title: t("good_job.batches.actions.retry"), data: { turbo_confirm: t("good_job.batches.actions.confirm_retry") } do %>
|
|
64
64
|
<%= render_icon "arrow_clockwise" %>
|
|
65
65
|
<%= t "good_job.batches.actions.retry" %>
|
|
66
66
|
<% end %>
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
</div>
|
|
13
13
|
<div class="col text-end">
|
|
14
14
|
<% if @batch.discarded? %>
|
|
15
|
-
<%= button_to retry_batch_path(@batch), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.batches.actions.retry") }, title: t("good_job.batches.actions.retry"), data: {
|
|
15
|
+
<%= button_to retry_batch_path(@batch), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.batches.actions.retry") }, title: t("good_job.batches.actions.retry"), data: { turbo_confirm: t("good_job.batches.actions.confirm_retry") } do %>
|
|
16
16
|
<%= render_icon "arrow_clockwise" %>
|
|
17
17
|
<%= t "good_job.actions.retry" %>
|
|
18
18
|
<% end %>
|
|
@@ -58,11 +58,11 @@
|
|
|
58
58
|
<% end %>
|
|
59
59
|
|
|
60
60
|
<% if cron_entry.enabled? %>
|
|
61
|
-
<%= button_to disable_cron_entry_path(cron_entry), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.cron_entries.actions.disable") }, title: t("good_job.cron_entries.actions.disable"), data: {
|
|
61
|
+
<%= button_to disable_cron_entry_path(cron_entry), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.cron_entries.actions.disable") }, title: t("good_job.cron_entries.actions.disable"), data: { turbo_confirm: t("good_job.cron_entries.actions.confirm_disable") } do %>
|
|
62
62
|
<%= render_icon "pause" %>
|
|
63
63
|
<% end %>
|
|
64
64
|
<% else %>
|
|
65
|
-
<%= button_to enable_cron_entry_path(cron_entry), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.cron_entries.actions.enable") }, title: t("good_job.cron_entries.actions.enable"), data: {
|
|
65
|
+
<%= button_to enable_cron_entry_path(cron_entry), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.cron_entries.actions.enable") }, title: t("good_job.cron_entries.actions.enable"), data: { turbo_confirm: t("good_job.cron_entries.actions.confirm_enable") } do %>
|
|
66
66
|
<%= render_icon "play" %>
|
|
67
67
|
<% end %>
|
|
68
68
|
<% end %>
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
<%= form_with(id: "job_action_form", method: :put, model: false, local: true) { nil } %>
|
|
2
|
+
<%= form_with(id: "job_destroy_form", method: :delete, model: false, local: true) { nil } %>
|
|
3
|
+
|
|
1
4
|
<%= form_with(model: false, url: mass_update_jobs_path(filter.to_params), method: :put, local: true, data: { "checkbox-toggle": "job_ids" }) do |form| %>
|
|
2
5
|
<div class="my-3 card" data-gj-poll-replace id="jobs-table">
|
|
3
6
|
<div class="list-group list-group-flush text-nowrap table-jobs" role="table">
|
|
@@ -8,16 +11,16 @@
|
|
|
8
11
|
<%= check_box_tag('toggle_job_ids', "1", false, data: { "checkbox-toggle-all": "job_ids" }) %>
|
|
9
12
|
<%= label_tag('toggle_job_ids', t(".toggle_all_jobs"), class: "visually-hidden") %>
|
|
10
13
|
</div>
|
|
11
|
-
<%= form.button type: 'submit', name: 'mass_action', value: 'reschedule', class: 'ms-1 btn btn-sm btn-outline-secondary', title: t(".actions.reschedule_all"), data: {
|
|
14
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'reschedule', class: 'ms-1 btn btn-sm btn-outline-secondary', title: t(".actions.reschedule_all"), data: { turbo_confirm: t(".actions.confirm_reschedule_all") } do %>
|
|
12
15
|
<span class="me-1"><%= render_icon "skip_forward" %></span> <%= t "good_job.actions.reschedule" %>
|
|
13
16
|
<% end %>
|
|
14
17
|
|
|
15
|
-
<%= form.button type: 'submit', name: 'mass_action', value: 'retry', class: 'btn btn-sm btn-outline-secondary', title: t(".actions.retry_all"), data: {
|
|
18
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'retry', class: 'btn btn-sm btn-outline-secondary', title: t(".actions.retry_all"), data: { turbo_confirm: t(".actions.confirm_retry_all") } do %>
|
|
16
19
|
<span class="me-1"><%= render_icon "arrow_clockwise" %></span> <%= t "good_job.actions.retry" %>
|
|
17
20
|
<% end %>
|
|
18
21
|
|
|
19
22
|
<div class="btn-group" role="group">
|
|
20
|
-
<%= form.button type: 'submit', name: 'mass_action', value: 'discard', class: 'btn btn-sm btn-outline-secondary', title: t(".actions.discard_all"), data: {
|
|
23
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'discard', class: 'btn btn-sm btn-outline-secondary', title: t(".actions.discard_all"), data: { turbo_confirm: t(".actions.confirm_discard_all") } do %>
|
|
21
24
|
<span class="me-1"><%= render_icon "stop" %></span> <%= t "good_job.actions.discard" %>
|
|
22
25
|
<% end %>
|
|
23
26
|
<button id="destroy-dropdown-toggle" type="button" class="btn btn-sm btn-outline-secondary dropdown-toggle dropdown-toggle-split" data-bs-toggle="dropdown" aria-expanded="false">
|
|
@@ -25,7 +28,7 @@
|
|
|
25
28
|
</button>
|
|
26
29
|
<ul class="dropdown-menu" aria-labelledby="destroy-dropdown-toggle">
|
|
27
30
|
<li>
|
|
28
|
-
<%= form.button type: 'submit', name: 'mass_action', value: 'destroy', class: 'btn dropdown-item', title: t(".actions.destroy_all"), data: {
|
|
31
|
+
<%= form.button type: 'submit', name: 'mass_action', value: 'destroy', class: 'btn dropdown-item', title: t(".actions.destroy_all"), data: { turbo_confirm: t(".actions.confirm_destroy_all") } do %>
|
|
29
32
|
<span class="me-1"><%= render_icon "trash" %></span> <%= t "good_job.actions.destroy" %>
|
|
30
33
|
<% end %>
|
|
31
34
|
</li>
|
|
@@ -125,33 +128,33 @@
|
|
|
125
128
|
<ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
|
|
126
129
|
<li>
|
|
127
130
|
<% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
|
|
128
|
-
<%=
|
|
131
|
+
<%= tag.button form: "job_action_form", formaction: reschedule_job_path(job.id), class: "dropdown-item #{'disabled' unless job_reschedulable}", title: t("good_job.jobs.actions.reschedule"), data: { turbo_confirm: t("good_job.jobs.actions.confirm_reschedule") } do %>
|
|
129
132
|
<%= render_icon "skip_forward" %>
|
|
130
133
|
<%= t "good_job.actions.reschedule" %>
|
|
131
134
|
<% end %>
|
|
132
135
|
</li>
|
|
133
136
|
<li>
|
|
134
137
|
<% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
|
|
135
|
-
<%=
|
|
138
|
+
<%= tag.button form: "job_action_form", formaction: discard_job_path(job.id), class: "dropdown-item #{'disabled' unless job_discardable}", title: t("good_job.jobs.actions.discard"), data: { turbo_confirm: t("good_job.jobs.actions.confirm_discard") } do %>
|
|
136
139
|
<%= render_icon "stop" %>
|
|
137
140
|
<%= t "good_job.actions.discard" %>
|
|
138
141
|
<% end %>
|
|
139
142
|
</li>
|
|
140
143
|
<li>
|
|
141
144
|
<% job_force_discardable = job.status.in? [:running] %>
|
|
142
|
-
<%=
|
|
145
|
+
<%= tag.button form: "job_action_form", formaction: force_discard_job_path(job.id), class: "dropdown-item #{'disabled' unless job_force_discardable}", title: t("good_job.jobs.actions.force_discard"), data: { turbo_confirm: t("good_job.jobs.actions.confirm_force_discard") } do %>
|
|
143
146
|
<%= render_icon "eject" %>
|
|
144
147
|
<%= t "good_job.actions.force_discard" %>
|
|
145
148
|
<% end %>
|
|
146
149
|
</li>
|
|
147
150
|
<li>
|
|
148
|
-
<%=
|
|
151
|
+
<%= tag.button form: "job_action_form", formaction: retry_job_path(job.id), class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: t("good_job.jobs.actions.retry"), data: { turbo_confirm: t("good_job.jobs.actions.confirm_retry") } do %>
|
|
149
152
|
<%= render_icon "arrow_clockwise" %>
|
|
150
153
|
<%= t "good_job.actions.retry" %>
|
|
151
154
|
<% end %>
|
|
152
155
|
</li>
|
|
153
156
|
<li>
|
|
154
|
-
<%=
|
|
157
|
+
<%= tag.button form: "job_destroy_form", formaction: job_path(job.id), class: "dropdown-item #{'disabled' unless job.status.in? [:discarded, :succeeded]}", title: t("good_job.jobs.actions.destroy"), data: { turbo_confirm: t("good_job.jobs.actions.confirm_destroy") } do %>
|
|
155
158
|
<%= render_icon "trash" %>
|
|
156
159
|
<%= t "good_job.actions.destroy" %>
|
|
157
160
|
<% end %>
|
|
@@ -33,28 +33,28 @@
|
|
|
33
33
|
form_class: "d-inline-block",
|
|
34
34
|
aria: { label: t("good_job.jobs.actions.reschedule") },
|
|
35
35
|
title: t("good_job.jobs.actions.reschedule"),
|
|
36
|
-
data: {
|
|
36
|
+
data: { turbo_confirm: t("good_job.jobs.actions.confirm_reschedule") } do %>
|
|
37
37
|
<%= render_icon "skip_forward" %>
|
|
38
38
|
<%= t "good_job.actions.reschedule" %>
|
|
39
39
|
<% end %>
|
|
40
40
|
<% end %>
|
|
41
41
|
|
|
42
42
|
<% if @job.status.in? [:scheduled, :retried, :queued] %>
|
|
43
|
-
<%= button_to discard_job_path(@job.id), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.jobs.actions.discard") }, title: t("good_job.jobs.actions.discard"), data: {
|
|
43
|
+
<%= button_to discard_job_path(@job.id), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.jobs.actions.discard") }, title: t("good_job.jobs.actions.discard"), data: { turbo_confirm: t("good_job.jobs.actions.confirm_discard") } do %>
|
|
44
44
|
<%= render_icon "stop" %>
|
|
45
45
|
<%= t "good_job.actions.discard" %>
|
|
46
46
|
<% end %>
|
|
47
47
|
<% end %>
|
|
48
48
|
|
|
49
49
|
<% if @job.status == :discarded %>
|
|
50
|
-
<%= button_to retry_job_path(@job.id), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.jobs.actions.retry") }, title: t("good_job.jobs.actions.retry"), data: {
|
|
50
|
+
<%= button_to retry_job_path(@job.id), method: :put, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.jobs.actions.retry") }, title: t("good_job.jobs.actions.retry"), data: { turbo_confirm: t("good_job.jobs.actions.confirm_retry") } do %>
|
|
51
51
|
<%= render_icon "arrow_clockwise" %>
|
|
52
52
|
<%= t "good_job.actions.retry" %>
|
|
53
53
|
<% end %>
|
|
54
54
|
<% end %>
|
|
55
55
|
|
|
56
56
|
<% if @job.status.in? [:discarded, :succeeded] %>
|
|
57
|
-
<%= button_to job_path(@job.id), method: :delete, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.jobs.actions.destroy") }, title: t("good_job.jobs.actions.destroy"), data: {
|
|
57
|
+
<%= button_to job_path(@job.id), method: :delete, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: t("good_job.jobs.actions.destroy") }, title: t("good_job.jobs.actions.destroy"), data: { turbo_confirm: t("good_job.jobs.actions.confirm_destroy") } do %>
|
|
58
58
|
<%= render_icon "trash" %>
|
|
59
59
|
<%= t "good_job.actions.destroy" %>
|
|
60
60
|
<% end %>
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
{ action: :destroy, type: type, value: value },
|
|
17
17
|
method: :delete,
|
|
18
18
|
class: 'btn btn-sm btn-outline-primary',
|
|
19
|
-
data: {
|
|
19
|
+
data: { turbo_confirm: t('good_job.pauses.index.confirm_unpause', value: value) }
|
|
20
20
|
) do %>
|
|
21
21
|
<%= render_icon "play" %>
|
|
22
22
|
<%= t("good_job.pauses.index.unpause") %>
|
|
@@ -4,5 +4,5 @@
|
|
|
4
4
|
{ action: :destroy, type: type, value: value },
|
|
5
5
|
method: :delete,
|
|
6
6
|
class: 'btn btn-sm btn-outline-primary',
|
|
7
|
-
data: {
|
|
7
|
+
data: { turbo_confirm: t('good_job.pauses.index.confirm_unpause', value: value) } %>
|
|
8
8
|
</li>
|
|
@@ -4,14 +4,14 @@
|
|
|
4
4
|
<h2 class="pt-3 pb-2"><%= title %></h2>
|
|
5
5
|
</div>
|
|
6
6
|
|
|
7
|
-
<%= form_with(model: false, url: "", method: :get, local: true,
|
|
7
|
+
<%= form_with(model: false, url: "", method: :get, local: true, data: { controller: "form" }) do |form| %>
|
|
8
8
|
<%= hidden_field_tag :poll, params[:poll] %>
|
|
9
9
|
<%= hidden_field_tag :state, params[:state] %>
|
|
10
10
|
<%= hidden_field_tag :locale, params[:locale] if params[:locale] %>
|
|
11
11
|
<div class="d-md-flex flex-row w-100">
|
|
12
12
|
<div class="me-md-2 mb-2 mb-md-0">
|
|
13
13
|
<%= label_tag "job_queue_filter", t(".queue_name"), class: "visually-hidden" %>
|
|
14
|
-
<select name="queue_name" id="job_queue_filter" class="form-select form-select-sm">
|
|
14
|
+
<select name="queue_name" id="job_queue_filter" class="form-select form-select-sm" data-action="change->form#submit">
|
|
15
15
|
<option value="" <%= "selected='selected'" if params[:queue_name].blank? %>><%= t ".all_queues" %></option>
|
|
16
16
|
|
|
17
17
|
<% filter.queues.each do |name, count| %>
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
|
|
23
23
|
<div class="me-md-2 mb-2 mb-md-0">
|
|
24
24
|
<%= label_tag "job_class_filter", t(".job_name"), class: "visually-hidden" %>
|
|
25
|
-
<select name="job_class" id="job_class_filter" class="form-select form-select-sm">
|
|
25
|
+
<select name="job_class" id="job_class_filter" class="form-select form-select-sm" data-action="change->form#submit">
|
|
26
26
|
<option value="" <%= "selected='selected'" if params[:job_class].blank? %>><%= t ".all_jobs" %></option>
|
|
27
27
|
|
|
28
28
|
<% filter.job_classes.each do |name, count| %>
|
|
@@ -56,20 +56,10 @@
|
|
|
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="
|
|
59
|
+
<span data-async-values-target="value" data-async-values-key="<%= name %>" data-async-values-zero-class="bg-secondary" class="badge text-bg-primary rounded-pill d-none" id="filter_state_<%= name %>" data-turbo-permanent></span>
|
|
60
60
|
<% end %>
|
|
61
61
|
</li>
|
|
62
62
|
<% end %>
|
|
63
63
|
</ul>
|
|
64
64
|
</div>
|
|
65
|
-
|
|
66
|
-
<script nonce="<%= content_security_policy_nonce %>">
|
|
67
|
-
document.addEventListener("DOMContentLoaded", () => {
|
|
68
|
-
document.querySelectorAll("#job_class_filter, #job_queue_filter").forEach((filter) => {
|
|
69
|
-
filter.addEventListener("change", () => {
|
|
70
|
-
document.querySelector("#filter_form").submit();
|
|
71
|
-
});
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
</script>
|
|
75
65
|
</div>
|
|
@@ -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 text-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" id="navbar_jobs_count" data-turbo-permanent></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 text-bg-secondary rounded-pill
|
|
27
|
+
<span data-async-values-target="value" data-async-values-key="batches_count" class="badge text-bg-secondary rounded-pill d-none" id="navbar_batches_count" data-turbo-permanent></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 text-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" id="navbar_cron_entries_count" data-turbo-permanent></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="
|
|
39
|
+
<span data-async-values-target="value" data-async-values-key="processes_count" data-async-values-zero-class="bg-danger" class="badge text-bg-secondary rounded-pill d-none" id="navbar_processes_count" data-turbo-permanent></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 text-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" id="navbar_pauses_count" data-turbo-permanent></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 text-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" id="navbar_discarded_count" data-turbo-permanent></span>
|
|
57
57
|
<% end %>
|
|
58
58
|
</li>
|
|
59
59
|
</ul>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
|
-
<html lang="<%= I18n.locale %>" data-bs-theme="auto">
|
|
2
|
+
<html lang="<%= I18n.locale %>" data-bs-theme="auto" data-turbo-unloaded>
|
|
3
3
|
<head>
|
|
4
4
|
<meta charset="utf-8">
|
|
5
5
|
<meta content="width=device-width, initial-scale=1, shrink-to-fit=no" name="viewport">
|
|
@@ -18,18 +18,20 @@
|
|
|
18
18
|
</script>
|
|
19
19
|
|
|
20
20
|
<%# Do not use asset tag helpers to avoid paths being overriden by config.asset_host %>
|
|
21
|
-
<%= tag.link rel: "stylesheet", href: frontend_static_path(:bootstrap, format: :css, locale: nil), nonce: content_security_policy_nonce %>
|
|
22
|
-
<%= tag.link rel: "stylesheet", href: frontend_static_path(:style, format: :css, locale: nil), nonce: content_security_policy_nonce %>
|
|
21
|
+
<%= tag.link rel: "stylesheet", href: frontend_static_path(:bootstrap, format: :css, locale: nil), nonce: content_security_policy_nonce, data: { turbo_track: "reload" } %>
|
|
22
|
+
<%= tag.link rel: "stylesheet", href: frontend_static_path(:style, format: :css, locale: nil), nonce: content_security_policy_nonce, data: { turbo_track: "reload" } %>
|
|
23
23
|
<%= tag.script "", src: frontend_static_path(:bootstrap, format: :js, locale: nil), nonce: content_security_policy_nonce %>
|
|
24
24
|
<%= tag.script "", src: frontend_static_path(:chartjs, format: :js, locale: nil), nonce: content_security_policy_nonce %>
|
|
25
|
-
<%= tag.script "", src: frontend_static_path(:rails_ujs, format: :js, locale: nil), nonce: content_security_policy_nonce %>
|
|
26
25
|
<%= tag.script "", src: frontend_static_path(:es_module_shims, format: :js, locale: nil), async: true, nonce: content_security_policy_nonce %>
|
|
26
|
+
|
|
27
27
|
<% importmaps = GoodJob::FrontendsController.js_modules.keys.index_with { |module_name| frontend_module_path(module_name, format: :js, locale: nil) } %>
|
|
28
|
-
<%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce) %>
|
|
29
|
-
|
|
28
|
+
<%= tag.script({ imports: importmaps }.to_json.html_safe, type: "importmap", nonce: content_security_policy_nonce, data: { turbo_track: "reload" }) %>
|
|
29
|
+
<% importmaps.each do |_module_name, path| %>
|
|
30
|
+
<%= tag.link rel: "modulepreload", href: path, nonce: content_security_policy_nonce %>
|
|
31
|
+
<% end %>
|
|
32
|
+
|
|
33
|
+
<%= tag.script "import \"application\";".html_safe, type: "module", nonce: content_security_policy_nonce %>
|
|
30
34
|
|
|
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
35
|
<%= csrf_meta_tags %>
|
|
34
36
|
<%= csp_meta_tag %>
|
|
35
37
|
|
data/config/brakeman.ignore
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"check_name": "Render",
|
|
8
8
|
"message": "Render path contains parameter value",
|
|
9
9
|
"file": "app/controllers/good_job/frontends_controller.rb",
|
|
10
|
-
"line":
|
|
10
|
+
"line": 52,
|
|
11
11
|
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
|
|
12
12
|
"code": "render(file => (self.class.js_modules[params[:id].to_sym] or raise(ActionController::RoutingError, \"Not Found\")), {})",
|
|
13
13
|
"render_path": null,
|
|
@@ -23,29 +23,6 @@
|
|
|
23
23
|
],
|
|
24
24
|
"note": "Files are explicitly enumerated in the array"
|
|
25
25
|
},
|
|
26
|
-
{
|
|
27
|
-
"warning_type": "Dynamic Render Path",
|
|
28
|
-
"warning_code": 15,
|
|
29
|
-
"fingerprint": "b0c2888c9b217671d90d0141b49b036af3b2a70c63b02968cc97ae2052c86272",
|
|
30
|
-
"check_name": "Render",
|
|
31
|
-
"message": "Render path contains parameter value",
|
|
32
|
-
"file": "app/controllers/good_job/frontends_controller.rb",
|
|
33
|
-
"line": 41,
|
|
34
|
-
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
|
|
35
|
-
"code": "render(file => ({ :css => ({ :bootstrap => GoodJob::Engine.root.join(\"app\", \"frontend\", \"good_job\", \"vendor\", \"bootstrap\", \"bootstrap.min.css\"), :style => GoodJob::Engine.root.join(\"app\", \"frontend\", \"good_job\", \"style.css\") }), :js => ({ :bootstrap => GoodJob::Engine.root.join(\"app\", \"frontend\", \"good_job\", \"vendor\", \"bootstrap\", \"bootstrap.bundle.min.js\"), :chartjs => GoodJob::Engine.root.join(\"app\", \"frontend\", \"good_job\", \"vendor\", \"chartjs\", \"chart.min.js\"), :es_module_shims => GoodJob::Engine.root.join(\"app\", \"frontend\", \"good_job\", \"vendor\", \"es_module_shims.js\"), :rails_ujs => GoodJob::Engine.root.join(\"app\", \"frontend\", \"good_job\", \"vendor\", \"rails_ujs.js\") }), :svg => ({ :icons => GoodJob::Engine.root.join(\"app\", \"frontend\", \"good_job\", \"icons.svg\") }) }.dig(params[:format].to_sym, params[:id].to_sym) or raise(ActionController::RoutingError, \"Not Found\")), {})",
|
|
36
|
-
"render_path": null,
|
|
37
|
-
"location": {
|
|
38
|
-
"type": "method",
|
|
39
|
-
"class": "GoodJob::FrontendsController",
|
|
40
|
-
"method": "static"
|
|
41
|
-
},
|
|
42
|
-
"user_input": "params[:id].to_sym",
|
|
43
|
-
"confidence": "Weak",
|
|
44
|
-
"cwe_id": [
|
|
45
|
-
22
|
|
46
|
-
],
|
|
47
|
-
"note": "Files are explicitly enumerated in the array"
|
|
48
|
-
},
|
|
49
26
|
{
|
|
50
27
|
"warning_type": "Dangerous Eval",
|
|
51
28
|
"warning_code": 13,
|
|
@@ -115,7 +92,30 @@
|
|
|
115
92
|
77
|
|
116
93
|
],
|
|
117
94
|
"note": ""
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
"warning_type": "Dynamic Render Path",
|
|
98
|
+
"warning_code": 15,
|
|
99
|
+
"fingerprint": "ebc35a48169b7ee1f2e1b024f3bf6abe7138924a3a318ab2ad405c2b6e9bbb55",
|
|
100
|
+
"check_name": "Render",
|
|
101
|
+
"message": "Render path contains parameter value",
|
|
102
|
+
"file": "app/controllers/good_job/frontends_controller.rb",
|
|
103
|
+
"line": 46,
|
|
104
|
+
"link": "https://brakemanscanner.org/docs/warning_types/dynamic_render_path/",
|
|
105
|
+
"code": "render(file => ({ :css => ({ :bootstrap => asset_path(\"vendor\", \"bootstrap\", \"bootstrap.min.css\"), :style => asset_path(\"style.css\") }), :js => ({ :bootstrap => asset_path(\"vendor\", \"bootstrap\", \"bootstrap.bundle.min.js\"), :chartjs => asset_path(\"vendor\", \"chartjs\", \"chart.min.js\"), :es_module_shims => asset_path(\"vendor\", \"es_module_shims.js\"), :turbo => asset_path(\"vendor\", \"turbo.js\") }), :svg => ({ :icons => asset_path(\"icons.svg\") }) }.dig(params[:format].to_sym, params[:id].to_sym) or raise(ActionController::RoutingError, \"Not Found\")), {})",
|
|
106
|
+
"render_path": null,
|
|
107
|
+
"location": {
|
|
108
|
+
"type": "method",
|
|
109
|
+
"class": "GoodJob::FrontendsController",
|
|
110
|
+
"method": "static"
|
|
111
|
+
},
|
|
112
|
+
"user_input": "params[:id].to_sym",
|
|
113
|
+
"confidence": "Weak",
|
|
114
|
+
"cwe_id": [
|
|
115
|
+
22
|
|
116
|
+
],
|
|
117
|
+
"note": ""
|
|
118
118
|
}
|
|
119
119
|
],
|
|
120
|
-
"brakeman_version": "7.
|
|
120
|
+
"brakeman_version": "7.1.1"
|
|
121
121
|
}
|
|
@@ -85,7 +85,7 @@ class CreateGoodJobs < ActiveRecord::Migration<%= migration_version %>
|
|
|
85
85
|
add_index :good_jobs, [:concurrency_key, :created_at], name: :index_good_jobs_on_concurrency_key_and_created_at
|
|
86
86
|
add_index :good_jobs, [:cron_key, :created_at], where: "(cron_key IS NOT NULL)", name: :index_good_jobs_on_cron_key_and_created_at_cond
|
|
87
87
|
add_index :good_jobs, [:cron_key, :cron_at], where: "(cron_key IS NOT NULL)", unique: true, name: :index_good_jobs_on_cron_key_and_cron_at_cond
|
|
88
|
-
add_index :good_jobs, [:finished_at], where: "
|
|
88
|
+
add_index :good_jobs, [:finished_at], where: "finished_at IS NOT NULL", name: :index_good_jobs_jobs_on_finished_at_only
|
|
89
89
|
add_index :good_jobs, [:priority, :created_at], order: { priority: "DESC NULLS LAST", created_at: :asc },
|
|
90
90
|
where: "finished_at IS NULL", name: :index_good_jobs_jobs_on_priority_created_at_when_unfinished
|
|
91
91
|
add_index :good_jobs, [:priority, :created_at], order: { priority: "ASC NULLS LAST", created_at: :asc },
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddIndexGoodJobsFinishedAtForCleanup < 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, [:finished_at], name: :index_good_jobs_jobs_on_finished_at_only
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
add_index :good_jobs, [:finished_at], where: "finished_at IS NOT NULL", name: :index_good_jobs_jobs_on_finished_at_only, algorithm: :concurrently
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class RemoveExtraneousFinishedAtIndex < 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 unless connection.index_exists? :good_jobs, [:finished_at], name: :index_good_jobs_jobs_on_finished_at
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
remove_index :good_jobs, [:finished_at], where: "retried_good_job_id IS NULL AND finished_at IS NOT NULL", name: :index_good_jobs_jobs_on_finished_at, algorithm: :concurrently
|
|
16
|
+
end
|
|
17
|
+
end
|
data/lib/good_job/version.rb
CHANGED
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.
|
|
293
|
+
GoodJob::Job.historic_finished_at_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.13.
|
|
4
|
+
version: 4.13.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ben Sheldon
|
|
@@ -269,7 +269,7 @@ files:
|
|
|
269
269
|
- app/frontend/good_job/modules/async_values_controller.js
|
|
270
270
|
- app/frontend/good_job/modules/charts.js
|
|
271
271
|
- app/frontend/good_job/modules/checkbox_toggle.js
|
|
272
|
-
- app/frontend/good_job/modules/
|
|
272
|
+
- app/frontend/good_job/modules/form_controller.js
|
|
273
273
|
- app/frontend/good_job/modules/html_legend_plugin.js
|
|
274
274
|
- app/frontend/good_job/modules/live_poll.js
|
|
275
275
|
- app/frontend/good_job/modules/popovers.js
|
|
@@ -280,8 +280,8 @@ files:
|
|
|
280
280
|
- app/frontend/good_job/vendor/bootstrap/bootstrap.min.css
|
|
281
281
|
- app/frontend/good_job/vendor/chartjs/chart.min.js
|
|
282
282
|
- app/frontend/good_job/vendor/es_module_shims.js
|
|
283
|
-
- app/frontend/good_job/vendor/rails_ujs.js
|
|
284
283
|
- app/frontend/good_job/vendor/stimulus.js
|
|
284
|
+
- app/frontend/good_job/vendor/turbo.js
|
|
285
285
|
- app/helpers/good_job/application_helper.rb
|
|
286
286
|
- app/helpers/good_job/icons_helper.rb
|
|
287
287
|
- app/models/concerns/good_job/advisory_lockable.rb
|
|
@@ -352,6 +352,8 @@ files:
|
|
|
352
352
|
- lib/generators/good_job/templates/update/migrations/02_add_jobs_finished_at_to_good_job_batches.rb.erb
|
|
353
353
|
- lib/generators/good_job/templates/update/migrations/03_add_index_good_jobs_concurrency_key_created_at.rb.erb
|
|
354
354
|
- lib/generators/good_job/templates/update/migrations/04_add_index_good_jobs_job_class.rb.erb
|
|
355
|
+
- lib/generators/good_job/templates/update/migrations/05_add_index_good_jobs_finished_at_for_cleanup.rb.erb
|
|
356
|
+
- lib/generators/good_job/templates/update/migrations/06_remove_extraneous_finished_at_index.rb.erb
|
|
355
357
|
- lib/generators/good_job/update_generator.rb
|
|
356
358
|
- lib/good_job.rb
|
|
357
359
|
- lib/good_job/active_job_extensions/batches.rb
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Minified by jsDelivr using Terser v5.15.1.
|
|
3
|
-
* Original file: /npm/@rails/ujs@7.0.4-2/lib/assets/compiled/rails-ujs.js
|
|
4
|
-
*
|
|
5
|
-
* Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files
|
|
6
|
-
*/
|
|
7
|
-
(function(){(function(){(function(){this.Rails={linkClickSelector:"a[data-confirm], a[data-method], a[data-remote]:not([disabled]), a[data-disable-with], a[data-disable]",buttonClickSelector:{selector:"button[data-remote]:not([form]), button[data-confirm]:not([form])",exclude:"form button"},inputChangeSelector:"select[data-remote], input[data-remote], textarea[data-remote]",formSubmitSelector:"form:not([data-turbo=true])",formInputClickSelector:"form:not([data-turbo=true]) input[type=submit], form:not([data-turbo=true]) input[type=image], form:not([data-turbo=true]) button[type=submit], form:not([data-turbo=true]) button:not([type]), input[type=submit][form], input[type=image][form], button[type=submit][form], button[form]:not([type])",formDisableSelector:"input[data-disable-with]:enabled, button[data-disable-with]:enabled, textarea[data-disable-with]:enabled, input[data-disable]:enabled, button[data-disable]:enabled, textarea[data-disable]:enabled",formEnableSelector:"input[data-disable-with]:disabled, button[data-disable-with]:disabled, textarea[data-disable-with]:disabled, input[data-disable]:disabled, button[data-disable]:disabled, textarea[data-disable]:disabled",fileInputSelector:"input[name][type=file]:not([disabled])",linkDisableSelector:"a[data-disable-with], a[data-disable]",buttonDisableSelector:"button[data-remote][data-disable-with], button[data-remote][data-disable]"}}).call(this)}).call(this);var t=this.Rails;(function(){(function(){var e;e=null,t.loadCSPNonce=function(){var t;return e=null!=(t=document.querySelector("meta[name=csp-nonce]"))?t.content:void 0},t.cspNonce=function(){return null!=e?e:t.loadCSPNonce()}}).call(this),function(){var e;e=Element.prototype.matches||Element.prototype.matchesSelector||Element.prototype.mozMatchesSelector||Element.prototype.msMatchesSelector||Element.prototype.oMatchesSelector||Element.prototype.webkitMatchesSelector,t.matches=function(t,n){return null!=n.exclude?e.call(t,n.selector)&&!e.call(t,n.exclude):e.call(t,n)},t.getData=function(t,e){var n;return null!=(n=t._ujsData)?n[e]:void 0},t.setData=function(t,e,n){return null==t._ujsData&&(t._ujsData={}),t._ujsData[e]=n},t.$=function(t){return Array.prototype.slice.call(document.querySelectorAll(t))}}.call(this),function(){var e,n,a;e=t.$,a=t.csrfToken=function(){var t;return(t=document.querySelector("meta[name=csrf-token]"))&&t.content},n=t.csrfParam=function(){var t;return(t=document.querySelector("meta[name=csrf-param]"))&&t.content},t.CSRFProtection=function(t){var e;if(null!=(e=a()))return t.setRequestHeader("X-CSRF-Token",e)},t.refreshCSRFTokens=function(){var t,r;if(r=a(),t=n(),null!=r&&null!=t)return e('form input[name="'+t+'"]').forEach((function(t){return t.value=r}))}}.call(this),function(){var e,n,a,r;a=t.matches,"function"!=typeof(e=window.CustomEvent)&&((e=function(t,e){var n;return(n=document.createEvent("CustomEvent")).initCustomEvent(t,e.bubbles,e.cancelable,e.detail),n}).prototype=window.Event.prototype,r=e.prototype.preventDefault,e.prototype.preventDefault=function(){var t;return t=r.call(this),this.cancelable&&!this.defaultPrevented&&Object.defineProperty(this,"defaultPrevented",{get:function(){return!0}}),t}),n=t.fire=function(t,n,a){var r;return r=new e(n,{bubbles:!0,cancelable:!0,detail:a}),t.dispatchEvent(r),!r.defaultPrevented},t.stopEverything=function(t){return n(t.target,"ujs:everythingStopped"),t.preventDefault(),t.stopPropagation(),t.stopImmediatePropagation()},t.delegate=function(t,e,n,r){return t.addEventListener(n,(function(t){var n;for(n=t.target;n instanceof Element&&!a(n,e);)n=n.parentNode;if(n instanceof Element&&!1===r.call(n,t))return t.preventDefault(),t.stopPropagation()}))}}.call(this),function(){var e,n,a,r,o,i;r=t.cspNonce,n=t.CSRFProtection,t.fire,e={"*":"*/*",text:"text/plain",html:"text/html",xml:"application/xml, text/xml",json:"application/json, text/javascript",script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},t.ajax=function(t){var e;return t=o(t),e=a(t,(function(){var n,a;return a=i(null!=(n=e.response)?n:e.responseText,e.getResponseHeader("Content-Type")),2===Math.floor(e.status/100)?"function"==typeof t.success&&t.success(a,e.statusText,e):"function"==typeof t.error&&t.error(a,e.statusText,e),"function"==typeof t.complete?t.complete(e,e.statusText):void 0})),!(null!=t.beforeSend&&!t.beforeSend(e,t))&&(e.readyState===XMLHttpRequest.OPENED?e.send(t.data):void 0)},o=function(t){return t.url=t.url||location.href,t.type=t.type.toUpperCase(),"GET"===t.type&&t.data&&(t.url.indexOf("?")<0?t.url+="?"+t.data:t.url+="&"+t.data),null==e[t.dataType]&&(t.dataType="*"),t.accept=e[t.dataType],"*"!==t.dataType&&(t.accept+=", */*; q=0.01"),t},a=function(t,e){var a;return(a=new XMLHttpRequest).open(t.type,t.url,!0),a.setRequestHeader("Accept",t.accept),"string"==typeof t.data&&a.setRequestHeader("Content-Type","application/x-www-form-urlencoded; charset=UTF-8"),t.crossDomain||(a.setRequestHeader("X-Requested-With","XMLHttpRequest"),n(a)),a.withCredentials=!!t.withCredentials,a.onreadystatechange=function(){if(a.readyState===XMLHttpRequest.DONE)return e(a)},a},i=function(t,e){var n,a;if("string"==typeof t&&"string"==typeof e)if(e.match(/\bjson\b/))try{t=JSON.parse(t)}catch(t){}else if(e.match(/\b(?:java|ecma)script\b/))(a=document.createElement("script")).setAttribute("nonce",r()),a.text=t,document.head.appendChild(a).parentNode.removeChild(a);else if(e.match(/\b(xml|html|svg)\b/)){n=new DOMParser,e=e.replace(/;.+/,"");try{t=n.parseFromString(t,e)}catch(t){}}return t},t.href=function(t){return t.href},t.isCrossDomain=function(t){var e,n;(e=document.createElement("a")).href=location.href,n=document.createElement("a");try{return n.href=t,!((!n.protocol||":"===n.protocol)&&!n.host||e.protocol+"//"+e.host==n.protocol+"//"+n.host)}catch(t){return t,!0}}}.call(this),function(){var e,n;e=t.matches,n=function(t){return Array.prototype.slice.call(t)},t.serializeElement=function(t,a){var r,o;return r=[t],e(t,"form")&&(r=n(t.elements)),o=[],r.forEach((function(t){if(t.name&&!t.disabled&&!e(t,"fieldset[disabled] *"))return e(t,"select")?n(t.options).forEach((function(e){if(e.selected)return o.push({name:t.name,value:e.value})})):t.checked||-1===["radio","checkbox","submit"].indexOf(t.type)?o.push({name:t.name,value:t.value}):void 0})),a&&o.push(a),o.map((function(t){return null!=t.name?encodeURIComponent(t.name)+"="+encodeURIComponent(t.value):t})).join("&")},t.formElements=function(t,a){return e(t,"form")?n(t.elements).filter((function(t){return e(t,a)})):n(t.querySelectorAll(a))}}.call(this),function(){var e,n,a;n=t.fire,a=t.stopEverything,t.handleConfirm=function(t){if(!e(this))return a(t)},t.confirm=function(t,e){return confirm(t)},e=function(e){var a,r,o;if(!(o=e.getAttribute("data-confirm")))return!0;if(a=!1,n(e,"confirm")){try{a=t.confirm(o,e)}catch(t){}r=n(e,"confirm:complete",[a])}return a&&r}}.call(this),function(){var e,n,a,r,o,i,l,u,c,s,d,m;s=t.matches,u=t.getData,d=t.setData,m=t.stopEverything,l=t.formElements,t.handleDisabledElement=function(t){if(this,this.disabled)return m(t)},t.enableElement=function(e){var n;if(e instanceof Event){if(c(e))return;n=e.target}else n=e;return s(n,t.linkDisableSelector)?i(n):s(n,t.buttonDisableSelector)||s(n,t.formEnableSelector)?r(n):s(n,t.formSubmitSelector)?o(n):void 0},t.disableElement=function(r){var o;return o=r instanceof Event?r.target:r,s(o,t.linkDisableSelector)?a(o):s(o,t.buttonDisableSelector)||s(o,t.formDisableSelector)?e(o):s(o,t.formSubmitSelector)?n(o):void 0},a=function(t){var e;if(!u(t,"ujs:disabled"))return null!=(e=t.getAttribute("data-disable-with"))&&(d(t,"ujs:enable-with",t.innerHTML),t.innerHTML=e),t.addEventListener("click",m),d(t,"ujs:disabled",!0)},i=function(t){var e;return null!=(e=u(t,"ujs:enable-with"))&&(t.innerHTML=e,d(t,"ujs:enable-with",null)),t.removeEventListener("click",m),d(t,"ujs:disabled",null)},n=function(n){return l(n,t.formDisableSelector).forEach(e)},e=function(t){var e;if(!u(t,"ujs:disabled"))return null!=(e=t.getAttribute("data-disable-with"))&&(s(t,"button")?(d(t,"ujs:enable-with",t.innerHTML),t.innerHTML=e):(d(t,"ujs:enable-with",t.value),t.value=e)),t.disabled=!0,d(t,"ujs:disabled",!0)},o=function(e){return l(e,t.formEnableSelector).forEach(r)},r=function(t){var e;return null!=(e=u(t,"ujs:enable-with"))&&(s(t,"button")?t.innerHTML=e:t.value=e,d(t,"ujs:enable-with",null)),t.disabled=!1,d(t,"ujs:disabled",null)},c=function(t){var e,n;return null!=(null!=(n=null!=(e=t.detail)?e[0]:void 0)?n.getResponseHeader("X-Xhr-Redirect"):void 0)}}.call(this),function(){var e;e=t.stopEverything,t.handleMethod=function(n){var a,r,o,i,l,u,c;if(c=(u=this).getAttribute("data-method"))return l=t.href(u),r=t.csrfToken(),a=t.csrfParam(),o=document.createElement("form"),i="<input name='_method' value='"+c+"' type='hidden' />",null==a||null==r||t.isCrossDomain(l)||(i+="<input name='"+a+"' value='"+r+"' type='hidden' />"),i+='<input type="submit" />',o.method="post",o.action=l,o.target=u.target,o.innerHTML=i,o.style.display="none",document.body.appendChild(o),o.querySelector('[type="submit"]').click(),e(n)}}.call(this),function(){var e,n,a,r,o,i,l,u,c,s=[].slice;i=t.matches,a=t.getData,u=t.setData,n=t.fire,c=t.stopEverything,e=t.ajax,r=t.isCrossDomain,l=t.serializeElement,o=function(t){var e;return null!=(e=t.getAttribute("data-remote"))&&"false"!==e},t.handleRemote=function(d){var m,f,p,b,h,v,S;return!o(b=this)||(n(b,"ajax:before")?(S=b.getAttribute("data-with-credentials"),p=b.getAttribute("data-type")||"script",i(b,t.formSubmitSelector)?(m=a(b,"ujs:submit-button"),h=a(b,"ujs:submit-button-formmethod")||b.method,v=a(b,"ujs:submit-button-formaction")||b.getAttribute("action")||location.href,"GET"===h.toUpperCase()&&(v=v.replace(/\?.*$/,"")),"multipart/form-data"===b.enctype?(f=new FormData(b),null!=m&&f.append(m.name,m.value)):f=l(b,m),u(b,"ujs:submit-button",null),u(b,"ujs:submit-button-formmethod",null),u(b,"ujs:submit-button-formaction",null)):i(b,t.buttonClickSelector)||i(b,t.inputChangeSelector)?(h=b.getAttribute("data-method"),v=b.getAttribute("data-url"),f=l(b,b.getAttribute("data-params"))):(h=b.getAttribute("data-method"),v=t.href(b),f=b.getAttribute("data-params")),e({type:h||"GET",url:v,data:f,dataType:p,beforeSend:function(t,e){return n(b,"ajax:beforeSend",[t,e])?n(b,"ajax:send",[t]):(n(b,"ajax:stopped"),!1)},success:function(){var t;return t=1<=arguments.length?s.call(arguments,0):[],n(b,"ajax:success",t)},error:function(){var t;return t=1<=arguments.length?s.call(arguments,0):[],n(b,"ajax:error",t)},complete:function(){var t;return t=1<=arguments.length?s.call(arguments,0):[],n(b,"ajax:complete",t)},crossDomain:r(v),withCredentials:null!=S&&"false"!==S}),c(d)):(n(b,"ajax:stopped"),!1))},t.formSubmitButtonClick=function(t){var e,n;if(n=(e=this).form)return e.name&&u(n,"ujs:submit-button",{name:e.name,value:e.value}),u(n,"ujs:formnovalidate-button",e.formNoValidate),u(n,"ujs:submit-button-formaction",e.getAttribute("formaction")),u(n,"ujs:submit-button-formmethod",e.getAttribute("formmethod"))},t.preventInsignificantClick=function(t){var e,n,a;if(this,a=(this.getAttribute("data-method")||"GET").toUpperCase(),e=this.getAttribute("data-params"),n=(t.metaKey||t.ctrlKey)&&"GET"===a&&!e,null!=t.button&&0!==t.button||n)return t.stopImmediatePropagation()}}.call(this),function(){var e,n,a,r,o,i,l,u,c,s,d,m,f,p,b;if(i=t.fire,a=t.delegate,u=t.getData,e=t.$,b=t.refreshCSRFTokens,n=t.CSRFProtection,f=t.loadCSPNonce,o=t.enableElement,r=t.disableElement,s=t.handleDisabledElement,c=t.handleConfirm,p=t.preventInsignificantClick,m=t.handleRemote,l=t.formSubmitButtonClick,d=t.handleMethod,"undefined"!=typeof jQuery&&null!==jQuery&&null!=jQuery.ajax){if(jQuery.rails)throw new Error("If you load both jquery_ujs and rails-ujs, use rails-ujs only.");jQuery.rails=t,jQuery.ajaxPrefilter((function(t,e,a){if(!t.crossDomain)return n(a)}))}t.start=function(){if(window._rails_loaded)throw new Error("rails-ujs has already been loaded!");return window.addEventListener("pageshow",(function(){return e(t.formEnableSelector).forEach((function(t){if(u(t,"ujs:disabled"))return o(t)})),e(t.linkDisableSelector).forEach((function(t){if(u(t,"ujs:disabled"))return o(t)}))})),a(document,t.linkDisableSelector,"ajax:complete",o),a(document,t.linkDisableSelector,"ajax:stopped",o),a(document,t.buttonDisableSelector,"ajax:complete",o),a(document,t.buttonDisableSelector,"ajax:stopped",o),a(document,t.linkClickSelector,"click",p),a(document,t.linkClickSelector,"click",s),a(document,t.linkClickSelector,"click",c),a(document,t.linkClickSelector,"click",r),a(document,t.linkClickSelector,"click",m),a(document,t.linkClickSelector,"click",d),a(document,t.buttonClickSelector,"click",p),a(document,t.buttonClickSelector,"click",s),a(document,t.buttonClickSelector,"click",c),a(document,t.buttonClickSelector,"click",r),a(document,t.buttonClickSelector,"click",m),a(document,t.inputChangeSelector,"change",s),a(document,t.inputChangeSelector,"change",c),a(document,t.inputChangeSelector,"change",m),a(document,t.formSubmitSelector,"submit",s),a(document,t.formSubmitSelector,"submit",c),a(document,t.formSubmitSelector,"submit",m),a(document,t.formSubmitSelector,"submit",(function(t){return setTimeout((function(){return r(t)}),13)})),a(document,t.formSubmitSelector,"ajax:send",r),a(document,t.formSubmitSelector,"ajax:complete",o),a(document,t.formInputClickSelector,"click",p),a(document,t.formInputClickSelector,"click",s),a(document,t.formInputClickSelector,"click",c),a(document,t.formInputClickSelector,"click",l),document.addEventListener("DOMContentLoaded",b),document.addEventListener("DOMContentLoaded",f),window._rails_loaded=!0},window.Rails===t&&i(document,"rails:attachBindings")&&t.start()}.call(this)}).call(this),"object"==typeof module&&module.exports?module.exports=t:"function"==typeof define&&define.amd&&define(t)}).call(this);
|