Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
96 changes: 88 additions & 8 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ on:
branches: [ '*' ]

jobs:
test:
name: Build and test
compile:
name: Compile
runs-on: ubuntu-latest

steps:
Expand All @@ -31,19 +31,99 @@ jobs:
- name: Install dependencies
run: mix deps.get

- name: Compile
- name: Compile with warnings as errors
run: mix compile --warnings-as-errors

- name: Run Credo
format:
name: Format Check
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: '1.18'
otp-version: '28'

- name: Restore dependencies cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-mix-

- name: Install dependencies
run: mix deps.get

- name: Check code formatting
run: mix format --check-formatted

credo:
name: Credo
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: '1.18'
otp-version: '28'

- name: Restore dependencies cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-mix-

- name: Install dependencies
run: mix deps.get

- name: Run Credo strict
run: mix credo --strict

- name: Build Dialyzer PLT (cached)
id: plt
dialyzer:
name: Dialyzer
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Elixir
uses: erlef/setup-beam@v1
with:
elixir-version: '1.18'
otp-version: '28'

- name: Restore dependencies cache
uses: actions/cache@v4
with:
path: |
deps
_build
key: ${{ runner.os }}-mix-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-mix-

- name: Install dependencies
run: mix deps.get

- name: Restore Dialyzer PLT cache
uses: actions/cache@v4
with:
path: priv/plts
key: dialyzer-${{ hashFiles('mix.lock') }}
key: ${{ runner.os }}-dialyzer-${{ hashFiles('**/mix.lock') }}
restore-keys: ${{ runner.os }}-dialyzer-

- name: Run Dialyzer
run: mix dialyzer --halt-exit-status

23 changes: 16 additions & 7 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,22 @@ name: Test

on:
push:
branches: [ main ]
pull_request:
branches: [ main, develop ]
pull_request:
branches: [ main ]

jobs:
test:
name: Test
runs-on: ubuntu-latest

services:
mailhog:
image: mailhog/mailhog:latest
ports:
- 1025:1025
- 8025:8025

steps:
- name: Checkout code
uses: actions/checkout@v5
Expand All @@ -20,7 +28,11 @@ jobs:
elixir-version: '1.18'
otp-version: '28'

- name: Cache dependencies
- uses: oven-sh/setup-bun@v2
with:
bun-version: latest

- name: Restore dependencies cache
uses: actions/cache@v4
with:
path: |
Expand All @@ -31,10 +43,7 @@ jobs:
${{ runner.os }}-mix-

- name: Install dependencies
run: |
mix local.hex --force
mix local.rebar --force
mix deps.get
run: mix deps.get

- name: Run tests
run: mix test
94 changes: 76 additions & 18 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,35 @@ This is Phoenix.SessionProcess, an Elixir library that creates a process for eac

### Development Commands
- `mix deps.get` - Install dependencies
- `mix compile` - Compile the project
- `mix compile --warnings-as-errors` - Compile with strict warnings (CI requirement)
- `mix test` - Run all tests
- `mix test test/path/to/specific_test.exs` - Run a specific test file
- `mix compile` - Compile the project
- `mix docs` - Generate documentation
- `mix test test/phoenix/session_process/` - Run all tests in a directory
- `mix format` - Format code
- `mix format --check-formatted` - Check formatting without modifying files (CI requirement)
- `mix credo --strict` - Run static code analysis with strict mode (CI requirement)
- `mix dialyzer` - Run type checking (first run builds PLT cache)
- `mix dialyzer --halt-exit-status` - Run type checking and exit with error code on issues (CI requirement)
- `mix lint` - Run both Credo and Dialyzer (defined in mix.exs aliases)
- `mix docs` - Generate documentation
- `mix hex.publish` - Publish to Hex.pm (requires authentication)

### Code Quality Requirements
Before committing, ensure code passes all CI checks:
1. Compiles without warnings: `mix compile --warnings-as-errors`
2. Properly formatted: `mix format --check-formatted`
3. Passes Credo: `mix credo --strict`
4. Passes Dialyzer: `mix dialyzer --halt-exit-status`

### Testing
The test suite uses ExUnit. Tests are located in the `test/` directory. The test helper (test/test_helper.exs:3) automatically starts the supervisor.
The test suite uses ExUnit. Tests are located in the `test/` directory. The test helper (test/test_helper.exs:3) automatically starts the supervisor and configures the default TestProcess module.

### Development Environment
The project uses `devenv` for development environment setup with Nix. Key configuration:
- Uses Elixir/BEAM 27
- Runs `hello` script on shell entry for greeting
The project uses `devenv` for development environment setup with Nix:
- Elixir 1.18+ with OTP 28+ (minimum: Elixir 1.14, OTP 24)
- Includes git, figlet, and lolcat tools
- Run `devenv shell` to enter the development environment

### Benchmarking
Performance testing available via:
Expand All @@ -38,37 +52,76 @@ Expected performance:

## Architecture

### Module Organization

The library is organized into several logical groups:

**Core API** (primary interface for users):
- `Phoenix.SessionProcess` - Main public API
- `Phoenix.SessionProcess.SessionId` - Plug for session ID generation

