Receiver v0.1.1 Receiver View Source
Conveniences for creating processes that hold state.
A simple wrapper around an Agent that reduces boilerplate code and makes it easy to store
state in a separate supervised process.
Use cases
Storing persistent process state outside of the worker process, or as a shared repository for multiple processes.
Creating a "stash" to persist process state across restarts. See example below.
Testing higher order functions. By passing a function call to a
Receiverprocess into a higher order function you can test if the function is executed as intended by checking the change in state.
Example
defmodule Counter do
@moduledoc false
use GenServer
use Receiver, as: :stash
def start_link(arg) do
GenServer.start_link(__MODULE__, arg, name: __MODULE__)
end
def increment(num) do
GenServer.cast(__MODULE__, {:increment, num})
end
def get do
GenServer.call(__MODULE__, :get)
end
# The stash is started with the initial state of the counter. If the stash is already
# started its state will not change. The state of the stash is returned as the
# initial counter state whenever the counter is started.
def init(arg) do
start_stash(arg)
{:ok, get_stash()}
end
def handle_cast({:increment, num}, state) do
{:noreply, state + num}
end
def handle_call(:get, _from, state) do
{:reply, state, state}
end
# The stash is updated to the current counter state before the counter exits.
# This state will be stored for use as the initial state of the counter when
# it restarts.
def terminate(_reason, state) do
update_stash(state)
end
end
The line use Receiver, as: :stash creates a module and named Agent process with the name Counter.Stash.
The stash is supervised in the Receiver application supervision tree, not in your own application's. It also
defines the following client functions in the Counter module:
start_stash/0- Defaults the inital state to an empty list.start_stash/1- Expects a value or anonymous function that will return the initial state.start_stash/3- Expects a module, function name, and list of args that will return the initial state when called.stop_stash/2- Optionalreasonandtimeoutargs. SeeAgent.stop/3for more information.get_stash/0- Returns the current state of the stash.update_stash/1- Updates the state of the stash. expects a value or an anonymous function that receives the current state as an argument and returns the updated state.
If no :as option were given in this example then the process would take the name Counter.Receiver, and the
functions would be named like start_receiver/0.
The Counter can now be supervised and its state will be isolated from failure and persisted across restarts.
# Start the counter under a supervisor
{:ok, _pid} = Supervisor.start_link([{Counter, 0}], strategy: :one_for_one)
# Get the state of the counter
Counter.get()
#=> 0
# Increment the counter
Counter.increment(2)
#=> :ok
# Get the updated state of the counter
Counter.get()
#=> 2
# The stash is still set to the initial value
Counter.get_stash()
#=> 0
# Stop the counter, initiating a restart
GenServer.stop(Counter)
#=> :ok
# Get the counter state, which was persisted across restarts
Counter.get()
#=> 2
# Get the state of the stash, which was updated when the counter exited
Counter.get_stash()
#=> 2