diff --git a/README.md b/README.md index 6d70669..608c033 100644 --- a/README.md +++ b/README.md @@ -1,250 +1,13 @@ # dart_di -Экспериментальная разработка DI на ЯП Dart +Experimental development of DI in the Dart language - [New Api ENG](/doc/quick_start_en.md) - [New Api RU](/doc/quick_start_ru.md) -## Документация -### Быстрый старт +### Features -Основным классом для всех операций является `DiContainer`. Вы можете зарегистрировать свои зависимости, -получив `ResolvingContext` через метод `bind()` и используя его различные методы регистрации зависимостей. -Далее вы можете получить зависимости с помощью `resolve()`. - -Пример: - -```dart -final container = DiContainer(); -container.bind().toValue(SomeServiceImpl()); -/* -... - */ - -// Метод `resolve` просто возвращает зарегистрированный ранее экземпляр -final someService = container.resolve(); -``` - -### Ленивая инициализация - -Если вам нужно создать объект в момент резолвинга, вы можете использовать ленивую (другими словами, по запросу) инициализацию объекта -с помощью метода `toFactoryN()`. - -Пример: - -```dart -final container = DiContainer(); -// В методе `toFactory` вы просто определяете, как построить экземпляр через фабричную лямбду -container.bind().toFactory( () => SomeServiceImpl() ); -/* -... - */ -// Метод `resolve()` будет создавать экземпляр через зарегистрированную фабричную лямбду каждый раз, когда вы вызываете его -final someService = container.resolve(); -final anotherSomeService = container.resolve(); -assert(someService != anotherSomeService); -``` - -Но обычно у вас есть много типов с разными зависимостями, которые образуют граф зависимостей. - -Пример: - -```dart -class A {} -class B {} - -class C { - final A a; - final B b; - - C(this.a, this.b); -} -``` - - -Если вам нужно зарегистрировать некоторый тип, зависящий от других типов из контейнера, -вы можете использовать методы `toFactory1` - `toFactory8`, где число в конце, -является количеством запрошенных через аргументы типов зависимостей. -(Обратите внимание, что вам нужно определить все зависимости в аргументах - `toFactory2`). - - -Пример: - -```dart -class SomeService { - final A a; - final B b; - - SomeService(this.a, this.b); -} - -final container = DiContainer(); -container.bind(A::class).toFactory (() => A()); -container.bind(B::class).toFactory (() => B()); - -/// В фабричной лямбде вы определяете, как построить зависимость от других зависимостей -/// (Порядок разрешенных экземпляров соответствует порядку типов аргументов) -container.bind().toFactory2((a, b) => SomeService(a, b)); - -/* -... - */ - -/// Получаем экземпляр `SomeService` через resolve своих зависимостей. -/// В нашем случае - это resolve A и B -/// Внимание!!! То, что он будет создавать новые экземпляры A и B каждый раз, когда вы вызываете `resolve` SomeService -final someService = container.resolve(); -``` - -### Время жизни экземпляров и контроль области видимости - -Если вы хотите создать экземпляр зарегистрированной зависимости только один раз, -и вам нужно получить/разрешить зависимость много раз в контейнере, то вы можете зарегистрировать -свою зависимость с добавлением `asSingeton()`. Например: - -```dart -final container = DiContainer(); -container.bind() - .toFactory(() => A()) - .asSingleton(); - -container - .bind() - .toFactory(() => B()); - .asSingleton(); - -container.bind().toFactory2((a, b) -> SomeService(a, b)); - -// Код выше означает: Контейнер, регистрирует создание A и B только в первый раз, когда оно будет запрошен, -// и регистрирует создание SomeService каждый раз, когда оно будет запрошен. - -final a = container.resolve(); -final b = container.resolve(); -final anotherA = container.resolve(); -final anotherB = container.resolve(); - -assert(a == anotherA && b == anotherB); - -final someService = container.resolve(); -final anotherSomeService = container.resolve(); - -assert(someService != anotherSomeService); -``` - -Если вы хотите сразу создать свой зарегистрированный экземпляр, вы можете вызвать `resolve()`. Например: - - -```dart -final container = DiContainer(); -// Это заставит создать зависимость после регистрации -container.bind () - .toFactory(() => SomeService()) - .asSingleton() - .resolve(); -``` - -Когда вы работаете со сложным приложением, в большинстве случаев вы можете работать со многими модулями с собственными зависимостями. -Эти модули могут быть настроены различными `DiContainer`-ми. И вы можете прикрепить контейнер к другому, как родительский. -В этом случае родительские зависимости будут видны для дочернего контейнера, -и через него вы можете формировать различные области видимости зависимостей. Например: - -```dart -final parentContainer = DiContainer(); -parentContainer.bind().toFactory(() => A()) - -final childContainer = DiContainer(parentContainer); -// Обратите внимание, что родительская зависимость A видна для дочернего контейнера -final a = childContainer.resolve(); - -/* -// Но следующий код потерпит неудачу с ошибкой, потому что родитель не знает о своем потомке. -final parentContainer = DiContainer(); -final childContainer = DiContainer(); -childContainer.bind().toFactory(() => A()); - -// Выдает ошибку -final a = parentContainer.resolve(); - */ -``` - -### Структура библиотеки - -Библиотека состоит из DiContainer и Resolver. -DiContainer - это контейнер со всеми Resolver для разных типов. А `Resolver` - это просто объект, который знает, как разрешить данный тип. -Многие из resolver-ов обернуты другими, поэтому они могут быть составлены для разных вариантов использования. -Resolver - интерфейс, поэтому он имеет много реализаций. Основным является ResolvingContext. -Вы можете думать об этом как об объекте контекста, который имеет вспомогательные методы для создания различных вариантов resolver-ов (`toFactory`,` toValue`, `asSingleton`). -Но все они просто используют метод `toResolver` для определения некоторого корневого resolver в контексте. -Когда вы запрашиваете тип из контейнера с помощью метода `resolve()`, он просто находит контекст для типа и вызывает корневой resolver, который может вызывать другие resolver-ы. - - -Пример (из ```example```): - -```dart -import 'dart:async'; -import 'package:meta/meta.dart'; -import 'package:dart_di/dart_di.dart'; - -void main() async { - final dataModule = new DiContainer() - ..bind().toValue(new ApiClientMock()) - ..bind() - .toFactory1((c) => new NetworkDataRepository(c)) - ..bind().toFactory1((s) => new DataBloc(s)); - - final dataBloc = dataModule.resolve(); - dataBloc.data.listen((d) => print('Received data: $d'), - onError: (e) => print('Error: $e'), onDone: () => print('DONE')); - - await dataBloc.fetchData(); -} - -class DataBloc { - final DataRepository _dataRepository; - - Stream get data => _dataController.stream; - StreamController _dataController = new StreamController.broadcast(); - - DataBloc(this._dataRepository); - - Future fetchData() async { - try { - _dataController.sink.add(await _dataRepository.getData()); - } catch (e) { - _dataController.sink.addError(e); - } - } - - void dispose() { - _dataController.close(); - } -} - -abstract class DataRepository { - Future getData(); -} - -class NetworkDataRepository implements DataRepository { - final ApiClient _apiClient; - final _token = 'token'; - - NetworkDataRepository(this._apiClient); - - @override - Future getData() async => await _apiClient.sendRequest( - url: 'www.google.com', token: _token, requestBody: {'type': 'data'}); -} - -abstract class ApiClient { - Future sendRequest({@required String url, String token, Map requestBody}); -} - -class ApiClientMock implements ApiClient { - @override - Future sendRequest( - {@required String url, String token, Map requestBody}) async { - return 'hello world'; - } -} -``` +- [x] Scope +- [x] Sub scope +- [x] Initialization instance with name diff --git a/example/bin/main.dart b/example/bin/main.dart index 1affdec..f9c113a 100644 --- a/example/bin/main.dart +++ b/example/bin/main.dart @@ -1,15 +1,51 @@ import 'dart:async'; import 'package:meta/meta.dart'; -import 'package:dart_di/dart_di.dart'; +import 'package:dart_di/scope.dart'; +import 'package:dart_di/module.dart'; + +class AppModule extends Module { + @override + void builder(Scope currentScope) { + bind().withName("apiClientMock").toInstance(ApiClientMock()); + bind().withName("apiClientImpl").toInstance(ApiClientImpl()); + } +} + +class FeatureModule extends Module { + bool isMock; + + FeatureModule({required this.isMock}); + + @override + void builder(Scope currentScope) { + bind() + .withName("networkRepo") + .toProvide( + () => NetworkDataRepository( + currentScope.resolve( + named: isMock ? "apiClientMock" : "apiClientImpl", + ), + ), + ) + .singeltone(); + bind().toProvide( + () => DataBloc( + currentScope.resolve(named: "networkRepo"), + ), + ); + } +} void main() async { - final dataModule = new DiContainer() - ..bind().toValue(new ApiClientMock()) - ..bind() - .toFactory1((c) => new NetworkDataRepository(c)) - ..bind().toFactory1((s) => new DataBloc(s)); + final scope = openRootScope().installModules([ + AppModule(), + ]); - final dataBloc = dataModule.resolve(); + final subScope = scope + .openSubScope("featureScope") + .installModules([FeatureModule(isMock: true)]); + + final dataBloc = subScope.resolve(); dataBloc.data.listen((d) => print('Received data: $d'), onError: (e) => print('Error: $e'), onDone: () => print('DONE')); @@ -60,6 +96,14 @@ class ApiClientMock implements ApiClient { @override Future sendRequest( {@required String? url, String? token, Map? requestBody}) async { - return 'hello world'; + return 'Local Data'; + } +} + +class ApiClientImpl implements ApiClient { + @override + Future sendRequest( + {@required String? url, String? token, Map? requestBody}) async { + return 'Network data'; } } diff --git a/example/bin/main_experiment.dart b/example/bin/main_experiment.dart deleted file mode 100644 index fa2b968..0000000 --- a/example/bin/main_experiment.dart +++ /dev/null @@ -1,109 +0,0 @@ -import 'dart:async'; -import 'package:meta/meta.dart'; -import 'package:dart_di/experimental/scope.dart'; -import 'package:dart_di/experimental/module.dart'; - -class AppModule extends Module { - @override - void builder(Scope currentScope) { - bind().withName("apiClientMock").toInstance(ApiClientMock()); - bind().withName("apiClientImpl").toInstance(ApiClientImpl()); - } -} - -class FeatureModule extends Module { - bool isMock; - - FeatureModule({required this.isMock}); - - @override - void builder(Scope currentScope) { - bind() - .withName("networkRepo") - .toProvide( - () => NetworkDataRepository( - currentScope.resolve( - named: isMock ? "apiClientMock" : "apiClientImpl", - ), - ), - ) - .singeltone(); - bind().toProvide( - () => DataBloc( - currentScope.resolve(named: "networkRepo"), - ), - ); - } -} - -void main() async { - final scope = openRootScope().installModules([ - AppModule(), - ]); - - final subScope = scope - .openSubScope("featureScope") - .installModules([FeatureModule(isMock: true)]); - - final dataBloc = subScope.resolve(); - dataBloc.data.listen((d) => print('Received data: $d'), - onError: (e) => print('Error: $e'), onDone: () => print('DONE')); - - await dataBloc.fetchData(); -} - -class DataBloc { - final DataRepository _dataRepository; - - Stream get data => _dataController.stream; - StreamController _dataController = new StreamController.broadcast(); - - DataBloc(this._dataRepository); - - Future fetchData() async { - try { - _dataController.sink.add(await _dataRepository.getData()); - } catch (e) { - _dataController.sink.addError(e); - } - } - - void dispose() { - _dataController.close(); - } -} - -abstract class DataRepository { - Future getData(); -} - -class NetworkDataRepository implements DataRepository { - final ApiClient _apiClient; - final _token = 'token'; - - NetworkDataRepository(this._apiClient); - - @override - Future getData() async => await _apiClient.sendRequest( - url: 'www.google.com', token: _token, requestBody: {'type': 'data'}); -} - -abstract class ApiClient { - Future sendRequest({@required String url, String token, Map requestBody}); -} - -class ApiClientMock implements ApiClient { - @override - Future sendRequest( - {@required String? url, String? token, Map? requestBody}) async { - return 'Local Data'; - } -} - -class ApiClientImpl implements ApiClient { - @override - Future sendRequest( - {@required String? url, String? token, Map? requestBody}) async { - return 'Network data'; - } -} diff --git a/lib/experimental/binding.dart b/lib/binding.dart similarity index 100% rename from lib/experimental/binding.dart rename to lib/binding.dart diff --git a/lib/dart_di.dart b/lib/dart_di.dart index b673688..7cb51ab 100644 --- a/lib/dart_di.dart +++ b/lib/dart_di.dart @@ -1,5 +1,6 @@ library dart_di; -export 'package:dart_di/di_container.dart'; -export 'package:dart_di/resolvers/resolvers.dart'; -export 'package:dart_di/resolvers/resolving_context.dart'; +export 'package:dart_di/scope.dart'; +export 'package:dart_di/module.dart'; +export 'package:dart_di/binding.dart'; +export 'package:dart_di/di.dart'; diff --git a/lib/experimental/di.dart b/lib/di.dart similarity index 91% rename from lib/experimental/di.dart rename to lib/di.dart index 3f8578c..9f028e1 100644 --- a/lib/experimental/di.dart +++ b/lib/di.dart @@ -1,4 +1,4 @@ -import 'package:dart_di/experimental/scope.dart'; +import 'package:dart_di/scope.dart'; Scope? _rootScope = null; diff --git a/lib/di_container.dart b/lib/di_container.dart deleted file mode 100644 index 25a8842..0000000 --- a/lib/di_container.dart +++ /dev/null @@ -1,75 +0,0 @@ -import 'package:dart_di/resolvers/resolving_context.dart'; - -/** - * Контейнер - это объект, которой хранит все резолверы зависимостей. - */ -class DiContainer { - final DiContainer? _parent; - - final _resolvers = {}; - - DiContainer([this._parent]); - -/** - * Добавляет resolver зависимостей типа [T] в контейнер. - * Обратите внимание, что перезапись значений внутри одного контейнера запрещена. - * @return - возвращает [ResolvingContext] или [StateError] - */ - ResolvingContext bind() { - var context = ResolvingContext(this); - if (hasInTree()) { - throw StateError( - 'Dependency of type `$T` is already exist in containers tree'); - } - - _resolvers[T] = context; - return context; - } - - /** - * Возвращает разрешенную зависимость, определенную параметром типа [T]. - * Выдает [StateError], если зависимость не может быть разрешена. - * Если вы хотите получить [null], если зависимость не может быть разрешена, - * то используйте вместо этого [tryResolve] - * @return - возвращает объект типа [T] или [StateError] - */ - T resolve() { - var resolved = tryResolve(); - if (resolved != null) { - return resolved; - } else { - throw StateError( - 'Can\'t resolve dependency `$T`. Maybe you forget register it?'); - } - } - - /** - * Возвращает разрешенную зависимость типа [T] или null, если она не может быть разрешена. - */ - T? tryResolve() { - var resolver = _resolvers[T]; - if (resolver != null) { - return resolver.resolve(); - } else { - return _parent?.tryResolve(); - } - } - - /** - * Возвращает true, если у этого контейнера есть средство разрешения зависимостей для типа [T]. - * Если вы хотите проверить его для всего дерева контейнеров, используйте вместо него [hasInTree]. - * @return - возвращает булево значение - */ - bool has() { - return _resolvers.containsKey(T); - } - -/** - * Возвращает true, если контейнер или его родители содержат средство разрешения зависимостей для типа [T]. - * Если вы хотите проверить его только для этого контейнера, используйте вместо него [has]. - * @return - возвращает булево значение - */ - bool hasInTree() { - return has() || (_parent != null && _parent!.hasInTree()); - } -} diff --git a/lib/experimental/factory.dart b/lib/factory.dart similarity index 56% rename from lib/experimental/factory.dart rename to lib/factory.dart index 79fedd0..a7f73bd 100644 --- a/lib/experimental/factory.dart +++ b/lib/factory.dart @@ -1,4 +1,4 @@ -import 'package:dart_di/experimental/scope.dart'; +import 'package:dart_di/scope.dart'; abstract class Factory { T createInstance(Scope scope); diff --git a/lib/experimental/module.dart b/lib/module.dart similarity index 93% rename from lib/experimental/module.dart rename to lib/module.dart index f760594..d25d14e 100644 --- a/lib/experimental/module.dart +++ b/lib/module.dart @@ -1,7 +1,7 @@ import 'dart:collection'; -import 'package:dart_di/experimental/binding.dart'; -import 'package:dart_di/experimental/scope.dart'; +import 'package:dart_di/binding.dart'; +import 'package:dart_di/scope.dart'; /// RU: Класс Module является основой для пользовательских модулей. /// Этот класс нужен для инициализации [Scope]. diff --git a/lib/resolvers/factory_resolver.dart b/lib/resolvers/factory_resolver.dart deleted file mode 100644 index 6a70a7a..0000000 --- a/lib/resolvers/factory_resolver.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:dart_di/resolvers/resolver.dart'; - -/** - * Разрешает зависимость для фабричной функции - */ -class FactoryResolver extends Resolver { - final T Function() _factory; - - FactoryResolver(this._factory); - - @override - T resolve() { - return _factory(); - } -} diff --git a/lib/resolvers/resolver.dart b/lib/resolvers/resolver.dart deleted file mode 100644 index e99fc65..0000000 --- a/lib/resolvers/resolver.dart +++ /dev/null @@ -1,11 +0,0 @@ -/** - * Resolver - это абстракция, которая определяет, - * как контейнер будет разрешать зависимость - */ -abstract class Resolver { - /** - * Разрешает зависимость типа [T] - * @return - возвращает объект типа [T] - */ - T? resolve(); -} diff --git a/lib/resolvers/resolvers.dart b/lib/resolvers/resolvers.dart deleted file mode 100644 index 6640ead..0000000 --- a/lib/resolvers/resolvers.dart +++ /dev/null @@ -1,7 +0,0 @@ -/// Resolvers определяет, как разрешить зависимость для контейнера -library resolvers; - -export 'resolver.dart'; -export 'factory_resolver.dart'; -export 'value_resolver.dart'; -export 'singelton_resolver.dart'; diff --git a/lib/resolvers/resolving_context.dart b/lib/resolvers/resolving_context.dart deleted file mode 100644 index 65cb738..0000000 --- a/lib/resolvers/resolving_context.dart +++ /dev/null @@ -1,172 +0,0 @@ -import 'package:dart_di/di_container.dart'; -import 'package:dart_di/resolvers/factory_resolver.dart'; -import 'package:dart_di/resolvers/resolver.dart'; -import 'package:dart_di/resolvers/singelton_resolver.dart'; -import 'package:dart_di/resolvers/value_resolver.dart'; - -class ResolvingContext extends Resolver { - /// Корневой резолвер - Resolver get resolver { - return _resolver as Resolver; - } - - DiContainer _container; - - late Resolver _resolver; - - ResolvingContext(this._container); - -/** - * Разрешает зависимость типа [T] - * @return - возвращает объект типа [T] - */ - @override - T resolve() { - _verify(); - return _resolver.resolve(); - } - - /** - * Добавляет резолвер в качестве корневого резолвера - * С помощью этого метода вы можете добавить любой - * пользовательский резолвер - */ - ResolvingContext toResolver(Resolver resolver) { - _resolver = resolver; - return this; - } - - /** - * Создать резолвер значения - */ - ResolvingContext toValue(T value) { - Resolver resolver = ValueResolver(value as TImpl); - return toResolver(resolver); - } - - /** - * Преобразователь в сингелтон - */ - ResolvingContext asSingleton() { - return toResolver(SingletonResolver(resolver)); - } - - /** - * Создать фабричный resolver без каких-либо зависимостей - */ - ResolvingContext toFactory(TImpl Function() factory) { - Resolver resolver = FactoryResolver(factory); - return toResolver(resolver); - } - - /** - * Создать фабричный resolver с 1 зависимостью от контейнера - */ - ResolvingContext toFactory1(T Function(T1) factory) { - Resolver resolver = - FactoryResolver(() => factory(_container.resolve())); - return toResolver(resolver); - } - - /** - * Создать фабричный resolver с 2 зависимостями от контейнера - */ - ResolvingContext toFactory2(T Function(T1, T2) factory) { - Resolver resolver = FactoryResolver( - () => factory(_container.resolve(), _container.resolve())); - return toResolver(resolver); - } - - /** - * Создать фабричный resolver с 3 зависимостями от контейнера - */ - ResolvingContext toFactory3(T Function(T1, T2, T3) factory) { - Resolver resolver = FactoryResolver(() => factory( - _container.resolve(), - _container.resolve(), - _container.resolve())); - return toResolver(resolver); - } - - /** - * Создать фабричный resolver с 4 зависимостями от контейнера - */ - ResolvingContext toFactory4( - T Function(T1, T2, T3, T4) factory) { - Resolver resolver = FactoryResolver(() => factory( - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve())); - return toResolver(resolver); - } - - /** - * Создать фабричный resolver с 5 зависимостями от контейнера - */ - ResolvingContext toFactory5( - T Function(T1, T2, T3, T4, T5) factory) { - Resolver resolver = FactoryResolver(() => factory( - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve())); - return toResolver(resolver); - } - - /** - * Создать фабричный resolver с 6 зависимостями от контейнера - */ - ResolvingContext toFactory6( - T Function(T1, T2, T3, T4, T5, T6) factory) { - Resolver resolver = FactoryResolver(() => factory( - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve())); - return toResolver(resolver); - } - - /** - * Создать фабричный resolver с 7 зависимостями от контейнера - */ - ResolvingContext toFactory7( - T Function(T1, T2, T3, T4, T5, T6, T7) factory) { - Resolver resolver = FactoryResolver(() => factory( - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve())); - return toResolver(resolver); - } - - /** - * Создать фабричный resolver с 8 зависимостями от контейнера - */ - ResolvingContext toFactory8( - T Function(T1, T2, T3, T4, T5, T6, T7, T8) factory) { - Resolver resolver = FactoryResolver(() => factory( - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve(), - _container.resolve())); - return toResolver(resolver); - } - - void _verify() { - if (_resolver == null) { - throw StateError("Can\'t resolve T without any resolvers. " + - "Please check, may be you didn\'t do anything after bind()"); - } - } -} diff --git a/lib/resolvers/singelton_resolver.dart b/lib/resolvers/singelton_resolver.dart deleted file mode 100644 index 27906ba..0000000 --- a/lib/resolvers/singelton_resolver.dart +++ /dev/null @@ -1,16 +0,0 @@ -import 'package:dart_di/resolvers/resolver.dart'; - -class SingletonResolver extends Resolver { - Resolver _decoratedResolver; - T? _value = null; - - SingletonResolver(this._decoratedResolver); - - @override - T? resolve() { - if (_value == null) { - _value = _decoratedResolver.resolve(); - } - return _value; - } -} diff --git a/lib/resolvers/value_resolver.dart b/lib/resolvers/value_resolver.dart deleted file mode 100644 index 7460312..0000000 --- a/lib/resolvers/value_resolver.dart +++ /dev/null @@ -1,15 +0,0 @@ -import 'package:dart_di/resolvers/resolver.dart'; - -/** - * Разрешает зависимость для значения - */ -class ValueResolver extends Resolver { - T _value; - - ValueResolver(this._value); - - @override - T resolve() { - return _value; - } -} diff --git a/lib/experimental/scope.dart b/lib/scope.dart similarity index 97% rename from lib/experimental/scope.dart rename to lib/scope.dart index 3cc40e1..dd76f5c 100644 --- a/lib/experimental/scope.dart +++ b/lib/scope.dart @@ -1,7 +1,7 @@ import 'dart:collection'; -import 'package:dart_di/experimental/binding.dart'; -import 'package:dart_di/experimental/module.dart'; +import 'package:dart_di/binding.dart'; +import 'package:dart_di/module.dart'; Scope openRootScope() => Scope(null); diff --git a/pubspec.yaml b/pubspec.yaml index ce7dd6a..65d73d0 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: dart_di description: Experimental Dependency Injection library. -version: 0.0.2 +version: 0.1.0 author: Sergey Penkovsky homepage: locahost diff --git a/test/experimental/binding_test.dart b/test/binding_test.dart similarity index 99% rename from test/experimental/binding_test.dart rename to test/binding_test.dart index db57e7b..e976d7b 100644 --- a/test/experimental/binding_test.dart +++ b/test/binding_test.dart @@ -1,4 +1,4 @@ -import 'package:dart_di/experimental/binding.dart'; +import 'package:dart_di/binding.dart'; import 'package:test/test.dart'; void main() { diff --git a/test/di_container_test.dart b/test/di_container_test.dart deleted file mode 100644 index 5a5a3d8..0000000 --- a/test/di_container_test.dart +++ /dev/null @@ -1,458 +0,0 @@ -import 'package:dart_di/di_container.dart'; -import 'package:dart_di/resolvers/resolver.dart'; -import 'package:test/test.dart'; -import 'package:mockito/mockito.dart'; - -void main() { - group('Without parent', () { - test('Container bind throws state error if it\'s already has resolver', - () { - final container = new DiContainer(); - container.bind().toResolver(_makeResolver(5)); - - expect(() => container.bind().toResolver(_makeResolver(3)), - throwsA(isA())); - }); - - test("Container resolves value after adding a dependency", () { - final expectedValue = 3; - final container = new DiContainer(); - container.bind().toResolver(_makeResolver(expectedValue)); - expect(container.resolve(), expectedValue); - }); - - test("Container throws state error if the value can't be resolved", () { - final container = DiContainer(); - expect(() => container.resolve(), throwsA(isA())); - }); - - test("Container has() returns true if it has resolver", () { - final expectedValue = 5; - final container = new DiContainer(); - container.bind().toResolver(_makeResolver(expectedValue)); - expect(container.has(), true); - }); - - test("Container has() returns false if it hasn't resolver", () { - final container = new DiContainer(); - expect(container.has(), false); - }); - - test("Container hasInTree() returns true if it has resolver", () { - final expectedValue = 5; - final container = DiContainer(); - container.bind().toResolver(_makeResolver(expectedValue)); - expect(container.hasInTree(), true); - }); - - test("Container hasInTree() returns true if it hasn`t resolver", () { - final container = DiContainer(); - expect(container.hasInTree(), false); - }); - }); - - group('With parent', () { - test( - "Container bind() throws state error (if it's parent already has a resolver)", - () { - final parentContainer = new DiContainer(); - final container = new DiContainer(parentContainer); - - parentContainer.bind().toResolver(_makeResolver(5)); - - expect(() => container.bind().toResolver(_makeResolver(3)), - throwsA(isA())); - }); - - test("Container resolve() returns a value from parent container", () { - final expectedValue = 5; - final parentContainer = DiContainer(); - final container = DiContainer(parentContainer); - - parentContainer.bind().toResolver(_makeResolver(expectedValue)); - - expect(container.resolve(), expectedValue); - }); - - test("Container resolve() returns a several value from parent container", - () { - final expectedIntValue = 5; - final expectedStringValue = "Hello world"; - final parentContainer = DiContainer(); - final container = DiContainer(parentContainer); - - parentContainer.bind().toResolver(_makeResolver(expectedIntValue)); - parentContainer - .bind() - .toResolver(_makeResolver(expectedStringValue)); - - expect(container.resolve(), expectedIntValue); - expect(container.resolve(), expectedStringValue); - }); - - test("Container resolve() throws a state error if parent hasn't value too", - () { - final parentContainer = DiContainer(); - final container = DiContainer(parentContainer); - expect(() => container.resolve(), throwsA(isA())); - }); - - test("Container has() returns false if parent has a resolver", () { - final parentContainer = DiContainer(); - final container = DiContainer(parentContainer); - - parentContainer.bind().toResolver(_makeResolver(5)); - - expect(container.has(), false); - }); - - test("Container has() returns false if parent hasn't a resolver", () { - final parentContainer = DiContainer(); - final container = DiContainer(parentContainer); - - expect(container.has(), false); - }); - - test("Container hasInTree() returns true if parent has a resolver", () { - final parentContainer = DiContainer(); - final container = DiContainer(parentContainer); - - parentContainer.bind().toResolver(_makeResolver(5)); - - expect(container.hasInTree(), true); - }); - - test("Test asSingelton", () { - final expectedIntValue = 10; - final containerA = DiContainer(); - final containerB = DiContainer(containerA); - - containerA.bind().toValue(expectedIntValue).asSingleton(); - - expect(containerB.resolve(), expectedIntValue); - }); - - test("Child container can resolve parent container's value", () { - final containerA = DiContainer(); - final a = AA(); - containerA.bind().toValue(a); - - final containerB = DiContainer(containerA); - final containerC = DiContainer(containerB); - expect(containerC.resolve(), a); - }); - }); - - test("Bind to the factory resolves with value", () { - final container = DiContainer(); - final a = AA(); - container.bind().toFactory(() => a); - - expect(container.resolve(), a); - }); - - test("Bind to the factory resolves with value", () { - final container = DiContainer(); - final a = AA(); - container.bind().toValue(a); - container.bind().toFactory1((a) => DependOnA(a)); - - expect(container.resolve().a, a); - }); - - test("Bind to the factory resolves with 2 value", () { - final container = DiContainer(); - final a = AA(); - final b = BB(); - container.bind().toValue(a); - container.bind().toValue(b); - container.bind().toFactory2((a, b) => DependOnAB(a, b)); - - expect(container.resolve().a, a); - expect(container.resolve().b, b); - }); - - test("Bind to the factory resolves with 3 value", () { - final container = DiContainer(); - final a = AA(); - final b = BB(); - final c = CC(); - container.bind().toValue(a); - container.bind().toValue(b); - container.bind().toValue(c); - container - .bind() - .toFactory3((a, b, c) => DependOnABC(a, b, c)); - - expect(container.resolve().a, a); - expect(container.resolve().b, b); - expect(container.resolve().c, c); - }); - - test("Bind to the factory resolves with 4 value", () { - final container = DiContainer(); - final a = AA(); - final b = BB(); - final c = CC(); - final d = DD(); - container.bind().toValue(a); - container.bind().toValue(b); - container.bind().toValue(c); - container.bind().toValue(d); - container - .bind() - .toFactory4((a, b, c, d) => DependOnABCD(a, b, c, d)); - - expect(container.resolve().a, a); - expect(container.resolve().b, b); - expect(container.resolve().c, c); - expect(container.resolve().d, d); - }); - - test("Bind to the factory resolves with 5 value", () { - final container = DiContainer(); - final a = AA(); - final b = BB(); - final c = CC(); - final d = DD(); - final e = EE(); - container.bind().toValue(a); - container.bind().toValue(b); - container.bind().toValue(c); - container.bind().toValue(d); - container.bind().toValue(e); - container.bind().toFactory5( - (a, b, c, d, e) => DependOnABCDE(a, b, c, d, e)); - - expect(container.resolve().a, a); - expect(container.resolve().b, b); - expect(container.resolve().c, c); - expect(container.resolve().d, d); - expect(container.resolve().e, e); - }); - - test("Bind to the factory resolves with 6 value", () { - final container = DiContainer(); - final a = AA(); - final b = BB(); - final c = CC(); - final d = DD(); - final e = EE(); - final f = FF(); - container.bind().toValue(a); - container.bind().toValue(b); - container.bind().toValue(c); - container.bind().toValue(d); - container.bind().toValue(e); - container.bind().toValue(f); - container.bind().toFactory6( - (a, b, c, d, e, f) => DependOnABCDEF(a, b, c, d, e, f)); - - expect(container.resolve().a, a); - expect(container.resolve().b, b); - expect(container.resolve().c, c); - expect(container.resolve().d, d); - expect(container.resolve().e, e); - expect(container.resolve().f, f); - }); - - test("Bind to the factory resolves with 7 value", () { - final container = DiContainer(); - final a = AA(); - final b = BB(); - final c = CC(); - final d = DD(); - final e = EE(); - final f = FF(); - final g = GG(); - container.bind().toValue(a); - container.bind().toValue(b); - container.bind().toValue(c); - container.bind().toValue(d); - container.bind().toValue(e); - container.bind().toValue(f); - container.bind().toValue(g); - container.bind().toFactory7( - (a, b, c, d, e, f, g) => DependOnABCDEFG(a, b, c, d, e, f, g)); - - expect(container.resolve().a, a); - expect(container.resolve().b, b); - expect(container.resolve().c, c); - expect(container.resolve().d, d); - expect(container.resolve().e, e); - expect(container.resolve().f, f); - expect(container.resolve().g, g); - }); - - test("Bind to the factory resolves with 8 value", () { - final container = DiContainer(); - final a = AA(); - final b = BB(); - final c = CC(); - final d = DD(); - final e = EE(); - final f = FF(); - final g = GG(); - final h = HH(); - container.bind().toValue(a); - container.bind().toValue(b); - container.bind().toValue(c); - container.bind().toValue(d); - container.bind().toValue(e); - container.bind().toValue(f); - container.bind().toValue(g); - container.bind().toValue(h); - container.bind().toFactory8( - (a, b, c, d, e, f, g, h) => DependOnABCDEFGH(a, b, c, d, e, f, g, h)); - - expect(container.resolve().a, a); - expect(container.resolve().b, b); - expect(container.resolve().c, c); - expect(container.resolve().d, d); - expect(container.resolve().e, e); - expect(container.resolve().f, f); - expect(container.resolve().g, g); - expect(container.resolve().h, h); - }); -} - -ResolverMock _makeResolver(T expectedValue) { - final resolverMock = new ResolverMock(); - when(resolverMock.resolve()).thenReturn(expectedValue); - return resolverMock; -} - -class ResolverMock extends Mock implements Resolver {} - -abstract class A {} - -class AA implements A {} - -abstract class B {} - -class BB implements B {} - -abstract class C {} - -class CC implements C {} - -abstract class D {} - -class DD implements D {} - -abstract class E {} - -class EE implements E {} - -abstract class F {} - -class FF implements F {} - -abstract class G {} - -class GG implements G {} - -abstract class H {} - -class HH implements H {} - -class DependOnA { - final A a; - - DependOnA(this.a) : assert(a != null); -} - -class DependOnAB { - final A a; - final B b; - - DependOnAB(this.a, this.b) : assert(a != null && b != null); -} - -class DependOnABC { - final A a; - final B b; - final C c; - - DependOnABC(this.a, this.b, this.c) - : assert(a != null && b != null && c != null); -} - -class DependOnABCD { - final A a; - final B b; - final C c; - final D d; - - DependOnABCD(this.a, this.b, this.c, this.d) - : assert(a != null && b != null && c != null && d != null); -} - -class DependOnABCDE { - final A a; - final B b; - final C c; - final D d; - final E e; - - DependOnABCDE(this.a, this.b, this.c, this.d, this.e) - : assert(a != null && b != null && c != null && d != null && e != null); -} - -class DependOnABCDEF { - final A a; - final B b; - final C c; - final D d; - final E e; - final F f; - - DependOnABCDEF(this.a, this.b, this.c, this.d, this.e, this.f) - : assert(a != null && - b != null && - c != null && - d != null && - e != null && - f != null); -} - -class DependOnABCDEFG { - final A a; - final B b; - final C c; - final D d; - final E e; - final F f; - final G g; - - DependOnABCDEFG(this.a, this.b, this.c, this.d, this.e, this.f, this.g) - : assert(a != null && - b != null && - c != null && - d != null && - e != null && - f != null && - g != null); -} - -class DependOnABCDEFGH { - final A a; - final B b; - final C c; - final D d; - final E e; - final F f; - final G g; - final H h; - - DependOnABCDEFGH( - this.a, this.b, this.c, this.d, this.e, this.f, this.g, this.h) - : assert(a != null && - b != null && - c != null && - d != null && - e != null && - f != null && - g != null && - h != null); -} diff --git a/test/resolvers/factory_resolver_test.dart b/test/resolvers/factory_resolver_test.dart deleted file mode 100644 index 4339ecb..0000000 --- a/test/resolvers/factory_resolver_test.dart +++ /dev/null @@ -1,27 +0,0 @@ -import 'package:dart_di/resolvers/factory_resolver.dart'; -import 'package:test/test.dart'; -import 'package:mockito/mockito.dart' as mockito; - -void main() { - test('Factory resolver resolves with factory', () { - const expected = 3; - final factoryResolver = new FactoryResolver(() => expected); - - expect(factoryResolver.resolve(), expected); - }); - - test('Factory creates value only after resolve() call', () { - final spy = new SpyMock(); - final factoryResolver = new FactoryResolver(() => spy.onFactory()); - - mockito.verifyNever(spy.onFactory()); - factoryResolver.resolve(); - mockito.verify(spy.onFactory()); - }); -} - -abstract class Spy { - void onFactory(); -} - -class SpyMock extends mockito.Mock implements Spy {} diff --git a/test/resolvers/singelton_resolver_test.dart b/test/resolvers/singelton_resolver_test.dart deleted file mode 100644 index 197b890..0000000 --- a/test/resolvers/singelton_resolver_test.dart +++ /dev/null @@ -1,36 +0,0 @@ -import 'package:dart_di/resolvers/factory_resolver.dart'; -import 'package:dart_di/resolvers/singelton_resolver.dart'; -import 'package:test/test.dart'; -import 'package:mockito/mockito.dart' as mockito; - -void main() { - test( - 'Not singleton resolver resolves different values after multiple resolve() calls', - () { - const callCount = 3; - final spy = new SpyMock(); - final factoryResolver = new FactoryResolver(() => spy..onFactory()); - - for (var i = 0; i < callCount; i++) factoryResolver.resolve(); - - mockito.verify(spy.onFactory()).called(callCount); - }); - - test('Singleton resolver resolves same value after multiple resolve() calls', - () { - const callCount = 3; - final spy = new SpyMock(); - final singletonResolver = - new SingletonResolver(new FactoryResolver(() => spy..onFactory())); - - for (var i = 0; i < callCount; i++) singletonResolver.resolve(); - - mockito.verify(spy.onFactory()).called(1); - }); -} - -abstract class Spy { - void onFactory(); -} - -class SpyMock extends mockito.Mock implements Spy {} diff --git a/test/resolvers/value_resolver_test.dart b/test/resolvers/value_resolver_test.dart deleted file mode 100644 index 1899fa9..0000000 --- a/test/resolvers/value_resolver_test.dart +++ /dev/null @@ -1,11 +0,0 @@ -import 'package:dart_di/resolvers/value_resolver.dart'; -import 'package:test/test.dart'; - -void main() { - test('Value resolver resolves with selected value', () { - var a = 3; - final valResolver = new ValueResolver(a); - - expect(valResolver.resolve(), a); - }); -} diff --git a/test/experimental/scope_test.dart b/test/scope_test.dart similarity index 96% rename from test/experimental/scope_test.dart rename to test/scope_test.dart index 96017cb..99bb15c 100644 --- a/test/experimental/scope_test.dart +++ b/test/scope_test.dart @@ -1,5 +1,5 @@ -import 'package:dart_di/experimental/module.dart'; -import 'package:dart_di/experimental/scope.dart'; +import 'package:dart_di/module.dart'; +import 'package:dart_di/scope.dart'; import 'package:test/test.dart'; void main() {