Skip to content

Commit

Permalink
Add comments and more tests for reactive values.
Browse files Browse the repository at this point in the history
  • Loading branch information
renggli committed Apr 14, 2024
1 parent 277518c commit 697d68b
Show file tree
Hide file tree
Showing 4 changed files with 82 additions and 46 deletions.
1 change: 1 addition & 0 deletions lib/src/reactive/computed.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:more/functional.dart';
import '../../core.dart';
import 'value.dart';

/// A computed reactive value.
class Computed<T> extends Value<T> {
Computed(this._callback) {
_update();
Expand Down
2 changes: 2 additions & 0 deletions lib/src/reactive/mutable.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import '../core/observer.dart';
import 'value.dart';

/// A mutable reactive value.
class Mutable<T> extends Value<T> {
Mutable(this._value);

Expand All @@ -12,6 +13,7 @@ class Mutable<T> extends Value<T> {
return _value;
}

/// Update the currently held value.
set value(T value) {
if (value == _value) return;
update(_value = value);
Expand Down
2 changes: 1 addition & 1 deletion lib/src/reactive/value.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import '../core/observer.dart';
import '../disposables/collection.dart';
import '../disposables/disposable.dart';

/// Abstract reactive value
/// Abstract reactive value.
abstract class Value<T> implements Observable<T> {
/// The currently held value.
T get value;
Expand Down
123 changes: 78 additions & 45 deletions test/reactive_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -32,52 +32,85 @@ void main() {
});
});
group('computed', () {
test('no dependencies', () {
final ref = Computed(() => 42);
expect(ref.value, 42);
group('value', () {
test('no dependencies', () {
final ref = Computed(() => 42);
expect(ref.value, 42);
});
test('single dependency', () {
final dep = Mutable(1);
final ref = Computed(() => dep.value);
final log = <int>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, 1);
dep.value = 2;
expect(ref.value, 2);
expect(log, [2]);
});
test('double dependency', () {
final dep1 = Mutable('John'), dep2 = Mutable('Doe');
final ref = Computed(() => '${dep1.value} ${dep2.value}');
final log = <String>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, 'John Doe');
dep1.value = 'Jane';
dep2.value = 'Roe';
expect(ref.value, 'Jane Roe');
expect(log, ['Jane Doe', 'Jane Roe']);
});
test('linear dependency', () {
final dep1 = Mutable(2), dep2 = Computed(() => dep1.value * dep1.value);
final ref = Computed(() => dep2.value.toString());
final log = <String>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, '4');
dep1.value = 3;
expect(ref.value, '9');
expect(log, ['9']);
});
test('dynamic dependency', () {
final depBool = Mutable(false);
final depTrue = Mutable(1), depFalse = Mutable(2);
final ref =
Computed(() => depBool.value ? depTrue.value : depFalse.value);
final log = <int>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, 2);
depBool.value = true;
expect(ref.value, 1);
expect(log, [1]);
});
});
test('single dependency', () {
final dep = Mutable(1);
final ref = Computed(() => dep.value);
final log = <int>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, 1);
dep.value = 2;
expect(ref.value, 2);
expect(log, [2]);
});
test('double dependency', () {
final dep1 = Mutable('John'), dep2 = Mutable('Doe');
final ref = Computed(() => '${dep1.value} ${dep2.value}');
final log = <String>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, 'John Doe');
dep1.value = 'Jane';
dep2.value = 'Roe';
expect(ref.value, 'Jane Roe');
expect(log, ['Jane Doe', 'Jane Roe']);
});
test('linear dependency', () {
final dep1 = Mutable(2), dep2 = Computed(() => dep1.value * dep1.value);
final ref = Computed(() => dep2.value.toString());
final log = <String>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, '4');
dep1.value = 3;
expect(ref.value, '9');
expect(log, ['9']);
});
test('dynamic dependency', () {
final depBool = Mutable(false);
final depTrue = Mutable(1), depFalse = Mutable(2);
final ref =
Computed(() => depBool.value ? depTrue.value : depFalse.value);
final log = <int>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, 2);
depBool.value = true;
expect(ref.value, 1);
expect(log, [1]);
group('error', () {
test('no dependencies', () {
final ref = Computed(() => throw StateError('Failure'));
expect(
() => ref.value,
throwsA(isA<UnhandledError>().having(
(err) => err.error,
'error',
isA<StateError>()
.having((err) => err.message, 'message', 'Failure'))));
});
test('single dependency', () {
final dep = Mutable(1);
final ref = Computed(() =>
dep.value.isNegative ? throw StateError('Failure') : dep.value);
final log = <int>[];
ref.subscribe(Observer.next(log.add));
expect(ref.value, 1);
dep.value = -1;
expect(
() => ref.value,
throwsA(isA<UnhandledError>().having(
(err) => err.error,
'error',
isA<StateError>()
.having((err) => err.message, 'message', 'Failure'))));
dep.value = 2;
expect(ref.value, 2);
expect(log, [2]);
});
});
});
}

0 comments on commit 697d68b

Please sign in to comment.