From 9e8a9fa3b7380478a6200a6d8a49e8e60fe3400a Mon Sep 17 00:00:00 2001 From: Jop Middelkamp Date: Fri, 3 Dec 2021 23:52:27 +0100 Subject: [PATCH 1/8] Added the .vscode to the .gitignore --- .gitignore | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 8e91a92..0bd6966 100644 --- a/.gitignore +++ b/.gitignore @@ -127,4 +127,7 @@ fabric.properties # https://plugins.jetbrains.com/plugin/12206-codestream .idea/codestream.xml -# End of https://www.toptal.com/developers/gitignore/api/intellij,dart \ No newline at end of file +# End of https://www.toptal.com/developers/gitignore/api/intellij,dart + +# VS Code +.vscode/ \ No newline at end of file From fafa42f82fa6d4080203fb144d156bf1d016352a Mon Sep 17 00:00:00 2001 From: Jop Middelkamp Date: Sat, 4 Dec 2021 00:13:16 +0100 Subject: [PATCH 2/8] Added a reactive disposable mixin --- example/lib/rxdart_ext_example.dart | 32 +++++++++++++++++++++++++++++ lib/src/utils/disposable.dart | 31 ++++++++++++++++++++++++++++ lib/src/utils/index.dart | 1 + 3 files changed, 64 insertions(+) create mode 100644 lib/src/utils/disposable.dart diff --git a/example/lib/rxdart_ext_example.dart b/example/lib/rxdart_ext_example.dart index cb67e1d..473aaf3 100644 --- a/example/lib/rxdart_ext_example.dart +++ b/example/lib/rxdart_ext_example.dart @@ -26,4 +26,36 @@ void main() async { Single.fromCallable(() => delay(200).then((_) => 2)), (int p0, int p1) => p0 + p1, ).doOnData(state$.add).forEach((_) => print('<> ${state$.value}')); + + final dateTimeStream = BehaviorSubject.seeded( + DateTime.now().toUtc(), + ); + print('Disposable example created'); + final disposableExample = DisposableExample( + dateTimeStream: dateTimeStream, + ); + print('Periodic stream created'); + final periodicStreamSub = Stream.periodic( + const Duration(milliseconds: 100), + ).listen((_) { + final value = DateTime.now().toUtc(); + print('Periodic stream: $value'); + dateTimeStream.add(value); + }); + await delay(500); + disposableExample.dispose(); + print('Disposable example disposed'); + await delay(500); + periodicStreamSub.cancel(); + print('Periodic stream disposed'); +} + +class DisposableExample with Disposable { + DisposableExample({ + required Stream dateTimeStream, + }) { + dateTimeStream.takeUntil(dispose$).listen( + (value) => print('Disposable example: $value'), + ); + } } diff --git a/lib/src/utils/disposable.dart b/lib/src/utils/disposable.dart new file mode 100644 index 0000000..5c13738 --- /dev/null +++ b/lib/src/utils/disposable.dart @@ -0,0 +1,31 @@ +import 'package:meta/meta.dart'; +import 'package:rxdart/rxdart.dart'; + +/// This mixin adds an easy option to dispose streams without having to store a +/// [StreamSubscription] variable. +/// +/// {@tool snippet} +/// Typical usage is as follows: +/// +/// ```dart +/// class ExampleProvider with DisposableMixin { +/// void init() { +/// _stream.takeUntil(dispose$).listen(_handleStream); +/// } +/// } +/// ``` +/// {@end-tool} +mixin Disposable { + final _dispose$ = PublishSubject(); + + /// The [PublishSubject] that emits null when the dispose method is called. + Stream get dispose$ => _dispose$.stream.asBroadcastStream(); + + /// Dispose the object and emit null to the [dispose$] stream. + @mustCallSuper + void dispose() { + _dispose$ + ..add(null) + ..close(); + } +} diff --git a/lib/src/utils/index.dart b/lib/src/utils/index.dart index 3693b58..57d80fe 100644 --- a/lib/src/utils/index.dart +++ b/lib/src/utils/index.dart @@ -1,3 +1,4 @@ export 'add_null.dart'; export 'delay.dart'; +export 'disposable.dart'; export 'identity.dart'; From e1d309b68cbf685e264467528c6cefa7509fee6e Mon Sep 17 00:00:00 2001 From: Jop Middelkamp Date: Sat, 4 Dec 2021 00:35:20 +0100 Subject: [PATCH 3/8] Added documentation for the disposable mixin --- README.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index cf4a0ce..da1e9eb 100644 --- a/README.md +++ b/README.md @@ -185,7 +185,25 @@ A Stream that provides synchronous access to the last emitted item, but not repl - Single-subscription - [ValueStreamController](https://pub.dev/documentation/rxdart_ext/latest/not_replay_value_stream/ValueStreamController-class.html) - [toNotReplayValueStream](https://pub.dev/documentation/rxdart_ext/latest/not_replay_value_stream/ToNotReplayValueStreamExtension/toNotReplayValueStream.html) - + +### 5. Disposable + +A mixin that makes it easay to dispose streams without having to store and close a [StreamSubscription] variable. + +Typical usage is as follows: +```dart +class DisposableExample with Disposable { + DisposableExample({ + required Stream dateTimeStream, + }) { + dateTimeStream.takeUntil(dispose$).listen( + (value) => print('Disposable example: $value'), + ); + } +} +``` + + ## License MIT License From 2843df9f6bda1048b0b82bfa516705a6ff29f3a5 Mon Sep 17 00:00:00 2001 From: Jop Middelkamp Date: Thu, 27 Jan 2022 16:25:00 +0100 Subject: [PATCH 4/8] Added test for Disposable --- test/utils/disposable_test.dart | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 test/utils/disposable_test.dart diff --git a/test/utils/disposable_test.dart b/test/utils/disposable_test.dart new file mode 100644 index 0000000..6fcc425 --- /dev/null +++ b/test/utils/disposable_test.dart @@ -0,0 +1,26 @@ +import 'dart:async'; + +import 'package:rxdart_ext/utils.dart'; +import 'package:test/test.dart'; + +class _MockClass with Disposable { + _MockClass( + this.completer, + ) { + dispose$.listen((_) => completer.complete()); + } + + final Completer completer; +} + +void main() { + test('Disposable', () async { + final completer = Completer(); + final disposable = _MockClass(completer); + + disposable.dispose(); + await completer.future.timeout(Duration(milliseconds: 100)); + + expect(completer.isCompleted, isTrue); + }); +} From 2c737e991afc5dea22413e04ea3ca8401a4c0385 Mon Sep 17 00:00:00 2001 From: Jop Middelkamp Date: Tue, 1 Feb 2022 21:58:15 +0100 Subject: [PATCH 5/8] Added the disposable test to the rxdart_ext_test.dart --- test/rxdart_ext_test.dart | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/test/rxdart_ext_test.dart b/test/rxdart_ext_test.dart index f26431e..6120471 100644 --- a/test/rxdart_ext_test.dart +++ b/test/rxdart_ext_test.dart @@ -1,3 +1,8 @@ +import 'not_replay_value_stream/not_replay_value_connectable_stream_tests.dart' + as not_replay_value_connectable_stream_tests; +import 'not_replay_value_stream/value_controller_test.dart' + as value_controller_test; +import 'not_replay_value_stream/value_subject_test.dart' as value_subject_test; import 'operators/debug_test.dart' as debug_test; import 'operators/distinct_by_test.dart' as distinct_by_test; import 'operators/distinct_unique_by_test.dart' as distinct_unique_by_test; @@ -10,13 +15,6 @@ import 'operators/to_single_subscription_test.dart' as to_single_subscription_test; import 'operators/void_test.dart' as void_test; import 'operators/where_not_null_test.dart' as where_not_null_test; - -import 'not_replay_value_stream/not_replay_value_connectable_stream_tests.dart' - as not_replay_value_connectable_stream_tests; -import 'not_replay_value_stream/value_controller_test.dart' - as value_controller_test; -import 'not_replay_value_stream/value_subject_test.dart' as value_subject_test; - import 'single/as_single_test.dart' as as_single_test; import 'single/as_void_test.dart' as as_void_test; import 'single/async_expand_test.dart' as async_expand_test; @@ -25,19 +23,18 @@ import 'single/delay_test.dart' as delay_test; import 'single/do_test.dart' as do_test; import 'single/map_to_test.dart' as map_to_test; import 'single/on_error_resume_test.dart' as on_error_resume_test; +import 'single/rx_singles_test.dart' as rx_singles_test; import 'single/single_or_error_test.dart' as single_or_error_test; import 'single/single_test.dart' as single_test; -import 'single/rx_singles_test.dart' as rx_singles_test; import 'single/switch_flat_exhaust_map_test.dart' as switch_flat_exhaust_map_test; - -import 'utils/add_null_test.dart' as add_null_test; - import 'state_stream/as_broadcast_test.dart' as as_broadcast_test; import 'state_stream/state_connectable_stream_test.dart' as state_connectable_stream_test; -import 'state_stream/to_state_stream_test.dart' as to_state_stream_test; import 'state_stream/state_subject_test.dart' as state_subject_test; +import 'state_stream/to_state_stream_test.dart' as to_state_stream_test; +import 'utils/add_null_test.dart' as add_null_test; +import 'utils/disposable_test.dart' as disposable_test; void main() { debug_test.main(); @@ -70,6 +67,7 @@ void main() { switch_flat_exhaust_map_test.main(); add_null_test.main(); + disposable_test.main(); as_broadcast_test.main(); state_connectable_stream_test.main(); From 169094ddd11792473837879b82f0604b2977fcc9 Mon Sep 17 00:00:00 2001 From: Petrus Nguyen Thai Hoc Date: Wed, 2 Feb 2022 18:32:02 +0700 Subject: [PATCH 6/8] Rename Disposable to DisposableMixin and check _disposed$.isClosed --- lib/src/utils/disposable.dart | 9 ++++++--- rxdart_ext.iml | 3 +++ test/rxdart_ext_test.dart | 8 ++++++++ test/utils/disposable_test.dart | 5 +++-- 4 files changed, 20 insertions(+), 5 deletions(-) diff --git a/lib/src/utils/disposable.dart b/lib/src/utils/disposable.dart index 5c13738..d2619b9 100644 --- a/lib/src/utils/disposable.dart +++ b/lib/src/utils/disposable.dart @@ -15,15 +15,18 @@ import 'package:rxdart/rxdart.dart'; /// } /// ``` /// {@end-tool} -mixin Disposable { +mixin DisposableMixin { final _dispose$ = PublishSubject(); - /// The [PublishSubject] that emits null when the dispose method is called. - Stream get dispose$ => _dispose$.stream.asBroadcastStream(); + /// The [Stream] that emits `null` when the [dispose] method is called. + Stream get dispose$ => _dispose$.stream; /// Dispose the object and emit null to the [dispose$] stream. @mustCallSuper void dispose() { + if (_dispose$.isClosed) { + return; + } _dispose$ ..add(null) ..close(); diff --git a/rxdart_ext.iml b/rxdart_ext.iml index abb47ba..f97cd24 100644 --- a/rxdart_ext.iml +++ b/rxdart_ext.iml @@ -9,6 +9,9 @@ + + + diff --git a/test/rxdart_ext_test.dart b/test/rxdart_ext_test.dart index 6120471..a18a20a 100644 --- a/test/rxdart_ext_test.dart +++ b/test/rxdart_ext_test.dart @@ -3,6 +3,8 @@ import 'not_replay_value_stream/not_replay_value_connectable_stream_tests.dart' import 'not_replay_value_stream/value_controller_test.dart' as value_controller_test; import 'not_replay_value_stream/value_subject_test.dart' as value_subject_test; + +// import 'operators/debug_test.dart' as debug_test; import 'operators/distinct_by_test.dart' as distinct_by_test; import 'operators/distinct_unique_by_test.dart' as distinct_unique_by_test; @@ -15,6 +17,8 @@ import 'operators/to_single_subscription_test.dart' as to_single_subscription_test; import 'operators/void_test.dart' as void_test; import 'operators/where_not_null_test.dart' as where_not_null_test; + +// import 'single/as_single_test.dart' as as_single_test; import 'single/as_void_test.dart' as as_void_test; import 'single/async_expand_test.dart' as async_expand_test; @@ -28,11 +32,15 @@ import 'single/single_or_error_test.dart' as single_or_error_test; import 'single/single_test.dart' as single_test; import 'single/switch_flat_exhaust_map_test.dart' as switch_flat_exhaust_map_test; + +// import 'state_stream/as_broadcast_test.dart' as as_broadcast_test; import 'state_stream/state_connectable_stream_test.dart' as state_connectable_stream_test; import 'state_stream/state_subject_test.dart' as state_subject_test; import 'state_stream/to_state_stream_test.dart' as to_state_stream_test; + +// import 'utils/add_null_test.dart' as add_null_test; import 'utils/disposable_test.dart' as disposable_test; diff --git a/test/utils/disposable_test.dart b/test/utils/disposable_test.dart index 6fcc425..ca74861 100644 --- a/test/utils/disposable_test.dart +++ b/test/utils/disposable_test.dart @@ -3,7 +3,7 @@ import 'dart:async'; import 'package:rxdart_ext/utils.dart'; import 'package:test/test.dart'; -class _MockClass with Disposable { +class _MockClass with DisposableMixin { _MockClass( this.completer, ) { @@ -14,10 +14,11 @@ class _MockClass with Disposable { } void main() { - test('Disposable', () async { + test('DisposableMixin', () async { final completer = Completer(); final disposable = _MockClass(completer); + disposable.dispose(); disposable.dispose(); await completer.future.timeout(Duration(milliseconds: 100)); From 93c2a32808d20b4b58737c5368558d6f4bf93009 Mon Sep 17 00:00:00 2001 From: Petrus Nguyen Thai Hoc Date: Wed, 2 Feb 2022 18:35:36 +0700 Subject: [PATCH 7/8] fix example after Renaming Disposable to DisposableMixin --- example/lib/rxdart_ext_example.dart | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/example/lib/rxdart_ext_example.dart b/example/lib/rxdart_ext_example.dart index 473aaf3..0a79dcf 100644 --- a/example/lib/rxdart_ext_example.dart +++ b/example/lib/rxdart_ext_example.dart @@ -27,6 +27,10 @@ void main() async { (int p0, int p1) => p0 + p1, ).doOnData(state$.add).forEach((_) => print('<> ${state$.value}')); + await runDisposableMixinExample(); +} + +Future runDisposableMixinExample() async { final dateTimeStream = BehaviorSubject.seeded( DateTime.now().toUtc(), ); @@ -50,7 +54,7 @@ void main() async { print('Periodic stream disposed'); } -class DisposableExample with Disposable { +class DisposableExample with DisposableMixin { DisposableExample({ required Stream dateTimeStream, }) { From 9cc316f985b941f3f58d2cef28981a7fbad9f218 Mon Sep 17 00:00:00 2001 From: Petrus Nguyen Thai Hoc Date: Wed, 2 Feb 2022 18:40:11 +0700 Subject: [PATCH 8/8] update readme --- README.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index da1e9eb..8d39f41 100644 --- a/README.md +++ b/README.md @@ -186,13 +186,16 @@ A Stream that provides synchronous access to the last emitted item, but not repl - [ValueStreamController](https://pub.dev/documentation/rxdart_ext/latest/not_replay_value_stream/ValueStreamController-class.html) - [toNotReplayValueStream](https://pub.dev/documentation/rxdart_ext/latest/not_replay_value_stream/ToNotReplayValueStreamExtension/toNotReplayValueStream.html) -### 5. Disposable +### 5. Utils -A mixin that makes it easay to dispose streams without having to store and close a [StreamSubscription] variable. +#### DisposableMixin + +A mixin that makes it easy to dispose streams without having to store and close a `StreamSubscription` variable. Typical usage is as follows: + ```dart -class DisposableExample with Disposable { +class DisposableExample with DisposableMixin { DisposableExample({ required Stream dateTimeStream, }) { @@ -203,7 +206,6 @@ class DisposableExample with Disposable { } ``` - ## License MIT License