mission_control-jobs 1.1.0 → 1.2.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 (27) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -1
  3. data/app/controllers/concerns/mission_control/jobs/job_filters.rb +3 -3
  4. data/app/controllers/concerns/mission_control/jobs/not_found_redirections.rb +4 -1
  5. data/app/controllers/mission_control/jobs/application_controller.rb +2 -1
  6. data/app/controllers/mission_control/jobs/queues_controller.rb +1 -1
  7. data/app/helpers/mission_control/jobs/jobs_helper.rb +6 -1
  8. data/app/helpers/mission_control/jobs/navigation_helper.rb +4 -0
  9. data/app/views/layouts/mission_control/jobs/_application_selection.html.erb +2 -2
  10. data/app/views/layouts/mission_control/jobs/application.html.erb +1 -0
  11. data/app/views/mission_control/jobs/jobs/_error_information.html.erb +6 -4
  12. data/app/views/mission_control/jobs/jobs/_general_information.html.erb +6 -0
  13. data/app/views/mission_control/jobs/jobs/_raw_data.html.erb +1 -1
  14. data/app/views/mission_control/jobs/jobs/blocked/_job.html.erb +1 -1
  15. data/app/views/mission_control/jobs/jobs/scheduled/_job.html.erb +3 -0
  16. data/app/views/mission_control/jobs/queues/index.html.erb +2 -1
  17. data/app/views/mission_control/jobs/shared/_pagination_toolbar.html.erb +2 -0
  18. data/lib/active_job/job_proxy.rb +3 -1
  19. data/lib/active_job/jobs_relation.rb +3 -3
  20. data/lib/active_job/queue_adapters/resque_ext.rb +15 -2
  21. data/lib/active_job/queue_adapters/solid_queue_ext.rb +16 -2
  22. data/lib/mission_control/jobs/arguments_filter.rb +24 -0
  23. data/lib/mission_control/jobs/engine.rb +7 -1
  24. data/lib/mission_control/jobs/host_route_helpers.rb +33 -0
  25. data/lib/mission_control/jobs/version.rb +1 -1
  26. data/lib/mission_control/jobs.rb +7 -0
  27. metadata +4 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2bba1e3be2d257eea8a3956ca3f6b8c9e6708f2fc81fe23290953ac66ecd79b0
4
- data.tar.gz: c470df35a98c7f0f90163540dca2d3c8bd9ca14128ae283731c0e920fdae140e
3
+ metadata.gz: 4f358c4160d8065ff1069ba50a20305eaab8c6beebdca8eddb8a3426f3ea4771
4
+ data.tar.gz: 11e94ea39f4700cfa9a4d35eeb032e5bb721d62f6eb3b033911832737239a61a
5
5
  SHA512:
6
- metadata.gz: 8a6068c3fa2f86d32f07fb458b644efb9e8e0661f1feb65e26bf41d4051bca594ab29d15819b9ab94051451015b1d051bf33b7152797c87ec1952a074f968b2c
7
- data.tar.gz: 972eb81ce61459dcdd2e881ce88a5bf69da431f1b2a17ed6e10a3bf8572222f27b111035747c46492fc459b485542b5dfd072cfb04c4ae1dca6a0cbc5b73bc0c
6
+ metadata.gz: 35ef96bedf4688eb227212f910687239b2d2418935a27a450102351507bb0cf0abb6377d2a27e1f91b9985d638bac4263e18fc5a75806d2e35f721357818c939
7
+ data.tar.gz: 430fef8fade1f488a6a73f69600dd743d361f07e50cf6df8bbba38de0b152058ae8023bea962d51966558ae72de01024b399cb12113380d3d46a9e007623f25c
data/README.md CHANGED
@@ -24,7 +24,7 @@ Rails.application.routes.draw do
24
24
  mount MissionControl::Jobs::Engine, at: "/jobs"
25
25
  ```
26
26
 
27
- And that's it. With this alone, you should be able to access Mission Control Job's UI, where you can browse the existing queues, jobs pending in these queues, jobs in different statuses, and discard and retry failed jobs:
27
+ Once you set up [Authentication](#authentication), you should be able to access Mission Control Job's UI, where you can browse the existing queues, jobs pending in these queues, jobs in different statuses, and discard and retry failed jobs:
28
28
 
29
29
  ![Queues tab in a simple app](docs/images/queues-simple.png)
30
30
 
@@ -113,7 +113,9 @@ Besides `base_controller_class`, you can also set the following for `MissionCont
113
113
  - `internal_query_count_limit`: in count queries, the maximum number of records that will be counted if the adapter needs to limit these queries. True counts above this number will be returned as `INFINITY`. This keeps count queries fast—defaults to `500,000`
114
114
  - `scheduled_job_delay_threshold`: the time duration before a scheduled job is considered delayed. Defaults to `1.minute` (a job is considered delayed if it hasn't transitioned from the `scheduled` status 1 minute after the scheduled time).
115
115
  - `show_console_help`: whether to show the console help. If you don't want the console help message, set this to `false`—defaults to `true`.
116
+ - `back_to_main_app_path`: an optional string path for the "Back to main app" link. Example: `'/admin'`. Defaults to the host app's `root_path` when available.
116
117
  - `backtrace_cleaner`: a backtrace cleaner used for optionally filtering backtraces on the Failed Jobs detail page. Defaults to `Rails::BacktraceCleaner.new`. See the [Advanced configuration](#advanced-configuration) section for how to configure/override this setting on a per application/server basis.
118
+ - `filter_arguments`: an array of strings representing the job argument keys you want to filter out in the UI. This is useful for hiding sensitive user data. Currently, only root-level hash keys are supported.
117
119
 
118
120
  This library extends Active Job with a querying interface and the following setting:
119
121
  - `config.active_job.default_page_size`: the internal batch size that Active Job will use when sending queries to the underlying adapter and the batch size for the bulk operations defined above—defaults to `1000`.
@@ -10,8 +10,8 @@ module MissionControl::Jobs::JobFilters
10
10
  private
11
11
  def set_filters
12
12
  @job_filters = {
13
- job_class_name: params.dig(:filter, :job_class_name).presence,
14
- queue_name: params.dig(:filter, :queue_name).presence,
13
+ job_class_name: params.dig(:filter, :job_class_name).to_s.strip.presence,
14
+ queue_name: params.dig(:filter, :queue_name).to_s.strip.presence,
15
15
  finished_at: finished_at_range_params
16
16
  }.compact
17
17
  end
@@ -36,6 +36,6 @@ module MissionControl::Jobs::JobFilters
36
36
  end
37
37
 
38
38
  def parse_with_time_zone(date)
39
- DateTime.parse(date).in_time_zone if date.present?
39
+ Time.zone.parse(date) if date.present?
40
40
  end
41
41
  end
@@ -24,8 +24,11 @@ module MissionControl::Jobs::NotFoundRedirections
24
24
  end
25
25
 
26
26
  def best_location_for_resource_not_found_error(error)
27
- if error.message.match?(/recurring task/i)
27
+ case error.message
28
+ when /recurring task/i
28
29
  application_recurring_tasks_path(@application)
30
+ when /queue/i
31
+ application_queues_path(@application)
29
32
  else
30
33
  root_url
31
34
  end
@@ -9,6 +9,7 @@ class MissionControl::Jobs::ApplicationController < MissionControl::Jobs.base_co
9
9
  helper MissionControl::Jobs::ApplicationHelper unless self < MissionControl::Jobs::ApplicationHelper
10
10
  helper Importmap::ImportmapTagsHelper unless self < Importmap::ImportmapTagsHelper
11
11
 
12
+ include MissionControl::Jobs::HostRouteHelpers
12
13
  include MissionControl::Jobs::BasicAuthentication
13
14
  include MissionControl::Jobs::ApplicationScoped, MissionControl::Jobs::NotFoundRedirections
14
15
  include MissionControl::Jobs::AdapterFeatures
@@ -18,7 +19,7 @@ class MissionControl::Jobs::ApplicationController < MissionControl::Jobs.base_co
18
19
 
19
20
  private
20
21
  def default_url_options
21
- { server_id: MissionControl::Jobs::Current.server }
22
+ super.merge(server_id: MissionControl::Jobs::Current.server)
22
23
  end
23
24
 
24
25
  def set_current_locale(&block)
@@ -11,6 +11,6 @@ class MissionControl::Jobs::QueuesController < MissionControl::Jobs::Application
11
11
 
12
12
  private
13
13
  def set_queue
14
- @queue = ActiveJob.queues[params[:id]]
14
+ @queue = ActiveJob.queues[params[:id]] or raise MissionControl::Jobs::Errors::ResourceNotFound, "Queue '#{params[:id]}' not found"
15
15
  end
16
16
  end
@@ -38,6 +38,10 @@ module MissionControl::Jobs::JobsHelper
38
38
  job.scheduled_at.before?(MissionControl::Jobs.scheduled_job_delay_threshold.ago)
39
39
  end
40
40
 
41
+ def blocked_job_expiration(job)
42
+ "Expires #{bidirectional_time_distance_in_words_with_title(job.blocked_until)}".html_safe if job.blocked_until
43
+ end
44
+
41
45
  private
42
46
  def renderable_job_arguments_for(job)
43
47
  job.serialized_arguments.collect do |argument|
@@ -67,7 +71,8 @@ module MissionControl::Jobs::JobsHelper
67
71
  elsif argument["_aj_serialized"]
68
72
  ActiveJob::Arguments.deserialize([ argument ]).first
69
73
  else
70
- argument.without("_aj_symbol_keys", "_aj_ruby2_keywords")
74
+ MissionControl::Jobs.job_arguments_filter.apply_to(argument)
75
+ .without("_aj_symbol_keys", "_aj_ruby2_keywords")
71
76
  .transform_values { |v| as_renderable_argument(v) }
72
77
  .map { |k, v| "#{k}: #{v}" }
73
78
  .join(", ")
@@ -1,6 +1,10 @@
1
1
  module MissionControl::Jobs::NavigationHelper
2
2
  attr_reader :page_title, :current_section
3
3
 
4
+ def back_to_main_app_path
5
+ MissionControl::Jobs.back_to_main_app_path.presence || main_app.try(:root_path)
6
+ end
7
+
4
8
  def navigation_sections
5
9
  { queues: [ "Queues", application_queues_path(@application) ] }.tap do |sections|
6
10
  supported_job_statuses.without(:pending).each do |status|
@@ -1,8 +1,8 @@
1
1
  <nav class="navbar" role="navigation" aria-label="main navigation">
2
2
  <div class="navbar-menu is-active mb-4">
3
3
  <div class="navbar-start">
4
- <% if defined?(main_app.root_path) %>
5
- <%= link_to "Back to main app", main_app.root_path %>
4
+ <% if (path = back_to_main_app_path) %>
5
+ <%= link_to "Back to main app", path %>
6
6
  <% end %>
7
7
  </div>
8
8
 
@@ -7,6 +7,7 @@
7
7
 
8
8
  <meta name="viewport" content="width=device-width,initial-scale=1">
9
9
  <meta name="turbo-cache-control" content="no-cache">
10
+ <meta name="turbo-prefetch" content="false">
10
11
 
11
12
  <%= stylesheet_link_tag "mission_control/jobs/bulma.min" %>
12
13
  <%= stylesheet_link_tag "mission_control/jobs/application", "data-turbo-track": "reload" %>
@@ -14,9 +14,11 @@
14
14
  </tbody>
15
15
  </table>
16
16
 
17
- <% if @server.backtrace_cleaner %>
18
- <%= render "mission_control/jobs/jobs/failed/backtrace_toggle", application: @application, job: job %>
19
- <% end %>
17
+ <%= turbo_frame_tag "job-backtrace" do %>
18
+ <% if @server.backtrace_cleaner %>
19
+ <%= render "mission_control/jobs/jobs/failed/backtrace_toggle", application: @application, job: job %>
20
+ <% end %>
20
21
 
21
- <pre class="is-family-monospace mb-4 backtrace-content"><%= failed_job_backtrace(job, @server) %></pre>
22
+ <pre class="is-family-monospace mb-4 backtrace-content"><%= failed_job_backtrace(job, @server) %></pre>
23
+ <% end %>
22
24
  <% end %>
@@ -26,6 +26,12 @@
26
26
  <%= time_distance_in_words_with_title(job.enqueued_at.to_datetime) %> ago
27
27
  </td>
28
28
  </tr>
29
+ <% if job.executions > 0 %>
30
+ <tr>
31
+ <th>Retries</th>
32
+ <td><%= job.executions %></td>
33
+ </tr>
34
+ <% end %>
29
35
  <% if job.scheduled? %>
30
36
  <tr>
31
37
  <th>Scheduled</th>
@@ -1,4 +1,4 @@
1
1
  <h2 class="subtitle">Raw data</h2>
2
2
  <pre>
3
- <%= JSON.pretty_generate(job.raw_data.without("backtrace")) %>
3
+ <%= JSON.pretty_generate(job.filtered_raw_data.without("backtrace")) %>
4
4
  </pre>
@@ -1,7 +1,7 @@
1
1
  <td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
2
2
  <td>
3
3
  <div class="is-family-monospace is-size-7"><%= job.blocked_by %></div>
4
- <div class="has-text-grey is-size-7"><%= job.blocked_until ? "Expires #{bidirectional_time_distance_in_words_with_title(job.blocked_until)}" : "" %></div>
4
+ <div class="has-text-grey is-size-7"><%= blocked_job_expiration(job) %></div>
5
5
  </td>
6
6
  <td class="pr-0">
7
7
  <%= render "mission_control/jobs/jobs/blocked/actions", job: job %>
@@ -1,6 +1,9 @@
1
1
  <td><%= link_to job.queue_name, application_queue_path(@application, job.queue) %></td>
2
2
  <td>
3
3
  <%= bidirectional_time_distance_in_words_with_title(job.scheduled_at) %>
4
+ <% if job.executions > 0 %>
5
+ <div class="is-warning tag ml-4"><%= job.executions.ordinalize %> retry</div>
6
+ <% end %>
4
7
  <% if job_delayed?(job) %>
5
8
  <div class="is-danger tag ml-4">delayed</div>
6
9
  <% end %>
@@ -1,7 +1,6 @@
1
1
  <% navigation(title: "Queues", section: :queues) %>
2
2
 
3
3
  <table class="queues table is-hoverable is-fullwidth">
4
- <tbody>
5
4
  <thead>
6
5
  <tr>
7
6
  <th>Queue</th>
@@ -10,6 +9,8 @@
10
9
  </tr>
11
10
  </thead>
12
11
 
12
+ <tbody>
13
+
13
14
  <%= render partial: "mission_control/jobs/queues/queue", collection: @queues %>
14
15
 
15
16
  </tbody>
@@ -1,5 +1,7 @@
1
1
  <nav class="buttons is-right" role="navigation" aria-label="pagination">
2
2
  <span class="mr-3"><%= page.index %> / <%= page.pages_count || "..." %></span>
3
+ <%= link_to "↞", url_for(page: 1, **filter_param), class: "pagination-previous", disabled: page.first? %>
3
4
  <%= link_to "Previous page", url_for(page: page.previous_index, **filter_param), class: "pagination-previous", disabled: page.first? %>
4
5
  <%= link_to "Next page", url_for(page: page.next_index, **filter_param), class: "pagination-next", disabled: page.last? %>
6
+ <%= link_to "↠", url_for(page: page.pages_count, **filter_param), class: "pagination-next", disabled: page.last? %>
5
7
  </nav>
@@ -7,6 +7,8 @@ class ActiveJob::JobProxy < ActiveJob::Base
7
7
  class UnsupportedError < StandardError; end
8
8
 
9
9
  attr_reader :job_class_name
10
+ # Raw data with the sensitive user data filtered out.
11
+ attr_accessor :filtered_raw_data
10
12
 
11
13
  def initialize(job_data)
12
14
  super
@@ -25,7 +27,7 @@ class ActiveJob::JobProxy < ActiveJob::Base
25
27
  end
26
28
 
27
29
  def duration
28
- finished_at - scheduled_at
30
+ finished_at - (scheduled_at || enqueued_at)
29
31
  end
30
32
 
31
33
  ActiveJob::JobsRelation::STATUSES.each do |status|
@@ -26,7 +26,7 @@ class ActiveJob::JobsRelation
26
26
  FILTERS = %i[ queue_name job_class_name ]
27
27
 
28
28
  PROPERTIES = %i[ queue_name status offset_value limit_value job_class_name worker_id recurring_task_id finished_at ]
29
- attr_reader *PROPERTIES, :default_page_size
29
+ attr_reader(*PROPERTIES, :default_page_size)
30
30
 
31
31
  delegate :last, :[], :reverse, to: :to_a
32
32
  delegate :logger, to: MissionControl::Jobs
@@ -61,7 +61,7 @@ class ActiveJob::JobsRelation
61
61
  finished_at: finished_at
62
62
  }.compact
63
63
 
64
- clone_with **arguments
64
+ clone_with(**arguments)
65
65
  end
66
66
 
67
67
  def with_status(status)
@@ -219,7 +219,7 @@ class ActiveJob::JobsRelation
219
219
 
220
220
  private
221
221
  attr_reader :queue_adapter, :loaded_jobs
222
- attr_writer *PROPERTIES
222
+ attr_writer(*PROPERTIES)
223
223
 
224
224
  def set_defaults
225
225
  self.offset_value = 0
@@ -185,16 +185,29 @@ module ActiveJob::QueueAdapters::ResqueExt
185
185
  end
186
186
 
187
187
  def deserialize_resque_job(resque_job_hash, index)
188
- args_hash = resque_job_hash.dig("payload", "args") || resque_job_hash.dig("args")
189
- ActiveJob::JobProxy.new(args_hash&.first).tap do |job|
188
+ args_hash = extract_args_hash(resque_job_hash)
189
+ ActiveJob::JobProxy.new(args_hash).tap do |job|
190
190
  job.last_execution_error = execution_error_from_resque_job(resque_job_hash)
191
191
  job.raw_data = resque_job_hash
192
+ job.filtered_raw_data = filter_raw_data_arguments(resque_job_hash)
192
193
  job.position = jobs_relation.offset_value + index
193
194
  job.failed_at = resque_job_hash["failed_at"]&.to_datetime&.utc
194
195
  job.status = job.failed_at.present? ? :failed : :pending
195
196
  end
196
197
  end
197
198
 
199
+ def filter_raw_data_arguments(raw_data)
200
+ raw_data.deep_dup.tap do |filtered_data|
201
+ if args_hash = extract_args_hash(filtered_data)
202
+ args_hash["arguments"] = MissionControl::Jobs.job_arguments_filter.apply_to(args_hash["arguments"])
203
+ end
204
+ end
205
+ end
206
+
207
+ def extract_args_hash(raw_data)
208
+ (raw_data.dig("payload", "args") || raw_data.dig("args"))&.first
209
+ end
210
+
198
211
  def execution_error_from_resque_job(resque_job_hash)
199
212
  if resque_job_hash["exception"].present?
200
213
  ActiveJob::ExecutionError.new \
@@ -4,12 +4,14 @@ module ActiveJob::QueueAdapters::SolidQueueExt
4
4
 
5
5
  def queues
6
6
  queues = SolidQueue::Queue.all
7
- pauses = SolidQueue::Pause.where(queue_name: queues.map(&:name)).index_by(&:queue_name)
7
+ queue_names = queues.map(&:name)
8
+ pauses = SolidQueue::Pause.where(queue_name: queue_names).index_by(&:queue_name)
9
+ sizes = SolidQueue::ReadyExecution.where(queue_name: queue_names).group(:queue_name).count
8
10
 
9
11
  queues.collect do |queue|
10
12
  {
11
13
  name: queue.name,
12
- size: queue.size,
14
+ size: sizes[queue.name] || 0,
13
15
  active: pauses[queue.name].nil?
14
16
  }
15
17
  end
@@ -99,6 +101,7 @@ module ActiveJob::QueueAdapters::SolidQueueExt
99
101
  job.status = job_status
100
102
  job.last_execution_error = execution_error_from_solid_queue_job(solid_queue_job) if job_status == :failed
101
103
  job.raw_data = solid_queue_job.as_json
104
+ job.filtered_raw_data = filter_raw_data_arguments(job.raw_data)
102
105
  job.failed_at = solid_queue_job&.failed_execution&.created_at if job_status == :failed
103
106
  job.finished_at = solid_queue_job.finished_at
104
107
  job.blocked_by = solid_queue_job.concurrency_key
@@ -109,6 +112,12 @@ module ActiveJob::QueueAdapters::SolidQueueExt
109
112
  end
110
113
  end
111
114
 
115
+ def filter_raw_data_arguments(raw_data)
116
+ raw_data.deep_dup.tap do |filtered_raw_data|
117
+ filtered_raw_data["arguments"]["arguments"] = MissionControl::Jobs.job_arguments_filter.apply_to(filtered_raw_data.dig("arguments", "arguments"))
118
+ end
119
+ end
120
+
112
121
  def status_from_solid_queue_job(solid_queue_job)
113
122
  SolidQueueJobs::STATUS_MAP.invert[solid_queue_job.status]
114
123
  end
@@ -120,6 +129,11 @@ module ActiveJob::QueueAdapters::SolidQueueExt
120
129
  message: solid_queue_job.failed_execution.message,
121
130
  backtrace: solid_queue_job.failed_execution.backtrace || []
122
131
  end
132
+ rescue JSON::ParserError
133
+ ActiveJob::ExecutionError.new \
134
+ error_class: "",
135
+ message: solid_queue_job.failed_execution.error_before_type_cast,
136
+ backtrace: []
123
137
  end
124
138
 
125
139
  def dispatch_immediately(job)
@@ -0,0 +1,24 @@
1
+ # Replaces argument values with [FILTERED] for any keys that match a filter.
2
+ class MissionControl::Jobs::ArgumentsFilter
3
+ FILTERED = "[FILTERED]"
4
+
5
+ def initialize(filter)
6
+ @filter = filter
7
+ end
8
+
9
+ def apply_to(arguments)
10
+ case arguments
11
+ when Array
12
+ arguments.map { |a| apply_to(a) }
13
+ when Hash
14
+ arguments.map do |k, v|
15
+ [ k, filter.include?(k.to_s) ? FILTERED : v ]
16
+ end.to_h
17
+ else
18
+ arguments
19
+ end
20
+ end
21
+
22
+ private
23
+ attr_reader :filter
24
+ end
@@ -34,12 +34,18 @@ module MissionControl
34
34
  end
35
35
  end
36
36
 
37
+ initializer "mission_control-jobs.host_route_helpers" do |app|
38
+ ActiveSupport.on_load(:after_routes_loaded) do
39
+ MissionControl::Jobs::HostRouteHelpers.define_from(app.routes, MissionControl::Jobs::Engine.routes)
40
+ end
41
+ end
42
+
37
43
  initializer "mission_control-jobs.http_basic_auth" do |app|
38
44
  MissionControl::Jobs.http_basic_auth_user ||= app.credentials.dig(:mission_control, :http_basic_auth_user)
39
45
  MissionControl::Jobs.http_basic_auth_password ||= app.credentials.dig(:mission_control, :http_basic_auth_password)
40
46
  end
41
47
 
42
- initializer "mission_control-jobs.active_job.extensions" do
48
+ initializer "mission_control-jobs.active_job.extensions", before: "active_job.set_configs" do
43
49
  ActiveSupport.on_load :active_job do
44
50
  include ActiveJob::Querying
45
51
  include ActiveJob::Executing
@@ -0,0 +1,33 @@
1
+ # Route helpers of the host app that the engine doesn't define itself, routed through +main_app+.
2
+ #
3
+ # Mission Control's controllers inherit from the host app's +ApplicationController+ (see
4
+ # +base_controller_class+), so host code such as a +before_action+ redirecting to
5
+ # +new_session_path+ runs inside the engine, where route helpers resolve against the engine's
6
+ # routes and raise +ActionController::UrlGenerationError+. This module defines, for every route
7
+ # helper the host app has and the engine doesn't, a method delegating to the host app's routes,
8
+ # so that code keeps working unchanged.
9
+ #
10
+ # Helpers defined by both, like +root_path+, keep resolving to the engine's, as in any isolated
11
+ # engine; use +main_app.root_path+ for the host's.
12
+ module MissionControl::Jobs::HostRouteHelpers
13
+ class << self
14
+ def define_from(host_routes, engine_routes)
15
+ undefine_all
16
+
17
+ (host_routes.named_routes.helper_names - engine_routes.named_routes.helper_names).each do |name|
18
+ define_method(name) { |*args| main_app.public_send(name, *args) }
19
+ defined_helpers << name
20
+ end
21
+ end
22
+
23
+ private
24
+ def undefine_all
25
+ defined_helpers.each { |name| remove_method(name) }
26
+ defined_helpers.clear
27
+ end
28
+
29
+ def defined_helpers
30
+ @defined_helpers ||= []
31
+ end
32
+ end
33
+ end
@@ -1,5 +1,5 @@
1
1
  module MissionControl
2
2
  module Jobs
3
- VERSION = "1.1.0"
3
+ VERSION = "1.2.0"
4
4
  end
5
5
  end
@@ -25,11 +25,18 @@ module MissionControl
25
25
 
26
26
  mattr_accessor :show_console_help, default: true
27
27
  mattr_accessor :backtrace_cleaner
28
+ mattr_accessor :back_to_main_app_path
28
29
 
29
30
  mattr_accessor :importmap, default: Importmap::Map.new
30
31
 
31
32
  mattr_accessor :http_basic_auth_user
32
33
  mattr_accessor :http_basic_auth_password
33
34
  mattr_accessor :http_basic_auth_enabled, default: true
35
+
36
+ mattr_accessor :filter_arguments, default: []
37
+
38
+ def self.job_arguments_filter
39
+ MissionControl::Jobs::ArgumentsFilter.new(filter_arguments)
40
+ end
34
41
  end
35
42
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mission_control-jobs
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jorge Manrubia
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-11 00:00:00.000000000 Z
11
+ date: 2026-08-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -465,6 +465,7 @@ files:
465
465
  - lib/mission_control/jobs/adapter.rb
466
466
  - lib/mission_control/jobs/application.rb
467
467
  - lib/mission_control/jobs/applications.rb
468
+ - lib/mission_control/jobs/arguments_filter.rb
468
469
  - lib/mission_control/jobs/authentication.rb
469
470
  - lib/mission_control/jobs/console/connect_to.rb
470
471
  - lib/mission_control/jobs/console/context.rb
@@ -472,6 +473,7 @@ files:
472
473
  - lib/mission_control/jobs/engine.rb
473
474
  - lib/mission_control/jobs/errors/incompatible_adapter.rb
474
475
  - lib/mission_control/jobs/errors/resource_not_found.rb
476
+ - lib/mission_control/jobs/host_route_helpers.rb
475
477
  - lib/mission_control/jobs/i18n_config.rb
476
478
  - lib/mission_control/jobs/identified_by_name.rb
477
479
  - lib/mission_control/jobs/identified_elements.rb