maintenance_tasks 2.8.0 → 2.10.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: b984293788834b153823e857dad5020b8cca1902037c97fd158401c1da3cfee3
4
- data.tar.gz: 0c079b820fbfd3d7e26e7c19585ac8b41199de92f7a71e671a63c7db46b60d06
3
+ metadata.gz: 347d742ae5f71881fe2d54f7fff83b4129144eb72af60c2364a8069fc41412fe
4
+ data.tar.gz: 20ff959d1c2db4a003883955ef938414a462f16fa843d8f4ec526ff3333a0609
5
5
  SHA512:
6
- metadata.gz: f43e5c6535246bf7259bb21cfd1eb3f48f356a9f1f2e83fea19096cdff8610a601d4ea96b623d63bbecbaa593d1022a34fd16210896f83852f64c3e3bf003b41
7
- data.tar.gz: a506d5e89ec233e60a59c428801d8e2e79a57f6604f8ab9cf97882b71bf69f4304f7d1fa53ac33ecd4ab5bb76f9de3774cbf9d1bfdd7cdff52af1849379b7e6d
6
+ metadata.gz: 7f9e56bd030e2a6918d7e8e136108d6097cbcdaa8b73ebb9683072878f8789cb0b7da1b67c122d7dddf97553ca3dcb37b4e1af7670cbdf931825ec965cbbf9af
7
+ data.tar.gz: 384ec37a77b25d689ff46f965fd6cddd4ddc7669f520a99db808edc76c290c21ed8925b5d791839dd0d61738da6b944267fa65535b91ebd4ee6d8913293bb824
data/README.md CHANGED
@@ -90,6 +90,13 @@ take a look at the [Active Job documentation][active-job-docs].
90
90
  [async-adapter]: https://api.rubyonrails.org/classes/ActiveJob/QueueAdapters/AsyncAdapter.html
91
91
  [active-job-docs]: https://guides.rubyonrails.org/active_job_basics.html#setting-the-backend
92
92
 
