maintenance_tasks 1.7.0 → 1.8.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: 6019b1623a8cc72e877154f52e8437339598f0cab121189d78692e0989e86e61
4
- data.tar.gz: 21563dc6dffadd2e58dd551fe2b7d71297b2da36500722f5d9fed91892b1fd6e
3
+ metadata.gz: e66a4f3f2755a31f9dac0125d41d63d5b1b8ca28a4f11642c8648d94dbc2a044
4
+ data.tar.gz: 1cf5d80050866d6e78d4cf392c9ff888953a3d15da8c19f83f511aaaf94640d2
5
5
  SHA512:
6
- metadata.gz: 93228d08b49fab144297cb44a0aa4b9be53144ff13b88c2eba384ef29fdb47b9a8caefaf94aaf1fbfbf4584f4d544396720c17d86f3bad31508fd66dfd7507b8
7
- data.tar.gz: cab4cebb685fddf978eeebd62a5c16867215f849d424b9ba948beba0e93f42a5fe11ee1eccc15174c1494fa16a2fa100d3a7745c5eacef2527d425895805cf29
6
+ metadata.gz: ffc019d73a707936a483349862481e5a227e270be3578f859b7d385fa7d997d327082a6f090881dcb83306feaf496d36f03d75421533ac507d7472adc5cad991
7
+ data.tar.gz: ae2ca92ea5b989e11d6ffaa50bd6ce1e5fcd41f735f81e6261ae496b81de2b90e7da2f58fdc04803dc77d44f6c0a8814a86794413f2e7ce374aa557c7e0073fb
data/README.md CHANGED
@@ -160,6 +160,36 @@ primary keys of the records of the batch first, and then perform an additional
160
160
  query to load the records when calling `each` (or any `Enumerable` method)
161
161
  inside `#process`.
162
162
 
163
+ ### Tasks that don't need a Collection
164
+
165
+ Sometimes, you might want to run a Task that performs a single operation, such
166
+ as enqueuing another background job or hitting an external API. The gem supports
167
+ collection-less tasks.
168
+
169
+ Generate a collection-less Task by running:
170
+
171
+ ```bash
172
+ $ bin/rails generate maintenance_tasks:task no_collection_task --no-collection
173
+ ```
174
+
175
+ The generated task is a subclass of `MaintenanceTasks::Task` that implements:
176
+
177
+ * `process`: do the work of your maintenance task
178
+
179
+ ```ruby
180
+ # app/tasks/maintenance/no_collection_task.rb
181
+
182
+ module Maintenance
183
+ class NoCollectionTask < MaintenanceTasks::Task
184
+ no_collection
185
+
186
+ def process
187
+ SomeAsyncJob.perform_later
188
+ end
189
+ end
190
+ end
191
+ ```
192
+
163
193
  ### Throttling
164
194
 
165
195
  Maintenance Tasks often modify a lot of data and can be taxing on your database.
@@ -36,6 +36,8 @@ module MaintenanceTasks
36
36
  @enumerator = nil
37
37
 
38
38
  collection_enum = case collection
39
+ when :no_collection
40
+ enumerator_builder.build_once_enumerator(cursor: nil)
39
41
  when ActiveRecord::Relation
40
42
  enumerator_builder.active_record_on_records(collection, cursor: cursor)
41
43
  when ActiveRecord::Batches::BatchEnumerator
@@ -89,7 +91,11 @@ module MaintenanceTasks
89
91
  end
90
92
 
91
93
  def task_iteration(input)
92
- @task.process(input)
94
+ if @task.no_collection?
95
+ @task.process
96
+ else
97
+ @task.process(input)
98
+ end
93
99
  rescue => error
94
100
  @errored_element = input
95
101
  raise error
@@ -29,5 +29,10 @@ module MaintenanceTasks
29
29
  def has_csv_content?
30
30
  true
31
31
  end
32
+
33
+ # Returns that the Task processes a collection.
34
+ def no_collection?
35
+ false
36
+ end
32
37
  end
