maintenance_tasks 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +18 -0
  3. data/README.md +90 -32
  4. data/app/controllers/maintenance_tasks/application_controller.rb +2 -4
  5. data/app/controllers/maintenance_tasks/tasks_controller.rb +1 -1
  6. data/app/helpers/maintenance_tasks/application_helper.rb +2 -13
  7. data/app/helpers/maintenance_tasks/tasks_helper.rb +17 -16
  8. data/app/jobs/concerns/maintenance_tasks/task_job_concern.rb +150 -0
  9. data/app/jobs/maintenance_tasks/task_job.rb +1 -129
  10. data/app/models/maintenance_tasks/csv_collection.rb +1 -1
  11. data/app/models/maintenance_tasks/progress.rb +13 -11
  12. data/app/models/maintenance_tasks/run.rb +26 -4
  13. data/app/models/maintenance_tasks/runner.rb +2 -2
  14. data/app/models/maintenance_tasks/runs_page.rb +55 -0
  15. data/app/models/maintenance_tasks/task_data.rb +3 -3
  16. data/app/tasks/maintenance_tasks/task.rb +24 -1
  17. data/app/validators/maintenance_tasks/run_status_validator.rb +11 -11
  18. data/app/views/maintenance_tasks/runs/_info.html.erb +1 -1
  19. data/app/views/maintenance_tasks/runs/info/_running.html.erb +0 -2
  20. data/app/views/maintenance_tasks/tasks/show.html.erb +3 -3
  21. data/config/routes.rb +4 -4
  22. data/db/migrate/20210225152418_remove_index_on_task_name.rb +1 -1
  23. data/exe/maintenance_tasks +2 -2
  24. data/lib/generators/maintenance_tasks/install_generator.rb +4 -4
  25. data/lib/generators/maintenance_tasks/task_generator.rb +10 -10
  26. data/lib/generators/maintenance_tasks/templates/task_spec.rb.tt +7 -5
  27. data/lib/maintenance_tasks.rb +51 -29
  28. data/lib/maintenance_tasks/cli.rb +3 -3
  29. data/lib/maintenance_tasks/engine.rb +4 -3
  30. metadata +10 -21
  31. data/Rakefile +0 -29
@@ -9,7 +9,7 @@
9
9
  <%= render "maintenance_tasks/runs/info/#{run.status}", run: run %>
10
10
  </div>
11
11
 
12
- <% if run.csv_file.attached? %>
12
+ <% if run.csv_file.present? %>
13
13
  <div class="block">
14
14
  <%= link_to('Download CSV', csv_file_download_path(run)) %>
15
15
  </div>
@@ -1,7 +1,5 @@
1
1
  <p>
2
2
  <% if run.estimated_completion_time %>
3
3
  <%= estimated_time_to_completion(run).capitalize %> remaining.
4
- <% else %>
5
- Processed <%= pluralize run.tick_count, 'item' %> so far.
6
4
  <% end %>
7
5
  </p>
@@ -41,12 +41,12 @@
41
41
  <pre><code><%= highlight_code(code) %></code></pre>
42
42
  <% end %>
43
43
 
44
- <% if @previous_runs.present? %>
44
+ <% if @runs_page.records.present? %>
45
45
  <hr/>
46
46
 
47
47
  <h4 class="title is-4">Previous Runs</h4>
48
48
 
49
- <%= render @previous_runs %>
49
+ <%= render @runs_page.records %>
50
50
 
51
- <%= pagination(@pagy) %>
51
+ <%= link_to "Next page", task_path(@task, cursor: @runs_page.next_cursor) unless @runs_page.last? %>
52
52
  <% end %>
data/config/routes.rb CHANGED
@@ -2,16 +2,16 @@
2
2
  MaintenanceTasks::Engine.routes.draw do
3
3
  resources :tasks, only: [:index, :show], format: false do
4
4
  member do
5
- put 'run'
5
+ put "run"
6
6
  end
7
7
 
8
8
  resources :runs, only: [], format: false do
9
9
  member do
