Module: Pgbus::Streams::Renderer
- Defined in:
- lib/pgbus/streams/renderer.rb
Overview
Turns a renderable into a complete <turbo-stream> action tag,
ready to hand to Stream#broadcast. This centralises the off-request
render + tag-building that every consumer would otherwise stitch
together by hand (the #1 footgun in server-driven UI: rendering a
component outside a request without a usable view context).
pgbus deliberately has no hard dependency on turbo-rails, ActionView, or Phlex, so this builder is self-contained and matches Turbo's wire format directly. The browser's Turbo runtime consumes the tag; the exact string is the contract, not any particular Ruby library.
Renderable resolution (first match wins):
- String → used verbatim (already-rendered markup)
- responds to :call → Phlex::HTML#call (the issue's example shape)
- responds to :render_in → ViewComponent / phlex-rails
(`render_in(view_context)`; a nil context is passed because
off-request there is no controller — components that need URL
helpers should be rendered by the app and the string passed in)
- else → to_s
Tag format mirrors Turbo::Streams::TagBuilder:
- content actions wrap the markup in a <template>
- content-less actions (remove) emit no <template>
Constant Summary collapse
- CONTENTLESS_ACTIONS =
Turbo stream actions that carry no content (no wrapper).
%w[remove].freeze
Class Method Summary collapse
- .escape(value) ⇒ Object
-
.render(renderable) ⇒ Object
Resolves a renderable to an HTML string.
-
.turbo_stream_tag(action:, target:, renderable: nil) ⇒ Object
Builds a
<turbo-stream action target><template>...</template></turbo-stream>string.
Class Method Details
.escape(value) ⇒ Object
62 63 64 |
# File 'lib/pgbus/streams/renderer.rb', line 62 def escape(value) CGI.escape_html(value.to_s) end |
.render(renderable) ⇒ Object
Resolves a renderable to an HTML string. See module docs for the resolution order. Returns "" for nil (a content action with no renderable still emits an empty ).
53 54 55 56 57 58 59 60 |
# File 'lib/pgbus/streams/renderer.rb', line 53 def render(renderable) return "" if renderable.nil? return renderable if renderable.is_a?(String) return renderable.call.to_s if renderable.respond_to?(:call) return renderable.render_in(nil).to_s if renderable.respond_to?(:render_in) renderable.to_s end |
.turbo_stream_tag(action:, target:, renderable: nil) ⇒ Object
Builds a <turbo-stream action target><template>...</template></turbo-stream>
string. renderable may be nil for content-less actions.
38 39 40 41 42 43 44 45 46 47 48 |
# File 'lib/pgbus/streams/renderer.rb', line 38 def turbo_stream_tag(action:, target:, renderable: nil) raise ArgumentError, "target is required" if target.nil? || target.to_s.empty? action = action.to_s attrs = %(action="#{escape(action)}" target="#{escape(target)}") return "<turbo-stream #{attrs}></turbo-stream>" if CONTENTLESS_ACTIONS.include?(action) content = render(renderable) "<turbo-stream #{attrs}><template>#{content}</template></turbo-stream>" end |