Current section
Files
Jump to
Current section
Files
lib/amplified/pubsub/protocol.ex
defprotocol Amplified.PubSub.Protocol do
@moduledoc ~S'''
The protocol that powers `Amplified.PubSub`.
Each function in this protocol is dispatched based on the type of its
first argument. Built-in implementations exist for `BitString`, `Atom`,
`Tuple`, `List`, `Stream`, and `Phoenix.LiveView.Socket`. Struct
implementations are generated automatically when you `use Amplified.PubSub`
in a module.
You generally don't need to call these functions directly — use the
delegating functions in `Amplified.PubSub` instead. This module is
relevant when you need to implement the protocol manually with `defimpl`.
## Implementing for a struct
The easiest way is `use Amplified.PubSub` in your schema module, which
generates a full implementation with sensible defaults. If you need more
control, write a `defimpl` block and pass `impl: true` to get the
defaults as a starting point:
defimpl Amplified.PubSub.Protocol, for: MyApp.Blog.Post do
use Amplified.PubSub, impl: true
# Override just the channel derivation
def channel(%{slug: slug}, _ns), do: "post:#{slug}"
end
## Return conventions
* `broadcast/2,3` — return the subject (struct implementations) or the
message (string implementation), enabling pipeline chaining.
* `subscribe/1`, `unsubscribe/1` — return the subject for structs, or
`:ok` for strings.
* `handle_info/2,3,4` — return `{:cont, socket}` when the message was
not handled, or `{:halt, socket}` when it was.
'''
alias Phoenix.LiveView.Socket
@doc """
Broadcasts a message to the channel for the given subject.
For struct implementations generated by `use Amplified.PubSub`, atom and
binary events are automatically wrapped as `{event, subject}` before
broadcasting. The subject is returned for pipeline chaining.
For the `BitString` implementation, the first argument is treated as a
literal channel name and the message is broadcast as-is via
`Phoenix.PubSub.broadcast/3`.
"""
@spec broadcast(
subject :: subject,
message :: term()
) :: subject
when subject: nil | binary() | struct() | {:ok, struct()} | {:error, term()}
def broadcast(subject, message)
@doc """
Broadcasts a message with additional attributes to the channel for the
given subject.
Like `broadcast/2`, but the broadcast payload includes the attributes.
For atom/binary events on structs, the payload becomes
`{event, subject, attrs}`.
"""
@spec broadcast(
subject :: subject,
message :: term(),
attrs :: map()
) :: subject
when subject: nil | binary() | struct() | {:ok, struct()} | {:error, term()}
def broadcast(subject, message, attrs)
@doc """
Returns the PubSub channel name for the given subject.
For structs, the default implementation derives the channel from the
module's last name segment (snake_cased) and the struct's `:id` field,
e.g. `"post:abc-123"`. The optional `namespace` is appended with a `:`
separator.
"""
@spec channel(
subject :: struct(),
namespace :: String.t() | atom() | nil
) :: binary()
def channel(subject, namespace \\ nil)
@doc """
Subscribes the current process to the PubSub channel for the given subject.
The `BitString` implementation subscribes via the configured PubSub
server. Struct implementations derive the channel first, then delegate
to the string implementation.
"""
@spec subscribe(binary() | struct()) :: :ok | struct() | {:error, term()}
def subscribe(channel)
@doc """
Unsubscribes the current process from the PubSub channel for the given
subject.
"""
@spec unsubscribe(binary() | struct()) :: :ok | struct() | {:error, term()}
def unsubscribe(channel)
@doc """
Dispatches an incoming PubSub message through the protocol.
The `Tuple` implementation is the primary dispatcher — it pattern-matches
on `{action, subject}` and `{:flash, level, message}` tuples, delegating
to the subject's `handle_info/3` when a protocol implementation exists
for the subject.
Returns `{:cont, socket}` for unhandled messages, `{:halt, socket}` for
handled ones.
"""
@spec handle_info(
message :: term(),
socket :: Socket.t()
) :: {:cont | :halt, Socket.t()}
def handle_info(message, socket)
@doc """
Dispatches a message for a specific subject.
Called by the Tuple implementation after unpacking `{action, subject}`.
Override this in your `use Amplified.PubSub` block to handle events for
your struct type.
"""
@spec handle_info(
subject :: struct(),
message :: term(),
socket :: Socket.t()
) :: {:cont | :halt, Socket.t()}
def handle_info(subject, message, socket)
@doc """
Dispatches a message for a specific subject with additional attributes.
Called by the Tuple implementation after unpacking
`{action, subject, attrs}`. Override this in your `use Amplified.PubSub`
block to handle events that include a changeset or attributes map.
"""
@spec handle_info(
subject :: struct(),
message :: term(),
changeset :: map(),
socket :: Socket.t()
) :: {:cont | :halt, Socket.t()}
def handle_info(subject, message, changeset, socket)
end