Packages
ax_stateserver
0.1.0
Defines a GenServer with macros to prevent the GenServer's boilerplate.
Current section
Files
Jump to
Current section
Files
lib/state_server.ex
defmodule StateServer do
@moduledoc """
The StateServer is a module you can use in your modules to implement a simple GenServer
without the need of writting all that GenServer's boilerplate.any()
### Example
defmodule MyModule do
# make this module a GenServer and import some macros to define calls and casts.
use StateServer
# define the init block of the GenServer.
init params do
# this will become the state of our GenServer
fn(params) ->
# cast or prepare the initial state, based on the given params
params
end
end
# define the function `set_value(pid, params)` and the corresponding
# `handle_cast` function.
defcast set_value do
fn(state, [key, value] = _params) ->
# modify the state, based on params and return the new state
Map.put(state, key, value)
end
end
# define the function `get_value(pid, key, default)` and the corresponding
# `handle_call` function
defcall get_value do
fn(state, [key, default]) ->
Map.get(state,key) || default
end
end
end
{:ok, person_pid} = MyModule.start_link(name: "Alex", born: 1997)
MyModule.get_value(person_pid, :name)
# "Alex"
MyModule.set_value(person_pid, [:name, "Alexis"])
MyModule.state(person_pid)
# [ name: "Alexis", born: 1997 ]
By `use StateServer` you will get the default `GenServer`s `start_link/1` function.
We also add a function `state/1` and a corresponding `handle_call` to return the
current state.
Use the macro `definit` to define the code for the `GenServer`'s `init/1` and the
macros `defcall` and `defcast` to define pairs of a function and the corresponding
handle_call or handle_cast.
`handle_call` will never change the state where `handle_cast` does change the state
only. (querry, command separation.)
"""
defmacro __using__(_opts \\ []) do
quote do
use GenServer
import unquote(__MODULE__)
def start_link(opts \\ []) do
GenServer.start_link(__MODULE__, opts)
end
def state(pid) do
GenServer.call(pid, :state)
end
def handle_call(:state, _, state), do: {:reply, state, state}
end
end
defmacro definit(_args \\ [], do: blk) do
quote do
def init(params) do
{:ok, unquote(blk).(params)}
end
end
end
defmacro defcall(fun_name, do: blk) do
quote do
def unquote(fun_name)(pid, params \\ nil) do
GenServer.call(pid, {unquote(fun_name), params})
end
def handle_call({unquote(fun_name), params}, _, state) do
{:reply, unquote(blk).(state, params), state}
end
end
end
defmacro defcast(fun_name, do: blk) do
quote do
def unquote(fun_name)(pid, params \\ nil) do
GenServer.cast(pid, {unquote(fun_name), params})
end
def handle_cast({unquote(fun_name), params}, state) do
state = unquote(blk).(state, params)
{:noreply, state}
end
end
end
end