Module: Trane::Controller::ErrorHandler

Extended by:
ActiveSupport::Concern
Defined in:
lib/trane/controller/error_handler.rb

Overview

Mixin that captures StandardError subclasses and maps them to registered Trane errors. Resolves the exception class via the precomputed errors_by_name index on the Registry snapshot — single lookup on FQDN match, with a short-name fallback (via String#rpartition) for hosts that registered errors by short name. Include in your API base controller. Does NOT add render contract: support — combine with Trane::Controller::Renderer or use the composer Trane::Controller.

WARNING: this installs rescue_from StandardError at the class level. Including in a base controller that also serves HTML would catch errors Devise / Pundit / etc. expect to bubble up. Recommended: include only in API base controllers.

Rails-reserved exceptions (those in ActionDispatch::ExceptionWrapper.rescue_responses, e.g. ActiveRecord::RecordNotFound → 404, ActionController::ParameterMissing → 400) are re-raised when no Trane error is registered for them, so Rails' exception middleware applies its default status mapping. Hosts that want to swallow these into the Trane envelope can register them explicitly via Trane.errors { error "ActiveRecord::RecordNotFound", ... }; the Trane lookup wins over the re-raise path.

SECURITY NOTE: a registered error's envelope carries the exception's runtime #message, in EVERY environment including production. Framework and library exception messages are written for logs and may reveal internals (model names, query conditions). Prefer the recommended pattern (docs/wiki/Error-Handling.md) — rescue the framework exception and raise a domain error with a curated message — and register framework exceptions directly only when their messages are acceptable to expose.

PRODUCTION NOTE: re-raised exceptions surface via Rails' default middleware. Keep config.consider_all_requests_local = false in production so ActionDispatch::ShowExceptions serves the static public/404.html / 500.html pages. With consider_all_requests_local enabled in production, ActionDispatch::DebugExceptions would expose the exception class + message + backtrace.

Class Method Summary collapse

Class Method Details

.rails_reserved_namesObject

Returns the frozen Set of Rails-reserved exception class NAMES (Strings) derived from ActionDispatch::ExceptionWrapper.rescue_responses. Memoized on first call (after Rails boot completes). Returns an empty Set when Rails is not loaded.

Names, not Class objects, on purpose: memoizing classes would pin host-registered (Zeitwerk-reloadable) exception constants in the gem forever, and after a development reload the stale Class would no longer match its reloaded replacement. Names survive reloads.

NOTE: callers MUST invoke this post-boot. The Rails Engine initializers merge AR/AC entries into rescue_responses during boot; calling this earlier would memoize an incomplete list. In practice the first error arrives in a request thread, which is always post-boot — but tests or explicit pre-boot invocations would have to reset @rails_reserved_names afterwards.



67
68
69
70
71
72
73
74
75
# File 'lib/trane/controller/error_handler.rb', line 67

def self.rails_reserved_names
  @rails_reserved_names ||= begin
    if defined?(ActionDispatch::ExceptionWrapper)
      Set.new(ActionDispatch::ExceptionWrapper.rescue_responses.keys.grep(String)).freeze
    else
      Set.new.freeze
    end
  end
end