Module: Axn::Internal::Rendering
- Defined in:
- lib/axn/internal/rendering.rb
Overview
Facts about a foreign object, for a message being built ABOUT it.
An error path owes two obligations and every site that met one of them by hand met only that one.
First, do not DISPATCH what the object can override: an inspect, a class, a message or a
backtrace called to build the message can raise and replace the failure being reported, and outside
StandardError that escapes the rescue meant to settle it. Second, do not join raw foreign BYTES: what
a class name or a message HOLDS is foreign too, and a String with no UTF-8-compatible rendering cannot
be joined to axn's own UTF-8 prose at all. Both halves live here so no caller can meet one and miss
the other.
Sits above axn/exceptions because it needs Internal::ClassName; the byte half it renders through
is one layer further down (Internal::Text) precisely so axn/exceptions can render its own messages
without requiring the file that requires it.
Constant Summary collapse
- UNKNOWN_LOCATION =
"unknown location"
Class Method Summary collapse
-
.class_name(value) ⇒ Object
A value's CLASS named in prose, both halves composed:
ClassNameanswers from bound base implementations so nothing the value defines runs, and the constant path it answers with is rendered because a constant may hold non-UTF-8 bytes (Object.const_set(:"Caf\xE9", Class.new)is accepted, andModule#to_shands those bytes back). -
.exception_message(exception) ⇒ Object
An exception's own message, as a UTF-8 String this method owns.
-
.exception_source_location(exception) ⇒ Object
Just the filename and line an exception came from, for a warning that names where a swallowed failure happened.
-
.module_name(mod) ⇒ Object
A class or module named in its own right — a declared
type:, a tool axn — rather than a value's class. -
.value_rendering(value) ⇒ Object
A VALUE's own rendering, as a UTF-8 String this method owns — or nil when it has none.
Class Method Details
.class_name(value) ⇒ Object
A value's CLASS named in prose, both halves composed: ClassName answers from bound base
implementations so nothing the value defines runs, and the constant path it answers with is
rendered because a constant may hold non-UTF-8 bytes (Object.const_set(:"Caf\xE9", Class.new) is
accepted, and Module#to_s hands those bytes back).
DELEGATED to Internal::RenderedClassName, which owns that composition for the message paths built on
axn/exceptions — they cannot reach this file (it requires theirs), but this file requires theirs, so
the dependency runs one way and there is one composer rather than two identical ones.
40 |
# File 'lib/axn/internal/rendering.rb', line 40 def class_name(value) = RenderedClassName.of(value) |
.exception_message(exception) ⇒ Object
An exception's own message, as a UTF-8 String this method owns.
47 |
# File 'lib/axn/internal/rendering.rb', line 47 def (exception) = Text.renderable((exception)) |
.exception_source_location(exception) ⇒ Object
Just the filename and line an exception came from, for a warning that names where a swallowed failure happened.
Read through a BOUND Exception#backtrace, which settles two things at once. An override cannot
substitute a non-Array or a non-String frame — and an override that answers non-nil stops Ruby
recording a real backtrace at all, so the bound reader sees nil and this degrades to
UNKNOWN_LOCATION instead of crashing on the override's answer.
An empty backtrace has to be tolerated, and so does a BLANK frame inside one: raise repopulates
a nil backtrace, but a backtrace reconstructed with set_backtrace (what a death handler
rebuilding one from job data hands us) is kept exactly as given, [""] included. This runs from
inside an ensure often enough that a raise here would replace the exception already in flight.
91 92 93 94 95 96 97 98 99 100 |
# File 'lib/axn/internal/rendering.rb', line 91 def exception_source_location(exception) frame = first_frame(exception) return UNKNOWN_LOCATION unless frame path = STRING_SPLIT.bind_call(frame).first return UNKNOWN_LOCATION unless Identity.kind?(path, ::String) basename = STRING_SPLIT.bind_call(path, "/").last Text.renderable(STRING_SPLIT.bind_call(basename, ":")[0, 2].join(":")) end |
.module_name(mod) ⇒ Object
A class or module named in its own right — a declared type:, a tool axn — rather than a value's
class. Same two halves.
44 |
# File 'lib/axn/internal/rendering.rb', line 44 def module_name(mod) = Text.renderable(ClassName.of_module(mod)) |
.value_rendering(value) ⇒ Object
A VALUE's own rendering, as a UTF-8 String this method owns — or nil when it has none.
For a value that IS the message rather than one being described: a fail! reason, a declared
error/success handler's return, a user_facing: handler's return. to_s is DISPATCHED,
deliberately, on the same terms as exception_message dispatches #message — the object's own
rendering is what makes the message useful, and routing everything through Object#to_s would
degrade every well-behaved value to defend against broken ones. But these values are rendered while
a failure is being SETTLED, and again on every later result.error/result.message read, and
interpolating one dispatches its to_s under no guard at all. So the call is made here and its
failure absorbed.
Absorbs every class, including those axn never swallows: the outcome being settled has to win over
anything raised while rendering it, and a value's to_s is not a path a signal travels through. A
non-String to_s is a failure too, since rendering its answer would dispatch again.
A String is rendered from its BYTES with nothing dispatched, which is what interpolation itself does
("#{}" takes a String as it stands), so a subclass whose to_s raises still renders as its text.
nil rather than a fallback of its own, because what an unrenderable value should degrade TO belongs
to the caller: a composed message names the value's class (class_name, the fallback this module and
Identity.describe both take), while a user-facing message has a better answer to fall back on.
70 71 72 73 74 75 76 77 |
# File 'lib/axn/internal/rendering.rb', line 70 def value_rendering(value) return Text.renderable(value) if Identity.kind?(value, ::String) rendered = value.to_s Identity.kind?(rendered, ::String) ? Text.renderable(rendered) : nil rescue ::Exception # rubocop:disable Lint/RescueException nil end |