Module: Phlex::Reactive::Defer
- Defined in:
- lib/phlex/reactive/defer.rb
Overview
Deferred reply segments (issue #165): the machinery behind reply.defer — "placeholder now, the real render to the ACTOR when ready".
A Response only RECORDS segments (see Response#defer); this module turns them into wire streams at the ENDPOINT, after the action's transaction committed — so a rolled-back action can never leak a directive — and picks the delivery lane:
* pull (:fetch) — the directive carries a purpose-scoped, short-TTL
defer token; the client POSTs it to the defer endpoint OFF the action
queue and applies the rendered stream when it lands. Works on every
transport (it's just HTTP) — this is the universal lane.
* push (:stream) — pgbus durable one-shot stream + ActiveJob: the reply
carries a signed SSE src; a job renders and broadcasts durably, and
the since-id=0 replay closes the broadcast-before-subscribe race.
Capability-gated (Phlex::Reactive.defer_push_capable?); anything
missing degrades to pull.
Defined Under Namespace
Classes: Segment
Constant Summary collapse
- DIRECTIVE_ACTION =
The custom turbo-stream action the client's defer handler registers.
"reactive:defer"- DEFER_KEY_MARKER =
The stable marker every one-shot push-lane stream key starts with — how pgbus's orphan sweep and an operator recognize a phlex-reactive defer queue (issue #165).
"prdefer_"- DEFER_KEY_MIN_SUFFIX =
Minimum random hex chars on the key (64 bits) — collision-safe.
16- PGBUS_MAX_QUEUE_NAME =
pgbus's MAX_QUEUE_NAME_LENGTH (its QueueNameValidator); the live queue_prefix is subtracted from it to get the usable budget.
47- DEFAULT_PGBUS_QUEUE_PREFIX =
pgbus's default queue_prefix, used when the live one can't be read.
"pgbus"- REAL_RENDER_KEY =
Thread/fiber-local flag marking a REAL reactive-machinery render — inside it a reactive_lazy component renders its actual template instead of the placeholder shell. Set by Streamable.render_component (replies, broadcasts, the defer endpoint/job, the class stream builders) and Phlex::Reactive.render; a page-embedded initial mount never passes through either, so it gets the shell.
:phlex_reactive_defer_real_render
Class Method Summary collapse
- .real_render? ⇒ Boolean
-
.resolve_via ⇒ Object
Which lane deferred segments take, resolved PER REPLY (capability is probed live — cheap: without pgbus it's one defined? short-circuit).
-
.streams_for(segment, via: resolve_via) ⇒ Object
The wire streams for ONE segment on the given lane, in apply order: the optional placeholder shell FIRST (so the pending state paints before the directive kicks off delivery), then the directive.
-
.validate_segment!(component, placeholder) ⇒ Object
Validate reply.defer arguments LOUDLY at the call site — a dead segment (a component the endpoint could never rebuild, a bogus placeholder) must fail in the action that wrote it, not silently at reply render or, worse, at fetch time.
- .with_real_render ⇒ Object
Class Method Details
.real_render? ⇒ Boolean
54 55 56 |
# File 'lib/phlex/reactive/defer.rb', line 54 def real_render? Thread.current[REAL_RENDER_KEY] ? true : false end |
.resolve_via ⇒ Object
Which lane deferred segments take, resolved PER REPLY (capability is probed live — cheap: without pgbus it's one defined? short-circuit). :fetch forces pull; :stream requests push and DEGRADES to pull with a one-time warning when the capability is absent (never break); :auto picks push iff fully capable.
71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/phlex/reactive/defer.rb', line 71 def resolve_via case Phlex::Reactive.defer_transport when :fetch then :fetch when :stream return :stream if Phlex::Reactive.defer_push_capable? warn_stream_degraded :fetch else Phlex::Reactive.defer_push_capable? ? :stream : :fetch end end |
.streams_for(segment, via: resolve_via) ⇒ Object
The wire streams for ONE segment on the given lane, in apply order: the optional placeholder shell FIRST (so the pending state paints before the directive kicks off delivery), then the directive.
87 88 89 90 91 92 93 |
# File 'lib/phlex/reactive/defer.rb', line 87 def streams_for(segment, via: resolve_via) streams = [] shell = placeholder_stream(segment) streams << shell if shell streams << directive_stream(segment, via) streams end |
.validate_segment!(component, placeholder) ⇒ Object
Validate reply.defer arguments LOUDLY at the call site — a dead segment (a component the endpoint could never rebuild, a bogus placeholder) must fail in the action that wrote it, not silently at reply render or, worse, at fetch time.
99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/phlex/reactive/defer.rb', line 99 def validate_segment!(component, placeholder) unless component.class.include?(Phlex::Reactive::Component) raise ::ArgumentError, "reply.defer expects a Phlex::Reactive::Component instance (its identity is what the " \ "deferred render is rebuilt from) — got #{component.class}" end return if placeholder.nil? || placeholder == true || placeholder.is_a?(::String) || placeholder.is_a?(::Phlex::SGML) raise ::ArgumentError, "reply.defer placeholder: must be nil (keep current content), true (the component's " \ "deferred_placeholder or the built-in shell), a String, or a Phlex component — " \ "got #{placeholder.class}" end |
.with_real_render ⇒ Object
58 59 60 61 62 63 64 |
# File 'lib/phlex/reactive/defer.rb', line 58 def with_real_render previous = Thread.current[REAL_RENDER_KEY] Thread.current[REAL_RENDER_KEY] = true yield ensure Thread.current[REAL_RENDER_KEY] = previous end |