Phoenix.SessionProcess.DefaultSessionProcess (phoenix_session_process v1.0.1)

Copy Markdown View Source

Default session process implementation providing basic key-value storage.

This module serves as the default session process when no custom session module is specified. It provides a simple key-value store interface that can be used for basic session state management without requiring custom implementation.

Features

Simple Key-Value Storage

  • Store any Elixir term with a binary key
  • Retrieve values by key
  • Delete keys from the session state
  • Support for complex data structures

Process Communication

  • call/2 operations for synchronous requests
  • cast/2 operations for asynchronous updates
  • Echo and ping functionality for testing

Public API

Call Operations

  • :ping - Returns :pong (useful for health checks)
  • :get_state - Returns the application state (handled by macro)
  • {:sleep, duration} - Sleeps for specified milliseconds (testing)
  • Any other request - Echoes the request back

Cast Operations

  • {:put, key, value} - Stores a value with the given key
  • {:delete, key} - Removes the key from the session state
  • Any other cast - No-op (ignored)

Usage Examples

Basic Operations

# Store data in session
Phoenix.SessionProcess.cast(session_id, {:put, :user_id, 123})
Phoenix.SessionProcess.cast(session_id, {:put, :preferences, %{theme: :dark}})

# Retrieve session state
state = Phoenix.SessionProcess.get_state(session_id)
# => %{user_id: 123, preferences: %{theme: :dark}}

# Delete data from session
Phoenix.SessionProcess.cast(session_id, {:delete, :user_id})

Health Check

# Check if session process is responsive
:pong = Phoenix.SessionProcess.call(session_id, :ping)

Testing

# Simulate slow operations
:ok = Phoenix.SessionProcess.call(session_id, {:sleep, 1000})

Default Session Module

This module is used automatically when no custom session module is specified in the configuration:

# Uses DefaultSessionProcess
Phoenix.SessionProcess.start("session_123")

# Uses custom module
Phoenix.SessionProcess.start("session_123", MyApp.CustomSessionProcess)

Limitations

  • Simple key-value storage (no complex queries)
  • No data validation or type checking
  • No persistence across application restarts
  • No built-in security or access controls

Custom Implementation with Redux Store API (v1.0.0)

For more advanced use cases, create a custom session process using the Redux Store API with the :reducer macro:

defmodule MyApp.CartReducer do
  use Phoenix.SessionProcess, :reducer

  @name :cart
  @action_prefix "cart"

  def init_state do
    %{items: [], total: 0}
  end

  def handle_action(action, state) do
    alias Phoenix.SessionProcess.Action

    case action do
      %Action{type: "cart.add", payload: item} ->
        %{state | items: [item | state.items], total: state.total + item.price}

      %Action{type: "cart.clear"} ->
        %{state | items: [], total: 0}

      _ ->
        state
    end
  end
end

defmodule MyApp.SessionProcess do
  use Phoenix.SessionProcess, :process

  def init_state(_args) do
    %{}
  end

  def combined_reducers do
    [MyApp.CartReducer]
  end
end

Use the session process:

# Start session
Phoenix.SessionProcess.start(session_id, MyApp.SessionProcess)

# Dispatch actions (MUST use binary types)
:ok = Phoenix.SessionProcess.dispatch(session_id, "cart.add", %{id: 1, price: 10})
:ok = Phoenix.SessionProcess.dispatch(session_id, "cart.add", %{id: 2, price: 20})

# Get current state (namespaced by reducer)
state = Phoenix.SessionProcess.get_state(session_id)
# => %{cart: %{items: [...], total: 30}}

Configure it as the default:

config :phoenix_session_process,
  session_process: MyApp.SessionProcess

Summary

Functions

Returns a specification to start this module under a supervisor.

Define combined reducers for state slicing.

Initialize session process state with Redux infrastructure.

Initialize your application's state.

Functions

child_spec(init_arg)

Returns a specification to start this module under a supervisor.

See Supervisor.

combined_reducers()

Define combined reducers for state slicing.

Override this callback to return a list of reducer modules or tuples. Each reducer module manages its own portion of the state.

Examples

def combined_reducers() do
  [
    MyApp.Reducers.UserReducer,           # Uses module's @name and @action_prefix
    {:cart, MyApp.Reducers.CartReducer},  # Custom name, action_prefix = "cart"
    {:shipping, MyApp.Reducers.ShippingReducer, "ship"}  # Custom name and action_prefix
  ]
end

List Formats

The list can contain three formats:

  1. Module atom - Uses the reducer's @name and @action_prefix attributes:

    • UserReducer → name from @name, action_prefix from @action_prefix (defaults to stringified @name)
  2. {name, Module} tuple - Custom name, action_prefix defaults to stringified name:

    • {:cart, CartReducer} → name = :cart, action_prefix = "cart"
  3. {name, Module, action_prefix} tuple - Explicit name and action_prefix:

    • {:shipping, ShippingReducer, "ship"} → name = :shipping, action_prefix = "ship"

State Slicing

Each reducer receives only its slice of state based on the name:

  • UserReducer receives state.users (if @name is :users)
  • CartReducer receives state.cart (from {:cart, CartReducer})
  • ShippingReducer receives state.shipping (from {:shipping, ...})

Action Routing

Actions are routed to reducers based on their @action_prefix attribute:

  • "user.reload" → Routes to reducer with action_prefix "user"
  • "cart.add" → Routes to reducer with action_prefix "cart"
  • "ship.calculate" → Routes to reducer with action_prefix "ship"

You can also explicitly target reducers in dispatch:

dispatch(session_id, "reload", reducers: [:user, :cart])

get_session_id()

init(arg)

Initialize session process state with Redux infrastructure.

Override init_state/1 to provide your application's initial state.

init_state(arg1)

Initialize your application's state.

Override this callback to provide your initial state as a map.

Examples

def init_state(_arg) do
  %{count: 0, user: nil, items: []}
end

def init_state(user_id) do
  %{user_id: user_id, cart: [], preferences: %{}}
end

start_link(opts)