DSL for declaring a server-side state machine that a LiveView can mount
via Caravela.Live.Template. Mirrors the Ballerina "domain logic"
layer: state fields, named updaters that transition the state, and
event handlers that run on messages pushed from the Svelte side.
defmodule MyApp.BookEditorDomain do
use Caravela.Live.Domain
state do
field :book, :map, default: %{}
field :saving, :boolean, default: false
field :flash_message, :string, default: nil
end
updater :mark_saving, fn assigns -> %{assigns | saving: true} end
updater :mark_saved, fn assigns -> %{assigns | saving: false, flash_message: "Saved!"} end
updater :set_book, fn assigns, book -> %{assigns | book: book} end
# Inside on_event we have the full `apply_updater/2,3` sugar —
# Caravela.Live.Domain's `use` block sets `@caravela_live_domain
# __MODULE__`, so the macro resolves updater names against this
# module without an explicit third argument.
on_event "save", fn socket ->
apply_updater(socket, :mark_saving)
end
on_event "set_book", fn socket, %{"book" => book} ->
apply_updater(socket, :set_book, book)
end
on_info {:saved, book}, fn socket ->
socket
|> apply_updater(:set_book, book)
|> apply_updater(:mark_saved)
end
endAfter compilation the module exposes four lookup functions consumed
by Caravela.Live.Template:
__caravela_live_state__/0— default assigns map fromstate do ... end__caravela_live_updater__/1— returns the updater function for a name, ornil__caravela_live_event__/3— dispatches an event name to its handler, returning a socket__caravela_live_info__/2— dispatches an async message (handle_info) to its handler
Updater functions are stored as literal fns, so they can be composed
with Caravela.Live.Updater.compose/2 or piped through the ~>
operator as needed.
Summary
Functions
Declare a field on the domain state. type is an informational tag
only (for future typed-prop codegen). opts may include
default: — the value used when the domain mounts.
Declare an event handler matching a LiveSvelte/LiveView event name.
Declare an async-message handler matching a value sent to the
LiveView process (via send/2, Phoenix.PubSub.broadcast, etc.).
Declare the default state fields — the assigns map a mounted LiveView
starts with. Each field/2,3 inside state do ... end contributes
one key/default pair.
Declare a named updater. The function must take the assigns map and return a new assigns map, optionally with an extra event-payload argument.
Functions
Declare a field on the domain state. type is an informational tag
only (for future typed-prop codegen). opts may include
default: — the value used when the domain mounts.
field :saving, :boolean, default: false
Declare an event handler matching a LiveSvelte/LiveView event name.
The handler must accept a socket (and optionally a params map):
on_event "save", fn socket -> ... end
on_event "validate", fn socket, %{"field" => f, "value" => v} -> ... endGenerated __caravela_live_event__/3 dispatches on the event name and
returns the updated socket.
Declare an async-message handler matching a value sent to the
LiveView process (via send/2, Phoenix.PubSub.broadcast, etc.).
The handler accepts the socket and must return a socket:
on_info {:saved, book}, fn socket -> apply_updater(socket, :mark_saved) endThe first argument is a match pattern on the inbound message. It can be any Elixir pattern the compiler accepts (tuples, atoms, maps).
Declare the default state fields — the assigns map a mounted LiveView
starts with. Each field/2,3 inside state do ... end contributes
one key/default pair.
state do
field :books, :list, default: []
field :loading, :boolean, default: false
end
Declare a named updater. The function must take the assigns map and return a new assigns map, optionally with an extra event-payload argument.
updater :mark_saving, fn assigns -> %{assigns | saving: true} end
updater :set_book, fn assigns, book -> %{assigns | book: book} end