10
- put 'pause'
11
- put 'cancel'
10
+ put "pause"
11
+ put "cancel"
12
12
  end
13
13
  end
14
14
  end
15
15
 
16
- root to: 'tasks#index'
16
+ root to: "tasks#index"
17
17
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- class RemoveIndexOnTaskName < ActiveRecord::Migration[6.1]
2
+ class RemoveIndexOnTaskName < ActiveRecord::Migration[6.0]
3
3
  def up
4
4
  change_table(:maintenance_tasks_runs) do |t|
5
5
  t.remove_index(:task_name)
@@ -2,11 +2,11 @@
2
2
 
3
3
  # frozen_string_literal: true
4
4
 
5
- require File.expand_path('config/application', Dir.pwd)
5
+ require File.expand_path("config/application", Dir.pwd)
6
6
 
7
7
  Rails.application.require_environment!
8
8
 
9
- require 'maintenance_tasks/cli'
9
+ require "maintenance_tasks/cli"
10
10
 
11
11
  module MaintenanceTasks
12
12
  CLI.start(ARGV)
@@ -5,17 +5,17 @@ module MaintenanceTasks
5
5
  #
6
6
  # @api private
7
7
  class InstallGenerator < Rails::Generators::Base
8
- source_root File.expand_path('templates', __dir__)
8
+ source_root File.expand_path("templates", __dir__)
9
9
 
10
10
  # Mounts the engine in the host application's config/routes.rb
11
11
  def mount_engine
12
- route("mount MaintenanceTasks::Engine => '/maintenance_tasks'")
12
+ route("mount MaintenanceTasks::Engine => \"/maintenance_tasks\"")
13
13
  end
14
14
 
15
15
  # Copies engine migrations to host application and migrates the database
16
16
  def install_migrations
17
- rake('maintenance_tasks:install:migrations')
18
- rake('db:migrate')
17
+ rake("maintenance_tasks:install:migrations")
18
+ rake("db:migrate")
19
19
  end
20
20
  end
21
21
  end
@@ -5,14 +5,14 @@ module MaintenanceTasks
5
5
  #
6
6
  # @api private
7
7
  class TaskGenerator < Rails::Generators::NamedBase
8
- source_root File.expand_path('templates', __dir__)
9
- desc 'This generator creates a task file at app/tasks and a corresponding '\
10
- 'test.'
8
+ source_root File.expand_path("templates", __dir__)
9
+ desc "This generator creates a task file at app/tasks and a corresponding "\
10
+ "test."
11
11
 
12
12
  class_option :csv, type: :boolean, default: false,
13
- desc: 'Generate a CSV Task.'
13
+ desc: "Generate a CSV Task."
14
14
 
15
- check_class_collision suffix: 'Task'
15
+ check_class_collision suffix: "Task"
16
16
 
17
17
  # Creates the Task file.
18
18
  def create_task_file
@@ -22,9 +22,9 @@ module MaintenanceTasks
22
22
  "#{file_name}_task.rb"
23
23
  )
24
24
  if options[:csv]
25
- template('csv_task.rb', template_file)
25
+ template("csv_task.rb", template_file)
26
26
  else
27
- template('task.rb', template_file)
27
+ template("task.rb", template_file)
28
28
  end
29
29
  end
30
30
 
@@ -49,7 +49,7 @@ module MaintenanceTasks
49
49
  class_path,
50
50
  "#{file_name}_task_test.rb"
51
51
  )
52
- template('task_test.rb', template_file)
52
+ template("task_test.rb", template_file)
53
53
  end
54
54
 
55
55
  def create_task_spec_file
@@ -58,11 +58,11 @@ module MaintenanceTasks
58
58
  class_path,
59
59
  "#{file_name}_task_spec.rb"
60
60
  )
61
- template('task_spec.rb', template_file)
61
+ template("task_spec.rb", template_file)
62
62
  end
63
63
 
64
64
  def file_name
65
- super.sub(/_task\z/i, '')
65
+ super.sub(/_task\z/i, "")
66
66
  end
