Skip to content

Commit bad544e

Browse files
committed
feat: add behaviour contracts for :process and :reducer macros
Add formal behaviour definitions to provide compile-time callback verification and improve developer experience with better IDE support and documentation. Changes: - Add Phoenix.SessionProcess.ProcessBehaviour with callbacks: - init_state/1 (required) - Initialize session state - combined_reducers/0 (optional) - Define reducer modules - Add Phoenix.SessionProcess.ReducerBehaviour with callbacks: - init_state/0 (required) - Initialize reducer state slice - handle_action/2 (required) - Handle synchronous actions - handle_async/3 (optional) - Handle asynchronous actions - handle_unmatched_action/2 (optional) - Handle unmatched sync actions - handle_unmatched_async/3 (optional) - Handle unmatched async actions - Update :process macro to use @behaviour ProcessBehaviour - Update :reducer macro to use @behaviour ReducerBehaviour - Add comprehensive behaviour compliance tests (8 new tests) - Update CLAUDE.md with @impl annotations in examples - Update mix.exs to include Behaviours documentation group Benefits: - Compile-time warnings for missing @impl annotations - Better IDE autocomplete and documentation - Clear contract definitions for user modules - Improved developer experience and discoverability
1 parent 1d261f5 commit bad544e

File tree

6 files changed

+734
-8
lines changed

6 files changed

+734
-8
lines changed

CLAUDE.md

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,10 @@ The library is organized into several logical groups:
6666
- `Phoenix.SessionProcess.Cleanup` - TTL-based cleanup
6767
- `Phoenix.SessionProcess.DefaultSessionProcess` - Default session implementation
6868

69+
**Behaviours** (define contracts for user modules):
70+
- `Phoenix.SessionProcess.ProcessBehaviour` - Behaviour for session process modules
71+
- `Phoenix.SessionProcess.ReducerBehaviour` - Behaviour for reducer modules
72+
6973
**State Management**:
7074
- `Phoenix.SessionProcess.Action` - Internal action structure for dispatching
7175
- `Phoenix.SessionProcess.ReducerCompiler` - Compile-time reducer validation and code generation
@@ -111,17 +115,22 @@ The library is organized into several logical groups:
111115
use Phoenix.SessionProcess, :process
112116

113117
# Define initial state with init_state/1
118+
@impl true
114119
def init_state(_args) do
115120
%{count: 0, user: nil}
116121
end
117122

118123
# Optional: Define combined reducers
124+
@impl true
119125
def combined_reducers do
120126
[MyApp.CounterReducer, MyApp.UserReducer]
121127
end
122128
end
123129
```
124130

131+
**Note**: The `:process` macro automatically adds `@behaviour Phoenix.SessionProcess.ProcessBehaviour`,
132+
enabling compile-time warnings when callbacks are missing `@impl` annotations.
133+
125134
**Reducer Macro Usage** (v1.0.0+):
126135
```elixir
127136
defmodule MyApp.CounterReducer do
@@ -133,11 +142,13 @@ The library is organized into several logical groups:
133142
# OPTIONAL: Action prefix must be binary or nil
134143
@action_prefix "counter"
135144

145+
@impl true
136146
def init_state do
137147
%{count: 0}
138148
end
139149

140150
# Actions are Action structs with binary types
151+
@impl true
141152
def handle_action(action, state) do
142153
alias Phoenix.SessionProcess.Action
143154

@@ -149,11 +160,13 @@ The library is organized into several logical groups:
149160
%{state | count: value}
150161

151162
_ ->
152-
state
163+
# Delegate to handle_unmatched_action for logging/debugging
164+
handle_unmatched_action(action, state)
153165
end
154166
end
155167

156168
# Optional: Handle async actions, must return cancellation callback
169+
@impl true
157170
def handle_async(action, dispatch, state) do
158171
alias Phoenix.SessionProcess.Action
159172

@@ -171,12 +184,15 @@ The library is organized into several logical groups:
171184
end
172185

173186
_ ->
174-
fn -> nil end
187+
handle_unmatched_async(action, dispatch, state)
175188
end
176189
end
177190
end
178191
```
179192

193+
**Note**: The `:reducer` macro automatically adds `@behaviour Phoenix.SessionProcess.ReducerBehaviour`,
194+
enabling compile-time warnings when callbacks are missing `@impl` annotations.
195+
180196
2. **Phoenix.SessionProcess.Action** (lib/phoenix/session_process/action.ex:1)
181197
- Internal action structure for fast pattern matching
182198
- **IMPORTANT**: Action types MUST be binary strings, not atoms
@@ -193,34 +209,50 @@ The library is organized into several logical groups:
193209
dispatch(session_id, "fetch", nil, async: true) # meta: %{async: true}
194210
```
195211

