-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #58 from LoveCommunity/feature/scope
initial `Scope`
- Loading branch information
Showing
12 changed files
with
298 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
|
||
export 'src/observables/observables.dart'; | ||
export 'src/scopes/scopes.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import 'dart:async'; | ||
|
||
import '../scopes/configurable_scope.dart'; | ||
|
||
abstract class Configurable { | ||
FutureOr<void> configure(ConfigurableScope scope); | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
import 'package:typedef_foundation/typedef_foundation.dart'; | ||
|
||
abstract class ScopeExpose { | ||
void expose<T>({ | ||
Object? name, | ||
required Getter<T> expose, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
|
||
abstract class ScopeGet { | ||
T? getOrNull<T>({ | ||
Object? name, | ||
}); | ||
bool has<T>({ | ||
Object? name, | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
export 'configurables/configurable.dart'; | ||
export 'scope_methods/scope_get.dart'; | ||
export 'scope_methods/scope_expose.dart'; | ||
export 'scopes/configurable_scope.dart'; | ||
export 'scopes/scope.dart'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
import 'package:meta/meta.dart'; | ||
import 'package:typedef_foundation/typedef_foundation.dart'; | ||
|
||
import '../scope_methods/scope_expose.dart'; | ||
import 'scope.dart'; | ||
|
||
abstract class ConfigurableScope implements Scope, ScopeExpose { | ||
@internal | ||
factory ConfigurableScope() = _ConfigurableScopeImpl; | ||
} | ||
|
||
class _ConfigurableScopeImpl implements ConfigurableScope { | ||
_ConfigurableScopeImpl(): | ||
_storage = {}; | ||
|
||
final Map<Type, Map<Object?, Function>> _storage; | ||
|
||
@override | ||
T? getOrNull<T>({ | ||
Object? name, | ||
}) { | ||
final getter = _storage[T]?[name] as Getter<T>?; | ||
return getter?.call(); | ||
} | ||
|
||
@override | ||
bool has<T>({ | ||
Object? name, | ||
}) { | ||
return _storage.containsKey(T) && _storage[T]!.containsKey(name); | ||
} | ||
|
||
@override | ||
void expose<T>({ | ||
Object? name, | ||
required Getter<T> expose, | ||
}) { | ||
if (!_storage.containsKey(T)) { | ||
_storage[T] = <Object?, Getter<T>>{}; | ||
} | ||
_storage[T]![name] = expose; | ||
} | ||
} | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
|
||
import 'dart:async'; | ||
|
||
import '../configurables/configurable.dart'; | ||
import '../scope_methods/scope_get.dart'; | ||
import '../shared/build_scope.dart'; | ||
|
||
import 'configurable_scope.dart'; | ||
|
||
abstract class Scope implements ScopeGet { | ||
static FutureOr<Scope> root(List<Configurable> configure) => _scopeRoot(configure); | ||
} | ||
|
||
FutureOr<Scope> _scopeRoot(List<Configurable> configure) => buildScope(configure, ConfigurableScope()); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
|
||
import 'dart:async'; | ||
import 'package:meta/meta.dart'; | ||
|
||
import '../configurables/configurable.dart'; | ||
import '../scopes/configurable_scope.dart'; | ||
import '../scopes/scope.dart'; | ||
|
||
@internal | ||
FutureOr<Scope> buildScope(List<Configurable> configure, ConfigurableScope scope) { | ||
return configure | ||
.fold<FutureOr<void>>(null, (futureOrVoid, configurable) { | ||
return futureOrVoid.then((_) => configurable.configure(scope)); | ||
}) | ||
.then((_) => scope); | ||
} | ||
|
||
extension <T> on FutureOr<T> { | ||
|
||
FutureOr<R> then<R>( | ||
FutureOr<R> Function(T) onValue, { | ||
FutureOr<R> Function(Object error, StackTrace stackTrace)? onError, | ||
}) { | ||
final _this = this; | ||
if (_this is Future<T>) { | ||
return _this.then(onValue, onError: onError); | ||
} else { | ||
return onValue(_this); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
import 'package:test/test.dart'; | ||
import 'package:scopes/scopes.dart'; | ||
|
||
import '../../toolbox/mock_configurable.dart'; | ||
|
||
void main() { | ||
|
||
test('`scope` is `ScopeGet`', () { | ||
|
||
final scope = _MockScope(); | ||
|
||
expect(scope, isA<ScopeGet>()); | ||
}); | ||
|
||
test('`Scope.root` return sync scope if it only has sync configuration', () { | ||
|
||
final scope = Scope.root([ | ||
MockConfigurable((_) {}), | ||
]); | ||
|
||
expect(scope, isA<Scope>()); | ||
}); | ||
|
||
test('`Scope.root` return async scope if it has async configuration', () { | ||
|
||
final scope = Scope.root([ | ||
MockConfigurable((_) async {}), | ||
]); | ||
|
||
expect(scope, isA<Future<Scope>>()); | ||
}); | ||
|
||
test('`scope.has` return false if value not exposed', () async { | ||
|
||
final scope = await Scope.root([]); | ||
|
||
final hasValue = scope.has<String>(); | ||
|
||
expect(hasValue, false); | ||
}); | ||
|
||
test('`scope.has` return true if value exposed', () async { | ||
|
||
final scope = await Scope.root([ | ||
MockConfigurable((scope) { | ||
scope.expose<String>(expose: () => 'a'); | ||
}), | ||
]); | ||
|
||
final hasValue = scope.has<String>(); | ||
|
||
expect(hasValue, true); | ||
}); | ||
|
||
test('`scope.has` return false if value not exposed with name', () async { | ||
|
||
final scope = await Scope.root([]); | ||
|
||
final hasValue = scope.has<String>(name: 'state'); | ||
|
||
expect(hasValue, false); | ||
}); | ||
|
||
test('`scope.has` return true if value exposed with name', () async { | ||
|
||
final scope = await Scope.root([ | ||
MockConfigurable((scope) { | ||
scope.expose<String>( | ||
name: 'state', | ||
expose: () => 'a', | ||
); | ||
}), | ||
]); | ||
|
||
final hasValue = scope.has<String>(name: 'state'); | ||
|
||
expect(hasValue, true); | ||
}); | ||
|
||
test('`scope.getOrNull` return null if value not exposed', () async { | ||
|
||
final scope = await Scope.root([]); | ||
|
||
final value = scope.getOrNull<String>(); | ||
|
||
expect(value, null); | ||
}); | ||
|
||
test('`scope.getOrNull` return value if value exposed', () async { | ||
|
||
final scope = await Scope.root([ | ||
MockConfigurable((scope) { | ||
scope.expose<String>(expose: () => 'a'); | ||
}), | ||
]); | ||
|
||
final value = scope.getOrNull<String>(); | ||
|
||
expect(value, 'a'); | ||
}); | ||
|
||
test('`scope.getOrNull` return null if value not exposed with name', () async { | ||
|
||
final scope = await Scope.root([]); | ||
|
||
final value = scope.getOrNull<String>(name: 'state'); | ||
|
||
expect(value, null); | ||
}); | ||
|
||
test('`scope.getOrNull` return value if value exposed with name', () async { | ||
|
||
final scope = await Scope.root([ | ||
MockConfigurable((scope) { | ||
scope.expose<String>( | ||
name: 'state', | ||
expose: () => 'a', | ||
); | ||
}), | ||
]); | ||
|
||
final value = scope.getOrNull<String>(name: 'state'); | ||
|
||
expect(value, 'a'); | ||
}); | ||
|
||
test('`scope.getOrNull` return value if value exposed with name using async configuration', () async { | ||
|
||
final scope = await Scope.root([ | ||
MockConfigurable((scope) async { | ||
scope.expose<String>( | ||
name: 'state', | ||
expose: () => 'a', | ||
); | ||
}), | ||
]); | ||
|
||
final value = scope.getOrNull<String>(name: 'state'); | ||
|
||
expect(value, 'a'); | ||
}); | ||
} | ||
|
||
class _MockScope implements Scope { | ||
|
||
@override | ||
T? getOrNull<T>({ | ||
Object? name, | ||
}) => throw UnimplementedError(); | ||
|
||
@override | ||
bool has<T>({ | ||
Object? name, | ||
}) => throw UnimplementedError(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
|
||
import 'scopes/scope_test.dart' as scope_test; | ||
|
||
void main() { | ||
scope_test.main(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,10 @@ | ||
|
||
|
||
import 'observables/observables_test.dart' as observalbes_test; | ||
import 'scopes/scopes_test.dart' as scopes_test; | ||
|
||
void main() { | ||
|
||
observalbes_test.main(); | ||
scopes_test.main(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
import 'dart:async'; | ||
import 'package:scopes/scopes.dart'; | ||
|
||
class MockConfigurable implements Configurable { | ||
MockConfigurable(this._configure); | ||
|
||
final FutureOr<void> Function(ConfigurableScope scope) _configure; | ||
|
||
@override | ||
FutureOr<void> configure(ConfigurableScope scope) { | ||
return _configure(scope); | ||
} | ||
} |