Lavash.Lifecycle.MessagesMacro (Lavash v0.4.0-rc.1)

Copy Markdown View Source

Capability: react to a server-side message arriving at this LiveView. Used for PubSub broadcasts, self-scheduled timers (Process.send_after/3), monitor/DOWN messages, and the like.

Structurally parallel to actions do action :foo do ... end end: a messages do block holds N message pattern do ... end clauses. Each clause body is a sequence of ops drawn from the same vocabulary as actions — run, effect, set.

Shape

messages do
  message :tick do
    set :ticks, rx(@ticks + 1)
  end

  message {:custom_msg, msg}, [:msg] do
    run fn socket ->
      assign(socket, :last_msg, msg)
    end
  end

  message :pinged do
    run fn socket ->
      socket
      |> assign(:broadcasts, socket.assigns.broadcasts + 1)
      |> push_event("ack", %{})
    end

    effect fn socket ->
      require Logger
      Logger.info("pinged: #{socket.assigns.broadcasts}")
    end
  end
end

run fn socket -> ... end vs action's run fn assigns -> ... end

Messages frequently want the full Phoenix.LiveView API: push_event, push_patch, redirect, start_async, assign_async, etc. All operate on socket. So run inside a message body takes the socket and should return the socket.

Action run fn bodies take assigns because actions are state-mutation-focused — the assigns-shaped contract works well there. Messages and actions diverge intentionally on this point.

set :field, rx(...)

Same shape and semantics as inside an action. The reactive expression evaluates against the current state at fire time. Useful for the common "this message just bumps state" case without writing a full run fn.

Bind list

The optional second arg to message names the variables in the pattern that the body should see. So message {:custom, x}, [:x] makes x available in the body's run fn body. They're already bound by the pattern itself; the bind list is currently a docs hint and reserved for future validator checks.

Layer

Layer 1: the body is a sequence of declarative ops. set ..., rx(...) participates in layer 2 (reactive graph); run/effect are plain Elixir. Reactive recompute runs after the body so downstream calculate :foo, rx(...) updates correctly.

Summary

Functions

A single message pattern do <ops> end clause inside a messages block. bind is a list of atom names appearing in the pattern that should be in the body's scope alongside the socket.

Top-level messages do ... end block. The body should contain one or more message/2 or message/3 calls.

Functions

message(pattern, bind \\ [], list)

(macro)

A single message pattern do <ops> end clause inside a messages block. bind is a list of atom names appearing in the pattern that should be in the body's scope alongside the socket.

The body is a sequence of op calls (run/effect/set). Each is captured at compile time and stored on the clause; the runtime executes them in order, passing the socket through.

messages(list)

(macro)

Top-level messages do ... end block. The body should contain one or more message/2 or message/3 calls.