Commit 177c996
committed
feat: Add reducer modules with throttle, debounce, and combined reducers (v0.7.0)
Major architectural enhancement introducing reducer modules, combined reducers,
and rate limiting for Redux Store API.
BREAKING CHANGES:
- None - fully backward compatible
Features:
- Reducer modules via :reducer macro with @Throttle and @debounce attributes
- Combined reducers for state slicing (combineReducers pattern)
- ActionRateLimiter with throttle (execute immediately, block) and debounce (delay, reset timer)
- Async actions with handle_async/3 and dispatch callback
- init_state/1 callback replaces user_init/1 (backward compatible delegation)
- ReducerCompiler for compile-time module attribute processing
Implementation:
- lib/phoenix/session_process/redux/action_rate_limiter.ex (247 lines)
- lib/phoenix/session_process/redux/reducer_compiler.ex (104 lines)
- Refactored dispatch logic to support combined reducers
- Added @on_definition hook for capturing module attributes
Tests:
- 22 unit tests for ActionRateLimiter (parse_duration, throttle, debounce, scheduling)
- 12 integration tests for reducer modules and combined_reducers
- Total: 229 tests passing (34 new tests)
Rate Limiting:
- Duration format: "500ms", "1s", "5m", "1h"
- Throttle: First call executes, subsequent calls blocked for duration
- Debounce: Delays execution, resets timer on new call
- Per-reducer, per-action configuration
Example:
```elixir
defmodule MyApp.SessionProcess do
use Phoenix.SessionProcess, :process
def init_state(_), do: %{count: 0, users: %{}, cart: %{}}
def combined_reducers do
%{users: UserReducer, cart: CartReducer}
end
end
defmodule UserReducer do
use Phoenix.SessionProcess, :reducer
@Throttle {"fetch-users", "3000ms"}
def handle_action(%{type: "fetch-users"}, state), do: %{state | loading: true}
@debounce {"search-users", "500ms"}
def handle_action(%{type: "search-users", payload: q}, state), do: %{state | query: q}
def handle_async(%{type: "load", payload: p}, dispatch, state) do
Task.async(fn ->
data = async_get_data(p)
dispatch.(%{type: "load_success", payload: data})
end)
%{state | loading: true}
end
end
```
Backward Compatibility:
- init_state/1 delegates to user_init/1 by default
- Manually registered reducers work alongside combined reducers
- All existing tests passing
Code Quality:
- mix format applied
- mix credo --strict passing (minor macro suggestions only)
- Comprehensive test coverage
Refs: User request for better reducer API with throttle/debounce support1 parent 09163f2 commit 177c996
File tree
5 files changed
+1277
-27
lines changed- lib/phoenix
- session_process/redux
- test/phoenix/session_process
- redux
5 files changed
+1277
-27
lines changed
0 commit comments