196-
3. **Phoenix.SessionProcess.ReducerCompiler** (lib/phoenix/session_process/reducer_compiler.ex:1)
212+
3. **Phoenix.SessionProcess.ProcessBehaviour** (lib/phoenix/session_process/process_behaviour.ex:1)
213+
- Behaviour defining the contract for session process modules
214+
- Automatically included when using `use Phoenix.SessionProcess, :process`
215+
- Defines required callback: `init_state/1`
216+
- Defines optional callback: `combined_reducers/0`
217+
- Enables compile-time warnings for missing `@impl` annotations
218+
- See module documentation for detailed callback specifications
219+
220+
4. **Phoenix.SessionProcess.ReducerBehaviour** (lib/phoenix/session_process/reducer_behaviour.ex:1)
221+
- Behaviour defining the contract for reducer modules
222+
- Automatically included when using `use Phoenix.SessionProcess, :reducer`
223+
- Defines required callbacks: `init_state/0`, `handle_action/2`
224+
- Defines optional callbacks: `handle_async/3`, `handle_unmatched_action/2`, `handle_unmatched_async/3`
225+
- Enables compile-time warnings for missing `@impl` annotations
226+
- See module documentation for detailed callback specifications
227+
228+
5. **Phoenix.SessionProcess.ReducerCompiler** (lib/phoenix/session_process/reducer_compiler.ex:1)
197229
- Compile-time validation and code generation for reducers
198230
- Validates `@name` is an atom
199231
- Validates `@action_prefix` is binary, nil, or ""
200232
- Generates `get_name/0`, `get_action_prefix/0`, and default callbacks
201233

202-
4. **Phoenix.SessionProcess.Supervisor** (lib/phoenix/session_process/superviser.ex:1)
234+
6. **Phoenix.SessionProcess.Supervisor** (lib/phoenix/session_process/superviser.ex:1)
203235
- Top-level supervisor that manages the Registry, ProcessSupervisor, and Cleanup
204236
- Must be added to the application's supervision tree
205237
- Supervises: Registry, ProcessSupervisor, and Cleanup GenServer
206238

207-
5. **Phoenix.SessionProcess.ProcessSupervisor** (lib/phoenix/session_process/process_superviser.ex:1)
239+
7. **Phoenix.SessionProcess.ProcessSupervisor** (lib/phoenix/session_process/process_superviser.ex:1)
208240
- DynamicSupervisor that manages individual session processes
209241
- Handles starting, terminating, and communicating with session processes
210242
- Performs session validation and limit checks (max sessions, rate limiting)
211243
- Emits telemetry events for all operations
212244

213-
6. **Phoenix.SessionProcess.SessionId** (lib/phoenix/session_process/session_id.ex)
245+
8. **Phoenix.SessionProcess.SessionId** (lib/phoenix/session_process/session_id.ex)
214246
- Plug that generates unique session IDs
215247
- Must be placed after `:fetch_session` plug in router pipeline
216248
- Assigns session_id to conn.assigns for use in controllers/LiveViews
217249

218-
7. **Phoenix.SessionProcess.Cleanup** (lib/phoenix/session_process/cleanup.ex:1)
250+
9. **Phoenix.SessionProcess.Cleanup** (lib/phoenix/session_process/cleanup.ex:1)
219251
- GenServer for automatic TTL-based session cleanup
220252
- Schedules session expiration on creation
221253
- Runs cleanup tasks periodically
222254

223-
8. **Phoenix.SessionProcess.LiveView** (lib/phoenix/session_process/live_view.ex:1)
255+
10. **Phoenix.SessionProcess.LiveView** (lib/phoenix/session_process/live_view.ex:1)
224256
- LiveView integration helpers for Redux Store API
225257
- `mount_store/4` - Mount with direct SessionProcess subscriptions
226258
- `unmount_store/1` - Unmount (optional, automatic cleanup via monitoring)

lib/phoenix/session_process.ex

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,7 @@ defmodule Phoenix.SessionProcess do
965965
"""
966966
defmacro __using__(:reducer) do
967967
quote do
968+
@behaviour Phoenix.SessionProcess.ReducerBehaviour
968969
@before_compile Phoenix.SessionProcess.ReducerCompiler
969970

970971
# Reducer identity
@@ -1246,6 +1247,7 @@ defmodule Phoenix.SessionProcess do
12461247
# credo:disable-for-next-line Credo.Check.Refactor.LongQuoteBlocks
12471248
defmacro __using__(:process) do
12481249
quote do
1250+
@behaviour Phoenix.SessionProcess.ProcessBehaviour
12491251
use GenServer
12501252
alias Phoenix.SessionProcess.Action
12511253

0 commit comments

Comments
 (0)