Skip to content

Version 4.0.0 #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Nov 9, 2019
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
13 changes: 10 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,13 @@ dart:
- stable
- dev
with_content_shell: false
dart_task:
- test: --platform vm
- dartanalyzer: --fatal-warnings lib
script:
- dartanalyzer --fatal-infos --fatal-warnings ./
- dartfmt -n ./lib --set-exit-if-changed
- pub global activate coverage
- pub run test test/redux_test.dart
- dart --disable-service-auth-codes --enable-vm-service=8111 --pause-isolates-on-exit test/redux_test.dart &
- nohup pub global run coverage:collect_coverage --port=8111 --out=coverage.json --wait-paused --resume-isolates
- pub global run coverage:format_coverage --lcov --in=coverage.json --out=lcov.info --packages=.packages --report-on=lib
after_success:
- bash <(curl -s https://codecov.io/bash)
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,16 @@
# 4.0.0

* `dispatch` returns the provided action
* Removed Dart 1.x support
* Enforce Pedantic package lint rules
* Enforce `public_member_api_docs` lint rule and add docs to missing parts
* Update `travis.yml` with support for dartfmt, analysis, and code coverage
* Add coverage badge to root README
* Pub.dev updates
* Longer description
* https for package url
* update example folder to include README

# 3.0.1

* Update README based on feedback
Expand Down
10 changes: 1 addition & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,10 @@
# redux.dart

[![Build Status](https://api.travis-ci.org/johnpryan/redux.dart.svg?branch=master)](https://travis-ci.org/johnpryan/redux.dart)
[![codecov](https://codecov.io/gh/johnpryan/redux.dart/branch/master/graph/badge.svg)](https://codecov.io/gh/johnpryan/redux.dart)

[Redux](http://redux.js.org/) for Dart using generics for typed State. It includes a rich ecosystem of [Docs](#docs), [Middleware](#middleware), [Dev Tools](#dev-tools) and can be combined with Flutter using the [flutter_redux](https://pub.dartlang.org/packages/flutter_redux) package.

## Redux 3.0.0 Migration & Dart 2 support

In order to support Dart 2, some of the APIs needed to change. The good news: This actually simplifies Redux! The bad news: you may need to update your projects.

* Change `ReducerBinding` to `TypedReducer`
* Remove `combineTypedReducer`. Use `combineReducers` with a combination of normal reducers and/or `TypedReducer`s.
* Change `MiddlewareBinding` to `TypedMiddleware`.
* Remove `combineTypedMiddleware` -- no longer needed! Just create a normal `List<Middleware<State>>`!

## Docs

* [Motivation and Principles](https://github.com/johnpryan/redux.dart/blob/master/doc/why.md) - Learn why Redux might make sense for your app and the principles behind it.
Expand Down
3 changes: 3 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
include: package:pedantic/analysis_options.yaml

analyzer:
strong-mode:
implicit-casts: false
Expand All @@ -12,3 +14,4 @@ linter:
- test_types_in_equals
- unrelated_type_equality_checks
- valid_regexps
- public_member_api_docs
5 changes: 5 additions & 0 deletions example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
## Examples

* [Counter example](https://github.com/johnpryan/redux.dart/tree/master/example/vanilla_counter) - an example of how to search as a user types, demonstrating both the Middleware and Epic approaches.
* [Middleware example](https://github.com/johnpryan/redux.dart/tree/master/example/middleware) - an example of how to search as a user types, demonstrating both the Middleware and Epic approaches.
* [Combining Reducers example](https://github.com/johnpryan/redux.dart/tree/master/example/combined_reducers) - a port of the standard "Counter Button" example from Flutter
14 changes: 7 additions & 7 deletions example/combined_reducers/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,10 @@ enum AppAction { increment, decrement }
// to a console. For that, use Middleware.
AppState counterReducer(AppState state, dynamic action) {
if (action == AppAction.increment) {
return new AppState(state.count + 1, state.clickCount);
return AppState(state.count + 1, state.clickCount);
}
if (action == AppAction.decrement) {
return new AppState(state.count - 1, state.clickCount);
return AppState(state.count - 1, state.clickCount);
}

return state;
Expand All @@ -41,10 +41,10 @@ AppState counterReducer(AppState state, dynamic action) {
// can be used for Action and State.
AppState clickCounterReducer(AppState state, dynamic action) {
if (action == AppAction.increment) {
return new AppState(state.count, state.clickCount + 1);
return AppState(state.count, state.clickCount + 1);
}
if (action == AppAction.decrement) {
return new AppState(state.count, state.clickCount + 1);
return AppState(state.count, state.clickCount + 1);
}

return state;
Expand All @@ -56,9 +56,9 @@ void main() {
counterReducer,
clickCounterReducer,
]);
final store = new Store<AppState>(
final store = Store<AppState>(
combined,
initialState: new AppState(0, 0),
initialState: AppState(0, 0),
);

render(store.state);
Expand All @@ -79,7 +79,7 @@ void main() {
});

querySelector('#incrementAsync').onClick.listen((_) {
new Future<Null>.delayed(new Duration(milliseconds: 1000)).then((_) {
Future<Null>.delayed(Duration(milliseconds: 1000)).then((_) {
store.dispatch(AppAction.increment);
});
});
Expand Down
15 changes: 0 additions & 15 deletions example/index.html

This file was deleted.

6 changes: 3 additions & 3 deletions example/middleware/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,14 @@ int counterReducer(int state, dynamic action) {
// A piece of middleware that will log all actions with a timestamp to your
// console!
void loggingMiddleware(Store<int> store, dynamic action, NextDispatcher next) {
print('${new DateTime.now()}: $action');
print('${DateTime.now()}: $action');

next(action);
}

void main() {
// Create a new reducer and store for the app.
final store = new Store<int>(
final store = Store<int>(
counterReducer,
initialState: 0,
middleware: <Middleware<int>>[loggingMiddleware],
Expand All @@ -62,7 +62,7 @@ void main() {
});

querySelector('#incrementAsync').onClick.listen((_) {
new Future<Null>.delayed(new Duration(milliseconds: 1000)).then((_) {
Future<Null>.delayed(Duration(milliseconds: 1000)).then((_) {
store.dispatch(Actions.increment);
});
});
Expand Down
4 changes: 2 additions & 2 deletions example/vanilla_counter/index.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ int counterReducer(int state, dynamic action) {

void main() {
// Create a new reducer and store for the app.
final store = new Store(counterReducer, initialState: 0);
final store = Store(counterReducer, initialState: 0);

render(store.state);
store.onChange.listen(render);
Expand All @@ -50,7 +50,7 @@ void main() {
});

querySelector('#incrementAsync').onClick.listen((_) {
new Future<Null>.delayed(new Duration(milliseconds: 1000)).then((_) {
Future<Null>.delayed(Duration(milliseconds: 1000)).then((_) {
store.dispatch(Actions.increment);
});
});
Expand Down
28 changes: 23 additions & 5 deletions lib/src/store.dart
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ typedef State Reducer<State>(State state, dynamic action);
///
/// final store = new Store<int>(new CounterReducer());
abstract class ReducerClass<State> {
/// The [Reducer] function that converts the current state and action into a
/// new state
State call(State state, dynamic action);
}

Expand Down Expand Up @@ -98,6 +100,7 @@ typedef void Middleware<State>(
/// middleware: [new LoggingMiddleware()],
/// );
abstract class MiddlewareClass<State> {
/// A [Middleware] function that intercepts a dispatched action
void call(Store<State> store, dynamic action, NextDispatcher next);
}

Expand Down Expand Up @@ -159,21 +162,35 @@ class Store<State> {
State _state;
List<NextDispatcher> _dispatchers;

/// Creates an instance of a Redux Store.
///
/// The [reducer] argument specifies how the state should be changed in
/// response to dispatched actions.
///
/// The optional [initialState] argument defines the State of the store when
/// the Store is first created.
///
/// The optional [middleware] argument takes a list of [Middleware] functions
/// or [MiddlewareClass]. See the [Middleware] documentation for information
/// on how they are used.
///
/// The [syncStream] argument allows you to use a synchronous
/// [StreamController] instead of an async `StreamController` under the hood.
/// By default, the Stream is async.
Store(
this.reducer, {
State initialState,
List<Middleware<State>> middleware = const [],
bool syncStream: false,
bool syncStream = false,

/// If set to true, the Store will not emit onChange events if the new State
/// that is returned from your [reducer] in response to an Action is equal
/// to the previous state.
///
/// Under the hood, it will use the `==` method from your State class to
/// determine whether or not the two States are equal.
bool distinct: false,
})
: _changeController = new StreamController.broadcast(sync: syncStream) {
bool distinct = false,
}) : _changeController = StreamController.broadcast(sync: syncStream) {
_state = initialState;
_dispatchers = _createDispatchers(
middleware,
Expand Down Expand Up @@ -246,8 +263,9 @@ class Store<State> {
/// to the state using the given [Reducer]. Please note: [Middleware] can
/// intercept actions, and can modify actions or stop them from passing
/// through to the reducer.
void dispatch(dynamic action) {
dynamic dispatch(dynamic action) {
_dispatchers[0](action);
return action;
}

/// Closes down the Store so it will no longer be operational. Only use this
Expand Down
6 changes: 6 additions & 0 deletions lib/src/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,11 @@ import 'package:redux/src/store.dart';
/// ]);
/// ```
class TypedReducer<State, Action> implements ReducerClass<State> {
/// A [Reducer] function that only accepts an action of a specific type
final State Function(State state, Action action) reducer;

/// Creates a reducer that will only be executed if the dispatched action
/// matches the [Action] type.
TypedReducer(this.reducer);

@override
Expand Down Expand Up @@ -209,12 +212,15 @@ class TypedReducer<State, Action> implements ReducerClass<State> {
/// ];
/// ```
class TypedMiddleware<State, Action> implements MiddlewareClass<State> {
/// A [Middleware] function that only works on actions of a specific type.
final void Function(
Store<State> store,
Action action,
NextDispatcher next,
) middleware;

/// Create a [Middleware] that is only executed when the dispatched action
/// matches the [Action] type.
TypedMiddleware(this.middleware);

@override
Expand Down
12 changes: 7 additions & 5 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ name: redux
authors:
- John Ryan <[email protected]>
- Brian Egan <[email protected]>
description: Redux for Dart
homepage: http://github.com/johnpryan/redux.dart
version: 3.0.1
description: Redux is a predictable state container for Dart and Flutter apps
homepage: https://github.com/johnpryan/redux.dart
version: 4.0.0

environment:
sdk: '>=1.24.0 <3.0.0'
sdk: '>=2.0.0 <3.0.0'

dev_dependencies:
test: '>=0.12.24+2 <2.0.0'
test: '>=1.0.0 <2.0.0'
pedantic: ">=1.8.0+1 <2.0.0"
coverage: ">=0.13.0 <0.14.0"
22 changes: 11 additions & 11 deletions test/middleware_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import 'test_data.dart';
void main() {
group('Middleware', () {
test('are invoked by the store', () {
final middleware = new IncrementMiddleware();
final store = new Store<String>(
final middleware = IncrementMiddleware();
final store = Store<String>(
stringReducer,
initialState: 'hello',
middleware: [middleware],
Expand All @@ -17,9 +17,9 @@ void main() {
});

test('are applied in the correct order', () {
final middleware1 = new IncrementMiddleware();
final middleware2 = new IncrementMiddleware();
final store = new Store<String>(
final middleware1 = IncrementMiddleware();
final middleware2 = IncrementMiddleware();
final store = Store<String>(
stringReducer,
initialState: 'hello',
middleware: [middleware1, middleware2],
Expand All @@ -35,9 +35,9 @@ void main() {
});

test('actions can be dispatched multiple times', () {
final middleware1 = new ExtraActionIncrementMiddleware();
final middleware2 = new IncrementMiddleware();
final store = new Store<String>(
final middleware1 = ExtraActionIncrementMiddleware();
final middleware2 = IncrementMiddleware();
final store = Store<String>(
stringReducer,
initialState: 'hello',
middleware: [middleware1, middleware2],
Expand All @@ -54,9 +54,9 @@ void main() {
});

test('actions can be dispatched through entire chain', () {
final middleware1 = new ExtraActionIfDispatchedIncrementMiddleware();
final middleware2 = new IncrementMiddleware();
final store = new Store<String>(
final middleware1 = ExtraActionIfDispatchedIncrementMiddleware();
final middleware2 = IncrementMiddleware();
final store = Store<String>(
stringReducer,
initialState: 'hello',
middleware: [middleware1, middleware2],
Expand Down
9 changes: 9 additions & 0 deletions test/redux_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import 'middleware_test.dart' as middleware_test;
import 'store_test.dart' as store_test;
import 'utils_test.dart' as utils_test;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option for code coverage is to use package:coverage

Copy link
Collaborator Author

@brianegan brianegan Nov 9, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hrm -- this PR makes use of package:coverage to collect the coverage, but I created this file since I wanted collect coverage from all tests in one shot.

Does the coverage package have a way to do the same thing as "pub run test", which automatically runs all tests?


void main() {
middleware_test.main();
store_test.main();
utils_test.main();
}
8 changes: 4 additions & 4 deletions test/store_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import 'test_data.dart';
void main() {
group('Store', () {
test('calls the reducer when an action is fired', () {
final store = new Store<String>(stringReducer, initialState: 'Hello');
final store = Store<String>(stringReducer, initialState: 'Hello');
final action = 'test';
store.dispatch(action);
expect(store.state, equals(action));
Expand All @@ -15,7 +15,7 @@ void main() {
test('canceled subscriber should not be notified', () {
var subscriber1Called = false;
var subscriber2Called = false;
final store = new Store<String>(
final store = Store<String>(
stringReducer,
initialState: 'hello',
syncStream: true,
Expand All @@ -37,7 +37,7 @@ void main() {
test('store emits current state to subscribers', () {
final action = 'test';
final states = <String>[];
final store = new Store<String>(
final store = Store<String>(
stringReducer,
initialState: 'hello',
syncStream: true,
Expand All @@ -54,7 +54,7 @@ void main() {
test('store does not emit an onChange if distinct', () {
final action = 'test';
final states = <String>[];
final store = new Store<String>(stringReducer,
final store = Store<String>(stringReducer,
initialState: 'hello', syncStream: true, distinct: true);
store.onChange.listen((state) => states.add(state));

Expand Down
2 changes: 1 addition & 1 deletion test/test_data.dart
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class StringReducer extends ReducerClass<String> {
class IncrementMiddleware extends MiddlewareClass<String> {
int counter = 0;
final _invocationsController =
new StreamController<String>.broadcast(sync: true);
StreamController<String>.broadcast(sync: true);

Stream<String> get invocations => _invocationsController.stream;

Expand Down
Loading