Skip to content

Instantly share code, notes, and snippets.

@methyl
Created October 9, 2020 18:19
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save methyl/fe9a1450306bd1dc5549bfd8b2e19187 to your computer and use it in GitHub Desktop.
Save methyl/fe9a1450306bd1dc5549bfd8b2e19187 to your computer and use it in GitHub Desktop.
Observable GenServer
defmodule ObservableGenServer do
defmacro __using__(opts) do
quote do
defmodule Server do
use GenServer
def start_link(n) do
GenServer.start_link(__MODULE__, n)
end
def init(arg) do
parent_module =
__MODULE__
|> to_string
|> String.split(".")
|> Enum.drop(-1)
|> Enum.join(".")
|> String.to_atom()
{:ok, initial_state} = parent_module.init(arg)
{:ok, %{state: initial_state, parent: parent_module}}
end
def handle_call(msg, from, state) do
case state.parent.handle_call(msg, from, state.state) do
{:reply, reply, new_state} ->
case state.parent.handle_changed(new_state, state.state) do
{:noreply, new_state} -> {:reply, reply, %{state | state: new_state}}
{:stop, reason, new_state} -> {:stop, reason, reply, %{state | state: new_state}}
end
other ->
raise "Bad return from handle_call"
end
end
def handle_cast(msg, state) do
state.parent.handle_cast(msg, state.state)
|> handle_result({:handle_cast, 2, nil}, state)
end
def handle_info(msg, state) do
state.parent.handle_info(msg, state.state)
|> handle_result({:handle_info, 2, nil}, state)
end
defp handle_result({:noreply, new_state}, {_from, _arity, ref}, state) do
{:noreply, new_state} = state.parent.handle_changed(new_state, state.state)
{:noreply, %{state | state: new_state}}
end
end
end
end
end
defmodule Adder do
use ObservableGenServer
def init(n) do
{:ok, n}
end
def handle_call({:add, n}, _from, state) do
{:reply, state, state + n}
end
def handle_cast({:add, n}, state) do
{:noreply, state + n}
end
def handle_info({:add}, state) do
{:noreply, state + 1}
end
def handle_changed(state, previous_state) do
IO.inspect(state)
IO.inspect(previous_state)
{:noreply, state}
end
end
# usage:
# {:ok, pid} = Adder.start_link(0)
# GenServer.call(pid, {:add, 10})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment