Class: RuboCop::Cop::Heatwave::NoBareJobPathRedirect
- Inherits:
-
Base
- Object
- Base
- RuboCop::Cop::Heatwave::NoBareJobPathRedirect
- Defined in:
- lib/rubocop/cop/heatwave/no_bare_job_path_redirect.rb
Overview
Flags redirect_to job_path(jid) where the JID is not proven non-nil.
Every Sidekiq job inherits lock: :until_and_while_executing with
client on_conflict: :log (see Sidekiq.default_job_options), so
perform_async returns nil whenever the uniqueness lock rejects a
duplicate push — a double-clicked button, a double-submitted form. The
route has no nil-able segment, so job_path(nil) raises
ActionController::UrlGenerationError and the user gets a 500.
This has now bitten production three times (AppSignal #4231, #6312,
#6497). Each time the fix was a per-call-site guard, and each time new
call sites reintroduced it — hence this cop.
CrmController#redirect_to_job_or_fallback handles all three cases:
the job queued, a duplicate was already running (via duplicate_lookup:),
or the push genuinely failed (falls back with an explanatory flash).
A call is accepted when the JID is guarded — the cop walks up the AST
looking for an enclosing if/unless/&& condition that tests the same
variable, which covers the if job_id and if job_id.present? shapes
already used across the controllers, plus the trailing-modifier form.
Constant Summary collapse
- MSG =
Names the nil-JID failure mode and points at the two sanctioned fixes:
the shared helper, or an explicit guard. '`perform_async` returns nil when the uniqueness lock rejects a duplicate, and ' \ '`job_path(nil)` raises UrlGenerationError (AppSignal #4231/#6312/#6497). Use ' \ '`redirect_to_job_or_fallback(jid, fallback_path)` or guard the jid explicitly.'
- RESTRICT_ON_SEND =
Only
redirect_tosends can match, so skip the rest outright. %i[redirect_to].freeze
- VARIABLE_TYPES =
Node types that prove a variable is non-nil: a plain read, or the
assign-and-test idiomif (jid = Something.find). %i[lvar ivar lvasgn ivasgn].freeze
- EXIT_TYPES =
Node types that leave the action, making a preceding conditional a real
guard rather than just an unrelated branch. %i[return next break].freeze
Instance Method Summary collapse
Instance Method Details
#on_send(node) ⇒ Object
67 68 69 70 71 72 73 74 75 76 |
# File 'lib/rubocop/cop/heatwave/no_bare_job_path_redirect.rb', line 67 def on_send(node) jid = redirect_to_job_path(node) return unless jid # A literal (or anything that isn't a plain local/ivar read) can't be # the nil-from-perform_async case. return unless (name = jid_name(jid)) return if guarded?(node, name) add_offense(node) end |
#redirect_to_job_path(node) ⇒ Object
63 64 65 |
# File 'lib/rubocop/cop/heatwave/no_bare_job_path_redirect.rb', line 63 def_node_matcher :redirect_to_job_path, <<~PATTERN (send nil? :redirect_to (send nil? :job_path $_ ...) ...) PATTERN |