Class: CurrentScope::GatingReflection
- Inherits:
-
Object
- Object
- CurrentScope::GatingReflection
- Defined in:
- lib/current_scope/gating_reflection.rb
Overview
Answers one question about a controller path: is it PROVEN that current_scope_check! never runs there? True only when the callback is ABSENT from the class's callback chain — the one thing the chain states unconditionally. Everything else is false:
- callback present and unconditional → gated → false
- callback present wearing a condition (skip_before_action only:) →
UNPROVABLE without evaluating ActionFilter internals, which this class
must never do (KTD-3) → false. The naive any?/none? presence check
inverts exactly here — see the ProblemFrame characterization in
test/gating_reflection_test.rb — and per-action guessing is how a
fail-open gets reported as gated.
- no controller class routed at that path → nothing to prove → false
Absence is an EFFECT, not a cause: a bare skip_before_action, an inherited bare skip (#62), and never including Guard at all are the same verdict, because the chain looks the same. That is deliberate — the question is "does the gate run?", not "why not?".
Named beside GatingTripwire: both interrogate the same subject (is this action gated?), the tripwire at request time, this one by reflection.
Instance Method Summary collapse
-
#controller_class(controller_path) ⇒ Object
The controller class routed at this path, or nil when none is (a stale route, a not-yet-written controller).
-
#declared_skip_reason(controller_path) ⇒ Object
Reason from current_scope_skip_gate!(reason:), or nil when ungated without a declaration (bare skip / never included).
-
#missing_controller?(controller_path) ⇒ Boolean
True when the path is routed but no controller class loads (stale route / typo).
- #ungated?(controller_path) ⇒ Boolean
Instance Method Details
#controller_class(controller_path) ⇒ Object
The controller class routed at this path, or nil when none is (a stale route, a not-yet-written controller). PUBLIC because SodPreflight (#133) asks the same path→class question, and a second copy would have to reimplement the NameError triage described below — the one thing that keeps a broken controller from being reported as a clean one.
This is not a second way to ask the same question, and the difference is
the whole point: controller_class_for RAISES MissingController — every
private caller above rescues it into its own answer (ungated? → false,
missing_controller? → true, declared_skip_reason → nil), because those
three mean different things by "no class here". This one is the fourth such
answer, spelled nil, and it delegates rather than reimplements, so there is
still exactly one path→class rule. What must NOT be added is a public
method that swallows the NameError too. (#133 review — cubic)
82 83 84 85 86 |
# File 'lib/current_scope/gating_reflection.rb', line 82 def controller_class(controller_path) controller_class_for(controller_path) rescue ActionDispatch::MissingController nil end |
#declared_skip_reason(controller_path) ⇒ Object
Reason from current_scope_skip_gate!(reason:), or nil when ungated without a declaration (bare skip / never included). Meaningful when #ungated? is true (#76).
59 60 61 62 63 64 65 66 |
# File 'lib/current_scope/gating_reflection.rb', line 59 def declared_skip_reason(controller_path) klass = controller_class_for(controller_path) return nil unless klass.respond_to?(:current_scope_gate_skip_reason) klass.current_scope_gate_skip_reason.presence rescue ActionDispatch::MissingController nil end |
#missing_controller?(controller_path) ⇒ Boolean
True when the path is routed but no controller class loads (stale route / typo). Distinct from ungated?: there is nothing to gate. Hitting the route is ActionDispatch::MissingController (a 500 in production), so the role grid flags the row (#43). Uses the same Rails path→class rule as #ungated? so the two answers cannot disagree on loadability.
49 50 51 52 53 54 |
# File 'lib/current_scope/gating_reflection.rb', line 49 def missing_controller?(controller_path) controller_class_for(controller_path) false rescue ActionDispatch::MissingController true end |
#ungated?(controller_path) ⇒ Boolean
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/current_scope/gating_reflection.rb', line 24 def ungated?(controller_path) klass = controller_class_for(controller_path) # A controller without the callbacks API at all (a bare ActionController:: # Metal without AbstractController::Callbacks) cannot run a before_action # — the gate PROVABLY never runs there. Without this check the reflection # would raise NoMethodError instead of answering. (#79 review) return true unless klass.respond_to?(:_process_action_callbacks) klass ._process_action_callbacks .none? { |callback| callback.kind == :before && callback.filter == :current_scope_check! } rescue ActionDispatch::MissingController # A routed path with no controller class (a scaffolding leftover, a # not-yet-written controller) proves nothing about gating. Silent false, # matching the prove-or-stay-silent discipline of # warn_on_cross_controller_derivation. Surface that shape via # #missing_controller? instead — the grid badges it (#43). false end |