67
67
 
68
68
  def tasks_module
@@ -4,11 +4,13 @@ require 'rails_helper'
4
4
  module <%= tasks_module %>
5
5
  <% module_namespacing do -%>
6
6
  RSpec.describe <%= class_name %>Task do
7
- # describe '#process' do
8
- # it 'performs a task iteration' do
9
- # <%= tasks_module %>::<%= class_name %>Task.process(element)
10
- # end
11
- # end
7
+ describe "#process" do
8
+ subject(:process) { described_class.process(element) }
9
+ let(:element) {
10
+ # Object to be processed in a single iteration of this task
11
+ }
12
+ pending "add some examples to (or delete) #{__FILE__}"
13
+ end
12
14
  end
13
15
  <% end -%>
14
16
  end
@@ -1,53 +1,67 @@
1
1
  # frozen_string_literal: true
2
- require 'action_controller'
3
- require 'action_view'
4
- require 'active_job'
5
- require 'active_record'
2
+ require "action_controller"
3
+ require "action_view"
4
+ require "active_job"
5
+ require "active_record"
6
6
 
7
- require 'job-iteration'
8
- require 'maintenance_tasks/engine'
9
- require 'pagy'
10
- require 'pagy/extras/bulma'
11
-
12
- # Force the TaskJob class to load so we can verify upstream compatibility with
13
- # the JobIteration gem
14
- require_relative '../app/jobs/maintenance_tasks/task_job'
7
+ require "job-iteration"
8
+ require "maintenance_tasks/engine"
15
9
 
16
10
  # The engine's namespace module. It provides isolation between the host
17
11
  # application's code and the engine-specific code. Top-level engine constants
18
12
  # and variables are defined under this module.
19
13
  module MaintenanceTasks
20
- # The module to namespace Tasks in, as a String. Defaults to 'Maintenance'.
21
- # @param [String] the tasks_module value.
22
- mattr_accessor :tasks_module, default: 'Maintenance'
14
+ # @!attribute tasks_module
15
+ # @scope class
16
+ #
17
+ # The module to namespace Tasks in, as a String. Defaults to 'Maintenance'.
18
+ # @return [String] the name of the module.
19
+ mattr_accessor :tasks_module, default: "Maintenance"
23
20
 
24
- # Defines the job to be used to perform Tasks. This job must be either
25
- # `MaintenanceTasks::TaskJob` or a class that inherits from it.
21
+ # @!attribute job
22
+ # @scope class
23
+ #
24
+ # The name of the job to be used to perform Tasks. Defaults to
25
+ # `"MaintenanceTasks::TaskJob"`. This job must be either a class that
26
+ # inherits from {TaskJob} or a class that includes {TaskJobConcern}.
26
27
  #
27
- # @param [String] the name of the job class.
28
- mattr_accessor :job, default: 'MaintenanceTasks::TaskJob'
28
+ # @return [String] the name of the job class.
29
+ mattr_accessor :job, default: "MaintenanceTasks::TaskJob"
29
30
 
30
- # After each iteration, the progress of the task may be updated. This duration
31
- # in seconds limits these updates, skipping if the duration since the last
32
- # update is lower than this value, except if the job is interrupted, in which
33
- # case the progress will always be recorded.
31
+ # @!attribute ticker_delay
32
+ # @scope class
34
33
  #
35
- # @param [ActiveSupport::Duration, Numeric] Duration of the delay to update
36
- # the ticker during Task iterations.
34
+ # The delay between updates to the tick count. After each iteration, the
35
+ # progress of the Task may be updated. This duration in seconds limits
36
+ # these updates, skipping if the duration since the last update is lower
37
+ # than this value, except if the job is interrupted, in which case the
38
+ # progress will always be recorded.
39
+ #
40
+ # @return [ActiveSupport::Duration, Numeric] duration of the delay between
41
+ # updates to the tick count during Task iterations.
37
42
  mattr_accessor :ticker_delay, default: 1.second
38
43
 