33
38
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module MaintenanceTasks
4
+ # Strategy for building a Task that has no collection. These Tasks
5
+ # consist of a single iteration.
6
+ #
7
+ # @api private
8
+ class NoCollectionBuilder
9
+ # Specifies that this task does not process a collection.
10
+ def collection(_task)
11
+ :no_collection
12
+ end
13
+
14
+ # The number of rows to be processed. Always returns 1.
15
+ def count(_task)
16
+ 1
17
+ end
18
+
19
+ # Return that the Task does not process CSV content.
20
+ def has_csv_content?
21
+ false
22
+ end
23
+
24
+ # Returns that the Task is collection-less.
25
+ def no_collection?
26
+ true
27
+ end
28
+ end
29
+ end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module MaintenanceTasks
4
4
  # Base strategy for building a collection-based Task to be performed.
5
+ #
6
+ # @api private
5
7
  class NullCollectionBuilder
6
8
  # Placeholder method to raise in case a subclass fails to implement the
7
9
  # expected instance method.
@@ -27,5 +29,10 @@ module MaintenanceTasks
27
29
  def has_csv_content?
28
30
  false
29
31
  end
32
+
33
+ # Returns that the Task processes a collection.
34
+ def no_collection?
35
+ false
36
+ end
30
37
  end
31
38
  end
@@ -131,8 +131,8 @@ module MaintenanceTasks
131
131
  self.started_at ||= Time.now
132
132
  update!(
133
133
  status: :errored,
134
- error_class: error.class.to_s,
135
- error_message: error.message,
134
+ error_class: truncate(:error_class, error.class.name),
135
+ error_message: truncate(:error_message, error.message),
136
136
  backtrace: MaintenanceTasks.backtrace_cleaner.clean(error.backtrace),
137
137
  ended_at: Time.now,
138
138
  )
@@ -422,5 +422,11 @@ module MaintenanceTasks
422
422
  errors.add(:base, error_message)
423
423
  end
424
424
  end
425
+
426
+ def truncate(attribute_name, value)
427
+ limit = MaintenanceTasks::Run.column_for_attribute(attribute_name).limit
428
+ return value unless limit
429
+ value&.first(limit)
430
+ end
425
431
  end
426
432
  end
@@ -18,6 +18,7 @@ module MaintenanceTasks
18
18
  # @api private
19
19
  class_attribute :throttle_conditions, default: []
20
20
 
21
+ # @api private
21
22
  class_attribute :collection_builder_strategy,
22
23
  default: NullCollectionBuilder.new
23
24
 
@@ -63,20 +64,23 @@ module MaintenanceTasks
63
64
  MaintenanceTasks::CsvCollectionBuilder.new
64
65
  end
65
66
 
66
- # Returns whether the Task handles CSV.
67
- #
68
- # @return [Boolean] whether the Task handles CSV.
69
- def has_csv_content?
70
- collection_builder_strategy.has_csv_content?
67
+ # Make this a Task that calls #process once, instead of iterating over
68
+ # a collection.
69
+ def no_collection
70
+ self.collection_builder_strategy =
71
+ MaintenanceTasks::NoCollectionBuilder.new
71
72
  end
72
73
 
74
+ delegate :has_csv_content?, :no_collection?,
75
+ to: :collection_builder_strategy
76
+
73
77
  # Processes one item.
74
78
  #
75
79
  # Especially useful for tests.
76
80
  #
77
- # @param item the item to process.
78
- def process(item)
79
- new.process(item)
81
+ # @param args [Object, nil] the item to process
82
+ def process(*args)
83
+ new.process(*args)
80
84
  end
81
85
 
82
86
  # Returns the collection for this Task.
@@ -197,6 +201,13 @@ module MaintenanceTasks
197
201
  self.class.has_csv_content?
198
202
  end
199
203
 
204
+ # Returns whether the Task is collection-less.
205
+ #
206
+ # @return [Boolean] whether the Task is collection-less.
207
+ def no_collection?
208
+ self.class.no_collection?
209
+ end
210
+
200
211
  # The collection to be processed, delegated to the strategy.
201
212
  #
202
213
  # @return the collection.
@@ -12,10 +12,17 @@ module MaintenanceTasks
12
12
  class_option :csv, type: :boolean, default: false,
13
13
  desc: "Generate a CSV Task."
