good_job 3.0.2 → 3.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 03dafffb596eaa6c7cf0994a40586039ac38215dcb0942850b1067deec0ac04d
4
- data.tar.gz: 917cbb1baebf9e6f8f7767d54606d0a07b02d3c28971e65369603f71500abf34
3
+ metadata.gz: 2225caa419c57cd76c9acba358fa94e2fd2d29c914d25459093a5a65b785087d
4
+ data.tar.gz: 2c6af031bb80859108ed847a7d891a2066b06004edb518fa8921b01b3c9b662a
5
5
  SHA512:
6
- metadata.gz: fd0c36fee775613d86ba832c6777eb9c72b651630f24b6236ea1b5373957d045121c3d5a0aea528cded163da638b7b6e41c3b155915186350bd1675e27b3ffee
7
- data.tar.gz: 041f30d08e8128d81ffdbf59e19f958761b809d9993facb5919119346cfc30b2f8da715658be47e94f317ffed3953381a93141a6bdc6540ab3f0b186604970f0
6
+ metadata.gz: 00bfcfcf22a4f3e5138bc2d85a172bbf4c74515c38759af561567c42f5464825fd47aa3f5c369f91196ede2b73211c9e1b3422f5d555bcff439e7fd32fc9db1a
7
+ data.tar.gz: cc28292dd555ddd951ab465534302f0775784074e456bb181eb2f5efc07f4281bcd49783fa38e4a205591fb05daa19f88ff013995fe06fe3761e675b1e84e0ba
data/CHANGELOG.md CHANGED
@@ -1,5 +1,57 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.3.0](https://github.com/bensheldon/good_job/tree/v3.3.0) (2022-07-24)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.2.0...v3.3.0)
6
+
7
+ **Closed issues:**
8
+
9
+ - Calculating database connections [\#669](https://github.com/bensheldon/good_job/issues/669)
10
+ - Unable to Replace GoodJob's Logger [\#667](https://github.com/bensheldon/good_job/issues/667)
11
+ - Readme should consistently encourage usage of `config.good_job....` instead of `GoodJob.` configuration [\#628](https://github.com/bensheldon/good_job/issues/628)
12
+ - Improve the "Gem development" section of README? [\#551](https://github.com/bensheldon/good_job/issues/551)
13
+ - Simplify Rails initialization to only be a mountable Engine [\#543](https://github.com/bensheldon/good_job/issues/543)
14
+
15
+ **Merged pull requests:**
16
+
17
+ - Dashboard: Update cron and processes to match jobs listing [\#676](https://github.com/bensheldon/good_job/pull/676) ([bkeepers](https://github.com/bkeepers))
18
+ - Dashboard: improvements to jobs index and show pages [\#672](https://github.com/bensheldon/good_job/pull/672) ([bkeepers](https://github.com/bkeepers))
19
+ - Replace "timestamp" column-type in migrations with "datetime" [\#671](https://github.com/bensheldon/good_job/pull/671) ([bensheldon](https://github.com/bensheldon))
20
+ - Improve Readme description of v3 job preservation defaults [\#670](https://github.com/bensheldon/good_job/pull/670) ([bensheldon](https://github.com/bensheldon))
21
+ - update Gemfile.lock to latest dependencies [\#647](https://github.com/bensheldon/good_job/pull/647) ([jrochkind](https://github.com/jrochkind))
22
+
23
+ ## [v3.2.0](https://github.com/bensheldon/good_job/tree/v3.2.0) (2022-07-12)
24
+
25
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.1.0...v3.2.0)
26
+
27
+ **Merged pull requests:**
28
+
29
+ - Ordered queue handling by workers [\#665](https://github.com/bensheldon/good_job/pull/665) ([jrochkind](https://github.com/jrochkind))
30
+
31
+ ## [v3.1.0](https://github.com/bensheldon/good_job/tree/v3.1.0) (2022-07-11)
32
+
33
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.0.2...v3.1.0)
34
+
35
+ **Implemented enhancements:**
36
+
37
+ - Improve Dashboard display of parameters \(CronEntry kwargs; Process configuration; Job and Execution database values\) [\#662](https://github.com/bensheldon/good_job/pull/662) ([bensheldon](https://github.com/bensheldon))
38
+
39
+ **Fixed bugs:**
40
+
41
+ - Don't delegate `GoodJob::Job#status` to executions to avoid race condition [\#661](https://github.com/bensheldon/good_job/pull/661) ([bensheldon](https://github.com/bensheldon))
42
+
43
+ **Closed issues:**
44
+
45
+ - How to suppress repetitive logs in development? [\#658](https://github.com/bensheldon/good_job/issues/658)
46
+ - 500 Internal Server Error Exception in web interface trying to view running jobs [\#656](https://github.com/bensheldon/good_job/issues/656)
47
+ - Cron schedule page in dashboard not showing kwargs [\#608](https://github.com/bensheldon/good_job/issues/608)
48
+ - Paralelism x database connections [\#569](https://github.com/bensheldon/good_job/issues/569)
49
+
50
+ **Merged pull requests:**
51
+
52
+ - Show job/cron/process counts in the Navbar [\#663](https://github.com/bensheldon/good_job/pull/663) ([bensheldon](https://github.com/bensheldon))
53
+ - Dequeing should be first-in first-out [\#651](https://github.com/bensheldon/good_job/pull/651) ([jrochkind](https://github.com/jrochkind))
54
+
3
55
  ## [v3.0.2](https://github.com/bensheldon/good_job/tree/v3.0.2) (2022-07-10)
4
56
 
5
57
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.0.1...v3.0.2)
@@ -1023,7 +1075,6 @@
1023
1075
  - PgBouncer and prepared statements [\#269](https://github.com/bensheldon/good_job/issues/269)
1024
1076
  - Question about locking internals [\#212](https://github.com/bensheldon/good_job/issues/212)
1025
1077
  - Encoding::UndefinedConversionError \("\xE2" from ASCII-8BIT to UTF-8\) [\#198](https://github.com/bensheldon/good_job/issues/198)
1026
- - tools for managing a 'fleet' of processes [\#150](https://github.com/bensheldon/good_job/issues/150)
1027
1078
 
1028
1079
  **Merged pull requests:**
1029
1080
 
data/README.md CHANGED
@@ -117,7 +117,7 @@ For more of the story of GoodJob, read the [introductory blog post](https://isla
117
117
  YourJob.set(queue: :some_queue, wait: 5.minutes, priority: 10).perform_later
118
118
  ```
119
119
 
120
- 1. In development, GoodJob executes jobs immediately in a separate thread (async mode). In production, GoodJob provides different options:
120
+ 1. In development, GoodJob executes jobs immediately in a separate thread ("async" mode). In production, GoodJob provides different options:
121
121
 
122
122
  - By default, GoodJob separates job enqueuing from job execution so that jobs can be scaled independently of the web server. Use the GoodJob command-line tool to execute jobs:
123
123
 
@@ -199,7 +199,7 @@ Usage:
199
199
  good_job cleanup_preserved_jobs
200
200
 
201
201
  Options:
202
- [--before-seconds-ago=SECONDS] # Destroy records finished more than this many seconds ago (env var: GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO, default: 86400)
202
+ [--before-seconds-ago=SECONDS] # Destroy records finished more than this many seconds ago (env var: GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO, default: 1209600 (14 days))
203
203
 
204
204
  Destroys preserved job records.
205
205
 
@@ -226,6 +226,8 @@ GoodJob configuration can be placed within Rails `config` directory for all envi
226
226
  Configuration examples:
227
227
 
228
228
  ```ruby
229
+ # config/initializers/good_job.rb OR config/application.rb OR config/environments/{RAILS_ENV}.rb
230
+
229
231
  Rails.application.configure do
230
232
  # Configure options individually...
231
233
  config.good_job.preserve_job_records = true
@@ -275,9 +277,9 @@ Available configuration options are:
275
277
  - `enable_cron` (boolean) whether to run cron process. Defaults to `false`. You can also set this with the environment variable `GOOD_JOB_ENABLE_CRON`.
276
278
  - `cron` (hash) cron configuration. Defaults to `{}`. You can also set this as a JSON string with the environment variable `GOOD_JOB_CRON`
277
279
  - `cleanup_discarded_jobs` (boolean) whether to destroy discarded jobs when cleaning up preserved jobs using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `true`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_DISCARDED_JOBS`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
278
- - `cleanup_preserved_jobs_before_seconds_ago` (integer) number of seconds to preserve jobs when using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `86400` (1 day). Can also be set with the environment variable `GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
279
- - `cleanup_interval_jobs` (integer) Number of jobs a Scheduler will execute before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_JOBS`.
280
- - `cleanup_interval_seconds` (integer) Number of seconds a Scheduler will wait before cleaning up preserved jobs. Defaults to `nil`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_SECONDS`.
280
+ - `cleanup_preserved_jobs_before_seconds_ago` (integer) number of seconds to preserve jobs when using the `$ good_job cleanup_preserved_jobs` CLI command or calling `GoodJob.cleanup_preserved_jobs`. Defaults to `1209600` (14 days). Can also be set with the environment variable `GOOD_JOB_CLEANUP_PRESERVED_JOBS_BEFORE_SECONDS_AGO`. _This configuration is only used when {GoodJob.preserve_job_records} is `true`._
281
+ - `cleanup_interval_jobs` (integer) Number of jobs a Scheduler will execute before cleaning up preserved jobs. Defaults to `1000`. Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_JOBS`.
282
+ - `cleanup_interval_seconds` (integer) Number of seconds a Scheduler will wait before cleaning up preserved jobs. Defaults to `600` (10 minutes). Can also be set with the environment variable `GOOD_JOB_CLEANUP_INTERVAL_SECONDS`.
281
283
  - `inline_execution_respects_schedule` (boolean) Opt-in to future behavior of inline execution respecting scheduled jobs. Defaults to `false`.
282
284
  - `logger` ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger` (Default: `Rails.logger`).
283
285
  - `preserve_job_records` (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
@@ -310,21 +312,21 @@ config.good_job.execution_mode = :external
310
312
  Good Job’s general behavior can also be configured via attributes directly on the `GoodJob` module:
311
313
 
312
314
  - **`GoodJob.active_record_parent_class`** (string) The ActiveRecord parent class inherited by GoodJob's ActiveRecord model `GoodJob::Job` (defaults to `"ActiveRecord::Base"`). Configure this when using [multiple databases with ActiveRecord](https://guides.rubyonrails.org/active_record_multiple_databases.html) or when other custom configuration is necessary for the ActiveRecord model to connect to the Postgres database. _The value must be a String to avoid premature initialization of ActiveRecord._
313
- - **`GoodJob.logger`** ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger`.
314
- - **`GoodJob.preserve_job_records`** (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
315
- - **`GoodJob.retry_on_unhandled_error`** (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `false`)
316
- - **`GoodJob.on_thread_error`** (proc, lambda, or callable) will be called when an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake.
317
315
 
318
316
  You’ll generally want to configure these in `config/initializers/good_job.rb`, like so:
319
317
 
320
318
  ```ruby
321
319
  # config/initializers/good_job.rb
322
320
  GoodJob.active_record_parent_class = "ApplicationRecord"
323
- GoodJob.preserve_job_records = true
324
- GoodJob.retry_on_unhandled_error = false
325
- GoodJob.on_thread_error = -> (exception) { Raven.capture_exception(exception) }
326
321
  ```
327
322
 
323
+ The following options are also configurable via accessors, but you are encouraged to use the configuration attributes instead because these may be deprecated and removed in the future:
324
+
325
+ - **`GoodJob.logger`** ([Rails Logger](https://api.rubyonrails.org/classes/ActiveSupport/Logger.html)) lets you set a custom logger for GoodJob. It should be an instance of a Rails `Logger`.
326
+ - **`GoodJob.preserve_job_records`** (boolean) keeps job records in your database even after jobs are completed. (Default: `true`)
327
+ - **`GoodJob.retry_on_unhandled_error`** (boolean) causes jobs to be re-queued and retried if they raise an instance of `StandardError`. Be advised this may lead to jobs being repeated infinitely ([see below for more on retries](#retries)). Instances of `Exception`, like SIGINT, will *always* be retried, regardless of this attribute’s value. (Default: `false`)
328
+ - **`GoodJob.on_thread_error`** (proc, lambda, or callable) will be called when an Exception. It can be useful for logging errors to bug tracking services, like Sentry or Airbrake.
329
+
328
330
  ### Dashboard
329
331
 
330
332
  ![Dashboard UI](https://github.com/bensheldon/good_job/raw/main/SCREENSHOT.png)
@@ -361,14 +363,7 @@ GoodJob includes a Dashboard as a mountable `Rails::Engine`.
361
363
  end
362
364
  ```
363
365
 
364
- 1. If you want to see finished (successful) and discarded (failed) jobs on the dashboard, you will have to configure job records to be preserved:
365
-
366
- ```ruby
367
- # eg in config/initializers/good_job.rb
368
- GoodJob.preserve_job_records = true
369
- ```
370
-
371
- See more at [Monitor and preserve worked jobs](#monitor-and-preserve-worked-jobs)
366
+ _To view finished (successful) and discarded (failed) jobs on the Dashboard, GoodJob must be configured to preserve job records. Preservation is enabled by default._
372
367
 
373
368
  **Troubleshooting the Dashboard:** Some applications are unable to autoload the Goodjob Engine. To work around this, explicitly require the Engine at the top of your `config/application.rb` file, immediately after Rails is required and before Bundler requires the Rails' groups.
374
369
 
@@ -665,7 +660,7 @@ By default, GoodJob creates a single thread execution pool that will execute job
665
660
 
666
661
  A pool is configured with the following syntax `<participating_queues>:<thread_count>`:
667
662
 
668
- - `<participating_queues>`: either `queue1,queue2` (only those queues), `*` (all) or `-queue1,queue2` (all except those queues).
663
+ - `<participating_queues>`: either `queue1,queue2` (only those queues), `+queue1,queue2` (only those queues, and processed in order), `*` (all) or `-queue1,queue2` (all except those queues).
669
664
  - `<thread_count>`: a count overriding for this specific pool the global `max-threads`.
670
665
 
671
666
  Pool configurations are separated with a semicolon (;) in the `queues` configuration
@@ -683,6 +678,12 @@ By default, GoodJob creates a single thread execution pool that will execute job
683
678
  - `-transactional_messages,batch_processing`: execute jobs enqueued on _any_ queue _excluding_ `transactional_messages` or `batch_processing`, with up to 2 threads.
684
679
  - `*`: execute jobs on any queue, with up to 5 threads (as configured by `--max-threads=5`).
685
680
 
681
+ When a pool is performing jobs from multiple queues, jobs will be performed from specified queues, ordered by priority and creation time. To perform jobs from queues in the queues' given order, use the `+` modifier. In this example, jobs in `batch_processing` will be performed only when there are no jobs in `transactional_messages`:
682
+
683
+ ```bash
684
+ bundle exec good_job --queues="+transactional_messages,batch_processing"
685
+ ```
686
+
686
687
  Configuration can be injected by environment variables too:
687
688
 
688
689
  ```bash
@@ -872,25 +873,24 @@ If your application is already using an ActiveJob backend, you will need to inst
872
873
 
873
874
  GoodJob is fully instrumented with [`ActiveSupport::Notifications`](https://edgeguides.rubyonrails.org/active_support_instrumentation.html#introduction-to-instrumentation).
874
875
 
875
- By default, GoodJob will preserve job records for 14 days after they are run, regardless of whether they succeed or not (raising a kind of `StandardError`), unless they are interrupted (raising a kind of `Exception`).
876
+ By default, GoodJob will preserve job records for 14 days after they are run, regardless of whether they succeed or raised an exception.
876
877
 
877
- To not preserve job records for later inspection, set an initializer:
878
+ To instead delete job records immediately after they are finished:
878
879
 
879
880
  ```ruby
880
881
  # config/initializers/good_job.rb
881
- GoodJob.preserve_job_records = false # defaults to true, or `false` or `:on_unhandled_error`
882
+ config.good_job.preserve_job_records = false # defaults to true; can also be `false` or `:on_unhandled_error`
882
883
  ```
883
884
 
884
- GoodJob will automatically delete these job records after 14 days. The retention period, as well as the frequency GoodJob checks for deletable records can be configured:
885
+ GoodJob will automatically delete preserved job records after 14 days. The retention period, as well as the frequency GoodJob checks for deletable records can be configured:
885
886
 
886
887
  ```ruby
887
-
888
- config.cleanup_preserved_jobs_before_seconds_ago = 14.days.to_i
889
- config.cleanup_interval_jobs = 1_000 # Number of executed jobs between deletion sweeps.
890
- config.cleanup_interval_seconds = 10.minutes.to_i # Number of seconds between deletion sweeps.
888
+ config.good_job.cleanup_preserved_jobs_before_seconds_ago = 14.days.to_i
889
+ config.good_job.cleanup_interval_jobs = 1_000 # Number of executed jobs between deletion sweeps.
890
+ config.good_job.cleanup_interval_seconds = 10.minutes.to_i # Number of seconds between deletion sweeps.
891
891
  ```
892
892
 
893
- It is also possible to manually trigger a cleanup:
893
+ It is also possible to manually trigger a cleanup of preserved job records:
894
894
 
895
895
  - For example, in a Rake task:
896
896
 
@@ -1035,7 +1035,7 @@ For gem development and debugging information, please review the [README's Gem D
1035
1035
  # Clone the repository locally
1036
1036
  git clone git@github.com:bensheldon/good_job.git
1037
1037
 
1038
- # Set up the local environment
1038
+ # Set up the gem development environment
1039
1039
  bin/setup
1040
1040
  ```
1041
1041
 
@@ -1074,6 +1074,10 @@ bundle install
1074
1074
  Tests can be run against the primary development environment:
1075
1075
 
1076
1076
  ```bash
1077
+ # Set up the gem development environment
1078
+ bin/setup
1079
+
1080
+ # Run the tests
1077
1081
  bin/rspec
1078
1082
  ```
1079
1083
 
@@ -4,11 +4,13 @@ import renderCharts from "charts";
4
4
  import checkboxToggle from "checkbox_toggle";
5
5
  import documentReady from "document_ready";
6
6
  import showToasts from "toasts";
7
+ import setupPopovers from "popovers";
7
8
  import LivePoll from "live_poll";
8
9
 
9
10
  documentReady(function() {
10
11
  renderCharts();
11
12
  showToasts();
13
+ setupPopovers();
12
14
  checkboxToggle();
13
15
 
14
16
  const livePoll = new LivePoll
@@ -0,0 +1,7 @@
1
+ export default function() {
2
+ document.querySelectorAll('[data-bs-toggle="popover"]').forEach((el) => {
3
+ new bootstrap.Popover(el, {
4
+ template: '<div class="popover" role="tooltip"><div class="popover-arrow"></div><h3 class="popover-header"></h3><pre class="popover-body text-wrap text-break"></pre></div>'
5
+ })
6
+ })
7
+ }
@@ -39,3 +39,7 @@
39
39
  background-color: red;
40
40
  color: white;
41
41
  }
42
+
43
+ .btn-outline-secondary {
44
+ border-color: #ced4da; /* $gray-400 */
45
+ }
@@ -56,7 +56,7 @@ module GoodJob
56
56
  end
57
57
 
58
58
  def show
59
- @job = Job.find(params[:id])
59
+ @job = Job.includes_advisory_locks.find(params[:id])
60
60
  end
61
61
 
62
62
  def discard
@@ -34,8 +34,8 @@ module GoodJob
34
34
  STATUS_COLOR = {
35
35
  discarded: "danger",
36
36
  finished: "success",
37
- queued: "warning",
38
- retried: "secondary",
37
+ queued: "secondary",
38
+ retried: "warning",
39
39
  running: "primary",
40
40
  scheduled: "secondary",
41
41
  }.freeze
@@ -1,61 +1,64 @@
1
- <div class="my-3 flex">
2
- <h2>Cron Schedules</h2>
1
+ <div class="bg-light break-out border-bottom">
2
+ <h2 class="container-fluid pt-3 pb-2">Cron Schedules</h2>
3
3
  </div>
4
4
 
5
- <% if @cron_entries.present? %>
6
- <div class="card my-3">
7
- <div class="table-responsive">
8
- <table class="table card-table table-bordered table-hover table-sm mb-0">
9
- <thead>
10
- <th>Key</th>
11
- <th>Schedule</th>
12
- <th>
13
- Properties
14
- <%= tag.button "Toggle", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
15
- data: { bs_toggle: "collapse", bs_target: ".cron-entry-properties" },
16
- aria: { expanded: false, controls: @cron_entries.map { |cron_entry| "##{dom_id(cron_entry, 'properties')}" }.join(" ") }
17
- %>
18
- </th>
19
- <th>Description</th>
20
- <th>Next scheduled</th>
21
- <th>Last run</th>
22
- <th>Actions</th>
23
- </thead>
24
- <tbody>
25
- <% @cron_entries.each do |cron_entry| %>
26
- <tr id="<%= dom_id(cron_entry) %>">
27
- <td class="font-monospace"><%= cron_entry.key %></td>
28
- <td class="font-monospace"><%= cron_entry.schedule %></td>
29
- <td>
30
- <%= tag.button("Inspect", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
31
- data: { bs_toggle: "collapse", bs_target: "##{dom_id(cron_entry, 'properties')}" },
32
- aria: { expanded: false, controls: dom_id(cron_entry, 'properties') }) %>
33
- <%= tag.pre(JSON.pretty_generate(cron_entry.display_properties), id: dom_id(cron_entry, 'properties'), class: "collapse cron-entry-properties") %>
34
- </td>
35
- <td><%= cron_entry.description %></td>
36
- <td><%= cron_entry.next_at %></td>
37
- <td>
38
- <% if cron_entry.last_job.present? %>
39
- <%= link_to cron_entry.last_at, cron_entry_path(cron_entry), title: "Job #{cron_entry.last_job.id}" %>
40
- <% end %>
41
- </td>
42
- <td>
43
- <%= button_to enqueue_cron_entry_path(cron_entry.id), method: :post, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Run cron entry now" }, title: "Run cron entry now", data: { confirm: "Confirm run cron entry now" } do %>
44
- <%= render "good_job/shared/icons/play" %>
45
- <% end %>
46
- </td>
47
- </tr>
5
+ <div class="card my-3">
6
+ <div class="list-group list-group-flush text-nowrap" role="table">
7
+ <header class="list-group-item bg-light">
8
+ <div class="row small text-muted text-uppercase align-items-center">
9
+ <div class="col"></div>
10
+ <div class="col">Class</div>
11
+ <div class="col">Schedule</div>
12
+ <div class="col">Next scheduled</div>
13
+ <div class="col">Last run</div>
14
+ <div class="col text-end">
15
+ <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
16
+ data: { bs_toggle: "collapse", bs_target: ".cron-entry-properties" },
17
+ aria: { expanded: false, controls: @cron_entries.map { |cron_entry| "##{dom_id(cron_entry, 'properties')}" }.join(" ") } do %>
18
+ <%= render_icon "info" %>
19
+ <span class="visually-hidden">Inspect</span>
48
20
  <% end %>
49
- </tbody>
50
- </table>
51
- </div>
21
+ </div>
22
+ </div>
23
+ </header>
24
+ <% @cron_entries.each do |cron_entry| %>
25
+ <div id="<%= dom_id(cron_entry) %>" class="list-group-item py-3" role="row">
26
+ <div class="row align-items-center">
27
+ <div class="col">
28
+ <div class="small font-monospace"><%= cron_entry.key %></div>
29
+ <div class="small text-muted"><%= cron_entry.description %></div>
30
+ </div>
31
+ <div class="col"><%= tag.span tag.code(cron_entry.job_class), class: "fs-5 mb-0" %></div>
32
+ <div class="col font-monospace fw-bold"><%= cron_entry.schedule %></div>
33
+ <div class="col small"><%= relative_time cron_entry.next_at %></div>
34
+ <div class="col small">
35
+ <% if cron_entry.last_job.present? %>
36
+ <%= link_to relative_time(cron_entry.last_at), cron_entry_path(cron_entry), title: "Job #{cron_entry.last_job.id}" %>
37
+ <% end %>
38
+ </div>
39
+ <div class="col d-flex gap-3 justify-content-end">
40
+ <%= button_to enqueue_cron_entry_path(cron_entry.id), method: :post, class: "btn btn-sm btn-outline-primary", form_class: "d-inline-block", aria: { label: "Run cron entry now" }, title: "Run cron entry now", data: { confirm: "Confirm run cron entry now" } do %>
41
+ <%= render "good_job/shared/icons/play" %>
42
+ Run Now
43
+ <% end %>
44
+ <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
45
+ title: "Inspect",
46
+ data: { bs_toggle: "collapse", bs_target: "##{dom_id(cron_entry, 'properties')}" },
47
+ aria: { expanded: false, controls: dom_id(cron_entry, "properties") } do %>
48
+ <%= render_icon "info" %>
49
+ <span class="visually-hidden">Inspect</span>
50
+ <% end %>
51
+ </div>
52
+ </div>
53
+ </div>
54
+ <%= tag.div id: dom_id(cron_entry, 'properties'), class: "collapse cron-entry-properties list-group-item collapse small bg-dark text-light" do %>
55
+ <%= tag.pre JSON.pretty_generate(cron_entry.display_properties) %>
56
+ <% end %>
57
+ <% end %>
58
+ <% if @cron_entries.empty? %>
59
+ <div class="list-group-item py-4 text-center text-muted">
60
+ No cron schedules found.
61
+ </div>
62
+ <% end %>
52
63
  </div>
53
- <% else %>
54
- <div class="card my-3">
55
- <div class="card-body">
56
- <p class="card-text">
57
- <em>No cron schedules found.</em>
58
- </p>
59
- </div>
60
- </div>
61
- <% end %>
64
+ </div>
@@ -3,43 +3,46 @@
3
3
  <div class="list-group list-group-flush">
4
4
  <% executions.each do |execution| %>
5
5
  <%= tag.div id: dom_id(execution), class: "list-group-item py-3" do %>
6
- <div class="d-md-flex">
7
- <div class="flex-fill">
8
- <div class="small text-muted">
9
- #<%= execution.number %>:
10
- <%= tag.code link_to(execution.id, "##{dom_id(execution)}", class: "text-muted text-decoration-none") %>
11
- </div>
12
- <div class="d-flex gap-2 align-items-center text-muted mt-1">
6
+ <div class="row align-items-center text-nowrap">
7
+ <div class="col-5 d-flex gap-2">
8
+ <%= tag.span execution.number, class: "badge bg-secondary bg-opacity-50 rounded-pill" %>
9
+ <%= tag.code link_to(execution.id, "##{dom_id(execution)}", class: "text-muted text-decoration-none small") %>
10
+ </div>
11
+ <div class="col-2 small">
12
+ <% if execution.queue_latency %>
13
+ <%= format_duration execution.queue_latency %> <span class="text-muted">in queue</span>
14
+ <% end %>
15
+ </div>
16
+ <div class="col-2 small">
17
+ <% if execution.runtime_latency %>
18
+ <%= format_duration execution.runtime_latency %> <span class="text-muted">runtime</span>
19
+ <% end %>
20
+ </div>
21
+ <div class="col">
22
+ <div class="d-flex gap-3 align-items-center justify-content-end">
23
+ <%= tag.span relative_time(execution.last_status_at, include_seconds: true), class: "small" %>
13
24
  <%= status_badge execution.status %>
14
- <%= relative_time execution.last_status_at, include_seconds: true %>
15
-
16
- <% if execution.runtime_latency %>
17
- • <div><%= format_duration execution.runtime_latency %> runtime</div>
18
- <% end %>
19
- <% if execution.queue_latency %>
20
- • <div><%= format_duration execution.queue_latency %> in queue</div>
21
- <% end %>
22
25
  </div>
23
26
  </div>
24
- <div>
25
- <div class="mt-4 d-flex gap-2">
26
- <%= tag.button type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
27
- data: { bs_toggle: "collapse", bs_target: "##{dom_id(execution, 'params')}" },
28
- aria: { expanded: false, controls: dom_id(execution, "params") } do %>
29
- Inspect
30
- <% end %>
31
- </div>
27
+ <div class="col-auto">
28
+ <%= tag.button type: "button", class: "btn btn-sm text-muted ms-auto", role: "button",
29
+ title: "Inspect",
30
+ data: { bs_toggle: "collapse", bs_target: "##{dom_id(execution, 'params')}" },
31
+ aria: { expanded: false, controls: dom_id(execution, "params") } do %>
32
+ <%= render_icon "info" %>
33
+ <span class="visually-hidden">Inspect</span>
34
+ <% end %>
32
35
  </div>
33
36
  </div>
34
37
  <% if execution.error %>
35
- <div class="mt-3">
38
+ <div class="mt-3 small">
36
39
  <strong class="small">Error:</strong>
37
- <pre class="text-wrap text-break m-0"><%= execution.error %></pre>
40
+ <code class="text-wrap text-break m-0 text-black"><%= execution.error %></code>
38
41
  </div>
39
42
  <% end %>
40
- <div>
41
- <%= tag.pre JSON.pretty_generate(execution.serialized_params), id: dom_id(execution, "params"), class: "collapse bg-light card card-body p-3 my-3" %>
42
- </div>
43
+ <% end %>
44
+ <%= tag.div id: dom_id(execution, "params"), class: "list-group-item collapse small bg-dark text-light" do %>
45
+ <%= tag.pre JSON.pretty_generate(execution.display_serialized_params) %>
43
46
  <% end %>
44
47
  <% end %>
45
48
  </div>