**Internals** (supervision and lifecycle management):
- `Phoenix.SessionProcess.Supervisor` - Top-level supervisor (Note: filename is `superviser.ex`)
- `Phoenix.SessionProcess.ProcessSupervisor` - Dynamic supervisor for sessions (Note: filename is `process_superviser.ex`)
- `Phoenix.SessionProcess.Cleanup` - TTL-based cleanup
- `Phoenix.SessionProcess.DefaultSessionProcess` - Default session implementation

**State Management Utilities**:
- `Phoenix.SessionProcess.Redux` - Optional Redux-style state with actions/reducers, subscriptions, and selectors (advanced use cases)
- `Phoenix.SessionProcess.Redux.Selector` - Memoized selectors for efficient derived state
- `Phoenix.SessionProcess.Redux.Subscription` - Subscription management for reactive state changes
- `Phoenix.SessionProcess.Redux.LiveView` - LiveView integration helpers
- `Phoenix.SessionProcess.MigrationExamples` - Migration examples for Redux
- `Phoenix.SessionProcess.ReduxExamples` - Comprehensive Redux usage examples

**Configuration & Error Handling**:
- `Phoenix.SessionProcess.Config` - Configuration management
- `Phoenix.SessionProcess.Error` - Error types and messages

**Observability**:
- `Phoenix.SessionProcess.Telemetry` - Telemetry event emission
- `Phoenix.SessionProcess.TelemetryLogger` - Logging integration
- `Phoenix.SessionProcess.Helpers` - General utilities

### Core Components

1. **Phoenix.SessionProcess** (lib/phoenix/session_process.ex:1)
- Main module providing the public API
- Delegates to ProcessSupervisor for actual process management
- Provides two macros: `:process` (basic) and `:process_link` (with LiveView monitoring)
- Key functions: `start/1-3`, `call/2-3`, `cast/2`, `terminate/1`, `started?/1`, `list_session/0`

2. **Phoenix.SessionProcess.Supervisor** (lib/phoenix/session_process/superviser.ex:1)
- Top-level supervisor that manages the Registry, ProcessSupervisor, and Cleanup
- Must be added to the application's supervision tree
- Supervises: Registry, ProcessSupervisor, and Cleanup GenServer

3. **Phoenix.SessionProcess.ProcessSupervisor** (lib/phoenix/session_process/process_superviser.ex:1)
- DynamicSupervisor that manages individual session processes
- Handles starting, terminating, and communicating with session processes
- Performs session validation and limit checks
- Performs session validation and limit checks (max sessions, rate limiting)
- Emits telemetry events for all operations

4. **Phoenix.SessionProcess.SessionId** (lib/phoenix/session_process/session_id.ex)
- Plug that generates unique session IDs
- Must be placed after `:fetch_session` plug
- Must be placed after `:fetch_session` plug in router pipeline
- Assigns session_id to conn.assigns for use in controllers/LiveViews

5. **Phoenix.SessionProcess.Cleanup** (lib/phoenix/session_process/cleanup.ex:1)
- Automatic TTL-based session cleanup
- GenServer for automatic TTL-based session cleanup
- Schedules session expiration on creation
- Runs cleanup tasks periodically

6. **Phoenix.SessionProcess.Redux** (lib/phoenix/session_process/redux.ex:1)
- Redux-style state management with actions and reducers
- Optional Redux-style state management with actions, reducers, subscriptions, and selectors
- Provides time-travel debugging, middleware support, and action history

7. **Phoenix.SessionProcess.State** (lib/phoenix/session_process/state.ex:1)
- Agent-based state storage with Redux-style dispatch support
- Used for simpler state management scenarios
- **Redux.Selector**: Memoized selectors with reselect-style composition for efficient derived state
- **Redux.Subscription**: Subscribe to state changes with optional selectors (only notifies when selected values change)
- **Redux.LiveView**: Helper module for LiveView integration with automatic assign updates
- **Phoenix.PubSub integration**: Broadcast state changes across nodes for distributed applications
- **Comprehensive telemetry**: Monitor Redux operations (dispatch, subscribe, selector cache hits/misses, PubSub broadcasts)
- Best for complex applications requiring reactive UIs, predictable state updates, audit trails, or distributed state
- Note: Most applications don't need this - standard GenServer state is sufficient

### Process Management Flow

Expand Down Expand Up @@ -118,11 +171,16 @@ Configuration options:

## State Management Options

The library provides three state management approaches:
The library provides two state management approaches:

1. **Standard GenServer State** (Recommended) - Full control with standard GenServer callbacks
- Use `handle_call`, `handle_cast`, and `handle_info` to manage state
- Simple, idiomatic Elixir - this is what you should use for 95% of cases

1. **Basic GenServer** - Full control with standard GenServer callbacks
2. **Phoenix.SessionProcess.State** - Agent-based with simple get/put and Redux dispatch
3. **Phoenix.SessionProcess.Redux** - Full Redux pattern with actions, reducers, middleware, time-travel debugging
2. **Phoenix.SessionProcess.Redux** (Optional, Advanced) - Redux pattern for complex state machines
- Actions, reducers, middleware, time-travel debugging
- Only use if you need audit trails or complex state machine logic
- Adds complexity - most applications don't need this

## Telemetry and Error Handling

Expand Down
Loading