From 9c0932ecdac25e73be750bef75c773a6b6e235a4 Mon Sep 17 00:00:00 2001 From: Michael Date: Sun, 22 Dec 2019 18:11:16 +0100 Subject: [PATCH] fix #125 --- lib/src/middleware.dart | 50 +++++++----- lib/src/reducer_builder.dart | 144 ++++++++++++++++++++++----------- test/unit/middleware_test.dart | 15 +++- test/unit/reducer_test.dart | 28 +++++++ test/unit/test_counter.dart | 30 +++++-- test/unit/test_counter.g.dart | 16 +++- 6 files changed, 203 insertions(+), 80 deletions(-) create mode 100644 test/unit/reducer_test.dart diff --git a/lib/src/middleware.dart b/lib/src/middleware.dart index 0ab2c53..86c0b0f 100644 --- a/lib/src/middleware.dart +++ b/lib/src/middleware.dart @@ -38,7 +38,14 @@ class MiddlewareBuilder< void add(ActionName aMgr, MiddlewareHandler handler) { - _map[aMgr.name] = (api, next, action) { + _add(aMgr.name, handler); + } + + void _add(String name, + MiddlewareHandler handler) { + final oldMiddlware = _map[name]; + _map[name] = (api, next, action) { + if (oldMiddlware != null) oldMiddlware(api, next, action); handler(api, next, action as Action); }; } @@ -46,7 +53,9 @@ class MiddlewareBuilder< /// [combine] combines this MiddlewareBuilder with another MiddlewareBuilder /// for the same type void combine(MiddlewareBuilder other) { - _map.addAll(other._map); + for (final entry in other._map.entries) { + _add(entry.key, entry.value); + } } @pragma('dart2js:noInline') @@ -57,7 +66,9 @@ class MiddlewareBuilder< NestedMiddlewareBuilder other) { - _map.addAll(other._map); + for (final entry in other._map.entries) { + _add(entry.key, entry.value); + } } /// [build] returns a [Middleware] function that handles all actions added with [add] @@ -93,12 +104,22 @@ class NestedMiddlewareBuilder< ActionName aMgr, MiddlewareHandler handler) { - _map[aMgr.name] = (api, next, action) { + _add(aMgr.name, handler); + } + + void _add( + String name, + MiddlewareHandler + handler) { + final oldMiddlware = _map[name]; + _map[name] = (api, next, action) { + if (oldMiddlware != null) oldMiddlware(api, next, action); handler( - MiddlewareApi._( - () => _stateMapper(api.state), () => _actionsMapper(api.actions)), - next, - action as Action); + MiddlewareApi._( + () => _stateMapper(api.state), () => _actionsMapper(api.actions)), + next, + action as Action, + ); }; } @@ -107,16 +128,9 @@ class NestedMiddlewareBuilder< /// this `NestedMiddlewareBuilder`. void combineMiddlewareBuilder( MiddlewareBuilder other) { - var adapted = other._map.map((name, handler) => MapEntry( - name, - (MiddlewareApi api, ActionHandler next, - Action action) => - handler( - MiddlewareApi._(() => _stateMapper(api.state), - () => _actionsMapper(api.actions)), - next, - action))); - _map.addAll(adapted); + for (final entry in other._map.entries) { + _add(entry.key, entry.value); + } } } diff --git a/lib/src/reducer_builder.dart b/lib/src/reducer_builder.dart index 3f094e3..8b09b80 100644 --- a/lib/src/reducer_builder.dart +++ b/lib/src/reducer_builder.dart @@ -17,22 +17,35 @@ class ReducerBuilder, /// Registers [reducer] function to the given [actionName] void add(ActionName actionName, Reducer reducer) { - _map[actionName.name] = (state, action, builder) { + _add(actionName.name, reducer); + } + + void _add( + String name, Reducer reducer) { + final oldReducer = _map[name]; + _map[name] = (state, action, builder) { + if (oldReducer != null) oldReducer(state, action, builder); reducer(state, action as Action, builder); }; } + void _addAll(Map> map) { + for (final entry in map.entries) { + _add(entry.key, entry.value); + } + } + /// [combine] combines this ReducerBuilder with another ReducerBuilder /// for the same type void combine(ReducerBuilder other) { - _map.addAll(other._map); + _addAll(other._map); } /// [combineNested] combines this ReducerBuilder with a NestedReducerBuilder @pragma('dart2js:noInline') void combineNested, NB extends Builder>( NestedReducerBuilder nested) { - _map.addAll(nested._map); + _addAll(nested._map); } /// [combineAbstract] combines this ReducerBuilder with an AbstractReducerBuilder. @@ -40,34 +53,34 @@ class ReducerBuilder, /// which is a map. It does not take an AbstractReducerBuilder directly. void combineAbstract( Map> other) { - _map.addAll(other); + _addAll(other); } /// [combineList] combines this ReducerBuilder with a ListReducerBuilder void combineList(ListReducerBuilder other) { - _map.addAll(other._map); + _addAll(other._map); } /// [combineListMultimap] combines this ReducerBuilder with a ListMultimapReducerBuilder void combineListMultimap( ListMultimapReducerBuilder other) { - _map.addAll(other._map); + _addAll(other._map); } /// [combineMap] combines this ReducerBuilder with a MapReducerBuilder void combineMap(MapReducerBuilder other) { - _map.addAll(other._map); + _addAll(other._map); } /// [combineSet] combines this ReducerBuilder with a SetReducerBuilder void combineSet(SetReducerBuilder other) { - _map.addAll(other._map); + _addAll(other._map); } /// [combineSetMultimap] combines this ReducerBuilder with a SetMultimapReducerBuilder void combineSetMultimap( SetMultimapReducerBuilder other) { - _map.addAll(other._map); + _addAll(other._map); } /// [build] returns a reducer function that can be passed to a [Store]. @@ -128,25 +141,29 @@ class NestedReducerBuilder< /// Registers [reducer] function to the given [actionName] void add(ActionName actionName, Reducer reducer) { - _map[actionName.name] = (state, action, builder) => reducer( - _stateMapper(state), - action as Action, - _builderMapper(builder), - ); + _add(actionName.name, reducer); + } + + void _add( + String name, Reducer reducer) { + final oldReducer = _map[name]; + _map[name] = (state, action, builder) { + if (oldReducer != null) oldReducer(state, action, builder); + reducer( + _stateMapper(state), + action as Action, + _builderMapper(builder), + ); + }; } /// [combineReducerBuilder] takes a `ReducerBuilder` with the type arguments /// `NestedState`, `NestedStateBuilder`, and combines it with this `NestedReducerBuilder`. void combineReducerBuilder( ReducerBuilder other) { - final adapted = other._map.map((name, reducer) => MapEntry( - name, - (State state, Action action, StateBuilder builder) => reducer( - _stateMapper(state), - action, - _builderMapper(builder), - ))); - _map.addAll(adapted); + for (final entry in other._map.entries) { + _add(entry.key, entry.value); + } } } @@ -161,7 +178,9 @@ class AbstractReducerBuilder { /// Registers [reducer] function to the given [actionName] void add(ActionName actionName, CReducer reducer) { + final oldReducer = _map[actionName.name]; _map[actionName.name] = (state, action, builder) { + if (oldReducer != null) oldReducer(state, action, builder); reducer(state, action as Action, builder); }; } @@ -188,11 +207,17 @@ class ListReducerBuilder, /// Registers [reducer] function to the given [actionName] void add(ActionName actionName, CReducer, ListBuilder, Payload> reducer) { - _map[actionName.name] = (state, action, builder) => reducer( - _stateMapper(state), - action as Action, - _builderMapper(builder), - ); + final oldReducer = _map[actionName.name]; + _map[actionName.name] = (state, action, builder) { + if (oldReducer != null) { + oldReducer(state, action, builder); + } + reducer( + _stateMapper(state), + action as Action, + _builderMapper(builder), + ); + }; } } @@ -211,11 +236,17 @@ class ListMultimapReducerBuilder, ActionName actionName, CReducer, ListMultimapBuilder, Payload> reducer) { - _map[actionName.name] = (state, action, builder) => reducer( - _stateMapper(state), - action as Action, - _builderMapper(builder), - ); + final oldReducer = _map[actionName.name]; + _map[actionName.name] = (state, action, builder) { + if (oldReducer != null) { + oldReducer(state, action, builder); + } + reducer( + _stateMapper(state), + action as Action, + _builderMapper(builder), + ); + }; } } @@ -232,11 +263,18 @@ class MapReducerBuilder, /// Registers [reducer] function to the given [actionName] void add(ActionName actionName, CReducer, MapBuilder, Payload> reducer) { - _map[actionName.name] = (state, action, builder) => reducer( - _stateMapper(state), - action as Action, - _builderMapper(builder), - ); + final oldReducer = _map[actionName.name]; + + _map[actionName.name] = (state, action, builder) { + if (oldReducer != null) { + oldReducer(state, action, builder); + } + reducer( + _stateMapper(state), + action as Action, + _builderMapper(builder), + ); + }; } } @@ -253,11 +291,17 @@ class SetReducerBuilder, /// Registers [reducer] function to the given [actionName] void add(ActionName actionName, CReducer, SetBuilder, Payload> reducer) { - _map[actionName.name] = (state, action, builder) => reducer( - _stateMapper(state), - action as Action, - _builderMapper(builder), - ); + final oldReducer = _map[actionName.name]; + _map[actionName.name] = (state, action, builder) { + if (oldReducer != null) { + oldReducer(state, action, builder); + } + reducer( + _stateMapper(state), + action as Action, + _builderMapper(builder), + ); + }; } } @@ -276,10 +320,16 @@ class SetMultimapReducerBuilder, ActionName actionName, CReducer, SetMultimapBuilder, Payload> reducer) { - _map[actionName.name] = (state, action, builder) => reducer( - _stateMapper(state), - action as Action, - _builderMapper(builder), - ); + final oldReducer = _map[actionName.name]; + _map[actionName.name] = (state, action, builder) { + if (oldReducer != null) { + oldReducer(state, action, builder); + } + reducer( + _stateMapper(state), + action as Action, + _builderMapper(builder), + ); + }; } } diff --git a/test/unit/middleware_test.dart b/test/unit/middleware_test.dart index 3f33366..504e519 100644 --- a/test/unit/middleware_test.dart +++ b/test/unit/middleware_test.dart @@ -33,7 +33,7 @@ void main() { test('1 middleware doubles count and updates state', () async { setup(); expect(store.state.count, 1); - store.actions.middlewareActions.doubleIt(0); + store.actions.middlewareActions.doubleIt(); expect(store.state.count, 3); }); @@ -52,7 +52,7 @@ void main() { }); // should add double the current state twice - store.actions.middlewareActions.doubleIt(0); + store.actions.middlewareActions.doubleIt(); var stateChange = await onStateChangeCompleter.future; expect(stateChange.prev.count, 1); expect(stateChange.next.count, 3); @@ -64,14 +64,21 @@ void main() { test('combine works with tripleIt', () async { setup(); expect(store.state.count, 1); - store.actions.middlewareActions.tripleIt(0); + store.actions.middlewareActions.tripleIt(); expect(store.state.count, 4); }); + test('adding multiple middleware for the same action works', () async { + setup(); + expect(store.state.count, 1); + store.actions.middlewareActions.timesSix(); + expect(store.state.count, 6); + }); + test('combineNested works with SubCounter doubleIt', () async { setup(); expect(store.state.subCounter.subCount, 1); - store.actions.subCounterActions.doubleIt(0); + store.actions.subCounterActions.doubleIt(); expect(store.state.subCounter.subCount, 3); }); }); diff --git a/test/unit/reducer_test.dart b/test/unit/reducer_test.dart new file mode 100644 index 0000000..8db986b --- /dev/null +++ b/test/unit/reducer_test.dart @@ -0,0 +1,28 @@ +import 'package:built_redux/built_redux.dart'; +import 'package:test/test.dart'; + +import 'test_counter.dart'; + +void main() { + group('reducer', () { + Store store; + + setUp(() { + var actions = CounterActions(); + var defaultValue = Counter(); + + store = Store( + reducer, defaultValue, actions); + }); + + tearDown(() { + store.dispose(); + }); + + test('multiple reducer for the same action', () { + expect(store.state.count, 1); + store.actions.doubleIncrement(2); + expect(store.state.count, 5); + }); + }); +} diff --git a/test/unit/test_counter.dart b/test/unit/test_counter.dart index 1c8540f..3c55610 100644 --- a/test/unit/test_counter.dart +++ b/test/unit/test_counter.dart @@ -14,6 +14,7 @@ abstract class CounterActions extends ReduxActions { factory CounterActions() => _$CounterActions(); ActionDispatcher increment; + ActionDispatcher doubleIncrement; ActionDispatcher incrementOther; SubCounterActions subCounterActions; MiddlewareActions middlewareActions; @@ -28,18 +29,20 @@ abstract class SubCounterActions extends ReduxActions { } void _increment(Counter state, Action action, CounterBuilder builder) => - builder.count = state.count + action.payload; + builder.count += action.payload; void _incrementOther( Counter state, Action action, CounterBuilder builder) => - builder.otherCount = state.otherCount + action.payload; + builder.otherCount += action.payload; void _incrementSubCount( Counter state, Action action, CounterBuilder builder) => - builder.subCounter.subCount = state.subCounter.subCount + action.payload; + builder.subCounter.subCount += action.payload; final reducer = (ReducerBuilder() ..add(CounterActionsNames.increment, _increment) + ..add(CounterActionsNames.doubleIncrement, _increment) + ..add(CounterActionsNames.doubleIncrement, _increment) ..combine(_otherReducer) ..add(SubCounterActionsNames.increment, _incrementSubCount)) .build(); @@ -72,8 +75,9 @@ abstract class SubCounter implements Built { // Middleware abstract class MiddlewareActions extends ReduxActions { - ActionDispatcher doubleIt; - ActionDispatcher tripleIt; + ActionDispatcher doubleIt; + ActionDispatcher tripleIt; + ActionDispatcher timesSix; MiddlewareActions._(); factory MiddlewareActions() => _$MiddlewareActions(); @@ -83,11 +87,12 @@ var counterMiddleware = (MiddlewareBuilder() ..add(MiddlewareActionsNames.doubleIt, _doubleIt) ..combine(tripleItMiddlewareBuilder) + ..combine(timesSixMiddlewareBuilder) ..combineNested(subCountNested)) .build(); void _doubleIt(MiddlewareApi api, - ActionHandler next, Action action) { + ActionHandler next, Action action) { api.actions.increment(api.state.count * 2); next(action); } @@ -96,12 +101,23 @@ var tripleItMiddlewareBuilder = MiddlewareBuilder() ..add(MiddlewareActionsNames.tripleIt, _tripleIt); +var timesSixMiddlewareBuilder = + MiddlewareBuilder() + ..add(MiddlewareActionsNames.timesSix, _tripleIt) + ..add(MiddlewareActionsNames.timesSix, _addAHalf); + void _tripleIt(MiddlewareApi api, - ActionHandler next, Action action) { + ActionHandler next, Action action) { api.actions.increment(api.state.count * 3); next(action); } +void _addAHalf(MiddlewareApi api, + ActionHandler next, Action action) { + api.actions.increment(api.state.count ~/ 2); + next(action); +} + var subCountNested = NestedMiddlewareBuilder< Counter, CounterBuilder, diff --git a/test/unit/test_counter.g.dart b/test/unit/test_counter.g.dart index ddbe6d7..e9c022e 100644 --- a/test/unit/test_counter.g.dart +++ b/test/unit/test_counter.g.dart @@ -16,6 +16,8 @@ class _$CounterActions extends CounterActions { _$CounterActions._() : super._(); final increment = ActionDispatcher('CounterActions-increment'); + final doubleIncrement = + ActionDispatcher('CounterActions-doubleIncrement'); final incrementOther = ActionDispatcher('CounterActions-incrementOther'); final subCounterActions = SubCounterActions(); @@ -24,6 +26,7 @@ class _$CounterActions extends CounterActions { @override void setDispatcher(Dispatcher dispatcher) { increment.setDispatcher(dispatcher); + doubleIncrement.setDispatcher(dispatcher); incrementOther.setDispatcher(dispatcher); subCounterActions.setDispatcher(dispatcher); @@ -33,6 +36,8 @@ class _$CounterActions extends CounterActions { class CounterActionsNames { static final increment = ActionName('CounterActions-increment'); + static final doubleIncrement = + ActionName('CounterActions-doubleIncrement'); static final incrementOther = ActionName('CounterActions-incrementOther'); } @@ -60,19 +65,22 @@ class _$MiddlewareActions extends MiddlewareActions { factory _$MiddlewareActions() => _$MiddlewareActions._(); _$MiddlewareActions._() : super._(); - final doubleIt = ActionDispatcher('MiddlewareActions-doubleIt'); - final tripleIt = ActionDispatcher('MiddlewareActions-tripleIt'); + final doubleIt = ActionDispatcher('MiddlewareActions-doubleIt'); + final tripleIt = ActionDispatcher('MiddlewareActions-tripleIt'); + final timesSix = ActionDispatcher('MiddlewareActions-timesSix'); @override void setDispatcher(Dispatcher dispatcher) { doubleIt.setDispatcher(dispatcher); tripleIt.setDispatcher(dispatcher); + timesSix.setDispatcher(dispatcher); } } class MiddlewareActionsNames { - static final doubleIt = ActionName('MiddlewareActions-doubleIt'); - static final tripleIt = ActionName('MiddlewareActions-tripleIt'); + static final doubleIt = ActionName('MiddlewareActions-doubleIt'); + static final tripleIt = ActionName('MiddlewareActions-tripleIt'); + static final timesSix = ActionName('MiddlewareActions-timesSix'); } // **************************************************************************