View Source Synq.Channel behaviour (synq v0.2.0)
To start a Synq, you'll want to form a channel that use
s this module.
use Synq.Channel, web_module: MyAppWeb, max_version: 100
State Management:
- Specify which assigns to track (sync) using the :track option
- All other assigns remain in the ethereal plane (server-only)
- Changes to tracked assigns are automatically synchronized using JSON patches
Example:
use Synq.Channel,
web_module: MyAppWeb,
track: [:spirits, :energy, :mana]
track
optional, defaults to []. The keys in assigns to sync with the client.message_builder
optional, defaults to{Synq.MessageBuilder, ignore_keys: [:__meta__]}
. If set, should be a tuple whose first element is a module that definesupdate_state_message/4
andnew_state_message/3
and and second element contains any options. Options are passed as final arg to both functions when invoked. SeeSynq.MessageBuilder
for detailsmax_version
optional, defaults to 1000. This is the maximum version number, after which it will reset to 0 and begin incrementing again. Version numbers are used to detect a patch message arriving out of order. If such a condition is detected byphx-live-state
a new copy of state is requested.
System Events
synq:error
- Sent when an error occurssynq:change
- Sent for full state changessynq:patch
- Sent for incremental state updates using JSON patches
Summary
Types
Possible responses to send back to handle_event/3
Payload types that can be sent between client and server
Return value for event reply callbacks. Can be
Represents the tracked state that will be synced with the client
Version number for state updates, used to detect out-of-order messages
Callbacks
Called from join to authorize the connection. Return {:ok, socket}
to authorize or
{:error, reason}
to deny. Default implementation returns {:ok, socket}
Invoked when the channel process is about to exit.
Receives an event from the client.
Handle regular Elixir process messages. Will send events and sync state based on the returned socket.
Returns the initial application state. Called just after connection
Types
@type event_response() :: {:reply, response :: reply(), socket :: Phoenix.Socket.t()} | {:ok, payload :: payload(), socket :: Phoenix.Socket.t()} | {:error, error :: Synq.Error.t(), socket :: Phoenix.Socket.t()} | {:noreply, socket :: Phoenix.Socket.t()}
Possible responses to send back to handle_event/3
Payload types that can be sent between client and server
Return value for event reply callbacks. Can be:
- status :: atom() # e.g., :ok, :error
- {status :: atom(), response :: payload()} # e.g., {:ok, %{data: ...}}
@type track_map() :: map()
Represents the tracked state that will be synced with the client
@type version() :: non_neg_integer()
Version number for state updates, used to detect out-of-order messages
Callbacks
@callback authorize(topic :: binary(), payload :: term(), socket :: Phoenix.Socket.t()) :: {:ok, socket :: Phoenix.Socket.t()} | {:error, Synq.Error.t()}
Called from join to authorize the connection. Return {:ok, socket}
to authorize or
{:error, reason}
to deny. Default implementation returns {:ok, socket}
@callback before_close( reason :: :normal | :shutdown | {:shutdown, :left | :closed | term()}, Phoenix.Socket.t() ) :: term()
Invoked when the channel process is about to exit.
@callback handle_event(event :: binary(), payload :: term(), socket :: Phoenix.Socket.t()) :: event_response()
Receives an event from the client.
If you choose not to reply to the message, you can return back {:noreply, socket} and an :ok response will be sent back to confirm the event was received.
If you choose to respond to the event, you can respond with {:reply, reply, socket}. The reply needs to be in the form of:
atom()
- status{atom(), payload()}
- status with payload
You can also use one of these return types for convenience:
- -> {:reply, {:ok, payload}, socket}
- -> {:reply, {:error, error}, socket}
Any changes to the track will automatically materialize on the client after the response is sent back.
Examples
No reply:
def handle_event("spirit_movement", %{"direction" => direction}, socket) do
socket =
socket
|> update_spirit_position(direction)
|> assign(:spirit_position, new_position)
{:noreply, socket}
end
Reply back with result:
def handle_event("summon_spirit", %{"type" => type}, socket) do
case SpirtRegistry.summon(type) do
{:ok, spirit} ->
socket = assign(socket, :spirits, socket.assigns.spirits ++ [spirit])
{:ok, %{spirit: spirit}, socket}
{:error, error} ->
{:error, error}
end
end
@callback handle_message(msg :: term(), socket :: Phoenix.Socket.t()) :: {:noreply, Phoenix.Socket.t()}
Handle regular Elixir process messages. Will send events and sync state based on the returned socket.
@callback init(topic :: binary(), params :: map(), socket :: Phoenix.Socket.t()) :: {:ok, socket :: Phoenix.Socket.t()} | {:error, error :: Synq.Error.t()}
Returns the initial application state. Called just after connection