Exception: Axn::ReraiseFailed

Inherits:
StandardError
  • Object
show all
Includes:
Error
Defined in:
lib/axn/exceptions.rb

Overview

Raised by Axn::Extensions.best_effort under best_effort_raises_in_dev for a side-effect exception that raise cannot hand back AS ITSELF.

The dev-loud mode re-raises what the guarded block raised, and raise dispatches the 0-arg #exception on whatever object it is given — a bare raise re-raising $! included, since Ruby has no re-raise that skips that dispatch. So a class that owns #exception decides what leaves the guard: one answering a different object escaped as that object with the block's exception gone entirely, and one that raises escaped as whatever it raised. Either way the guard emitted a third exception, which is the one thing it promises never to do.

The decision is by OWNERSHIP, never by behaviour (NativeMethods.native_exception_reraise?), and the dispatch is AVOIDED rather than guarded — the doctrine Axn::Tools._named_invalid_contract settled for the boot path, for the same reason: an #exception that answers itself once and raises the second time defeats any probe, so the only bounded question is whether Ruby's own implementation is what will answer. When it is — the overwhelmingly common case, an ordinary ArgumentError included, and a frozen exception too — the original object is re-raised unchanged and this class never appears.

Dev-loud stays loud where it cannot: a developer who configured a raise gets one. Only the CLASS degrades. The original is this error's cause, its class and message are repeated here, and a StandardError so an enclosing rescue => e catches it exactly where it would have caught the ordinary case.

Every value interpolated came from someone else's object, so each is RENDERED rather than joined — the whole point being that reporting must not become the failure (see Tools::InvalidContract above, which composes the same way for the same reason). The caller renders them too, needing the same text for its warning path; rendering is idempotent, so the guarantee holds for any caller rather than resting on that one's diligence.

Instance Method Summary collapse

Constructor Details

#initialize(desc:, reason:, original_class:) ⇒ ReraiseFailed

Returns a new instance of ReraiseFailed.



299
300
301
302
303
304
305
306
# File 'lib/axn/exceptions.rb', line 299

def initialize(desc:, reason:, original_class:)
  desc, reason, original_class = [desc, reason, original_class].map { |text| Axn::Internal::RenderedText.of(text) }

  super("Exception raised while #{desc}, re-raised as #{self.class} and not as the original " \
        "#{original_class}, because that class supplies its own `#exception` — which `raise` dispatches on " \
        "whatever object it is handed — and axn does not run an exception's own code while re-raising it. " \
        "The original is this error's `cause`, and its message was: #{reason}")
end