Exception: Axn::Extensions::Serialization::UnserializableValue
- Inherits:
-
ArgumentError
- Object
- ArgumentError
- Axn::Extensions::Serialization::UnserializableValue
- Includes:
- Axn::Error
- Defined in:
- lib/axn/exceptions.rb
Overview
Raised when an exposed value has no honest JSON representation, so a serializing adapter
(axn-openapi, axn-mcp, axn-ruby_llm) fails the call rather than emitting garbage or a placeholder
where data belongs. Six shapes, in two categories. The rendering would be WRONG, or not JSON at
all: a self-referential container (no JSON representation at all), two Hash keys that stringify
to one JSON property (a value silently dropped), a non-finite Float (no JSON literal exists), or
a String whose bytes have no UTF-8 rendering (JSON is a UTF-8 format). The rendering would be
UGLY, rejected only under serialize_value(reject_opaque: true): a value or a Hash key whose
only to_s is the inherited Object#to_s, which renders an object address into a response body.
An ArgumentError so an adapter's existing rescue StandardError maps it to an error response
with no adapter-side change; a SystemStackError, being outside StandardError, would escape the
adapter entirely. Names the path to the offending value.
Instance Method Summary collapse
-
#initialize(path:, value:, reason: nil) ⇒ UnserializableValue
constructor
reason:names the specific defect, punctuation included. -
#message ⇒ Object
The offending value's class is named through
Internal::RenderedClassName, not@value.class: the value is caller-supplied and may overrideclass, and running that override here would replace this failure with the value's own exception.
Constructor Details
#initialize(path:, value:, reason: nil) ⇒ UnserializableValue
reason: names the specific defect, punctuation included. It defaults to the cycle case —
both the original meaning of this error and the only one an external caller is likely to
construct — so new(path:, value:) remains a complete call.
383 384 385 386 387 388 |
# File 'lib/axn/exceptions.rb', line 383 def initialize(path:, value:, reason: nil) @path = path @value = value @reason = reason super() end |
Instance Method Details
#message ⇒ Object
The offending value's class is named through Internal::RenderedClassName, not @value.class: the
value is caller-supplied and may override class, and running that override here would replace this
failure with the value's own exception. Its bytes are foreign too — a constant may hold non-UTF-8
ones, and Module#to_s hands those back — so the name is RENDERED before it joins this message.
That module composes both halves without delegating to Internal::Rendering (a require cycle) and
without lifting the composition onto ClassName (which promises never to render); see its own
comment. Both moves stay off limits; reaching for the shared owner is the point.
path: and reason: are rendered on the same terms, because EVERY operand of a composition owes it
or none of them do. Inside the gem both are axn's own UTF-8 text (a canonicalized wire path, or an
escaped spelling for a name that has no UTF-8 rendering), but this is a PUBLIC class an adapter
constructs directly — new(path:, value:) is documented as a complete call — so a path in another
encoding is a caller away. A raw Latin-1 path beside a raw Latin-1 class name joined fine; beside a
RENDERED class name it raises Encoding::CompatibilityError from #message itself, which is the
serialization failure destroyed by the report of it.
Every operand normalized AT the join, including the reason — whose two sources (the caller's reason:
and this class's own cycle_reason) are normalized by one call rather than one each, so which source
answered cannot decide whether the message composes.
408 409 410 411 |
# File 'lib/axn/exceptions.rb', line 408 def "Cannot serialize exposed value at `#{Axn::Internal::RenderedText.of(@path)}` (#{value_class_name}): " \ "#{Axn::Internal::RenderedText.of(@reason || cycle_reason)}" end |