GenServer SSD
Mix.install([
{:kino, "~> 0.6.2"},
{:kino_vega_lite, "~> 0.1.1"}
])
Sequence Diagram Generator
defmodule GenServerSSD do
def draw_call(frame, msg, {origin, _}, dest, state),
do: update(frame, msg, origin, dest, state, :call)
def draw_cast(frame, msg, dest, state), do: update(frame, msg, dest, dest, state, :cast)
def draw_info(frame, msg, dest, state), do: update(frame, msg, dest, dest, state, :info)
defp update(frame, msg, origin, dest, state, type) do
message = handle_input(msg, origin, dest, type)
messages = Map.get(state, :ssd_messages, []) ++ [message]
Kino.Frame.clear(frame)
Kino.Frame.append(frame, Kino.Markdown.new(mermaid(messages)))
Map.put(state, :ssd_messages, messages)
end
defp handle_input(msg, origin, dest, type) do
msg = inspect(msg)
origin = sanitize_pid(origin)
dest = sanitize_pid(dest)
{msg, origin, dest, type}
end
defp sanitize_pid(pid) do
[_, pid] = Regex.run(~r/#PID<(.*)>/, inspect(pid))
pid
end
defp mermaid(messages) do
messages
|> Enum.map(fn
{msg, origin, dest, :call} -> "#{origin} ->> #{dest}: #{msg}"
{msg, origin, dest, :cast} -> "#{origin} -->> #{dest}: #{msg}"
{msg, origin, dest, :info} -> "#{origin} -->> #{dest}: #{msg}"
end)
|> Enum.join("\n")
|> then(
&"""
```mermaid
sequenceDiagram
#{&1}
```
"""
)
end
end
defmodule Test do
use GenServer
import GenServerSSD
def start_link(frame), do: GenServer.start_link(__MODULE__, %{frame: frame}, name: __MODULE__)
@impl true
def init(state), do: {:ok, state}
@impl true
def handle_call(msg, origin, %{frame: frame} = state) do
state = draw_call(frame, msg, origin, self(), state)
{:reply, :ok, state}
end
@impl true
def handle_cast(msg, %{frame: frame} = state) do
state = draw_cast(frame, msg, self(), state)
{:noreply, state}
end
@impl true
def handle_info(msg, %{frame: frame} = state) do
state = draw_info(frame, msg, self(), state)
{:noreply, state}
end
end
frame = Kino.Frame.new()
Test.start_link(frame)
Enum.each(1..5, fn _ ->
Task.start(fn ->
case Enum.random(0..2) do
0 -> GenServer.call(Test, :call)
1 -> GenServer.cast(Test, :cast)
2 -> send(Test, :info)
end
end)
end)
frame