good_job 2.4.0 → 2.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +89 -0
- data/README.md +51 -20
- data/engine/app/assets/vendor/rails_ujs.js +747 -0
- data/engine/app/controllers/good_job/assets_controller.rb +4 -0
- data/engine/app/controllers/good_job/base_controller.rb +8 -0
- data/engine/app/controllers/good_job/cron_entries_controller.rb +19 -0
- data/engine/app/controllers/good_job/jobs_controller.rb +36 -0
- data/engine/app/filters/good_job/base_filter.rb +12 -7
- data/engine/app/filters/good_job/executions_filter.rb +1 -1
- data/engine/app/filters/good_job/jobs_filter.rb +4 -2
- data/engine/app/views/good_job/cron_entries/index.html.erb +51 -0
- data/engine/app/views/good_job/cron_entries/show.html.erb +4 -0
- data/engine/app/views/good_job/{shared/_executions_table.erb → executions/_table.erb} +1 -1
- data/engine/app/views/good_job/executions/index.html.erb +1 -1
- data/engine/app/views/good_job/{shared/_jobs_table.erb → jobs/_table.erb} +17 -5
- data/engine/app/views/good_job/jobs/index.html.erb +14 -1
- data/engine/app/views/good_job/jobs/show.html.erb +2 -2
- data/engine/app/views/good_job/shared/_filter.erb +9 -10
- data/engine/app/views/good_job/shared/icons/_arrow_clockwise.html.erb +5 -0
- data/engine/app/views/good_job/shared/icons/_play.html.erb +4 -0
- data/engine/app/views/good_job/shared/icons/_skip_forward.html.erb +4 -0
- data/engine/app/views/good_job/shared/icons/_stop.html.erb +4 -0
- data/engine/app/views/layouts/good_job/base.html.erb +3 -1
- data/engine/config/routes.rb +15 -2
- data/lib/generators/good_job/install_generator.rb +6 -0
- data/lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb +3 -1
- data/lib/generators/good_job/templates/update/migrations/{01_create_good_jobs.rb → 01_create_good_jobs.rb.erb} +1 -1
- data/lib/generators/good_job/templates/update/migrations/02_add_cron_at_to_good_jobs.rb.erb +14 -0
- data/lib/generators/good_job/templates/update/migrations/03_add_cron_key_cron_at_index_to_good_jobs.rb.erb +20 -0
- data/lib/generators/good_job/update_generator.rb +6 -0
- data/lib/good_job/active_job_extensions/concurrency.rb +3 -4
- data/lib/good_job/active_job_job.rb +245 -0
- data/lib/good_job/adapter.rb +4 -2
- data/lib/good_job/cli.rb +3 -1
- data/lib/good_job/configuration.rb +5 -1
- data/lib/good_job/cron_entry.rb +138 -0
- data/lib/good_job/cron_manager.rb +17 -31
- data/lib/good_job/current_thread.rb +38 -5
- data/lib/good_job/execution.rb +50 -25
- data/lib/good_job/lockable.rb +1 -1
- data/lib/good_job/log_subscriber.rb +3 -3
- data/lib/good_job/scheduler.rb +1 -0
- data/lib/good_job/version.rb +1 -1
- metadata +21 -12
- data/engine/app/controllers/good_job/cron_schedules_controller.rb +0 -9
- data/engine/app/models/good_job/active_job_job.rb +0 -127
- data/engine/app/views/good_job/cron_schedules/index.html.erb +0 -50
data/lib/good_job/execution.rb
CHANGED
@@ -10,6 +10,9 @@ module GoodJob
|
|
10
10
|
# Raised if something attempts to execute a previously completed Execution again.
|
11
11
|
PreviouslyPerformedError = Class.new(StandardError)
|
12
12
|
|
13
|
+
# String separating Error Class from Error Message
|
14
|
+
ERROR_MESSAGE_SEPARATOR = ": "
|
15
|
+
|
13
16
|
# ActiveJob jobs without a +queue_name+ attribute are placed on this queue.
|
14
17
|
DEFAULT_QUEUE_NAME = 'default'
|
15
18
|
# ActiveJob jobs without a +priority+ attribute are given this priority.
|
@@ -50,6 +53,16 @@ module GoodJob
|
|
50
53
|
end
|
51
54
|
end
|
52
55
|
|
56
|
+
def self._migration_pending_warning
|
57
|
+
ActiveSupport::Deprecation.warn(<<~DEPRECATION)
|
58
|
+
GoodJob has pending database migrations. To create the migration files, run:
|
59
|
+
rails generate good_job:update
|
60
|
+
To apply the migration files, run:
|
61
|
+
rails db:migrate
|
62
|
+
DEPRECATION
|
63
|
+
nil
|
64
|
+
end
|
65
|
+
|
53
66
|
# Get Jobs with given ActiveJob ID
|
54
67
|
# @!method active_job_id
|
55
68
|
# @!scope class
|
@@ -174,13 +187,7 @@ module GoodJob
|
|
174
187
|
break if execution.blank?
|
175
188
|
break :unlocked unless execution&.executable?
|
176
189
|
|
177
|
-
|
178
|
-
execution.with_advisory_lock(key: "good_jobs-#{execution.active_job_id}") do
|
179
|
-
execution.perform
|
180
|
-
end
|
181
|
-
rescue RecordAlreadyAdvisoryLockedError => e
|
182
|
-
ExecutionResult.new(value: nil, handled_error: e)
|
183
|
-
end
|
190
|
+
execution.perform
|
184
191
|
end
|
185
192
|
end
|
186
193
|
|
@@ -228,7 +235,15 @@ module GoodJob
|
|
228
235
|
|
229
236
|
if CurrentThread.cron_key
|
230
237
|
execution_args[:cron_key] = CurrentThread.cron_key
|
231
|
-
|
238
|
+
|
239
|
+
@cron_at_index = column_names.include?('cron_at') && connection.index_name_exists?(:good_jobs, :index_good_jobs_on_cron_key_and_cron_at) unless instance_variable_defined?(:@cron_at_index)
|
240
|
+
|
241
|
+
if @cron_at_index
|
242
|
+
execution_args[:cron_at] = CurrentThread.cron_at
|
243
|
+
else
|
244
|
+
_migration_pending_warning
|
245
|
+
end
|
246
|
+
elsif CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
|
232
247
|
execution_args[:cron_key] = CurrentThread.execution.cron_key
|
233
248
|
end
|
234
249
|
|
@@ -239,7 +254,7 @@ module GoodJob
|
|
239
254
|
execution.save!
|
240
255
|
active_job.provider_job_id = execution.id
|
241
256
|
|
242
|
-
CurrentThread.execution.retried_good_job_id = execution.id if CurrentThread.
|
257
|
+
CurrentThread.execution.retried_good_job_id = execution.id if CurrentThread.active_job_id && CurrentThread.active_job_id == active_job.job_id
|
243
258
|
|
244
259
|
execution
|
245
260
|
end
|
@@ -259,7 +274,7 @@ module GoodJob
|
|
259
274
|
result = execute
|
260
275
|
|
261
276
|
job_error = result.handled_error || result.unhandled_error
|
262
|
-
self.error =
|
277
|
+
self.error = [job_error.class, ERROR_MESSAGE_SEPARATOR, job_error.message].join if job_error
|
263
278
|
|
264
279
|
if result.unhandled_error && GoodJob.retry_on_unhandled_error
|
265
280
|
save!
|
@@ -279,29 +294,39 @@ module GoodJob
|
|
279
294
|
self.class.unscoped.unfinished.owns_advisory_locked.exists?(id: id)
|
280
295
|
end
|
281
296
|
|
297
|
+
def active_job
|
298
|
+
ActiveJob::Base.deserialize(active_job_data)
|
299
|
+
end
|
300
|
+
|
282
301
|
private
|
283
302
|
|
303
|
+
def active_job_data
|
304
|
+
serialized_params.deep_dup
|
305
|
+
.tap do |job_data|
|
306
|
+
job_data["provider_job_id"] = id
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
284
310
|
# @return [ExecutionResult]
|
285
311
|
def execute
|
286
|
-
GoodJob::CurrentThread.
|
287
|
-
|
312
|
+
GoodJob::CurrentThread.within do |current_thread|
|
313
|
+
current_thread.reset
|
314
|
+
current_thread.execution = self
|
288
315
|
|
289
|
-
|
290
|
-
|
316
|
+
# DEPRECATION: Remove deprecated `good_job:` parameter in GoodJob v3
|
317
|
+
ActiveSupport::Notifications.instrument("perform_job.good_job", { good_job: self, execution: self, process_id: current_thread.process_id, thread_name: current_thread.thread_name }) do
|
318
|
+
value = ActiveJob::Base.execute(active_job_data)
|
291
319
|
|
292
|
-
|
293
|
-
|
294
|
-
|
320
|
+
if value.is_a?(Exception)
|
321
|
+
handled_error = value
|
322
|
+
value = nil
|
323
|
+
end
|
324
|
+
handled_error ||= current_thread.error_on_retry || current_thread.error_on_discard
|
295
325
|
|
296
|
-
|
297
|
-
|
298
|
-
value
|
326
|
+
ExecutionResult.new(value: value, handled_error: handled_error)
|
327
|
+
rescue StandardError => e
|
328
|
+
ExecutionResult.new(value: nil, unhandled_error: e)
|
299
329
|
end
|
300
|
-
handled_error ||= GoodJob::CurrentThread.error_on_retry || GoodJob::CurrentThread.error_on_discard
|
301
|
-
|
302
|
-
ExecutionResult.new(value: value, handled_error: handled_error)
|
303
|
-
rescue StandardError => e
|
304
|
-
ExecutionResult.new(value: nil, unhandled_error: e)
|
305
330
|
end
|
306
331
|
end
|
307
332
|
end
|
data/lib/good_job/lockable.rb
CHANGED
@@ -59,11 +59,11 @@ module GoodJob
|
|
59
59
|
|
60
60
|
# @!macro notification_responder
|
61
61
|
def cron_manager_start(event)
|
62
|
-
|
63
|
-
cron_jobs_count =
|
62
|
+
cron_entries = event.payload[:cron_entries]
|
63
|
+
cron_jobs_count = cron_entries.size
|
64
64
|
|
65
65
|
info do
|
66
|
-
"GoodJob started cron with #{cron_jobs_count} #{'
|
66
|
+
"GoodJob started cron with #{cron_jobs_count} #{'job'.pluralize(cron_jobs_count)}."
|
67
67
|
end
|
68
68
|
end
|
69
69
|
|
data/lib/good_job/scheduler.rb
CHANGED
@@ -230,6 +230,7 @@ module GoodJob # :nodoc:
|
|
230
230
|
# @return [void]
|
231
231
|
def create_task(delay = 0)
|
232
232
|
future = Concurrent::ScheduledTask.new(delay, args: [performer], executor: executor, timer_set: timer_set) do |thr_performer|
|
233
|
+
Thread.current.name = Thread.current.name.sub("-worker-", "-thread-") if Thread.current.name
|
233
234
|
Rails.application.reloader.wrap do
|
234
235
|
thr_performer.next
|
235
236
|
end
|
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.6.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: 2021-10-
|
11
|
+
date: 2021-10-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activejob
|
@@ -126,16 +126,16 @@ dependencies:
|
|
126
126
|
name: capybara
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: 3.35.0
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 3.35.0
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: database_cleaner
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
@@ -351,26 +351,31 @@ files:
|
|
351
351
|
- engine/app/assets/vendor/bootstrap/bootstrap.min.css
|
352
352
|
- engine/app/assets/vendor/chartist/chartist.css
|
353
353
|
- engine/app/assets/vendor/chartist/chartist.js
|
354
|
+
- engine/app/assets/vendor/rails_ujs.js
|
354
355
|
- engine/app/controllers/good_job/assets_controller.rb
|
355
356
|
- engine/app/controllers/good_job/base_controller.rb
|
356
|
-
- engine/app/controllers/good_job/
|
357
|
+
- engine/app/controllers/good_job/cron_entries_controller.rb
|
357
358
|
- engine/app/controllers/good_job/executions_controller.rb
|
358
359
|
- engine/app/controllers/good_job/jobs_controller.rb
|
359
360
|
- engine/app/filters/good_job/base_filter.rb
|
360
361
|
- engine/app/filters/good_job/executions_filter.rb
|
361
362
|
- engine/app/filters/good_job/jobs_filter.rb
|
362
363
|
- engine/app/helpers/good_job/application_helper.rb
|
363
|
-
- engine/app/
|
364
|
-
- engine/app/views/good_job/
|
364
|
+
- engine/app/views/good_job/cron_entries/index.html.erb
|
365
|
+
- engine/app/views/good_job/cron_entries/show.html.erb
|
366
|
+
- engine/app/views/good_job/executions/_table.erb
|
365
367
|
- engine/app/views/good_job/executions/index.html.erb
|
368
|
+
- engine/app/views/good_job/jobs/_table.erb
|
366
369
|
- engine/app/views/good_job/jobs/index.html.erb
|
367
370
|
- engine/app/views/good_job/jobs/show.html.erb
|
368
371
|
- engine/app/views/good_job/shared/_chart.erb
|
369
|
-
- engine/app/views/good_job/shared/_executions_table.erb
|
370
372
|
- engine/app/views/good_job/shared/_filter.erb
|
371
|
-
- engine/app/views/good_job/shared/
|
373
|
+
- engine/app/views/good_job/shared/icons/_arrow_clockwise.html.erb
|
372
374
|
- engine/app/views/good_job/shared/icons/_check.html.erb
|
373
375
|
- engine/app/views/good_job/shared/icons/_exclamation.html.erb
|
376
|
+
- engine/app/views/good_job/shared/icons/_play.html.erb
|
377
|
+
- engine/app/views/good_job/shared/icons/_skip_forward.html.erb
|
378
|
+
- engine/app/views/good_job/shared/icons/_stop.html.erb
|
374
379
|
- engine/app/views/good_job/shared/icons/_trash.html.erb
|
375
380
|
- engine/app/views/layouts/good_job/base.html.erb
|
376
381
|
- engine/config/routes.rb
|
@@ -379,14 +384,18 @@ files:
|
|
379
384
|
- lib/active_job/queue_adapters/good_job_adapter.rb
|
380
385
|
- lib/generators/good_job/install_generator.rb
|
381
386
|
- lib/generators/good_job/templates/install/migrations/create_good_jobs.rb.erb
|
382
|
-
- lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb
|
387
|
+
- lib/generators/good_job/templates/update/migrations/01_create_good_jobs.rb.erb
|
388
|
+
- lib/generators/good_job/templates/update/migrations/02_add_cron_at_to_good_jobs.rb.erb
|
389
|
+
- lib/generators/good_job/templates/update/migrations/03_add_cron_key_cron_at_index_to_good_jobs.rb.erb
|
383
390
|
- lib/generators/good_job/update_generator.rb
|
384
391
|
- lib/good_job.rb
|
385
392
|
- lib/good_job/active_job_extensions.rb
|
386
393
|
- lib/good_job/active_job_extensions/concurrency.rb
|
394
|
+
- lib/good_job/active_job_job.rb
|
387
395
|
- lib/good_job/adapter.rb
|
388
396
|
- lib/good_job/cli.rb
|
389
397
|
- lib/good_job/configuration.rb
|
398
|
+
- lib/good_job/cron_entry.rb
|
390
399
|
- lib/good_job/cron_manager.rb
|
391
400
|
- lib/good_job/current_thread.rb
|
392
401
|
- lib/good_job/daemon.rb
|
@@ -1,127 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
module GoodJob
|
3
|
-
# ActiveRecord model that represents an +ActiveJob+ job.
|
4
|
-
# Is the same record data as a {GoodJob::Execution} but only the most recent execution.
|
5
|
-
# Parent class can be configured with +GoodJob.active_record_parent_class+.
|
6
|
-
# @!parse
|
7
|
-
# class ActiveJob < ActiveRecord::Base; end
|
8
|
-
class ActiveJobJob < Object.const_get(GoodJob.active_record_parent_class)
|
9
|
-
include GoodJob::Lockable
|
10
|
-
|
11
|
-
self.table_name = 'good_jobs'
|
12
|
-
self.primary_key = 'active_job_id'
|
13
|
-
self.advisory_lockable_column = 'active_job_id'
|
14
|
-
|
15
|
-
has_many :executions, -> { order(created_at: :asc) }, class_name: 'GoodJob::Execution', foreign_key: 'active_job_id'
|
16
|
-
|
17
|
-
# Only the most-recent unretried execution represents a "Job"
|
18
|
-
default_scope { where(retried_good_job_id: nil) }
|
19
|
-
|
20
|
-
# Get Jobs with given class name
|
21
|
-
# @!method job_class
|
22
|
-
# @!scope class
|
23
|
-
# @param string [String]
|
24
|
-
# Execution class name
|
25
|
-
# @return [ActiveRecord::Relation]
|
26
|
-
scope :job_class, ->(job_class) { where("serialized_params->>'job_class' = ?", job_class) }
|
27
|
-
|
28
|
-
# First execution will run in the future
|
29
|
-
scope :scheduled, -> { where(finished_at: nil).where('COALESCE(scheduled_at, created_at) > ?', DateTime.current).where("(serialized_params->>'executions')::integer < 2") }
|
30
|
-
# Execution errored, will run in the future
|
31
|
-
scope :retried, -> { where(finished_at: nil).where('COALESCE(scheduled_at, created_at) > ?', DateTime.current).where("(serialized_params->>'executions')::integer > 1") }
|
32
|
-
# Immediate/Scheduled time to run has passed, waiting for an available thread run
|
33
|
-
scope :queued, -> { where(finished_at: nil).where('COALESCE(scheduled_at, created_at) <= ?', DateTime.current).joins_advisory_locks.where(pg_locks: { locktype: nil }) }
|
34
|
-
# Advisory locked and executing
|
35
|
-
scope :running, -> { where(finished_at: nil).joins_advisory_locks.where.not(pg_locks: { locktype: nil }) }
|
36
|
-
# Completed executing successfully
|
37
|
-
scope :finished, -> { where.not(finished_at: nil).where(error: nil) }
|
38
|
-
# Errored but will not be retried
|
39
|
-
scope :discarded, -> { where.not(finished_at: nil).where.not(error: nil) }
|
40
|
-
|
41
|
-
# Get Jobs in display order with optional keyset pagination.
|
42
|
-
# @!method display_all(after_scheduled_at: nil, after_id: nil)
|
43
|
-
# @!scope class
|
44
|
-
# @param after_scheduled_at [DateTime, String, nil]
|
45
|
-
# Display records scheduled after this time for keyset pagination
|
46
|
-
# @param after_id [Numeric, String, nil]
|
47
|
-
# Display records after this ID for keyset pagination
|
48
|
-
# @return [ActiveRecord::Relation]
|
49
|
-
scope :display_all, (lambda do |after_scheduled_at: nil, after_id: nil|
|
50
|
-
query = order(Arel.sql('COALESCE(scheduled_at, created_at) DESC, id DESC'))
|
51
|
-
if after_scheduled_at.present? && after_id.present?
|
52
|
-
query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at), id) < (:after_scheduled_at, :after_id)'), after_scheduled_at: after_scheduled_at, after_id: after_id)
|
53
|
-
elsif after_scheduled_at.present?
|
54
|
-
query = query.where(Arel.sql('(COALESCE(scheduled_at, created_at)) < (:after_scheduled_at)'), after_scheduled_at: after_scheduled_at)
|
55
|
-
end
|
56
|
-
query
|
57
|
-
end)
|
58
|
-
|
59
|
-
def id
|
60
|
-
active_job_id
|
61
|
-
end
|
62
|
-
|
63
|
-
def _execution_id
|
64
|
-
attributes['id']
|
65
|
-
end
|
66
|
-
|
67
|
-
def job_class
|
68
|
-
serialized_params['job_class']
|
69
|
-
end
|
70
|
-
|
71
|
-
def status
|
72
|
-
if finished_at.present?
|
73
|
-
if error.present?
|
74
|
-
:discarded
|
75
|
-
else
|
76
|
-
:finished
|
77
|
-
end
|
78
|
-
elsif (scheduled_at || created_at) > DateTime.current
|
79
|
-
if serialized_params.fetch('executions', 0) > 1
|
80
|
-
:retried
|
81
|
-
else
|
82
|
-
:scheduled
|
83
|
-
end
|
84
|
-
elsif running?
|
85
|
-
:running
|
86
|
-
else
|
87
|
-
:queued
|
88
|
-
end
|
89
|
-
end
|
90
|
-
|
91
|
-
def head_execution
|
92
|
-
executions.last
|
93
|
-
end
|
94
|
-
|
95
|
-
def tail_execution
|
96
|
-
executions.first
|
97
|
-
end
|
98
|
-
|
99
|
-
def executions_count
|
100
|
-
aj_count = serialized_params.fetch('executions', 0)
|
101
|
-
# The execution count within serialized_params is not updated
|
102
|
-
# once the underlying execution has been executed.
|
103
|
-
if status.in? [:discarded, :finished, :running]
|
104
|
-
aj_count + 1
|
105
|
-
else
|
106
|
-
aj_count
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
|
-
def preserved_executions_count
|
111
|
-
executions.size
|
112
|
-
end
|
113
|
-
|
114
|
-
def recent_error
|
115
|
-
error.presence || executions[-2]&.error
|
116
|
-
end
|
117
|
-
|
118
|
-
def running?
|
119
|
-
# Avoid N+1 Query: `.joins_advisory_locks.select('good_jobs.*', 'pg_locks.locktype AS locktype')`
|
120
|
-
if has_attribute?(:locktype)
|
121
|
-
self['locktype'].present?
|
122
|
-
else
|
123
|
-
advisory_locked?
|
124
|
-
end
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,50 +0,0 @@
|
|
1
|
-
<% if @cron_schedules.present? %>
|
2
|
-
<div class="card my-3">
|
3
|
-
<div class="table-responsive">
|
4
|
-
<table class="table card-table table-bordered table-hover table-sm mb-0">
|
5
|
-
<thead>
|
6
|
-
<th>Cron Job Name</th>
|
7
|
-
<th>Configuration</th>
|
8
|
-
<th>
|
9
|
-
Set
|
10
|
-
<%= tag.button "Toggle", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
11
|
-
data: { bs_toggle: "collapse", bs_target: ".job-properties" },
|
12
|
-
aria: { expanded: false, controls: @cron_schedules.map { |job_key, _| "##{job_key.to_param}" }.join(" ") }
|
13
|
-
%>
|
14
|
-
</th>
|
15
|
-
<th>Class</th>
|
16
|
-
<th>Description</th>
|
17
|
-
<th>Next scheduled</th>
|
18
|
-
</thead>
|
19
|
-
<tbody>
|
20
|
-
<% @cron_schedules.each do |job_key, job| %>
|
21
|
-
<tr>
|
22
|
-
<td class="font-monospace"><%= job_key %></td>
|
23
|
-
<td class="font-monospace"><%= job[:cron] %></td>
|
24
|
-
<td>
|
25
|
-
<%=
|
26
|
-
case job[:set]
|
27
|
-
when NilClass
|
28
|
-
"None"
|
29
|
-
when Proc
|
30
|
-
"Lambda/Callable"
|
31
|
-
when Hash
|
32
|
-
tag.button("Preview", type: "button", class: "btn btn-sm btn-outline-primary", role: "button",
|
33
|
-
data: { bs_toggle: "collapse", bs_target: "##{job_key.to_param}" },
|
34
|
-
aria: { expanded: false, controls: job_key.to_param }) +
|
35
|
-
tag.pre(JSON.pretty_generate(job[:set]), id: job_key.to_param, class: "collapse job-properties")
|
36
|
-
end
|
37
|
-
%>
|
38
|
-
</td>
|
39
|
-
<td class="font-monospace"><%= job[:class] %></td>
|
40
|
-
<td><%= job[:description] %></td>
|
41
|
-
<td><%= Fugit.parse_cron(job[:cron]).next_time.to_local_time %></td>
|
42
|
-
</tr>
|
43
|
-
<% end %>
|
44
|
-
</tbody>
|
45
|
-
</table>
|
46
|
-
</div>
|
47
|
-
</div>
|
48
|
-
<% else %>
|
49
|
-
<em>No cron jobs present.</em>
|
50
|
-
<% end %>
|