14
14
 
15
+ class_option :no_collection, type: :boolean, default: false,
16
+ desc: "Generate a collection-less Task."
17
+
15
18
  check_class_collision suffix: "Task"
16
19
 
17
20
  # Creates the Task file.
18
21
  def create_task_file
22
+ if options[:csv] && options[:no_collection]
23
+ raise "Multiple Task type options provided. Please use either "\
24
+ "--csv or --no-collection."
25
+ end
19
26
  template_file = File.join(
20
27
  "app/tasks/#{tasks_module_file_path}",
21
28
  class_path,
@@ -23,6 +30,8 @@ module MaintenanceTasks
23
30
  )
24
31
  if options[:csv]
25
32
  template("csv_task.rb", template_file)
33
+ elsif no_collection?
34
+ template("no_collection_task.rb", template_file)
26
35
  else
27
36
  template("task.rb", template_file)
28
37
  end
@@ -76,5 +85,9 @@ module MaintenanceTasks
76
85
  def test_framework
77
86
  Rails.application.config.generators.options[:rails][:test_framework]
78
87
  end
88
+
89
+ def no_collection?
90
+ options[:no_collection]
91
+ end
79
92
  end
80
93
  end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ module <%= tasks_module %>
4
+ <% module_namespacing do -%>
5
+ class <%= class_name %>Task < MaintenanceTasks::Task
6
+ no_collection
7
+
8
+ def process
9
+ # The work to be done
10
+ end
11
+ end
12
+ <% end -%>
13
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ module <%= tasks_module %>
5
+ <% module_namespacing do -%>
6
+ class <%= class_name %>TaskTest < ActiveSupport::TestCase
7
+ # test "#process performs a task iteration" do
8
+ # <%= tasks_module %>::<%= class_name %>Task.process
9
+ # end
10
+ end
11
+ <% end -%>
12
+ end
@@ -5,7 +5,11 @@ module <%= tasks_module %>
5
5
  <% module_namespacing do -%>
6
6
  class <%= class_name %>TaskTest < ActiveSupport::TestCase
7
7
  # test "#process performs a task iteration" do
8
+ <%- if no_collection? -%>
9
+ # <%= tasks_module %>::<%= class_name %>Task.process
10
+ <%- else -%>
8
11
  # <%= tasks_module %>::<%= class_name %>Task.process(element)
12
+ <%- end -%>
9
13
  # end
10
14
  end
11
15
  <% 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.7.0
4
+ version: 1.8.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: 2022-01-28 00:00:00.000000000 Z
11
+ date: 2022-02-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -98,6 +98,7 @@ files:
98
98
  - app/jobs/maintenance_tasks/task_job.rb
99
99
  - app/models/maintenance_tasks/application_record.rb
100
100
  - app/models/maintenance_tasks/csv_collection_builder.rb
101
+ - app/models/maintenance_tasks/no_collection_builder.rb
101
102
  - app/models/maintenance_tasks/null_collection_builder.rb
102
103
  - app/models/maintenance_tasks/progress.rb
103
104
  - app/models/maintenance_tasks/run.rb
@@ -135,6 +136,8 @@ files:
135
136
  - lib/generators/maintenance_tasks/install_generator.rb
136
137
  - lib/generators/maintenance_tasks/task_generator.rb
137
138
  - lib/generators/maintenance_tasks/templates/csv_task.rb.tt
139
+ - lib/generators/maintenance_tasks/templates/no_collection_task.rb.tt
140
+ - lib/generators/maintenance_tasks/templates/no_collection_task_test.rb.tt
138
141
  - lib/generators/maintenance_tasks/templates/task.rb.tt
139
142
  - lib/generators/maintenance_tasks/templates/task_spec.rb.tt
140
143
  - lib/generators/maintenance_tasks/templates/task_test.rb.tt
@@ -147,7 +150,7 @@ homepage: https://github.com/Shopify/maintenance_tasks
147
150
  licenses:
148
151
  - MIT
149
152
  metadata:
150
- source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.7.0
153
+ source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v1.8.0
151
154
  allowed_push_host: https://rubygems.org
152
155
  post_install_message:
153
156
  rdoc_options: []