93
+ ### Action Controller & Action View Dependency
94
+
95
+ The Maintenance Tasks framework relies on Action Controller and Action View to
96
+ render the UI. If you're using Rails in API-only mode, see [Using Maintenance
97
+ Tasks in API-only
98
+ applications](#using-maintenance-tasks-in-api-only-applications).
99
+
93
100
  ### Autoloading
94
101
 
95
102
  The Maintenance Tasks framework does not support autoloading in `:classic` mode.
@@ -485,6 +492,23 @@ to run. Since arguments are specified in the user interface via text area
485
492
  inputs, it’s important to check that they conform to the format your Task
486
493
  expects, and to sanitize any inputs if necessary.
487
494
 
495
+ #### Validating Task Parameters
496
+
497
+ Task attributes can be validated using Active Model Validations. Attributes are
498
+ validated before a Task is enqueued.
499
+
500
+ If an attribute uses an inclusion validator with a supported `in:` option, the
501
+ set of values will be used to populate a dropdown in the user interface. The
502
+ following types are supported:
503
+
504
+ * Arrays
505
+ * Procs and lambdas that optionally accept the Task instance, and return an Array.
506
+ * Callable objects that receive one argument, the Task instance, and return an Array.
507
+ * Methods that return an Array, called on the Task instance.
508
+
509
+ For enumerables that don't match the supported types, a text field will be
510
+ rendered instead.
511
+
488
512
  ### Custom cursor columns to improve performance
489
513
 
490
514
  The [job-iteration gem][job-iteration], on which this gem depends, adds an
@@ -541,8 +565,8 @@ your application.
541
565
 
542
566
  Usage example:
543
567
 
544
- ```ruby
545
- ActiveSupport::Notifications.subscribe("succeeded.maintenance_tasks") do |*, payload|
568
+ ```ruby
569
+ ActiveSupport::Notifications.subscribe("succeeded.maintenance_tasks") do |*, payload|
546
570
  task_name = payload[:task_name]
547
571
  arguments = payload[:arguments]
548
572
  metadata = payload[:metadata]
@@ -871,6 +895,42 @@ a Task can be in:
871
895
  * **succeeded**: A Task that finished successfully.
872
896
  * **errored**: A Task that encountered an unhandled exception while performing.
873
897
 
898
+ ### Using Maintenance Tasks in API-only applications
899
+
900
+ The Maintenance Tasks engine uses Rails sessions for flash messages and storing
901
+ the CSRF token. For the engine to work in an API-only Rails application, you
902
+ need to add a [session middleware][] and the `ActionDispatch::Flash`
903
+ middleware. The engine also defines a strict [Content Security Policy][], make
904
+ sure to include `ActionDispatch::ContentSecurityPolicy::Middleware` in your
905
+ app's middleware stack to ensure the CSP is delivered to the user's browser.
906
+
907
+ [session middleware]: https://guides.rubyonrails.org/api_app.html#using-session-middlewares
908
+ [Content Security Policy]: https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP
909
+
910
+ Configuring Rails applications is beyond the scope of this documentation, but
911
+ one way to do this is to add these lines to your application configuration:
912
+
913
+ ```ruby
914
+ # config/application.rb
915
+ module YourApplication
916
+ class Application < Rails::Application
917
+ # ...
918
+ config.api_only = true
919
+
920
+ config.middleware.insert_before ::Rack::Head, ::ActionDispatch::Flash
921
+ config.middleware.insert_before ::Rack::Head, ::ActionDispatch::ContentSecurityPolicy::Middleware
922
+ config.session_store :cookie_store, key: "_#{railtie_name.chomp("_application")}_session", secure: true
923
+ config.middleware.insert_before ::ActionDispatch::Flash, config.session_store, config.session_options
924
+ config.middleware.insert_before config.session_store, ActionDispatch::Cookies
925
+ end
926
+ end
927
+ ```
928
+
929
+ You can read more in the [Using Rails for API-only Applications][rails api] Rails
930
+ guide.
931
+
932
+ [rails api]: https://guides.rubyonrails.org/api_app.html
933
+
874
934
  ### How Maintenance Tasks runs a Task
875
935
 
876
936
  Maintenance tasks can be running for a long time, and the purpose of the gem is
@@ -1128,7 +1188,7 @@ The value for `MaintenanceTasks.stuck_task_duration` must be an
1128
1188
  `ActiveSupport::Duration`. If no value is specified, it will default to 5
1129
1189
  minutes.
1130
1190
 
1131
- ### Metadata
1191
+ #### Metadata
1132
1192
 
1133
1193
  `MaintenanceTasks.metadata` can be configured to specify a proc from which to
1134
1194
  get extra information about the run. Since this proc will be ran in the context
@@ -6,6 +6,7 @@ module MaintenanceTasks
6
6
  #
7
7
  # @api private
8
8
  class RunsController < ApplicationController
9
+ helper TasksHelper
9
10
  before_action :set_run, except: :create
10
11
 
11
12
  # Creates a Run for a given Task and redirects to the Task page.
@@ -19,7 +20,9 @@ module MaintenanceTasks
19
20
  )
20
21
  redirect_to(task_path(task))
21
22
  rescue ActiveRecord::RecordInvalid => error
22
- redirect_to(task_path(error.record.task_name), alert: error.message)
23
+ flash.now.alert = error.message
24
+ @task = TaskDataShow.prepare(error.record.task_name, arguments: error.record.arguments)
25
+ render(template: "maintenance_tasks/tasks/show")
23
26
  rescue ActiveRecord::ValueTooLong => error
24
27
  task_name = params.fetch(:task_id)
25
28
  redirect_to(task_path(task_name), alert: error.message)
@@ -18,14 +18,11 @@ module MaintenanceTasks
18
18
  # Renders the page responsible for providing Task actions to users.
19
19
  # Shows running and completed instances of the Task.
20
20
  def show
21
- task_name = params.fetch(:id)
22
- @task = TaskDataShow.new(task_name)
23
- @task.active_runs.load
24
- set_refresh if @task.active_runs.any?
25
- @runs_page = RunsPage.new(@task.completed_runs, params[:cursor])
26
- if @task.active_runs.none? && @runs_page.records.none?
27
- Task.named(task_name)
28
- end
21
+ @task = TaskDataShow.prepare(
22
+ params.fetch(:id),
23
+ runs_cursor: params[:cursor],
24
+ arguments: params.except(:id, :controller, :action).permit!,
25
+ )
29
26
  end
30
27
 
31
28
  private
@@ -102,8 +102,13 @@ module MaintenanceTasks
102
102
  end
103
103
 
