Current section

Files

Jump to
phoenix_session_process lib phoenix session_process state.ex
Raw

lib/phoenix/session_process/state.ex

defmodule Phoenix.SessionProcess.State do
@moduledoc """
An agent to store session state with Redux-style state management support.
This module provides both the traditional Agent-based state storage and
Redux-style state management with actions and reducers.
"""
use Agent
@type state :: any()
@type action :: any()
@type reducer :: (state(), action() -> state())
@doc """
Starts the state agent with initial state.
"""
@spec start_link(any()) :: {:ok, pid()} | {:error, any()}
def start_link(initial_state \\ %{}) do
Agent.start_link(fn -> initial_state end)
end
@doc """
Gets a value from the state by key.
"""
@spec get(pid(), any()) :: any()
def get(pid, key) do
Agent.get(pid, &Map.get(&1, key))
end
@doc """
Puts a value into the state by key.
"""
@spec put(pid(), any(), any()) :: :ok
def put(pid, key, value) do
Agent.update(pid, &Map.put(&1, key, value))
end
@doc """
Gets the entire state.
"""
@spec get_state(pid()) :: state()
def get_state(pid) do
Agent.get(pid, & &1)
end
@doc """
Updates the entire state.
"""
@spec update_state(pid(), (state() -> state())) :: :ok
def update_state(pid, update_fn) do
Agent.update(pid, update_fn)
end
@doc """
Redux-style state dispatch using a reducer function or module.
## Examples
iex> {:ok, pid} = State.start_link(%{count: 0})
iex> State.dispatch(pid, {:increment, 1}, fn state, {:increment, val} -> %{state | count: state.count + val} end)
iex> State.get_state(pid)
%{count: 1}
iex> {:ok, pid} = State.start_link(%{total: 0})
iex> State.dispatch(pid, {:add, 5}, StateTest.TestStateReducer)
iex> State.get_state(pid)
%{total: 5}
"""
@spec dispatch(pid(), action(), reducer() | module()) :: :ok
def dispatch(pid, action, reducer) when is_function(reducer, 2) do
Agent.update(pid, fn state -> reducer.(state, action) end)
end
def dispatch(pid, action, reducer_module) do
Agent.update(pid, fn state -> reducer_module.reduce(state, action) end)
end
@doc """
Resets the state to the initial value.
"""
@spec reset(pid(), state()) :: :ok
def reset(pid, initial_state \\ %{}) do
Agent.update(pid, fn _ -> initial_state end)
end
end