39
- # Retrieves the callback to be performed when an error occurs in the task.
44
+ # @!attribute active_storage_service
45
+ # @scope class
46
+ #
47
+ # The Active Storage service to use for uploading CSV file blobs.
48
+ #
49
+ # @return [Symbol] the key for the storage service, as specified in the
50
+ # app's config/storage.yml.
51
+ mattr_accessor :active_storage_service
52
+
53
+ # @private
40
54
  def self.error_handler
41
55
  return @error_handler if defined?(@error_handler)
42
56
  @error_handler = ->(_error, _task_context, _errored_element) {}
43
57
  end
44
58
 
45
- # Defines a callback to be performed when an error occurs in the task.
59
+ # @private
46
60
  def self.error_handler=(error_handler)
47
61
  unless error_handler.arity == 3
48
62
  ActiveSupport::Deprecation.warn(
49
- 'MaintenanceTasks.error_handler should be a lambda that takes three '\
50
- 'arguments: error, task_context, and errored_element.'
63
+ "MaintenanceTasks.error_handler should be a lambda that takes three "\
64
+ "arguments: error, task_context, and errored_element."
51
65
  )
52
66
  @error_handler = ->(error, _task_context, _errored_element) do
53
67
  error_handler.call(error)
@@ -55,4 +69,12 @@ module MaintenanceTasks
55
69
  end
56
70
  @error_handler = error_handler
57
71
  end
72
+
73
+ # @!attribute error_handler
74
+ # @scope class
75
+ #
76
+ # The callback to perform when an error occurs in the Task. See the
77
+ # {file:README#label-Customizing+the+error+handler} for details.
78
+ #
79
+ # @return [Proc] the callback to perform when an error occurs in the Task.
58
80
  end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'thor'
3
+ require "thor"
4
4
 
5
5
  module MaintenanceTasks
6
6
  # Defines the command line interface commands exposed by Maintenance Tasks in
@@ -13,7 +13,7 @@ module MaintenanceTasks
13
13
  end
14
14
  end
15
15
 
16
- desc 'perform [TASK NAME]', 'Runs the given Maintenance Task'
16
+ desc "perform [TASK NAME]", "Runs the given Maintenance Task"
17
17
 
18
18
  long_desc <<-LONGDESC
19
19
  `maintenance_tasks perform` will run the Maintenance Task specified by the
@@ -25,7 +25,7 @@ module MaintenanceTasks
25
25
  LONGDESC
26
26
 
27
27
  # Specify the CSV file to process for CSV Tasks
28
- option :csv, desc: 'Supply a CSV file to be processed by a CSV Task, '\
28
+ option :csv, desc: "Supply a CSV file to be processed by a CSV Task, "\
29
29
  '--csv "path/to/csv/file.csv"'
30
30
 
31
31
  # Command to run a Task.
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
- require 'active_record/railtie'
2
+ require "active_record/railtie"
3
3
 
4
4
  module MaintenanceTasks
5
5
  # The engine's main class, which defines its namespace. The engine is mounted
@@ -7,11 +7,12 @@ module MaintenanceTasks
7
7
  class Engine < ::Rails::Engine
8
8
  isolate_namespace MaintenanceTasks
9
9
 
10
- initializer 'eager_load_for_classic_autoloader' do
10
+ initializer "eager_load_for_classic_autoloader" do
11
11
  eager_load! unless Rails.autoloaders.zeitwerk_enabled?
12
12
  end
13
13
 
14
14
  config.to_prepare do
15
+ _ = TaskJobConcern # load this for JobIteration compatibility check
15
16
  unless Rails.autoloaders.zeitwerk_enabled?
16
17
  tasks_module = MaintenanceTasks.tasks_module.underscore
17
18
  Dir["#{Rails.root}/app/tasks/#{tasks_module}/*.rb"].each do |file|
@@ -25,7 +26,7 @@ module MaintenanceTasks
25
26
  end
26
27
 
27
28
  config.action_dispatch.rescue_responses.merge!(
28
- 'MaintenanceTasks::Task::NotFoundError' => :not_found,
29
+ "MaintenanceTasks::Task::NotFoundError" => :not_found,
29
30
  )
