Class: MaintenanceTasks::Task

Inherits:
Object
  • Object
show all
Extended by:
ActiveSupport::DescendantsTracker
Includes:
ActiveModel::AttributeAssignment, ActiveModel::Attributes, ActiveModel::Validations, ActiveSupport::Callbacks
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

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.after_cancel(*filter_list, &block) ⇒ Object

Initialize a callback to run after the task is cancelled.

Parameters:



191
192
193
# File 'app/models/maintenance_tasks/task.rb', line 191

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:



167
168
169
# File 'app/models/maintenance_tasks/task.rb', line 167

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:



199
200
201
# File 'app/models/maintenance_tasks/task.rb', line 199

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:



183
184
185
# File 'app/models/maintenance_tasks/task.rb', line 183

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:



175
176
177
# File 'app/models/maintenance_tasks/task.rb', line 175

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:



159
160
161
# File 'app/models/maintenance_tasks/task.rb', line 159

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.



63
64
65
66
67
68
69
# File 'app/models/maintenance_tasks/task.rb', line 63

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.



118
119
120
# File 'app/models/maintenance_tasks/task.rb', line 118

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.



151
152
153
# File 'app/models/maintenance_tasks/task.rb', line 151

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.



127
128
129
# File 'app/models/maintenance_tasks/task.rb', line 127

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:



81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/models/maintenance_tasks/task.rb', line 81

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.



54
55
56
57
# File 'app/models/maintenance_tasks/task.rb', line 54

def load_all
  load_constants
  descendants
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.



40
41
42
43
44
45
46
47
48
# File 'app/models/maintenance_tasks/task.rb', line 40

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.



98
99
100
# File 'app/models/maintenance_tasks/task.rb', line 98

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



109
110
111
# File 'app/models/maintenance_tasks/task.rb', line 109

def process(*args)
  new.process(*args)
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.



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

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.



248
249
250
# File 'app/models/maintenance_tasks/task.rb', line 248

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)


278
279
280
# File 'app/models/maintenance_tasks/task.rb', line 278

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)


216
217
218
219
220
# File 'app/models/maintenance_tasks/task.rb', line 216

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)


225
226
227
228
229
# File 'app/models/maintenance_tasks/task.rb', line 225

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.



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

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)


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

def enumerator_builder(cursor:)
  nil
end

#has_csv_content?Boolean

Returns whether the Task handles CSV.

Returns:

  • (Boolean)

    whether the Task handles CSV.



234
235
236
# File 'app/models/maintenance_tasks/task.rb', line 234

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.



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

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.



271
272
273
# File 'app/models/maintenance_tasks/task.rb', line 271

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