Skip to content

Commit

Permalink
Merge pull request #58 from LoveCommunity/feature/scope
Browse files Browse the repository at this point in the history
initial `Scope`
  • Loading branch information
beeth0ven committed Jun 30, 2022
2 parents b1afb95 + dd55b2e commit 59b0094
Show file tree
Hide file tree
Showing 12 changed files with 298 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/scopes.dart
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@

export 'src/observables/observables.dart';
export 'src/scopes/scopes.dart';
8 changes: 8 additions & 0 deletions lib/src/scopes/configurables/configurable.dart
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);
}

9 changes: 9 additions & 0 deletions lib/src/scopes/scope_methods/scope_expose.dart
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,
});
}
9 changes: 9 additions & 0 deletions lib/src/scopes/scope_methods/scope_get.dart
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,
});
}
6 changes: 6 additions & 0 deletions lib/src/scopes/scopes.dart
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';
44 changes: 44 additions & 0 deletions lib/src/scopes/scopes/configurable_scope.dart
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;
}
}

14 changes: 14 additions & 0 deletions lib/src/scopes/scopes/scope.dart
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());
31 changes: 31 additions & 0 deletions lib/src/scopes/shared/build_scope.dart
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);
}
}
}
155 changes: 155 additions & 0 deletions test/scopes/scopes/scope_test.dart
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();
}
6 changes: 6 additions & 0 deletions test/scopes/scopes_test.dart
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();
}
2 changes: 2 additions & 0 deletions test/scopes_test.dart
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();
}
13 changes: 13 additions & 0 deletions test/toolbox/mock_configurable.dart
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);
}
}

0 comments on commit 59b0094

Please sign in to comment.