30
31
  end
31
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maintenance_tasks
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.1
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shopify Engineering
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-03-01 00:00:00.000000000 Z
11
+ date: 2021-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '1.1'
69
- - !ruby/object:Gem::Dependency
70
- name: pagy
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '3.9'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '3.9'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: railties
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -101,19 +87,21 @@ executables:
101
87
  extensions: []
102
88
  extra_rdoc_files: []
103
89
  files:
90
+ - LICENSE.md
104
91
  - README.md
105
- - Rakefile
106
92
  - app/controllers/maintenance_tasks/application_controller.rb
107
93
  - app/controllers/maintenance_tasks/runs_controller.rb
108
94
  - app/controllers/maintenance_tasks/tasks_controller.rb
109
95
  - app/helpers/maintenance_tasks/application_helper.rb
110
96
  - app/helpers/maintenance_tasks/tasks_helper.rb
97
+ - app/jobs/concerns/maintenance_tasks/task_job_concern.rb
111
98
  - app/jobs/maintenance_tasks/task_job.rb
112
99
  - app/models/maintenance_tasks/application_record.rb
113
100
  - app/models/maintenance_tasks/csv_collection.rb
114
101
  - app/models/maintenance_tasks/progress.rb
115
102
  - app/models/maintenance_tasks/run.rb
116
103
  - app/models/maintenance_tasks/runner.rb
104
+ - app/models/maintenance_tasks/runs_page.rb
117
105
  - app/models/maintenance_tasks/task_data.rb
118
106
  - app/models/maintenance_tasks/ticker.rb
119
107
  - app/tasks/maintenance_tasks/task.rb
@@ -149,12 +137,13 @@ files:
149
137
  - lib/maintenance_tasks/engine.rb
150
138
  - lib/tasks/maintenance_tasks_tasks.rake
151
139
  homepage: https://github.com/Shopify/maintenance_tasks
152
- licenses: []
140
+ licenses:
141
+ - MIT
153
142
  metadata:
154
- source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.1.1
143
+ source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.3.0
155
144
  allowed_push_host: https://rubygems.org
156
145
  post_install_message: |-
157
- Thank you for installing Maintenance Tasks 1.1.1. To complete, please run:
146
+ Thank you for installing Maintenance Tasks 1.3.0. To complete, please run:
158
147
 
159
148
  rails generate maintenance_tasks:install
160
149
  rdoc_options: []
@@ -171,7 +160,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
171
160
  - !ruby/object:Gem::Version
172
161
  version: '0'
173
162
  requirements: []
174
- rubygems_version: 3.0.3
163
+ rubygems_version: 3.2.17
175
164
  signing_key:
176
165
  specification_version: 4
177
166
  summary: A Rails engine for queuing and managing maintenance tasks
data/Rakefile DELETED
@@ -1,29 +0,0 @@
1
- # frozen_string_literal: true
2
- begin
3
- require 'bundler/setup'
4
- rescue LoadError
5
- puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
- end
7
-
8
- require 'rdoc/task'
9
- RDoc::Task.new(:rdoc) do |rdoc|
10
- rdoc.rdoc_dir = 'rdoc'
11
- rdoc.title = 'MaintenanceTasks'
12
- rdoc.options << '--line-numbers'
13
- rdoc.rdoc_files.include('README.md')
14
- rdoc.rdoc_files.include('lib/**/*.rb')
15
- end
16
-
17
- APP_RAKEFILE = File.expand_path('test/dummy/Rakefile', __dir__)
18
- load('rails/tasks/engine.rake')
19
-
20
- load('rails/tasks/statistics.rake')
21
-
22
- require 'bundler/gem_tasks'
23
-
24
- require 'rubocop/rake_task'
25
- RuboCop::RakeTask.new
26
-
27
- task(test: 'app:test')
28
- task('test:system' => 'app:test:system')
29
- task(default: ['db:setup', 'test', 'test:system', 'rubocop'])