Skip to content

Commit 177c996

Browse files
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 support
1 parent 09163f2 commit 177c996

File tree

5 files changed

+1277
-27
lines changed

5 files changed

+1277
-27
lines changed

0 commit comments

Comments
 (0)