Class: MaintenanceTasks::Task

Inherits:
Object
  • Object
show all
Extended by:
ActiveSupport::DescendantsTracker
Includes:
ActiveModel::AttributeAssignment, ActiveModel::Attributes, ActiveModel::Validations, ActiveSupport::Callbacks, ActiveSupport::Rescuable
Defined in:
app/models/maintenance_tasks/task.rb

Overview

Base class that is inherited by the host application’s task classes.

Defined Under Namespace

Classes: NotFoundError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#metadataObject

Returns the value of attribute metadata.



44
45
46
# File 'app/models/maintenance_tasks/task.rb', line 44

def 
  @metadata
end

Class Method Details

.after_cancel(*filter_list, &block) ⇒ Object

Initialize a callback to run after the task is cancelled.

Parameters:



221
222
223
# File 'app/models/maintenance_tasks/task.rb', line 221

def after_cancel(*filter_list, &block)
  set_callback(:cancel, :after, *filter_list, &block)
end

.after_complete(*filter_list, &block) ⇒ Object

Initialize a callback to run after the task completes.

Parameters:



197
198
199
# File 'app/models/maintenance_tasks/task.rb', line 197

def after_complete(*filter_list, &block)
  set_callback(:complete, :after, *filter_list, &block)
end

.after_error(*filter_list, &block) ⇒ Object

Initialize a callback to run after the task produces an error.

Parameters:



229
230
231
# File 'app/models/maintenance_tasks/task.rb', line 229

def after_error(*filter_list, &block)
  set_callback(:error, :after, *filter_list, &block)
end

.after_interrupt(*filter_list, &block) ⇒ Object

Initialize a callback to run after the task is interrupted.

Parameters:



213
214
215
# File 'app/models/maintenance_tasks/task.rb', line 213

def after_interrupt(*filter_list, &block)
  set_callback(:interrupt, :after, *filter_list, &block)
end

.after_pause(*filter_list, &block) ⇒ Object

Initialize a callback to run after the task pauses.

Parameters:



205
206
207
# File 'app/models/maintenance_tasks/task.rb', line 205

def after_pause(*filter_list, &block)
  set_callback(:pause, :after, *filter_list, &block)
end

.after_start(*filter_list, &block) ⇒ Object

Initialize a callback to run after the task starts.

Parameters:



189
190
191
# File 'app/models/maintenance_tasks/task.rb', line 189

def after_start(*filter_list, &block)
  set_callback(:start, :after, *filter_list, &block)
end

.available_tasksArray<Class>

Loads and returns a list of concrete classes that inherit from the Task superclass.

Returns:

  • (Array<Class>)

    the list of classes.



77
78
79
80
81
82
83
# File 'app/models/maintenance_tasks/task.rb', line 77

def available_tasks
  warn(<<~MSG.squish, category: :deprecated)
    MaintenanceTasks::Task.available_tasks is deprecated and will be
    removed from maintenance-tasks 3.0.0. Use .load_all instead.
  MSG
  load_all
end

.collectionObject

Returns the collection for this Task.

Especially useful for tests.

Returns:

  • the collection.



132
133
134
# File 'app/models/maintenance_tasks/task.rb', line 132

def collection
  new.collection
end

.collection_batch_size(size) ⇒ Object

Limit the number of records that will be fetched in a single query when iterating over an Active Record collection task.

Parameters:

  • size (Integer)

    the number of records to fetch in a single query.



165
166
167
# File 'app/models/maintenance_tasks/task.rb', line 165

def collection_batch_size(size)
  self.active_record_enumerator_batch_size = size
end

.countObject

Returns the count of items for this Task.

Especially useful for tests.

Returns:

  • the count of items.



141
142
143
# File 'app/models/maintenance_tasks/task.rb', line 141

def count
  new.count
end

.csv_collection(in_batches: nil, **csv_options) ⇒ Object

Make this Task a task that handles CSV.

An input to upload a CSV will be added in the form to start a Run. The collection and count method are implemented.

