Skuld.Coroutine.PageMachine (skuld_concurrency v0.40.0)

View Source

Synchronous page-machine for LiveView integration.

Wraps Skuld.Coroutine with a callback-based API. Callbacks are provided once at mount; subsequent resumes are one-liners. The fiber is stored in socket.assigns.pm automatically — callbacks never see it.

Example

alias Skuld.Coroutine.PageMachine

# mount — one-time setup
PageMachine.run(MyApp.CheckoutFlow.flow(cart), socket, :checkout,
  on_yield: fn step, socket -> {:noreply, assign(socket, step: step)} end,
  on_complete: fn {:ok, order}, socket -> {:noreply, assign(socket, order: order, step: :done)} end,
  on_error: fn reason, socket -> {:noreply, put_flash(socket, :error, inspect(reason))} end,
  on_cancel: fn reason, socket -> {:noreply, push_navigate(socket, to: ~p"/cart")} end
)

# handle_event — one-liner
def handle_event("submit", %{"value" => v}, socket),
  do: PageMachine.run(socket.assigns.checkout, {:ok, v}, socket)

For cross-process use, see Skuld.AsyncCoroutine.AsyncPageMachine.

Summary

Types

t()

A page machine carrying the current fiber and callback functions. Stored in socket.assigns[key] between calls.

Functions

Imports def_pipe_event/2 and def_pipe_event/4 into the caller so they can be used without module prefix.

Cancel a page machine. Dispatches through on_cancel if available.

Generate a handle_event/3 clause that pipes a Phoenix event into the PageMachine as a Yield resume value. Multiple def_pipe_event calls produce multiple handle_event/3 clauses — one per event name.

Generate a handle_event/3 clause with params pattern matching, a value-transformation block, and optional :before callback.

Start a page machine with callbacks. Runs the first step of the computation and dispatches the result through the appropriate callback.

Types

t()

@type t() :: %Skuld.Coroutine.PageMachine{
  assign_key: atom(),
  fiber: Skuld.Coroutine.ExternalSuspended.t(),
  on_cancel: (term(), map() -> term()) | nil,
  on_complete: (term(), map() -> term()) | nil,
  on_error: (term(), map() -> term()) | nil,
  on_yield: (term(), map() -> term())
}

A page machine carrying the current fiber and callback functions. Stored in socket.assigns[key] between calls.

Functions

__using__(opts)

(macro)

Imports def_pipe_event/2 and def_pipe_event/4 into the caller so they can be used without module prefix.

cancel(pm, socket, reason \\ :cancelled)

Cancel a page machine. Dispatches through on_cancel if available.

Returns {:noreply, socket}.

def_pipe_event(event, assign_key)

(macro)

Generate a handle_event/3 clause that pipes a Phoenix event into the PageMachine as a Yield resume value. Multiple def_pipe_event calls produce multiple handle_event/3 clauses — one per event name.

Options

  • :before — an optional (socket -> socket) callback called before the event is piped to the PageMachine. Useful for setting a loading spinner.

Without pattern matching

def_pipe_event "submit_payment", :runner
def_pipe_event "submit_payment", :runner, before: &start_spinner/1

Generates:

def handle_event("submit_payment", params, socket) do
  socket = start_spinner(socket)
  PageMachine.run(socket.assigns[:runner], {"submit_payment", params}, socket)
end

With pattern matching and transformation

def_pipe_event "submit_shipping", :runner, %{"address" => addr}, before: &start_spinner/1 do
  {:ok, %{address: addr}}
end

Generates:

def handle_event("submit_shipping", %{"address" => addr}, socket) do
  socket = start_spinner(socket)
  PageMachine.run(socket.assigns[:runner], {:ok, %{address: addr}}, socket)
end

def_pipe_event(event, assign_key, list)

(macro)

def_pipe_event(event, assign_key, pattern, list)

(macro)

Generate a handle_event/3 clause with params pattern matching, a value-transformation block, and optional :before callback.

run(pm, value, socket)

run(computation, socket, key, callbacks)

Start a page machine with callbacks. Runs the first step of the computation and dispatches the result through the appropriate callback.

The page machine struct is stored in socket.assigns[key].

Callbacks:

  • :on_yield (required) — (value, socket) -> {:noreply, socket}
  • :on_complete(result, socket) -> {:noreply, socket}
  • :on_error(error, socket) -> {:noreply, socket}
  • :on_cancel(reason, socket) -> {:noreply, socket}

Returns {:noreply, socket}.