Created
June 4, 2023 16:03
-
-
Save shamshirz/5fcf82942e98be38531064b9bccdd3e0 to your computer and use it in GitHub Desktop.
Liveview PubSub Example
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule App.Application do | |
… | |
@impl true | |
def start(_type, _args) do | |
children = [ | |
… | |
{App.ScanTracker, []} # <- Add this line | |
] | |
opts = [strategy: :one_for_one, name: App.Supervisor] | |
Supervisor.start_link(children, opts) | |
end | |
… | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule AppWeb.Router do | |
… | |
scope "/", AppWeb do | |
pipe_through :browser | |
live "/scan", ScanStatus # <- Add this line (and scope if you don't have one already) | |
end | |
… | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule AppWeb.ScanStatus do | |
@moduledoc """ | |
View only page that displays the live status of scan events. | |
All events are automatically pushed to any client on this page | |
No `handle_event/3` because the client pushes no events to the server | |
""" | |
use AppWeb, :live_view | |
def mount(_params, _session, socket) do | |
App.ScanTracker.subscribe() | |
{:ok, assign(socket, :scan_data, App.ScanTracker.get())} | |
end | |
# This handles messages from other Elixir Processes | |
def handle_info({:update, scan_data}, socket) do | |
{:noreply, assign(socket, :scan_data, scan_data)} | |
end | |
attr :scan_data, :map, required: true | |
def render(assigns) do | |
~H""" | |
<h1>Scan Status</h1> | |
<ul> | |
<%= for {scan_id, [event | _older_events]} <- @scan_data do %> | |
<li> | |
<%= scan_id %> : <%= event %> | |
</li> | |
<% end %> | |
</ul> | |
""" | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule App.ScanTracker do | |
@moduledoc """ | |
An Agent that stores the current state of any scan events sent to it. Ephemeral, not in DB | |
Additionally, it offers a PubSub channel to subscribed to state changes | |
Any process on the server can "push" a scan event via this module. | |
The Agent process is updated with the new state, and that is broadcast to any liveview's subscribed to the channel. | |
## Thoughts - ETS & Genserver vs. Agent? Why and When? | |
""" | |
use Agent | |
alias Phoenix.PubSub | |
@channel "scan_tracker" | |
@doc "Starts the Tracker Process" | |
def start_link(_opts), do: Agent.start_link(fn -> %{} end, name: __MODULE__) | |
@doc "Gets all scan data" | |
def get, do: Agent.get(__MODULE__, & &1) | |
@doc "Put the `event` for the given `scan_id` at the head of it's event list. Broadcast that an update was made." | |
@spec put_scan_event(scan_id :: String.t, event :: any()) :: :ok | |
def put_scan_event(scan_id, event) do | |
Agent.update(__MODULE__, &Map.update(&1, scan_id, [event], fn existing_events -> [event | existing_events] end)) | |
PubSub.broadcast(Blog.PubSub, @channel, {:update, get()}) | |
end | |
def subscribe, do: PubSub.subscribe(Blog.PubSub, @channel) | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Diagram of the relationship between events and modules.
Any process -> Tracker -> Liveviews
Demo