Caravela.Live.Domain (Caravela v0.5.2)

Copy Markdown View Source

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
end

After compilation the module exposes four lookup functions consumed by Caravela.Live.Template:

  • __caravela_live_state__/0 — default assigns map from state do ... end
  • __caravela_live_updater__/1 — returns the updater function for a name, or nil
  • __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

field(name, type, opts \\ [])

(macro)

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

on_event(event, fun)

(macro)

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} -> ... end

Generated __caravela_live_event__/3 dispatches on the event name and returns the updated socket.

on_info(pattern, fun)

(macro)

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) end

The first argument is a match pattern on the inbound message. It can be any Elixir pattern the compiler accepts (tuples, atoms, maps).

state(list)

(macro)

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

updater(name, fun)

(macro)

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