Module: Phlex::Reactive::Component
- Extended by:
- ActiveSupport::Concern
- Includes:
- DSL, Helpers, Identity, Lazy, Streamable
- Defined in:
- lib/phlex/reactive/component.rb,
lib/phlex/reactive/component/dsl.rb,
lib/phlex/reactive/component/lazy.rb,
lib/phlex/reactive/component/helpers.rb,
lib/phlex/reactive/component/identity.rb,
lib/phlex/reactive/component/registry.rb
Overview
Component turns a self-contained Phlex component into a Livewire-style
reactive unit: declare actions in Ruby, and the generic reactive
Stimulus controller wires clicks/inputs to an HTTP round trip that
re-renders the component and applies it back into the DOM (a plain replace
by default; return Response.morph(self) to morph in place and keep the
focused input — issue #28). No per-feature Stimulus controllers, no
hand-picked Turbo targets.
Including Component pulls in Phlex::Reactive::Streamable automatically (Concern dependency — Streamable lands on the base first, exactly the old manual order), so ONE include is enough. The legacy explicit double include remains a harmless no-op.
Security model (the decisive design choice) ===
We do NOT ship component STATE to the browser (no snapshot). The DOM carries a signed IDENTITY:
* Record-backed (the common case): reactive_record :todo signs the
record's GlobalID. The server re-finds it via GlobalID — the client
can neither forge the component class nor swap the record. State =
the database.
* State-backed (record-less, e.g. a counter): reactive_state :count
signs the listed instance variables. Use when there is genuinely no
record to re-find.
* Both (the inline_edit pattern): reactive_record :record plus
reactive_state :attribute, :editing signs the record's GlobalID AND
the transient mode in one token, so "which field / what mode" survives
every action round trip and stays tamper-proof.
Actions are DEFAULT-DENY: only methods declared with action :name may be
invoked. The signature proves the token is ours, NOT that this user may
act — your action must still authorize the record. Action params pass
through a declared schema; nothing else reaches the method.
Usage (record-backed — #id defaults to dom_id(@todo), issue #81):
class Todos::Item < ApplicationComponent
include Phlex::Reactive::Component
reactive_record :todo
action :toggle
action :rename, params: { title: :string }
def initialize(todo:) = @todo = todo
def toggle = ((@todo, :update?); @todo.toggle!(:done))
def rename(title:) = ((@todo, :update?); @todo.update!(title:))
def view_template
li(id:, **reactive_attrs) do
(**on(:toggle)) { @todo.done? ? "✓" : "○" }
span { @todo.title }
end
end
end
Assembled from three cohesive concerns (issue #115) — one include, zero public API change:
* Component::DSL — the five declaration registries (via
Component::Registry) + from_identity
* Component::Identity — reactive_token + the hot-path ivar precomputation
* Component::Helpers — reply/js, reactive_attrs/root, on/on_client,
the field/select/text bindings, and the nested-attributes helpers
Defined Under Namespace
Modules: DSL, Helpers, Identity, Lazy, Registry Classes: Action, CollectionDefinition, ComputeDefinition
Constant Summary
Constants included from Helpers
Helpers::EMPTY_PARAMS_JSON, Helpers::LISTNAV_ACTIONS, Helpers::OPTIMISTIC_CLASS_OPS, Helpers::SHOW_CONNECTIVE_KEYS, Helpers::SHOW_LITERAL_KEYS, Helpers::SHOW_NUMERIC_KEYS, Helpers::SHOW_PREDICATE_KEYS
Constants included from DSL
Constants included from Streamable
Streamable::BROADCAST_REFUSED_OPS
Method Summary
Methods included from Helpers
#busy_on, #compute_inputs_param, #compute_mirror_param, #js, #mark_authorized!, #nested_attributes, #nested_update!, #on, #on_client, #reactive_attrs, #reactive_compute_attrs, #reactive_connection_id, #reactive_field, #reactive_filter, #reactive_input, #reactive_listnav, #reactive_root, #reactive_select, #reactive_show, #reactive_show_targets, #reactive_text, #reply
Methods included from Streamable
#dom_id, #id, register, registered_classes, reset_all_view_contexts!, #to_stream_morph, #to_stream_remove, #to_stream_replace, #to_stream_token, #to_stream_update