Parameters:

  • in_batches (Integer) (defaults to: nil)

    optionally, supply a batch size if the CSV should be processed in batches.

  • csv_options (Hash)

    optionally, supply options for the CSV parser. If not given, defaults to: { headers: true }

See Also:



95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'app/models/maintenance_tasks/task.rb', line 95

def csv_collection(in_batches: nil, **csv_options)
  unless defined?(ActiveStorage)
    raise NotImplementedError, "Active Storage needs to be installed\n" \
      "To resolve this issue run: bin/rails active_storage:install"
  end

  csv_options[:headers] = true unless csv_options.key?(:headers)
  csv_options[:encoding] ||= Encoding.default_external
  self.collection_builder_strategy = if in_batches
    BatchCsvCollectionBuilder.new(in_batches, **csv_options)
  else
    CsvCollectionBuilder.new(**csv_options)
  end
end

.load_allArray<Class>

Loads and returns a list of concrete classes that inherit from the Task superclass.

Returns:

  • (Array<Class>)

    the list of classes.



68
69
70
71
# File 'app/models/maintenance_tasks/task.rb', line 68

def load_all
  load_constants
  descendants
end

.mask_attribute(*attributes) ⇒ Object

Adds attribute names to sensitive arguments list.

Parameters:

  • attributes (Array<Symbol>)

    the attribute names to filter.



172
173
174
# File 'app/models/maintenance_tasks/task.rb', line 172

def mask_attribute(*attributes)
  self.masked_arguments += attributes
end

.named(name) ⇒ Task

Finds a Task with the given name.

Parameters:

  • name (String)

    the name of the Task to be found.

Returns:

  • (Task)

    the Task with the given name.

Raises:

  • (NotFoundError)

    if a Task with the given name does not exist.



54
55
56
57
58
59
60
61
62
# File 'app/models/maintenance_tasks/task.rb', line 54

def named(name)
  task = name.safe_constantize
  raise NotFoundError.new("Task #{name} not found.", name) unless task
  unless task.is_a?(Class) && task < Task
    raise NotFoundError.new("#{name} is not a Task.", name)
  end

  task
end

.no_collectionObject

Make this a Task that calls #process once, instead of iterating over a collection.



112
113
114
# File 'app/models/maintenance_tasks/task.rb', line 112

def no_collection
  self.collection_builder_strategy = MaintenanceTasks::NoCollectionBuilder.new
end

.process(*args) ⇒ Object

Processes one item.

Especially useful for tests.

Parameters:

  • args (Object, nil)

    the item to process



123
124
125
# File 'app/models/maintenance_tasks/task.rb', line 123

def process(*args)
  new.process(*args)
end

.reload_status_every(frequency) ⇒ Object

Configure how frequently the run status should be reloaded during iteration. Use this to reduce database queries when processing large collections.

Parameters:

  • frequency (ActiveSupport::Duration, Numeric)

    reload status every N seconds (default: 1 second). Setting this to 10.seconds means status will be reloaded every 10 seconds.



181
182
183
# File 'app/models/maintenance_tasks/task.rb', line 181

def reload_status_every(frequency)
  self.status_reload_frequency = frequency
end

.report_on(*exceptions, **report_options) ⇒ Object

Rescue listed exceptions during an iteration and report them to the error reporter, then continue iteration.