104
104
  # Resolves values covered by the inclusion validator for a task attribute.
105
- # Only Arrays are supported, option types such as:
106
- # Procs, lambdas, symbols, and Range are not supported and return nil.
105
+ # Supported option types:
106
+ # - Arrays
107
+ # - Procs and lambdas that optionally accept the Task instance, and return an Array.
108
+ # - Callable objects that receive one argument, the Task instance, and return an Array.
109
+ # - Methods that return an Array, called on the Task instance.
110
+ #
111
+ # Other types are not supported and will return nil.
107
112
  #
108
113
  # Returned values are used to populate a dropdown list of options.
109
114
  #
@@ -111,20 +116,39 @@ module MaintenanceTasks
111
116
  # @param parameter_name [String] The parameter name.
112
117
  #
113
118
  # @return [Array] value of the resolved inclusion option.
114
- def resolve_inclusion_value(task_class, parameter_name)
119
+ def resolve_inclusion_value(task, parameter_name)
120
+ task_class = task.class
115
121
  inclusion_validator = task_class.validators_on(parameter_name).find do |validator|
116
122
  validator.kind == :inclusion
117
123
  end
118
124
  return unless inclusion_validator
119
125
 
120
126
  in_option = inclusion_validator.options[:in] || inclusion_validator.options[:within]
121
- in_option if in_option.is_a?(Array)
127
+ resolved_in_option = case in_option
128
+ when Proc
129
+ if in_option.arity == 0
130
+ in_option.call
131
+ else
132
+ in_option.call(task)
133
+ end
134
+ when Symbol
135
+ method = task.method(in_option)
136
+ method.call if method.arity.zero?
137
+ else
138
+ if in_option.respond_to?(:call)
139
+ in_option.call(task)
140
+ else
141
+ in_option
142
+ end
143
+ end
144
+
145
+ resolved_in_option if resolved_in_option.is_a?(Array)
122
146
  end
123
147
 
124
148
  # Return the appropriate field tag for the parameter, based on its type.
125
149
  # If the parameter has a `validates_inclusion_of` validator, return a dropdown list of options instead.
126
150
  def parameter_field(form_builder, parameter_name)
127
- inclusion_values = resolve_inclusion_value(form_builder.object.class, parameter_name)
151
+ inclusion_values = resolve_inclusion_value(form_builder.object, parameter_name)
128
152
  return form_builder.select(parameter_name, inclusion_values, prompt: "Select a value") if inclusion_values
129
153
 
130
154
  case form_builder.object.class.attribute_types[parameter_name]
@@ -386,40 +386,20 @@ module MaintenanceTasks
386
386
  nil
387
387
  end
388
388
 
389
- # Support iterating over ActiveModel::Errors in Rails 6.0 and Rails 6.1+.
390
- # To be removed when Rails 6.0 is no longer supported.
391
- if Rails::VERSION::STRING.match?(/^6.0/)
392
- # Performs validation on the arguments to use for the Task. If the Task is
393
- # invalid, the errors are added to the Run.
394
- def validate_task_arguments
395
- arguments_match_task_attributes if arguments.present?
396
- if task.invalid?
397
- error_messages = task.errors
398
- .map { |attribute, message| "#{attribute.inspect} #{message}" }
399
- errors.add(
400
- :arguments,
401
- "are invalid: #{error_messages.join("; ")}",
402
- )
403
- end
404
- rescue Task::NotFoundError
405
- nil
406
- end
407
- else
408
- # Performs validation on the arguments to use for the Task. If the Task is
409
- # invalid, the errors are added to the Run.
410
- def validate_task_arguments
411
- arguments_match_task_attributes if arguments.present?
412
- if task.invalid?
413
- error_messages = task.errors
414
- .map { |error| "#{error.attribute.inspect} #{error.message}" }
415
- errors.add(
416
- :arguments,
417
- "are invalid: #{error_messages.join("; ")}",
418
- )
419
- end
420
- rescue Task::NotFoundError
421
- nil
389
+ # Performs validation on the arguments to use for the Task. If the Task is
390
+ # invalid, the errors are added to the Run.
391
+ def validate_task_arguments
392
+ arguments_match_task_attributes if arguments.present?
393
+ if task.invalid?
394
+ error_messages = task.errors
395
+ .map { |error| "#{error.attribute.inspect} #{error.message}" }
396
+ errors.add(
397
+ :arguments,
398
+ "are invalid: #{error_messages.join("; ")}",
399
+ )
422
400
  end
