Packages

A protocol-based PubSub abstraction for Phoenix LiveView

Current section

Files

Jump to
amplified_pubsub lib amplified pubsub list.ex
Raw

lib/amplified/pubsub/list.ex

defimpl Amplified.PubSub.Protocol, for: List do
@moduledoc ~S'''
Protocol implementation for lists.
Maps PubSub operations across each element of the list. This lets you
subscribe to or broadcast for a collection of structs in a single call.
## Broadcasting
For a single-element list, `broadcast/2` delegates directly to the
element's implementation.
For multi-element lists, items are grouped by channel and a single
`[{item, event}, ...]` message is sent per channel. This is more
efficient than broadcasting individually and lets subscribers receive
batch updates. Items wrapped in `{:ok, item}` are unwrapped; `{:error, _}`
items are silently skipped.
## Subscribing
`subscribe/1` subscribes to each element's channel individually.
## Channel
`channel/1` returns a list of channel names, one per element.
## Message handling
`handle_info/2` expects a list of `{struct, message}` tuples (as produced
by the multi-element broadcast). It reduces over the list, calling each
struct's `handle_info/3` and threading the socket through.
## Examples
posts = [%Post{id: "1"}, %Post{id: "2"}]
Amplified.PubSub.subscribe(posts)
# subscribes to "post:1" and "post:2"
Amplified.PubSub.channel(posts)
#=> ["post:1", "post:2"]
Amplified.PubSub.broadcast(posts, :archived)
# groups by channel, sends [{post, :archived}] per channel
'''
use Amplified.PubSub, impl: true
def broadcast([item], message), do: [PubSub.broadcast(item, message)]
def broadcast(items, message) do
items
|> Stream.flat_map(&extract_tuple/1)
|> Enum.group_by(&PubSub.channel/1)
|> Enum.each(fn {channel, items} ->
items |> Enum.map(&{&1, message}) |> then(&PubSub.broadcast(channel, &1))
end)
items
end
defp extract_tuple({:ok, item}), do: [item]
defp extract_tuple({:error, _}), do: []
defp extract_tuple(item), do: [item]
def channel(list, ns \\ nil), do: Enum.map(list, &PubSub.channel(&1, ns))
def subscribe(list), do: Enum.map(list, &PubSub.subscribe/1)
def unsubscribe(list), do: Enum.map(list, &PubSub.subscribe/1)
def handle_info(list, socket) do
list
|> Enum.reduce(socket, fn {struct, message}, socket ->
struct |> PubSub.handle_info(message, socket) |> elem(1)
end)
|> then(&{:cont, &1})
end
end