View Source Synq.Channel behaviour (synq v0.3.0)
To start a Synq, you'll want to form a channel that uses this module.
use Synq.Channel, web_module: MyAppWeb, max_version: 100State 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]trackoptional, defaults to []. The keys in assigns to sync with the client.message_builderoptional, defaults to{Synq.MessageBuilder, ignore_keys: [:__meta__]}. If set, should be a tuple whose first element is a module that definesupdate_state_message/4andnew_state_message/3and and second element contains any options. Options are passed as final arg to both functions when invoked. SeeSynq.MessageBuilderfor detailsmax_versionoptional, 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-statea 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}
endend
@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