Class: LcpRuby::Authorization::InvariantCheck::Configuration
- Inherits:
-
Object
- Object
- LcpRuby::Authorization::InvariantCheck::Configuration
- Defined in:
- lib/lcp_ruby/authorization/invariant_check/configuration.rb
Overview
Configuration for the runtime invariant validator. Hosts override defaults via:
LcpRuby.configuration.invariant_check = {
severity: :error,
severity_per_code: { "AUTH-005" => :off, "AUTH-002" => :error },
fail_boot: true # production-only; ignored in dev/test
}
in ‘config/initializers/lcp_ruby.rb`.
Mirrors ‘LcpRuby::I18nCheck::Configuration`’s shape so host apps learn one API for both linters. See docs/design/authorization_hardening.md § “Configuration class”.
Constant Summary collapse
- VALID_SEVERITIES =
%i[error warn off].freeze
- DEFAULT_SEVERITIES =
Per-code defaults for AUTH-001..AUTH-009. Single source of truth; both ‘Configuration#severity_for` (host introspection) and `RuntimeInvariantValidator#emit` (boot path) consult this table so they cannot diverge. Host overrides via `severity_per_code` win; otherwise this table; otherwise `severity` (top-level). See docs/reference/invariant_check.md § “Codes” for rationale per code.
{ "AUTH-001" => :error, # custom scope method missing on klass "AUTH-002" => :error, # routable standalone page with no visible_when and no zone gate "AUTH-003" => :error, # malformed visible_when shape "AUTH-004" => :error, # field_match.field not a column on klass "AUTH-005" => :warn, # user_field/current_user_<m> not on user class "AUTH-006" => :error, # association.field not a column on klass "AUTH-007" => :error, # inherits parent unresolvable "AUTH-008" => :error, # union scope with empty scopes: array "AUTH-009" => :warn # preset prerequisite missing (reserved for generators) # NOTE: AUTH-010 (runtime evaluator failure) and AUTH-011 (sentinel # wiring check on first request) are runtime-only — they raise # directly without consulting this severity table. }.freeze
Instance Attribute Summary collapse
-
#fail_boot ⇒ Object
readonly
Returns the value of attribute fail_boot.
-
#severity ⇒ Object
readonly
Returns the value of attribute severity.
-
#severity_per_code ⇒ Object
readonly
Returns the value of attribute severity_per_code.
Class Method Summary collapse
-
.coerce(value) ⇒ Object
Normalize an arbitrary host-supplied value into a Configuration: nil / unset → fresh defaults Configuration → returned as-is Hash → built eagerly via splat.
Instance Method Summary collapse
-
#initialize(severity: :error, severity_per_code: {}, fail_boot: nil) ⇒ Configuration
constructor
A new instance of Configuration.
-
#severity_for(code) ⇒ Object
Resolve severity for a given code.
Constructor Details
#initialize(severity: :error, severity_per_code: {}, fail_boot: nil) ⇒ Configuration
Returns a new instance of Configuration.
45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/lcp_ruby/authorization/invariant_check/configuration.rb', line 45 def initialize(severity: :error, severity_per_code: {}, fail_boot: nil) @severity = normalize(severity) @severity_per_code = severity_per_code .each_with_object({}) { |(k, v), h| h[k.to_s] = normalize(v) } # Dev/test: always strict (knob ignored). Production: opt-in via # `fail_boot:` (default false — observability-only until host # trusts the validator). The asymmetry localizes deployment risk # to one knob while keeping the dev feedback loop tight. See # docs/design/authorization_hardening.md § "Dev/test boot-fail # is unconditional; production opt-in". @fail_boot = if defined?(Rails) && Rails.env.production? fail_boot == true else true end end |
Instance Attribute Details
#fail_boot ⇒ Object (readonly)
Returns the value of attribute fail_boot.
43 44 45 |
# File 'lib/lcp_ruby/authorization/invariant_check/configuration.rb', line 43 def fail_boot @fail_boot end |
#severity ⇒ Object (readonly)
Returns the value of attribute severity.
43 44 45 |
# File 'lib/lcp_ruby/authorization/invariant_check/configuration.rb', line 43 def severity @severity end |
#severity_per_code ⇒ Object (readonly)
Returns the value of attribute severity_per_code.
43 44 45 |
# File 'lib/lcp_ruby/authorization/invariant_check/configuration.rb', line 43 def severity_per_code @severity_per_code end |
Class Method Details
.coerce(value) ⇒ Object
Normalize an arbitrary host-supplied value into a Configuration:
nil / unset → fresh defaults
Configuration → returned as-is
Hash → built eagerly via splat
Single source of truth for the three call sites that consume ‘LcpRuby.configuration.invariant_check`: the Configuration setter itself (eager normalization), the validator’s ‘resolve_config` (defensive against specs that bypass the setter), and the `lcp_ruby:invariant_check` rake task. Keeps the Hash↔Configuration branch table in one place.
Reload-resilience: a Configuration instance stored in ‘LcpRuby.configuration.invariant_check` survives `LcpRuby.reset_for_reload!` (because `@configuration` is preserved), but Zeitwerk reloads this class on dev autoreload — the stored instance is then a different class object than `self`, so `when self` (which is `is_a?` under the hood) misses. The duck-type fallback rebuilds a fresh Configuration from the stale instance’s public surface, so ‘Engine.reload!` doesn’t crash on the second pass through. See docs/design/boot_reload_lifecycle.md § Q5 / D9.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/lcp_ruby/authorization/invariant_check/configuration.rb', line 94 def self.coerce(value) case value when nil then new when self then value when Hash then new(**value.transform_keys(&:to_sym)) else if value.respond_to?(:severity) && value.respond_to?(:severity_per_code) && value.respond_to?(:fail_boot) new(severity: value.severity, severity_per_code: value.severity_per_code, fail_boot: value.fail_boot) else raise ArgumentError, "expected a Hash, #{name}, or nil (got #{value.class})" end end end |
Instance Method Details
#severity_for(code) ⇒ Object
Resolve severity for a given code. Precedence:
1. host's `severity_per_code` override (explicit)
2. `DEFAULT_SEVERITIES` table (per-code shipping default)
3. top-level `severity` (catch-all for unlisted codes)
67 68 69 70 71 |
# File 'lib/lcp_ruby/authorization/invariant_check/configuration.rb', line 67 def severity_for(code) key = code.to_s return @severity_per_code[key] if @severity_per_code.key?(key) DEFAULT_SEVERITIES[key] || @severity end |