401
+ rescue Task::NotFoundError
402
+ nil
423
403
  end
424
404
 
425
405
  # Fetches the attached ActiveStorage CSV file for the run. Checks first
@@ -21,6 +21,9 @@ module MaintenanceTasks
21
21
  @cursor = cursor
22
22
  end
23
23
 
24
+ # @return [String, nil] the cursor for the page of Runs.
25
+ attr_reader :cursor
26
+
24
27
  # Returns the records for a Page, taking into account the cursor if one is
25
28
  # present. Limits the number of records to 20.
26
29
  #
@@ -11,17 +11,39 @@ module MaintenanceTasks
11
11
  #
12
12
  # @api private
13
13
  class TaskDataShow
14
- # Initializes a Task Data with a name and optionally a related run.
14
+ # Initializes a Task Data with a name.
15
15
  #
16
16
  # @param name [String] the name of the Task subclass.
17
- def initialize(name)
17
+ # @param runs_cursor [String, nil] the cursor for the runs page.
18
+ # @param arguments [Hash, nil] the Task arguments.
19
+ def initialize(name, runs_cursor: nil, arguments: nil)
18
20
  @name = name
21
+ @arguments = arguments
22
+ @runs_page = RunsPage.new(completed_runs, runs_cursor)
23
+ end
24
+
25
+ class << self
26
+ # Prepares a Task Data from a task name.
27
+ #
28
+ # @param name [String] the name of the Task subclass.
29
+ # @param runs_cursor [String, nil] the cursor for the runs page.
30
+ # @param arguments [Hash, nil] the Task arguments.
31
+ # @raise [Task::NotFoundError] if the Task doesn't have runs (for the given cursor) and doesn't exist.
32
+ def prepare(name, runs_cursor: nil, arguments: nil)
33
+ new(name, runs_cursor:, arguments:)
34
+ .load_active_runs
35
+ .ensure_task_exists
36
+ end
19
37
  end
20
38
 
21
39
  # @return [String] the name of the Task.
22
40
  attr_reader :name
23
41
  alias_method :to_s, :name
24
42
 
43
+ # @return [RunsPage] the current page of completed runs, based on the cursor
44
+ # passed in initialize.
45
+ attr_reader :runs_page
46
+
25
47
  # The Task's source code.
26
48
  #
27
49
  # @return [String] the contents of the file which defines the Task.
@@ -38,6 +60,11 @@ module MaintenanceTasks
38
60
  File.read(file)
39
61
  end
40
62
 
63
+ # @return [Boolean] whether the task data needs to be refreshed.
64
+ def refresh?
65
+ active_runs.any?
66
+ end
67
+
41
68
  # Returns the set of currently active Run records associated with the Task.
42
69
  #
43
70
  # @return [ActiveRecord::Relation<MaintenanceTasks::Run>] the relation of
@@ -79,12 +106,34 @@ module MaintenanceTasks
79
106
  end
80
107
  end
81
108
 
82
- # @return [MaintenanceTasks::Task, nil] an instance of the Task class.
109
+ # @return [MaintenanceTasks::Task] an instance of the Task class.
83
110
  # @return [nil] if the Task file was deleted.
84
111
  def new
85
112
  return if deleted?
86
113
 
87
- MaintenanceTasks::Task.named(name).new
114
+ task = MaintenanceTasks::Task.named(name).new
115
+ begin
116
+ task.assign_attributes(@arguments) if @arguments
117
+ rescue ActiveModel::UnknownAttributeError
118
+ # nothing to do
119
+ end
120
+ task
121
+ end
122
+
123
+ # Preloads the records from the active_runs ActiveRecord::Relation
124
+ # @return [self]
125
+ def load_active_runs
126
+ active_runs.load
127
+ self
128
+ end
129
+
130
+ # @raise [Task::NotFoundError] if the Task doesn't have Runs (for the given cursor) and doesn't exist.
131
+ # @return [self]
132
+ def ensure_task_exists
133
+ if active_runs.none? && runs_page.records.none?
134
+ Task.named(name)
135
+ end
136
+ self
88
137
  end
89
138
 
90
139
  private
@@ -23,23 +23,23 @@
23
23
 
24
24
  <div class="buttons">
