good_job 3.14.1 → 3.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +31 -0
  3. data/README.md +1 -1
  4. data/app/controllers/good_job/application_controller.rb +1 -0
  5. data/app/controllers/good_job/cron_entries_controller.rb +4 -4
  6. data/app/controllers/good_job/jobs_controller.rb +4 -4
  7. data/app/filters/good_job/base_filter.rb +1 -1
  8. data/app/helpers/good_job/application_helper.rb +11 -2
  9. data/app/models/good_job/base_execution.rb +44 -0
  10. data/app/models/good_job/execution.rb +3 -12
  11. data/app/models/good_job/job.rb +1 -34
  12. data/app/views/good_job/batches/_jobs.erb +18 -18
  13. data/app/views/good_job/batches/_table.erb +15 -15
  14. data/app/views/good_job/batches/index.html.erb +3 -3
  15. data/app/views/good_job/batches/show.html.erb +4 -4
  16. data/app/views/good_job/cron_entries/index.html.erb +15 -15
  17. data/app/views/good_job/cron_entries/show.html.erb +1 -1
  18. data/app/views/good_job/jobs/_executions.erb +7 -7
  19. data/app/views/good_job/jobs/_table.erb +29 -29
  20. data/app/views/good_job/jobs/index.html.erb +3 -3
  21. data/app/views/good_job/jobs/show.html.erb +14 -14
  22. data/app/views/good_job/processes/index.html.erb +8 -8
  23. data/app/views/good_job/shared/_filter.erb +12 -12
  24. data/app/views/good_job/shared/_navbar.erb +5 -4
  25. data/config/locales/de.yml +219 -0
  26. data/config/locales/en.yml +137 -0
  27. data/config/locales/es.yml +152 -5
  28. data/config/locales/fr.yml +140 -3
  29. data/config/locales/nl.yml +138 -1
  30. data/config/locales/ru.yml +140 -1
  31. data/config/locales/ua.yml +172 -57
  32. data/lib/good_job/version.rb +1 -1
  33. metadata +5 -4
  34. data/app/models/good_job/active_job_job.rb +0 -11
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d3457088e2e7120f5660f8c1395adb27887a0aad22d7b2ec816060f7619ebec
4
- data.tar.gz: a6978f42d4a988b202c891633a8390dd585edbc07cf11f35511aaca005ce67e5
3
+ metadata.gz: 9aa61d924db48712cf404f47b7a6f5b977f1464a29b7e97f068b21422ac8261a
4
+ data.tar.gz: 9701e674925f45189bb8763f31d35a11eeb8d1f02f132cd4cc7e3db1fa6221f7
5
5
  SHA512:
6
- metadata.gz: b5226e5cf17704c8cf1eaa0401fd3bc384d892bb02d6538653bde412b9f627537ba333816e589a05089f590df0d602ab163e57f2cdd4452e5ba539a9c57b7ff3
7
- data.tar.gz: 49529f8c59b0a62cf6fe0f91b848ef01979c6e245b23e71cc2be3939c3497a6edfe92cb66c6171722e6473ba1d2d759a427855665a6419344ad0dbff69c73783
6
+ metadata.gz: 8a07c81b1a23c12b26c21c8d3316bc01ee931e0313d3f51d811564fe80dacf86efd8a1d37f693be0e4d893983ce9aaeeccbb5c0e523410815fea052287b56c3b
7
+ data.tar.gz: 46e3e5a9fe2c7fd8e82a51494e4fac3c480f0e323db1aab57ea2291d7f7db39284f0a9da70692e5b160312894f69ec31453279f541f61cbec99bfb091012af26
data/CHANGELOG.md CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## [v3.15.0](https://github.com/bensheldon/good_job/tree/v3.15.0) (2023-04-02)
4
+
5
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.14.2...v3.15.0)
6
+
7
+ **Implemented enhancements:**
8
+
9
+ - Extract more Dashboard strings into localization strings [\#899](https://github.com/bensheldon/good_job/pull/899) ([patriciomacadden](https://github.com/patriciomacadden))
10
+
11
+ **Closed issues:**
12
+
13
+ - ActionView::Template::Error - undefined method `symbolize\_keys' [\#889](https://github.com/bensheldon/good_job/issues/889)
14
+
15
+ **Merged pull requests:**
16
+
17
+ - Bump benchmark-ips from 2.11.0 to 2.12.0 [\#907](https://github.com/bensheldon/good_job/pull/907) ([dependabot[bot]](https://github.com/apps/dependabot))
18
+ - Bump selenium-webdriver from 4.8.1 to 4.8.6 [\#905](https://github.com/bensheldon/good_job/pull/905) ([dependabot[bot]](https://github.com/apps/dependabot))
19
+ - Bump rails from 7.0.4.2 to 7.0.4.3 [\#904](https://github.com/bensheldon/good_job/pull/904) ([dependabot[bot]](https://github.com/apps/dependabot))
20
+ - feat: locales for de [\#903](https://github.com/bensheldon/good_job/pull/903) ([eric-christian](https://github.com/eric-christian))
21
+ - Update README.md [\#902](https://github.com/bensheldon/good_job/pull/902) ([ain2108](https://github.com/ain2108))
22
+ - Extract shared methods/scopes into a BaseExecution to share between Execution and Job models [\#894](https://github.com/bensheldon/good_job/pull/894) ([bensheldon](https://github.com/bensheldon))
23
+ - Add translate\_hash to handle missing translation keys that return hashes; only show translatable/available locales in dropdown [\#891](https://github.com/bensheldon/good_job/pull/891) ([bensheldon](https://github.com/bensheldon))
24
+
25
+ ## [v3.14.2](https://github.com/bensheldon/good_job/tree/v3.14.2) (2023-03-16)
26
+
27
+ [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.14.1...v3.14.2)
28
+
29
+ **Merged pull requests:**
30
+
31
+ - Bump rack from 2.2.6.3 to 2.2.6.4 [\#893](https://github.com/bensheldon/good_job/pull/893) ([dependabot[bot]](https://github.com/apps/dependabot))
32
+ - Remove non-ActiveRecord::Base backed association [\#892](https://github.com/bensheldon/good_job/pull/892) ([rrunyon](https://github.com/rrunyon))
33
+
3
34
  ## [v3.14.1](https://github.com/bensheldon/good_job/tree/v3.14.1) (2023-03-14)
4
35
 
5
36
  [Full Changelog](https://github.com/bensheldon/good_job/compare/v3.14.0...v3.14.1)
data/README.md CHANGED
@@ -452,7 +452,7 @@ GoodJob's concurrency control strategy for `perform_limit` is "optimistic retry
452
452
  - "Optimistic" meaning that the implementation's performance trade-off assumes that collisions are atypical (e.g. two users enqueue the same job at the same time) rather than regular (e.g. the system enqueues thousands of colliding jobs at the same time). Depending on your concurrency requirements, you may also want to manage concurrency through the number of GoodJob threads and processes that are performing a given queue.
453
453
  - "Retry with an incremental backoff" means that when `perform_limit` is exceeded, the job will raise a `GoodJob::ActiveJobExtensions::Concurrency::ConcurrencyExceededError` which is caught by a `retry_on` handler which re-schedules the job to execute in the near future with an incremental backoff.
454
454
  - First-in-first-out job execution order is not preserved when a job is retried with incremental back-off.
455
- - For pessimistic usecases that collisions are expected, use number of threads/processes (e.g., `good_job --queue "serial:1;-serial:5"`) to control concurrency. It is also a good idea to use `perform_limit` as backstop.
455
+ - For pessimistic usecases that collisions are expected, use number of threads/processes (e.g., `good_job --queues "serial:1;-serial:5"`) to control concurrency. It is also a good idea to use `perform_limit` as backstop.
456
456
 
457
457
  ### Cron-style repeating/recurring jobs
458
458
 
@@ -47,5 +47,6 @@ module GoodJob
47
47
  def good_job_available_locales
48
48
  @_good_job_available_locales ||= GoodJob::Engine.root.join("config/locales").glob("*.yml").map { |path| File.basename(path, ".yml").to_sym }.uniq
49
49
  end
50
+ helper_method :good_job_available_locales
50
51
  end
51
52
  end
@@ -15,25 +15,25 @@ module GoodJob
15
15
  def enqueue
16
16
  @cron_entry = CronEntry.find(params[:cron_key])
17
17
  @cron_entry.enqueue(Time.current)
18
- redirect_back(fallback_location: cron_entries_path, notice: "Cron entry has been enqueued.")
18
+ redirect_back(fallback_location: cron_entries_path, notice: t(".notice"))
19
19
  end
20
20
 
21
21
  def enable
22
22
  @cron_entry = CronEntry.find(params[:cron_key])
23
23
  @cron_entry.enable
24
- redirect_back(fallback_location: cron_entries_path, notice: "Cron entry has been enabled.")
24
+ redirect_back(fallback_location: cron_entries_path, notice: t(".notice"))
25
25
  end
26
26
 
27
27
  def disable
28
28
  @cron_entry = CronEntry.find(params[:cron_key])
29
29
  @cron_entry.disable
30
- redirect_back(fallback_location: cron_entries_path, notice: "Cron entry has been disabled.")
30
+ redirect_back(fallback_location: cron_entries_path, notice: t(".notice"))
31
31
  end
32
32
 
33
33
  private
34
34
 
35
35
  def check_settings_migration!
36
- redirect_back(fallback_location: cron_entries_path, alert: "Requires pending GoodJob database migration.") unless GoodJob::Setting.migrated?
36
+ redirect_back(fallback_location: cron_entries_path, alert: t("good_job.cron_entries.pending_migrations")) unless GoodJob::Setting.migrated?
37
37
  end
38
38
  end
39
39
  end
@@ -62,25 +62,25 @@ module GoodJob
62
62
  def discard
63
63
  @job = Job.find(params[:id])
64
64
  @job.discard_job(DISCARD_MESSAGE)
65
- redirect_back(fallback_location: jobs_path, notice: "Job has been discarded")
65
+ redirect_back(fallback_location: jobs_path, notice: t(".notice"))
66
66
  end
67
67
 
68
68
  def reschedule
69
69
  @job = Job.find(params[:id])
70
70
  @job.reschedule_job
71
- redirect_back(fallback_location: jobs_path, notice: "Job has been rescheduled")
71
+ redirect_back(fallback_location: jobs_path, notice: t(".notice"))
72
72
  end
73
73
 
74
74
  def retry
75
75
  @job = Job.find(params[:id])
76
76
  @job.retry_job
77
- redirect_back(fallback_location: jobs_path, notice: "Job has been retried")
77
+ redirect_back(fallback_location: jobs_path, notice: t(".notice"))
78
78
  end
79
79
 
80
80
  def destroy
81
81
  @job = Job.find(params[:id])
82
82
  @job.destroy_job
83
- redirect_to jobs_path, notice: "Job has been destroyed"
83
+ redirect_to jobs_path, notice: t(".notice")
84
84
  end
85
85
 
86
86
  private
@@ -32,7 +32,7 @@ module GoodJob
32
32
 
33
33
  def job_classes
34
34
  filtered_query(params.slice(:queue_name)).unscope(:select)
35
- .group("serialized_params->>'job_class'").count
35
+ .group(GoodJob::BaseExecution.params_job_class).count
36
36
  .sort_by { |name, _count| name.to_s }
37
37
  .to_h
38
38
  end
@@ -19,7 +19,7 @@ module GoodJob
19
19
 
20
20
  def relative_time(timestamp, **options)
21
21
  options = options.reverse_merge({ scope: "good_job.datetime.distance_in_words" })
22
- text = timestamp.future? ? "in #{time_ago_in_words(timestamp, **options)}" : "#{time_ago_in_words(timestamp, **options)} ago"
22
+ text = t("good_job.helpers.relative_time.#{timestamp.future? ? 'future' : 'past'}", time: time_ago_in_words(timestamp, **options))
23
23
  tag.time(text, datetime: timestamp, title: timestamp)
24
24
  end
25
25
 
@@ -42,7 +42,7 @@ module GoodJob
42
42
  }.freeze
43
43
 
44
44
  def status_badge(status)
45
- content_tag :span, status_icon(status, class: "text-white") + t(status, scope: 'good_job.status'),
45
+ content_tag :span, status_icon(status, class: "text-white") + t(status, scope: 'good_job.status', count: 1),
46
46
  class: "badge rounded-pill bg-#{STATUS_COLOR.fetch(status)} d-inline-flex gap-2 ps-1 pe-3 align-items-center"
47
47
  end
48
48
 
@@ -57,5 +57,14 @@ module GoodJob
57
57
  partial = lookup_context.find_template("good_job/shared/icons/#{name}", [], true)
58
58
  partial.render(self, {})
59
59
  end
60
+
61
+ def translate_hash(key, **options)
62
+ translation_exists?(key, **options) ? translate(key, **options) : {}
63
+ end
64
+
65
+ def translation_exists?(key, **options)
66
+ true if good_job_available_locales.include?(I18n.locale)
67
+ I18n.exists?(scope_key_by_partial(key), **options)
68
+ end
60
69
  end
61
70
  end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module GoodJob
4
+ # ActiveRecord model to share behavior between {Job} and {Execution} models
5
+ # which both read out of the same table.
6
+ class BaseExecution < BaseRecord
7
+ self.table_name = 'good_jobs'
8
+
9
+ # With a given class name
10
+ # @!method job_class
11
+ # @!scope class
12
+ # @param string [String] Execution class name
13
+ # @return [ActiveRecord::Relation]
14
+ scope :job_class, ->(name) { where(params_job_class.eq(name)) }
15
+
16
+ class << self
17
+ def json_string(json, attr)
18
+ Arel::Nodes::Grouping.new(Arel::Nodes::InfixOperation.new('->>', json, Arel::Nodes.build_quoted(attr)))
19
+ end
20
+
21
+ def params_job_class
22
+ json_string(arel_table['serialized_params'], 'job_class')
23
+ end
24
+
25
+ def params_execution_count
26
+ Arel::Nodes::InfixOperation.new(
27
+ '::',
28
+ json_string(arel_table['serialized_params'], 'executions'),
29
+ Arel.sql('integer')
30
+ )
31
+ end
32
+
33
+ def coalesce_scheduled_at_created_at
34
+ arel_table.coalesce(arel_table['scheduled_at'], arel_table['created_at'])
35
+ end
36
+ end
37
+
38
+ # The ActiveJob job class, as a string
39
+ # @return [String]
40
+ def job_class
41
+ serialized_params['job_class']
42
+ end
43
+ end
44
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
  module GoodJob
3
3
  # ActiveRecord model that represents an +ActiveJob+ job.
4
- class Execution < BaseRecord
4
+ class Execution < BaseExecution
5
5
  include Lockable
6
6
  include Filterable
7
7
  include Reportable
@@ -70,7 +70,6 @@ module GoodJob
70
70
  end
71
71
 
72
72
  belongs_to :batch, class_name: 'GoodJob::BatchRecord', optional: true, inverse_of: :executions
73
- belongs_to :batch_callback, class_name: 'GoodJob::Batch', optional: true
74
73
 
75
74
  belongs_to :job, class_name: 'GoodJob::Job', foreign_key: 'active_job_id', primary_key: 'active_job_id', optional: true, inverse_of: :executions
76
75
  after_destroy -> { self.class.active_job_id(active_job_id).delete_all }, if: -> { @_destroy_job }
@@ -83,14 +82,6 @@ module GoodJob
83
82
  # @return [ActiveRecord::Relation]
84
83
  scope :active_job_id, ->(active_job_id) { where(active_job_id: active_job_id) }
85
84
 
86
- # Get executions with given class name
87
- # @!method job_class
88
- # @!scope class
89
- # @param string [String]
90
- # Execution class name
91
- # @return [ActiveRecord::Relation]
92
- scope :job_class, ->(job_class) { where("serialized_params->>'job_class' = ?", job_class) }
93
-
94
85
  # Get executions that have not yet finished (succeeded or discarded).
95
86
  # @!method unfinished
96
87
  # @!scope class
@@ -120,7 +111,7 @@ module GoodJob
120
111
  # @!method creation_ordered
121
112
  # @!scope class
122
113
  # @return [ActiveRecord:Relation]
123
- scope :creation_ordered, -> { order('created_at ASC') }
114
+ scope :creation_ordered, -> { order(created_at: :asc) }
124
115
 
125
116
  # Order executions for de-queueing
126
117
  # @!method dequeueing_ordered
@@ -156,7 +147,7 @@ module GoodJob
156
147
  # @!method schedule_ordered
157
148
  # @!scope class
158
149
  # @return [ActiveRecord::Relation]
159
- scope :schedule_ordered, -> { order(Arel.sql('COALESCE(scheduled_at, created_at) ASC')) }
150
+ scope :schedule_ordered, -> { order(coalesce_scheduled_at_created_at.asc) }
160
151
 
161
152
  # Get Jobs were completed before the given timestamp. If no timestamp is
162
153
  # provided, get all jobs that have been completed. By default, GoodJob
@@ -5,7 +5,7 @@ module GoodJob
5
5
  # The +good_jobs+ table is a table of individual {GoodJob::Execution}s that share the same +active_job_id+.
6
6
  # A single row from the +good_jobs+ table of executions is fetched to represent a Job.
7
7
  #
8
- class Job < BaseRecord
8
+ class Job < BaseExecution
9
9
  include Filterable
10
10
  include Lockable
11
11
  include Reportable
@@ -23,26 +23,6 @@ module GoodJob
23
23
  def table_name=(_value)
24
24
  raise NotImplementedError, 'Assign GoodJob::Execution.table_name directly'
25
25
  end
26
-
27
- def json_string(json, attr)
28
- Arel::Nodes::Grouping.new(Arel::Nodes::InfixOperation.new('->>', json, Arel::Nodes.build_quoted(attr)))
29
- end
30
-
31
- def params_job_class
32
- json_string(arel_table['serialized_params'], 'job_class')
33
- end
34
-
35
- def params_execution_count
36
- Arel::Nodes::InfixOperation.new(
37
- '::',
38
- json_string(arel_table['serialized_params'], 'executions'),
39
- Arel.sql('integer')
40
- )
41
- end
42
-
43
- def coalesce_scheduled_at_created_at
44
- arel_table.coalesce(arel_table['scheduled_at'], arel_table['created_at'])
45
- end
46
26
  end
47
27
 
48
28
  self.primary_key = 'active_job_id'
@@ -54,13 +34,6 @@ module GoodJob
54
34
  # Only the most-recent unretried execution represents a "Job"
55
35
  default_scope { where(retried_good_job_id: nil) }
56
36
 
57
- # Get Jobs with given class name
58
- # @!method job_class
59
- # @!scope class
60
- # @param string [String] Execution class name
61
- # @return [ActiveRecord::Relation]
62
- scope :job_class, ->(name) { where(params_job_class.eq(name)) }
63
-
64
37
  # Get Jobs finished before the given timestamp.
65
38
  # @!method finished_before(timestamp)
66
39
  # @!scope class
@@ -89,12 +62,6 @@ module GoodJob
89
62
  active_job_id
90
63
  end
91
64
 
92
- # The ActiveJob job class, as a string
93
- # @return [String]
94
- def job_class
95
- serialized_params['job_class']
96
- end
97
-
98
65
  # Override #reload to add a custom scope to ensure the reloaded record is the head execution
99
66
  # @return [Job]
100
67
  def reload(options = nil)
@@ -2,10 +2,10 @@
2
2
  <div class="list-group list-group-flush text-nowrap table-jobs" role="table">
3
3
  <header class="list-group-item bg-light">
4
4
  <div class="row small text-muted text-uppercase align-items-center">
5
- <div class="col-4">Jobs</div>
6
- <div class="d-none d-lg-block col-lg-1 text-lg-center">Queue</div>
7
- <div class="d-none d-lg-block col-lg-1 text-lg-end">Priority</div>
8
- <div class="d-none d-lg-block col-lg-1 text-lg-end">Attempts</div>
5
+ <div class="col-4"><%=t "good_job.models.batch.jobs" %></div>
6
+ <div class="d-none d-lg-block col-lg-1 text-lg-center"><%=t "good_job.models.job.queue" %></div>
7
+ <div class="d-none d-lg-block col-lg-1 text-lg-end"><%=t "good_job.models.job.priority" %></div>
8
+ <div class="d-none d-lg-block col-lg-1 text-lg-end"><%=t "good_job.models.job.attempts" %></div>
9
9
  <div class="col text-end">
10
10
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
11
11
  data: { bs_toggle: "collapse", bs_target: ".job-params" },
@@ -26,15 +26,15 @@
26
26
  <%= tag.h5 tag.code(link_to(job.job_class, job_path(job), class: "text-reset text-decoration-none")), class: "text-reset mb-0" %>
27
27
  </div>
28
28
  <div class="col-4 col-lg-1 text-lg-center">
29
- <div class="d-lg-none small text-muted mt-1">Queue</div>
29
+ <div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.queue" %></div>
30
30
  <span class="badge bg-primary bg-opacity-25 text-dark font-monospace"><%= job.queue_name %></span>
31
31
  </div>
32
32
  <div class="col-4 col-lg-1 text-lg-end">
33
- <div class="d-lg-none small text-muted mt-1">Priority</div>
33
+ <div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.priority" %>Priority</div>
34
34
  <span class="font-monospace fw-bold"><%= job.priority %></span>
35
35
  </div>
36
36
  <div class="col-4 col-lg-1 text-lg-end">
37
- <div class="d-lg-none small text-muted mt-1">Attempts</div>
37
+ <div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.job.attempts" %></div>
38
38
  <% if job.executions_count > 0 && job.status != :finished %>
39
39
  <%= tag.span job.executions_count, class: "badge rounded-pill bg-danger", data: {
40
40
  bs_toggle: "popover",
@@ -53,33 +53,33 @@
53
53
  <div class="dropdown float-end">
54
54
  <button class="d-flex align-items-center btn btn-sm" type="button" id="<%= dom_id(job, :actions) %>" data-bs-toggle="dropdown" aria-expanded="false">
55
55
  <%= render "good_job/shared/icons/dots" %>
56
- <span class="visually-hidden">Actions</span>
56
+ <span class="visually-hidden"><%=t ".actions.title" %></span>
57
57
  </button>
58
58
  <ul class="dropdown-menu shadow" aria-labelledby="<%= dom_id(job, :actions) %>">
59
59
  <li>
60
60
  <% job_reschedulable = job.status.in? [:scheduled, :retried, :queued] %>
61
- <%= link_to reschedule_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_reschedulable}", title: "Reschedule job", data: { confirm: "Confirm reschedule", disable: true } do %>
61
+ <%= link_to reschedule_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_reschedulable}", title: t(".actions.reschedule"), data: { confirm: t(".actions.confirm_reschedule"), disable: true } do %>
62
62
  <%= render "good_job/shared/icons/skip_forward" %>
63
- Reschedule
63
+ <%=t "good_job.actions.reschedule" %>
64
64
  <% end %>
65
65
  </li>
66
66
  <li>
67
67
  <% job_discardable = job.status.in? [:scheduled, :retried, :queued] %>
68
- <%= link_to discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_discardable}", title: "Discard job", data: { confirm: "Confirm discard", disable: true } do %>
68
+ <%= link_to discard_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job_discardable}", title: t(".actions.discard"), data: { confirm: t(".actions.confirm_discard"), disable: true } do %>
69
69
  <%= render "good_job/shared/icons/stop" %>
70
- Discard
70
+ <%=t "good_job.actions.discard" %>
71
71
  <% end %>
72
72
  </li>
73
73
  <li>
74
- <%= link_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: "Retry job", data: { confirm: "Confirm retry", disable: true } do %>
74
+ <%= link_to retry_job_path(job.id), method: :put, class: "dropdown-item #{'disabled' unless job.status == :discarded}", title: t(".actions.retry"), data: { confirm: t(".actions.confirm_retry"), disable: true } do %>
75
75
  <%= render "good_job/shared/icons/arrow_clockwise" %>
76
- Retry
76
+ <%=t "good_job.actions.retry" %>
77
77
  <% end %>
78
78
  </li>
79
79
  <li>
80
- <%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.status.in? [:discarded, :finished]}", title: "Destroy job", data: { confirm: "Confirm destroy", disable: true } do %>
80
+ <%= link_to job_path(job.id), method: :delete, class: "dropdown-item #{'disabled' unless job.status.in? [:discarded, :finished]}", title: t(".actions.destroy"), data: { confirm: t(".actions.confirm_destroy"), disable: true } do %>
81
81
  <%= render_icon "trash" %>
82
- Destroy
82
+ <%=t "good_job.actions.destroy" %>
83
83
  <% end %>
84
84
  </li>
85
85
 
@@ -89,7 +89,7 @@
89
89
  data: { bs_toggle: "collapse" },
90
90
  aria: { expanded: false, controls: dom_id(job, "params") } do %>
91
91
  <%= render_icon "info" %>
92
- Inspect
92
+ <%=t "good_job.actions.inspect" %>
93
93
  <% end %>
94
94
  </li>
95
95
  </ul>
@@ -103,7 +103,7 @@
103
103
  <% end %>
104
104
  <% else %>
105
105
  <div class="list-group-item py-4 text-center text-muted">
106
- No jobs found.
106
+ <%=t ".no_jobs_found" %>
107
107
  </div>
108
108
  <% end %>
109
109
  </div>
@@ -2,18 +2,18 @@
2
2
  <div class="list-group list-group-flush text-nowrap table-batches" role="table">
3
3
  <header class="list-group-item bg-light">
4
4
  <div class="row small text-muted text-uppercase align-items-center">
5
- <div class="col-4">Name</div>
6
- <div class="col-lg-1 d-none d-lg-block">Created</div>
7
- <div class="col-lg-1 d-none d-lg-block">Enqueued</div>
8
- <div class="col-lg-1 d-none d-lg-block">Discarded</div>
9
- <div class="col-lg-1 d-none d-lg-block">Finished</div>
10
- <div class="col-lg-1 d-none d-lg-block">Jobs</div>
5
+ <div class="col-4"><%=t "good_job.models.batch.name" %></div>
6
+ <div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.created" %></div>
7
+ <div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.enqueued" %></div>
8
+ <div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.discarded" %></div>
9
+ <div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.finished" %></div>
10
+ <div class="col-lg-1 d-none d-lg-block"><%=t "good_job.models.batch.jobs" %></div>
11
11
  <div class="col text-end">
12
12
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
13
13
  data: { bs_toggle: "collapse", bs_target: ".batch-properties" },
14
14
  aria: { expanded: false, controls: batches.map { |batch| "##{dom_id(batch, "params")}" }.join(" ") } do %>
15
15
  <%= render_icon "info" %>
16
- <span class="visually-hidden">Inspect</span>
16
+ <span class="visually-hidden"><%=t "good_job.actions.inspect" %></span>
17
17
  <% end %>
18
18
  </div>
19
19
  </div>
@@ -33,38 +33,38 @@
33
33
  <% end %>
34
34
  </div>
35
35
  <div class="col-6 col-lg-1 text-wrap">
36
- <div class="d-lg-none small text-muted mt-1">Created at</div>
36
+ <div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.created_at" %></div>
37
37
  <%= relative_time(batch.created_at) %>
38
38
  </div>
39
39
  <div class="col-6 col-lg-1 text-wrap">
40
40
  <% if batch.enqueued_at %>
41
- <div class="d-lg-none small text-muted mt-1">Enqueued at</div>
41
+ <div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.enqueued_at" %></div>
42
42
  <%= relative_time(batch.enqueued_at) %>
43
43
  <% end %>
44
44
  </div>
45
45
  <div class="col-6 col-lg-1 text-wrap">
46
46
  <% if batch.discarded_at %>
47
- <div class="d-lg-none small text-muted mt-1">Discarded at</div>
47
+ <div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.discarded_at" %></div>
48
48
  <%= relative_time(batch.discarded_at) %>
49
49
  <% end %>
50
50
  </div>
51
51
  <div class="col-6 col-lg-1 text-wrap">
52
52
  <% if batch.finished_at %>
53
- <div class="d-lg-none small text-muted mt-1">Finished at</div>
53
+ <div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.finished_at" %></div>
54
54
  <%= relative_time(batch.finished_at) %>
55
55
  <% end %>
56
56
  </div>
57
57
  <div class="col-6 col-lg-1">
58
- <div class="d-lg-none small text-muted mt-1">Jobs</div>
58
+ <div class="d-lg-none small text-muted mt-1"><%=t "good_job.models.batch.jobs" %></div>
59
59
  <%= batch.jobs.count %>
60
60
  </div>
61
61
  <div class="col text-end">
62
62
  <%= tag.button type: "button", class: "btn btn-sm text-muted", role: "button",
63
- title: "Inspect",
63
+ title: t("good_job.actions.inspect"),
64
64
  data: { bs_toggle: "collapse", bs_target: "##{dom_id(batch, 'properties')}" },
65
65
  aria: { expanded: false, controls: dom_id(batch, "state") } do %>
66
66
  <%= render_icon "info" %>
67
- <span class="visually-hidden">Inspect</span>
67
+ <span class="visually-hidden"><%=t "good_job.actions.inspect" %></span>
68
68
  <% end %>
69
69
  </div>
70
70
  </div>
@@ -75,7 +75,7 @@
75
75
  <% end %>
76
76
  <% else %>
77
77
  <div class="list-group-item py-4 text-center text-muted">
78
- No batches found.
78
+ <%=t ".no_batches_found" %>
79
79
  </div>
80
80
  <% end %>
81
81
  </div>
@@ -1,5 +1,5 @@
1
1
  <div class="border-bottom">
2
- <h2 class="pt-3 pb-2">Batches</h2>
2
+ <h2 class="pt-3 pb-2"><%= t ".title" %></h2>
3
3
  </div>
4
4
 
5
5
  <% if GoodJob::BatchRecord.migrated? %>
@@ -9,12 +9,12 @@
9
9
  <ul class="pagination">
10
10
  <li class="page-item">
11
11
  <%= link_to(@filter.to_params(after_created_at: @filter.last.created_at, after_id: @filter.last.id), class: "page-link") do %>
12
- Older batches <span aria-hidden="true">&raquo;</span>
12
+ <%= t ".older_batches" %> <span aria-hidden="true">&raquo;</span>
13
13
  <% end %>
14
14
  </li>
15
15
  </ul>
16
16
  </nav>
17
17
  <% end %>
18
18
  <% else %>
19
- <h3 class="text-center my-5">GoodJob has pending database migrations.</h3>
19
+ <h3 class="text-center my-5"><%= t ".pending_migrations" %></h3>
20
20
  <% end %>
@@ -4,7 +4,7 @@
4
4
  <div class="col">
5
5
  <nav aria-label="breadcrumb">
6
6
  <ol class="breadcrumb small mb-0">
7
- <li class="breadcrumb-item"><%= link_to "Batches", batches_path %></li>
7
+ <li class="breadcrumb-item"><%= link_to t("good_job.batches.index.title"), batches_path %></li>
8
8
  <li class="breadcrumb-item active" aria-current="page"><%= tag.code @batch.id, class: "text-muted" %></li>
9
9
  </ol>
10
10
  <h2 class="h5 mt-2"><%= @batch.description %></h2>
@@ -15,18 +15,18 @@
15
15
  </div>
16
16
 
17
17
  <div class="my-4">
18
- <h5>Attributes</h5>
18
+ <h5><%= t ".attributes" %></h5>
19
19
  <div class="bg-dark text-light p-3 rounded">
20
20
  <%= tag.pre JSON.pretty_generate @batch.display_attributes, class: 'text-wrap text-break' %>
21
21
  </div>
22
22
  </div>
23
23
 
24
24
  <div class="my-4">
25
- <h5>Callback Jobs</h5>
25
+ <h5><%= t ".callback_jobs" %></h5>
26
26
  <%= render 'jobs', jobs: @batch.callback_jobs.reverse %>
27
27
  </div>
28
28
 
29
29
  <div class="my-4">
30
- <h5>Batched Jobs</h5>
30
+ <h5><%= t ".batched_jobs" %></h5>
31
31
  <%= render 'jobs', jobs: @batch.jobs.reverse %>
32
32
  </div>