Receiver v0.1.0 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
Receiver
process 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
- Optionalreason
andtimeout
args. SeeAgent.stop/3
for 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