25
25
  <% if run.paused? %>
26
- <%= button_to 'Resume', resume_task_run_path(@task, run), method: :put, class: 'button is-primary', disabled: @task.deleted? %>
27
- <%= button_to 'Cancel', cancel_task_run_path(@task, run), method: :put, class: 'button is-danger' %>
26
+ <%= button_to 'Resume', resume_task_run_path(@task, run), class: 'button is-primary', disabled: @task.deleted? %>
27
+ <%= button_to 'Cancel', cancel_task_run_path(@task, run), class: 'button is-danger' %>
28
28
  <% elsif run.errored? %>
29
- <%= button_to 'Resume', resume_task_run_path(@task, run), method: :put, class: 'button is-primary', disabled: @task.deleted? %>
29
+ <%= button_to 'Resume', resume_task_run_path(@task, run), class: 'button is-primary', disabled: @task.deleted? %>
30
30
  <% elsif run.cancelling? %>
31
31
  <% if run.stuck? %>
32
- <%= button_to 'Cancel', cancel_task_run_path(@task, run), method: :put, class: 'button is-danger', disabled: @task.deleted? %>
32
+ <%= button_to 'Cancel', cancel_task_run_path(@task, run), class: 'button is-danger', disabled: @task.deleted? %>
33
33
  <% end %>
34
34
  <% elsif run.pausing? %>
35
- <%= button_to 'Pausing', pause_task_run_path(@task, run), method: :put, class: 'button is-warning', disabled: true %>
36
- <%= button_to 'Cancel', cancel_task_run_path(@task, run), method: :put, class: 'button is-danger' %>
35
+ <%= button_to 'Pausing', pause_task_run_path(@task, run), class: 'button is-warning', disabled: true %>
36
+ <%= button_to 'Cancel', cancel_task_run_path(@task, run), class: 'button is-danger' %>
37
37
  <% if run.stuck? %>
38
- <%= button_to 'Force pause', pause_task_run_path(@task, run), method: :put, class: 'button is-danger', disabled: @task.deleted? %>
38
+ <%= button_to 'Force pause', pause_task_run_path(@task, run), class: 'button is-danger', disabled: @task.deleted? %>
39
39
  <% end %>
40
40
  <% elsif run.active? %>
41
- <%= button_to 'Pause', pause_task_run_path(@task, run), method: :put, class: 'button is-warning', disabled: @task.deleted? %>
42
- <%= button_to 'Cancel', cancel_task_run_path(@task, run), method: :put, class: 'button is-danger' %>
41
+ <%= button_to 'Pause', pause_task_run_path(@task, run), class: 'button is-warning', disabled: @task.deleted? %>
42
+ <%= button_to 'Cancel', cancel_task_run_path(@task, run), class: 'button is-danger' %>
43
43
  <% end%>
44
44
  </div>
45
45
  </div>
@@ -38,7 +38,7 @@
38
38
  <pre><code><%= highlight_code(code) %></code></pre>
39
39
  <% end %>
40
40
 
41
- <%= tag.div(data: { refresh: (defined?(@refresh) && @refresh) || "" }) do %>
41
+ <%= tag.div(data: { refresh: @task.refresh? || "" }) do %>
42
42
  <% if @task.active_runs.any? %>
43
43
  <hr/>
44
44
 
@@ -47,13 +47,13 @@
47
47
  <%= render partial: "maintenance_tasks/runs/run", collection: @task.active_runs %>
48
48
  <% end %>
49
49
 
50
- <% if @runs_page.records.present? %>
50
+ <% if @task.runs_page.records.present? %>
51
51
  <hr/>
52
52
 
53
53
  <h4 class="title is-4">Previous Runs</h4>
54
54
 
55
- <%= render partial: "maintenance_tasks/runs/run", collection: @runs_page.records %>
55
+ <%= render partial: "maintenance_tasks/runs/run", collection: @task.runs_page.records %>
56
56
 
57
- <%= link_to "Next page", task_path(@task, cursor: @runs_page.next_cursor) unless @runs_page.last? %>
57
+ <%= link_to "Next page", task_path(@task, cursor: @task.runs_page.next_cursor) unless @task.runs_page.last? %>
58
58
  <% end %>
59
59
  <% end %>