Parameters:

  • exceptions

    list of exceptions to rescue and report

  • report_options (Hash)

    optionally, supply additional options for ‘Rails.error.report`. By default: { handled: true, source: "maintenance-tasks" }.



239
240
241
242
243
# File 'app/models/maintenance_tasks/task.rb', line 239

def report_on(*exceptions, **report_options)
  rescue_from(*exceptions) do |exception|
    Rails.error.report(exception, source: "maintenance-tasks", **report_options)
  end
end

.throttle_on(backoff: 30.seconds, &condition) ⇒ Object

Add a condition under which this Task will be throttled.

Parameters:

  • backoff (ActiveSupport::Duration, #call) (defaults to: 30.seconds)

    a custom backoff can be specified. This is the time to wait before retrying the Task, defaulting to 30 seconds. If provided as a Duration, the backoff is wrapped in a proc. Alternatively,an object responding to call can be used. It must return an ActiveSupport::Duration.

Yield Returns:

  • (Boolean)

    where the throttle condition is being met, indicating that the Task should throttle.



154
155
156
157
158
159
# File 'app/models/maintenance_tasks/task.rb', line 154

def throttle_on(backoff: 30.seconds, &condition)
  backoff_as_proc = backoff
  backoff_as_proc = -> { backoff } unless backoff.respond_to?(:call)

  self.throttle_conditions += [{ throttle_on: condition, backoff: backoff_as_proc }]
end

Instance Method Details

#collectionObject

The collection to be processed, delegated to the strategy.

Returns:

  • the collection.



290
291
292
# File 'app/models/maintenance_tasks/task.rb', line 290

def collection
  self.class.collection_builder_strategy.collection(self)
end

#countInteger?

Total count of iterations to be performed, delegated to the strategy.

Returns:

  • (Integer, nil)


320
321
322
# File 'app/models/maintenance_tasks/task.rb', line 320

def count
  self.class.collection_builder_strategy.count(self)
end

#csv_contentString

The contents of a CSV file to be processed by a Task.

Returns:

  • (String)

    the content of the CSV file to process.

Raises:

  • (NoMethodError)


258
259
260
261
262
# File 'app/models/maintenance_tasks/task.rb', line 258

def csv_content
  raise NoMethodError unless has_csv_content?

  @csv_content
end

#csv_content=(csv_content) ⇒ Object

Set the contents of a CSV file to be processed by a Task.

Parameters:

  • csv_content (String)

    the content of the CSV file to process.

Raises:

  • (NoMethodError)


267
268
269
270
271
# File 'app/models/maintenance_tasks/task.rb', line 267

def csv_content=(csv_content)
  raise NoMethodError unless has_csv_content?

  @csv_content = csv_content
end

#cursor_columnsObject

The columns used to build the ‘ORDER BY` clause of the query for iteration.

If cursor_columns returns nil, the query is ordered by the primary key. If cursor columns values change during an iteration, records may be skipped or yielded multiple times. More details in the documentation of JobIteration::EnumeratorBuilder.build_active_record_enumerator_on_records: www.rubydoc.info/gems/job-iteration/JobIteration/EnumeratorBuilder#build_active_record_enumerator_on_records-instance_method

Returns:

  • the cursor_columns.



302
303
304
# File 'app/models/maintenance_tasks/task.rb', line 302

def cursor_columns
  nil
end

#enumerator_builder(cursor:) ⇒ Enumerator

Default enumerator builder. You may override this method to return any Enumerator yielding pairs of ‘[item, item_cursor]`.

Parameters:

  • cursor (String, nil)

    cursor position to resume from, or nil on initial call.

Returns:

  • (Enumerator)


331
332
333
# File 'app/models/maintenance_tasks/task.rb', line 331

def enumerator_builder(cursor:)
  nil
end

#has_csv_content?Boolean

Returns whether the Task handles CSV.

Returns:

  • (Boolean)

    whether the Task handles CSV.



276
277
278
# File 'app/models/maintenance_tasks/task.rb', line 276

def has_csv_content?
  self.class.has_csv_content?
end

#no_collection?Boolean

Returns whether the Task is collection-less.

Returns:

  • (Boolean)

    whether the Task is collection-less.



283
284
285
# File 'app/models/maintenance_tasks/task.rb', line 283

def no_collection?
  self.class.no_collection?
end

#process(_item) ⇒ Object

Placeholder method to raise in case a subclass fails to implement the expected instance method.

Parameters:

  • _item (Object)

    the current item from the enumerator being iterated.

Raises:

  • (NotImplementedError)

    with a message advising subclasses to implement an override for this method.



313
314
315
# File 'app/models/maintenance_tasks/task.rb', line 313

def process(_item)
  raise NoMethodError, "#{self.class.name} must implement `process`."
end