data/config/routes.rb CHANGED
@@ -4,11 +4,12 @@ MaintenanceTasks::Engine.routes.draw do
4
4
  resources :tasks, only: [:index, :show], format: false do
5
5
  resources :runs, only: [:create], format: false do
6
6
  member do
7
- put "pause"
8
- put "cancel"
9
- put "resume"
7
+ post "pause"
8
+ post "cancel"
9
+ post "resume"
10
10
  end
11
11
  end
12
+ get :runs, to: redirect("tasks/%{task_id}")
12
13
  end
13
14
 
14
15
  root to: "tasks#index"
@@ -1,23 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- # TODO: Remove this patch once all supported Rails versions include the changes
4
- # upstream - https://github.com/rails/rails/pull/42312/commits/a031a43d969c87542c4ee8d0d338d55fcbb53376
5
- module ActiveRecordBatchEnumerator
6
- # The primary key value from which the BatchEnumerator starts,
7
- # inclusive of the value.
8
- attr_reader :start
3
+ if Rails.gem_version < Gem::Version.new("7.0")
4
+ # Add attribute readers.
5
+ module ActiveRecordBatchEnumerator
6
+ # The primary key value from which the BatchEnumerator starts,
7
+ # inclusive of the value.
8
+ attr_reader :start
9
9
 
10
- # The primary key value at which the BatchEnumerator ends,
11
- # inclusive of the value.
12
- attr_reader :finish
10
+ # The primary key value at which the BatchEnumerator ends,
11
+ # inclusive of the value.
12
+ attr_reader :finish
13
13
 
14
- # The relation from which the BatchEnumerator yields batches.
15
- attr_reader :relation
14
+ # The relation from which the BatchEnumerator yields batches.
15
+ attr_reader :relation
16
16
 
17
- # The size of the batches yielded by the BatchEnumerator.
18
- def batch_size
19
- @of
17
+ # The size of the batches yielded by the BatchEnumerator.
18
+ def batch_size
19
+ @of
20
+ end
20
21
  end
21
- end
22
22
 
23
- ActiveRecord::Batches::BatchEnumerator.include(ActiveRecordBatchEnumerator)
23
+ ActiveRecord::Batches::BatchEnumerator.include(ActiveRecordBatchEnumerator)
24
+ 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: 2.8.0
4
+ version: 2.10.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: 2024-08-29 00:00:00.000000000 Z
11
+ date: 2024-12-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: actionpack
@@ -16,42 +16,42 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '6.0'
19
+ version: '6.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '6.0'
26
+ version: '6.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activejob
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '6.0'
33
+ version: '6.1'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '6.0'
40
+ version: '6.1'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: activerecord
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '6.0'
47
+ version: '6.1'
48
48
  type: :runtime
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '6.0'
54
+ version: '6.1'
55
55
  - !ruby/object:Gem::Dependency
56
56
  name: csv
57
57
  requirement: !ruby/object:Gem::Requirement
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - ">="
88
88
  - !ruby/object:Gem::Version
89
- version: '6.0'
89
+ version: '6.1'
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - ">="
95
95
  - !ruby/object:Gem::Version
96
- version: '6.0'
96
+ version: '6.1'
97
97
  - !ruby/object:Gem::Dependency
98
98
  name: zeitwerk
99
99
  requirement: !ruby/object:Gem::Requirement
@@ -185,7 +185,7 @@ homepage: https://github.com/Shopify/maintenance_tasks
185
185
  licenses:
186
186
  - MIT
187
187
  metadata:
188
- source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.8.0
188
+ source_code_uri: https://github.com/Shopify/maintenance_tasks/tree/v2.10.0
189
189
  allowed_push_host: https://rubygems.org
190
190
  post_install_message:
191
191
  rdoc_options: []
@@ -195,14 +195,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
195
195
  requirements:
196
196
  - - ">="
197
197
  - !ruby/object:Gem::Version
198
- version: '3.0'
198
+ version: '3.1'
199
199
  required_rubygems_version: !ruby/object:Gem::Requirement
200
200
  requirements:
201
201
  - - ">="
202
202
  - !ruby/object:Gem::Version
203
203
  version: '0'
204
204
  requirements: []
205
- rubygems_version: 3.5.17
205
+ rubygems_version: 3.5.23
206
206
  signing_key:
207
207
  specification_version: 4
208
208
  summary: A Rails engine for queuing and managing maintenance tasks