mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-24 05:25:19 +00:00
Compare commits
75 Commits
0.1.0
...
cherrypick
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff55ddb491 | ||
|
|
3d3130914a | ||
|
|
35f7c27360 | ||
|
|
36e42171b7 | ||
|
|
1a1fe9c4e4 | ||
|
|
6900b649e1 | ||
|
|
75222a3471 | ||
|
|
c573e9840f | ||
|
|
490355b145 | ||
|
|
63cd56a696 | ||
|
|
585385980f | ||
|
|
e1a556d193 | ||
|
|
f6da7568fe | ||
|
|
938f8df8b6 | ||
|
|
f94c2df7cd | ||
|
|
13c96acca5 | ||
|
|
c90f96708f | ||
|
|
b789dd0179 | ||
|
|
a9b0ff4f36 | ||
|
|
21c3e83a6a | ||
|
|
a983281727 | ||
|
|
e0f5874621 | ||
|
|
8c3a0df452 | ||
|
|
085ccb55f5 | ||
|
|
c91392c978 | ||
|
|
4205993ea7 | ||
|
|
58245fb665 | ||
|
|
7a53844c7d | ||
|
|
73d199b012 | ||
|
|
75bc73d62f | ||
|
|
bdc8951438 | ||
|
|
3c95bf4947 | ||
|
|
643a830d2d | ||
|
|
c44abaaedb | ||
|
|
e2cc712840 | ||
|
|
c49c9012ac | ||
|
|
4cb210d0c2 | ||
|
|
8f2ae95b8e | ||
|
|
276d6bfb12 | ||
|
|
0e37d7f222 | ||
|
|
5ea3744961 | ||
|
|
37c676cefa | ||
|
|
8a9fb1d55c | ||
|
|
8f86662c9b | ||
|
|
dcdfce41db | ||
|
|
c2f2577cc6 | ||
|
|
93f431ce93 | ||
|
|
5462f9da07 | ||
|
|
ac30908f2d | ||
|
|
4cfca7c063 | ||
|
|
5afb8bda35 | ||
|
|
86c58191e5 | ||
|
|
f3b1ee84b2 | ||
|
|
b6a4b86b19 | ||
|
|
a20d153c1a | ||
|
|
b621865e82 | ||
|
|
4f127751d8 | ||
|
|
1fb6db6dec | ||
|
|
565fb3e682 | ||
|
|
de404d4ee1 | ||
|
|
35879380d0 | ||
|
|
bab560a856 | ||
|
|
98f12c5eb7 | ||
|
|
2568414a1b | ||
|
|
1ddbb74e3f | ||
|
|
e102b15022 | ||
|
|
ec75ad9172 | ||
|
|
ed0c2fae53 | ||
|
|
4302d733ba | ||
|
|
c9ddc2ffa8 | ||
|
|
b2b66bdcfd | ||
|
|
6a2d86c83c | ||
|
|
2d6fdbe04c | ||
|
|
35a9478446 | ||
|
|
0e3c5037fb |
27
.gitignore
vendored
27
.gitignore
vendored
@@ -1,21 +1,14 @@
|
||||
# See https://www.dartlang.org/guides/libraries/private-files
|
||||
.DS_Store
|
||||
|
||||
# FVM Version Cache
|
||||
.fvm/
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
.idea/
|
||||
.vscode/
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
pubspec_overrides.yaml
|
||||
|
||||
# Avoid committing generated Javascript files:
|
||||
*.dart.js
|
||||
*.info.json # Produced by the --dump-info flag.
|
||||
*.js # When generated by dart2js. Don't specify *.js if your
|
||||
# project includes source files written in JavaScript.
|
||||
*.js_
|
||||
*.js.deps
|
||||
*.js.map
|
||||
melos_cherrypick.iml
|
||||
melos_cherrypick_workspace.iml
|
||||
melos_cherrypick_flutter.iml
|
||||
55
CHANGELOG.md
Normal file
55
CHANGELOG.md
Normal file
@@ -0,0 +1,55 @@
|
||||
# Change Log
|
||||
|
||||
All notable changes to this project will be documented in this file.
|
||||
See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
|
||||
|
||||
## 2025-05-03
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`cherrypick_flutter` - `v1.1.0`](#cherrypick_flutter---v110)
|
||||
|
||||
---
|
||||
|
||||
#### `cherrypick_flutter` - `v1.1.0`
|
||||
|
||||
- **FIX**: update description.
|
||||
- **FIX**: update gitignore.
|
||||
- **FEAT**: modify api in CherryPickProvider.
|
||||
|
||||
|
||||
## 2025-05-02
|
||||
|
||||
### Changes
|
||||
|
||||
---
|
||||
|
||||
Packages with breaking changes:
|
||||
|
||||
- There are no breaking changes in this release.
|
||||
|
||||
Packages with other changes:
|
||||
|
||||
- [`cherrypick` - `v2.0.1`](#cherrypick---v201)
|
||||
- [`cherrypick_flutter` - `v1.0.1`](#cherrypick_flutter---v101)
|
||||
|
||||
Packages with dependency updates only:
|
||||
|
||||
> Packages listed below depend on other packages in this workspace that have had changes. Their versions have been incremented to bump the minimum dependency versions of the packages they depend upon in this project.
|
||||
|
||||
- `cherrypick_flutter` - `v1.0.1`
|
||||
|
||||
---
|
||||
|
||||
#### `cherrypick` - `v2.0.1`
|
||||
|
||||
- **FIX**: fix warning.
|
||||
|
||||
297
README.md
297
README.md
@@ -1,247 +1,100 @@
|
||||
# dart_di
|
||||
# CherryPick Workspace
|
||||
|
||||
Экспериментальная разработка DI на ЯП Dart
|
||||
Welcome to the CherryPick Workspace, a comprehensive suite for dependency management in Flutter applications. It consists of the `cherrypick` and `cherrypick_flutter` packages, designed to enhance modularity and testability by providing robust dependency and state management tools.
|
||||
|
||||
## Документация
|
||||
## Overview
|
||||
|
||||
### Быстрый старт
|
||||
- **`cherrypick`**: A Dart library offering core tools for dependency injection and management through modules and scopes.
|
||||
- **`cherrypick_flutter`**: A Flutter-specific library facilitating access to the root scope via the context using `CherryPickProvider`, simplifying state management within the widget tree.
|
||||
|
||||
Основным классом для всех операций является `DiContainer`. Вы можете зарегистрировать свои зависимости,
|
||||
получив `ResolvingContext` через метод `bind<T>()` и используя его различные методы разрешений.
|
||||
Далее вы можете получить зависимости с помощью `resolve<T>()`.
|
||||
## Repository Structure
|
||||
|
||||
Пример:
|
||||
- **Packages**:
|
||||
- `cherrypick`: Core DI functionalities.
|
||||
- `cherrypick_flutter`: Flutter integration for context-based root scope access.
|
||||
|
||||
```dart
|
||||
final container = DiContainer();
|
||||
container.bind<SomeService>().toValue(SomeServiceImpl());
|
||||
/*
|
||||
...
|
||||
*/
|
||||
## Quick Start Guide
|
||||
|
||||
// Метод `resolve` просто возвращает зарегистрированный ранее экземпляр
|
||||
final someService = container.resolve<SomeService>();
|
||||
### Installation
|
||||
|
||||
To add the packages to your project, include the dependencies in your `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
cherrypick: any
|
||||
cherrypick_flutter: any
|
||||
```
|
||||
|
||||
### Ленивая инициализация
|
||||
Run `flutter pub get` to install the dependencies.
|
||||
|
||||
Если вам нужно создать объект в момент резолвинга, вы можете использовать ленивую (другими словами, по запросу) инициализацию объекта
|
||||
с помощью метода `toFactoryN()`.
|
||||
### Usage
|
||||
|
||||
Пример:
|
||||
#### cherrypick
|
||||
|
||||
```dart
|
||||
final container = DiContainer();
|
||||
// В методе `toFactory` вы просто определяете, как построить экземпляр через фабричную лямбду
|
||||
container.bind<SomeService>().toFactory( () => SomeServiceImpl() );
|
||||
/*
|
||||
...
|
||||
*/
|
||||
// Метод `resolve()` будет создавать экземпляр через зарегистрированную фабричную лямбду каждый раз, когда вы вызываете его
|
||||
final someService = container.resolve<SomeService>();
|
||||
final anotherSomeService = container.resolve<SomeService>();
|
||||
assert(someService != anotherSomeService);
|
||||
```
|
||||
- **Binding Dependencies**: Use `Binding` to set up dependencies.
|
||||
|
||||
Но обычно у вас есть много типов с разными зависимостями, которые образуют граф зависимостей.
|
||||
```dart
|
||||
Binding<String>().toInstance("hello world");
|
||||
Binding<String>().toProvide(() => "hello world").singleton();
|
||||
```
|
||||
|
||||
Пример:
|
||||
- **Creating Modules**: Define dependencies within a module.
|
||||
|
||||
```dart
|
||||
class A {}
|
||||
class B {}
|
||||
|
||||
class C {
|
||||
final A a;
|
||||
final B b;
|
||||
|
||||
C(this.a, this.b);
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
Если вам нужно зарегистрировать некоторый тип, зависящий от других типов из контейнера,
|
||||
вы можете использовать методы `toFactory1<T1>` - `toFactory8<T1 ... T8>`, где число в конце,
|
||||
является количеством запрошенных через аргументы типов зависимостей.
|
||||
(Обратите внимание, что вам нужно определить все зависимости в аргументах - `toFactory2<A1, A2>`).
|
||||
|
||||
|
||||
Пример:
|
||||
|
||||
```dart
|
||||
class SomeService {
|
||||
final A a;
|
||||
final B b;
|
||||
|
||||
SomeService(this.a, this.b);
|
||||
}
|
||||
|
||||
final container = DiContainer();
|
||||
container.bind<A>(A::class).toFactory (() => A());
|
||||
container.bind<B>(B::class).toFactory (() => B());
|
||||
|
||||
/// В фабричной лямбде вы определяете, как построить зависимость от других зависимостей
|
||||
/// (Порядок разрешенных экземпляров соответствует порядку типов аргументов)
|
||||
container.bind<SomeService>().toFactory2<A, B>((a, b) => SomeService(a, b));
|
||||
|
||||
/*
|
||||
...
|
||||
*/
|
||||
|
||||
/// Получаем экземпляр `SomeService` через resolve своих зависимостей.
|
||||
/// В нашем случае - это resolve A и B
|
||||
/// Внимание!!! То, что он будет создавать новые экземпляры A и B каждый раз, когда вы вызываете `resolve` SomeService
|
||||
final someService = container.resolve<SomeService>();
|
||||
```
|
||||
|
||||
### Время жизни экземпляров и контроль области видимости
|
||||
|
||||
Если вы хотите создать экземпляр зарегистрированной зависимости только один раз,
|
||||
и вам нужно получить/разрешить зависимость много раз в контейнере, то вы можете зарегистрировать
|
||||
свою зависимость с добавлением `asSingeton()`. Например:
|
||||
|
||||
```dart
|
||||
final container = DiContainer();
|
||||
container.bind<A>()
|
||||
.toFactory(() => A())
|
||||
.asSingleton();
|
||||
|
||||
container
|
||||
.bind<B>()
|
||||
.toFactory(() => B());
|
||||
.asSingleton();
|
||||
|
||||
container.bind<SomeService>().toFactory2<A, B>((a, b) -> SomeService(a, b));
|
||||
|
||||
// Код выше означает: Контейнер, регистрирует создание A и B только в первый раз, когда оно будет запрошен,
|
||||
// и регистрирует создание SomeService каждый раз, когда оно будет запрошен.
|
||||
|
||||
final a = container.resolve<A>();
|
||||
final b = container.resolve<B>();
|
||||
final anotherA = container.resolve<A>();
|
||||
final anotherB = container.resolve<B>();
|
||||
|
||||
assert(a == anotherA && b == anotherB);
|
||||
|
||||
final someService = container.resolve<SomeService>();
|
||||
final anotherSomeService = container.resolve<SomeService>();
|
||||
|
||||
assert(someService != anotherSomeService);
|
||||
```
|
||||
|
||||
Если вы хотите сразу создать свой зарегистрированный экземпляр, вы можете вызвать `resolve()`. Например:
|
||||
|
||||
|
||||
```dart
|
||||
final container = DiContainer();
|
||||
// Это заставит создать зависимость после регистрации
|
||||
container.bind <SomeService>()
|
||||
.toFactory(() => SomeService())
|
||||
.asSingleton()
|
||||
.resolve();
|
||||
```
|
||||
|
||||
Когда вы работаете со сложным приложением, в большинстве случаев вы можете работать со многими модулями с собственными зависимостями.
|
||||
Эти модули могут быть настроены различными `DiContainer`-ми. И вы можете прикрепить контейнер к другому, как родительский.
|
||||
В этом случае родительские зависимости будут видны для дочернего контейнера,
|
||||
и через него вы можете формировать различные области видимости зависимостей. Например:
|
||||
|
||||
```dart
|
||||
final parentContainer = DiContainer();
|
||||
parentContainer.bind<A>().toFactory(() => A())
|
||||
|
||||
final childContainer = DiContainer(parentContainer);
|
||||
// Обратите внимание, что родительская зависимость A видна для дочернего контейнера
|
||||
final a = childContainer.resolve<A>();
|
||||
|
||||
/*
|
||||
// Но следующий код потерпит неудачу с ошибкой, потому что родитель не знает о своем потомке.
|
||||
final parentContainer = DiContainer();
|
||||
final childContainer = DiContainer();
|
||||
childContainer.bind<A>().toFactory(() => A());
|
||||
|
||||
// Выдает ошибку
|
||||
final a = parentContainer.resolve<A>();
|
||||
*/
|
||||
```
|
||||
|
||||
### Структура библиотеки
|
||||
|
||||
Библиотека состоит из DiContainer и Resolver.
|
||||
DiContainer - это контейнер со всеми Resolver для разных типов. А `Resolver` - это просто объект, который знает, как разрешить данный тип.
|
||||
Многие из resolver-ов обернуты другими, поэтому они могут быть составлены для разных вариантов использования.
|
||||
Resolver - интерфейс, поэтому он имеет много реализаций. Основным является ResolvingContext.
|
||||
Вы можете думать об этом как об объекте контекста, который имеет вспомогательные методы для создания различных вариантов resolver-ов (`toFactory`,` toValue`, `asSingleton`).
|
||||
Но все они просто используют метод `toResolver` для определения некоторого корневого resolver в контексте.
|
||||
Когда вы запрашиваете тип из контейнера с помощью метода `resolve<T>()`, он просто находит контекст для типа и вызывает корневой 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<ApiClient>().toValue(new ApiClientMock())
|
||||
..bind<DataRepository>()
|
||||
.toFactory1<ApiClient>((c) => new NetworkDataRepository(c))
|
||||
..bind<DataBloc>().toFactory1<DataRepository>((s) => new DataBloc(s));
|
||||
|
||||
final dataBloc = dataModule.resolve<DataBloc>();
|
||||
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<String> get data => _dataController.stream;
|
||||
StreamController<String> _dataController = new StreamController.broadcast();
|
||||
|
||||
DataBloc(this._dataRepository);
|
||||
|
||||
Future<void> fetchData() async {
|
||||
try {
|
||||
_dataController.sink.add(await _dataRepository.getData());
|
||||
} catch (e) {
|
||||
_dataController.sink.addError(e);
|
||||
```dart
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().toInstance(ApiClientMock());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
void dispose() {
|
||||
_dataController.close();
|
||||
- **Managing Scopes**: Control dependency lifecycles with scopes.
|
||||
|
||||
```dart
|
||||
final rootScope = Cherrypick.openRootScope();
|
||||
rootScope.installModules([AppModule()]);
|
||||
final apiClient = rootScope.resolve<ApiClient>();
|
||||
```
|
||||
|
||||
#### cherrypick_flutter
|
||||
|
||||
- **CherryPickProvider**: Wrap your widget tree to access the root scope via context.
|
||||
|
||||
```dart
|
||||
void main() {
|
||||
runApp(CherryPickProvider(
|
||||
rootScope: yourRootScopeInstance,
|
||||
child: MyApp(),
|
||||
));
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
abstract class DataRepository {
|
||||
Future<String> getData();
|
||||
}
|
||||
- **Accessing Root Scope**: Use `CherryPickProvider.of(context).rootScope` to interact with the root scope in your widgets.
|
||||
|
||||
class NetworkDataRepository implements DataRepository {
|
||||
final ApiClient _apiClient;
|
||||
final _token = 'token';
|
||||
```dart
|
||||
final rootScope = CherryPickProvider.of(context).rootScope;
|
||||
```
|
||||
|
||||
NetworkDataRepository(this._apiClient);
|
||||
### Example Project
|
||||
|
||||
@override
|
||||
Future<String> getData() async => await _apiClient.sendRequest(
|
||||
url: 'www.google.com', token: _token, requestBody: {'type': 'data'});
|
||||
}
|
||||
Check the `example` directory for a complete demonstration of implementing CherryPick Workspace in a Flutter app.
|
||||
|
||||
abstract class ApiClient {
|
||||
Future sendRequest({@required String url, String token, Map requestBody});
|
||||
}
|
||||
## Features
|
||||
|
||||
class ApiClientMock implements ApiClient {
|
||||
@override
|
||||
Future sendRequest(
|
||||
{@required String url, String token, Map requestBody}) async {
|
||||
return 'hello world';
|
||||
}
|
||||
}
|
||||
```
|
||||
- [x] Dependency Binding and Resolution
|
||||
- [x] Custom Module Creation
|
||||
- [x] Root and Sub-Scopes
|
||||
- [x] Convenient Root Scope Access in Flutter
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions from the community. Feel free to open issues or submit pull requests with suggestions and enhancements.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License 2.0. You may obtain a copy of the License at [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
## Links
|
||||
|
||||
- [GitHub Repository](https://github.com/pese-git/cherrypick)
|
||||
4
cherrypick/.fvmrc
Normal file
4
cherrypick/.fvmrc
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"flutter": "3.24.2",
|
||||
"flavors": {}
|
||||
}
|
||||
21
cherrypick/.gitignore
vendored
Normal file
21
cherrypick/.gitignore
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
# See https://www.dartlang.org/guides/libraries/private-files
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool/
|
||||
.packages
|
||||
build/
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
# Avoid committing generated Javascript files:
|
||||
*.dart.js
|
||||
*.info.json # Produced by the --dump-info flag.
|
||||
*.js # When generated by dart2js. Don't specify *.js if your
|
||||
# project includes source files written in JavaScript.
|
||||
*.js_
|
||||
*.js.deps
|
||||
*.js.map
|
||||
23
cherrypick/.gitlab-ci.yml
Normal file
23
cherrypick/.gitlab-ci.yml
Normal file
@@ -0,0 +1,23 @@
|
||||
# This file is a template, and might need editing before it works on your project.
|
||||
# https://hub.docker.com/r/google/dart
|
||||
image: google/dart:latest
|
||||
|
||||
variables:
|
||||
# Use to learn more:
|
||||
# pub run test --help
|
||||
PUB_VARS: "--platform vm --timeout 30s --concurrency=6 --test-randomize-ordering-seed=random --reporter=expanded"
|
||||
|
||||
# Cache downloaded dependencies and plugins between builds.
|
||||
# To keep cache across branches add 'key: "$CI_JOB_NAME"'
|
||||
cache:
|
||||
paths:
|
||||
- .pub-cache/global_packages
|
||||
|
||||
before_script:
|
||||
- export PATH="$PATH":"~/.pub-cache/bin"
|
||||
- pub get --no-precompile
|
||||
|
||||
test:
|
||||
stage: test
|
||||
script:
|
||||
- pub run test $PUB_VARS
|
||||
1
cherrypick/AUTHORS.md
Normal file
1
cherrypick/AUTHORS.md
Normal file
@@ -0,0 +1 @@
|
||||
Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
54
cherrypick/CHANGELOG.md
Normal file
54
cherrypick/CHANGELOG.md
Normal file
@@ -0,0 +1,54 @@
|
||||
## 2.0.1
|
||||
|
||||
- **FIX**: fix warning.
|
||||
|
||||
|
||||
# Changelog
|
||||
|
||||
2.0.0 supported Dart 3.0
|
||||
|
||||
---
|
||||
|
||||
1.1.0 cheked support Dart 3.0
|
||||
|
||||
---
|
||||
|
||||
1.0.4 Fixed exception "ConcurrentModificationError"
|
||||
|
||||
---
|
||||
|
||||
1.0.3 Added provider with params
|
||||
|
||||
---
|
||||
|
||||
1.0.2 Updated doc and fixed syntax error
|
||||
|
||||
---
|
||||
1.0.1 Fixed syntax error
|
||||
|
||||
---
|
||||
1.0.0 Refactored code and added experimental api
|
||||
|
||||
---
|
||||
|
||||
0.1.2+1 Fixed initializtaion error
|
||||
|
||||
---
|
||||
0.1.2 Fixed warnings in code
|
||||
|
||||
---
|
||||
0.1.1+2 Updated libraries and fixed warnings
|
||||
|
||||
---
|
||||
|
||||
0.1.1+1 Updated pubspec and readme.md
|
||||
|
||||
---
|
||||
|
||||
0.1.1 Updated pubspec
|
||||
|
||||
---
|
||||
|
||||
0.1.0 Initial release
|
||||
|
||||
---
|
||||
201
cherrypick/LICENSE
Normal file
201
cherrypick/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
201
cherrypick/README.md
Normal file
201
cherrypick/README.md
Normal file
@@ -0,0 +1,201 @@
|
||||
# CherryPick Flutter
|
||||
|
||||
`cherrypick_flutter` is a powerful Flutter library for managing and accessing dependencies within your application through a root scope context using `CherryPickProvider`. It offers simplified dependency injection, making your application more modular and test-friendly.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Main Components in Dependency Injection (DI)
|
||||
|
||||
#### Binding
|
||||
|
||||
A Binding is a custom instance configurator, essential for setting up dependencies. It offers the following key methods:
|
||||
|
||||
- `toInstance()`: Directly provides an initialized instance.
|
||||
- `toProvide()`: Accepts a provider function or constructor for lazy initialization.
|
||||
- `withName()`: Assigns a name to an instance, allowing for retrieval by name.
|
||||
- `singleton()`: Marks the instance as a singleton, ensuring only one instance exists in the scope.
|
||||
|
||||
##### Example:
|
||||
|
||||
```dart
|
||||
// Direct instance initialization with toInstance()
|
||||
Binding<String>().toInstance("hello world");
|
||||
|
||||
// Or use a provider for lazy initialization
|
||||
Binding<String>().toProvide(() => "hello world");
|
||||
|
||||
// Named instance
|
||||
Binding<String>().withName("my_string").toInstance("hello world");
|
||||
|
||||
// Singleton instance
|
||||
Binding<String>().toProvide(() => "hello world").singleton();
|
||||
```
|
||||
|
||||
#### Module
|
||||
|
||||
A Module encapsulates bindings, allowing you to organize dependencies logically. To create a custom module, implement the `void builder(Scope currentScope)` method.
|
||||
|
||||
##### Example:
|
||||
|
||||
```dart
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().toInstance(ApiClientMock());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Scope
|
||||
|
||||
A Scope is the container that manages your dependency tree, holding modules and instances. Use the scope to access dependencies with the `resolve<T>()` method.
|
||||
|
||||
##### Example:
|
||||
|
||||
```dart
|
||||
// Open the main scope
|
||||
final rootScope = Cherrypick.openRootScope();
|
||||
|
||||
// Install custom modules
|
||||
rootScope.installModules([AppModule()]);
|
||||
|
||||
// Resolve an instance
|
||||
final str = rootScope.resolve<String>();
|
||||
|
||||
// Close the main scope
|
||||
Cherrypick.closeRootScope();
|
||||
```
|
||||
|
||||
## Example Application
|
||||
|
||||
The following example demonstrates module setup, scope management, and dependency resolution.
|
||||
|
||||
```dart
|
||||
import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().withName("apiClientMock").toInstance(ApiClientMock());
|
||||
bind<ApiClient>().withName("apiClientImpl").toInstance(ApiClientImpl());
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureModule extends Module {
|
||||
bool isMock;
|
||||
|
||||
FeatureModule({required this.isMock});
|
||||
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<DataRepository>()
|
||||
.withName("networkRepo")
|
||||
.toProvide(
|
||||
() => NetworkDataRepository(
|
||||
currentScope.resolve<ApiClient>(
|
||||
named: isMock ? "apiClientMock" : "apiClientImpl",
|
||||
),
|
||||
),
|
||||
)
|
||||
.singleton();
|
||||
bind<DataBloc>().toProvide(
|
||||
() => DataBloc(
|
||||
currentScope.resolve<DataRepository>(named: "networkRepo"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
final scope = openRootScope().installModules([
|
||||
AppModule(),
|
||||
]);
|
||||
|
||||
final subScope = scope
|
||||
.openSubScope("featureScope")
|
||||
.installModules([FeatureModule(isMock: true)]);
|
||||
|
||||
final dataBloc = subScope.resolve<DataBloc>();
|
||||
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<String> get data => _dataController.stream;
|
||||
StreamController<String> _dataController = new StreamController.broadcast();
|
||||
|
||||
DataBloc(this._dataRepository);
|
||||
|
||||
Future<void> fetchData() async {
|
||||
try {
|
||||
_dataController.sink.add(await _dataRepository.getData());
|
||||
} catch (e) {
|
||||
_dataController.sink.addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_dataController.close();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DataRepository {
|
||||
Future<String> getData();
|
||||
}
|
||||
|
||||
class NetworkDataRepository implements DataRepository {
|
||||
final ApiClient _apiClient;
|
||||
final _token = 'token';
|
||||
|
||||
NetworkDataRepository(this._apiClient);
|
||||
|
||||
@override
|
||||
Future<String> 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';
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
- [x] Main Scope and Sub Scopes
|
||||
- [x] Named Instance Initialization
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions from the community. Please feel free to submit issues or pull requests with suggestions or improvements.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License 2.0. You may obtain a copy of the License at [Apache License 2.0](http://www.apache.org/licenses/LICENSE-2.0).
|
||||
|
||||
**Important:** Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for specific language governing permissions and limitations under the License.
|
||||
|
||||
## Links
|
||||
|
||||
- [GitHub Repository](https://github.com/pese-git/cherrypick)
|
||||
30
cherrypick/analysis_options.yaml
Normal file
30
cherrypick/analysis_options.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
194
cherrypick/doc/quick_start_en.md
Normal file
194
cherrypick/doc/quick_start_en.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Quick start
|
||||
|
||||
## Main components DI
|
||||
|
||||
|
||||
### Binding
|
||||
|
||||
Binding is a custom instance configurator that contains methods for configuring a dependency.
|
||||
|
||||
There are two main methods for initializing a custom instance `toInstance ()` and `toProvide ()` and auxiliary `withName ()` and `singleton ()`.
|
||||
|
||||
`toInstance()` - takes a initialized instance
|
||||
|
||||
`toProvide()` - takes a `provider` function (instance constructor)
|
||||
|
||||
`withName()` - takes a string to name the instance. By this name, it will be possible to extract instance from the DI container
|
||||
|
||||
`singleton()` - sets a flag in the Binding that tells the DI container that there is only one dependency.
|
||||
|
||||
Example:
|
||||
|
||||
```dart
|
||||
// initializing a text string instance through a method toInstance()
|
||||
Binding<String>().toInstance("hello world");
|
||||
|
||||
// or
|
||||
|
||||
// initializing a text string instance
|
||||
Binding<String>().toProvide(() => "hello world");
|
||||
|
||||
// initializing an instance of a string named
|
||||
Binding<String>().withName("my_string").toInstance("hello world");
|
||||
// or
|
||||
Binding<String>().withName("my_string").toProvide(() => "hello world");
|
||||
|
||||
// instance initialization like singleton
|
||||
Binding<String>().toInstance("hello world");
|
||||
// or
|
||||
Binding<String>().toProvide(() => "hello world").singleton();
|
||||
|
||||
```
|
||||
|
||||
### Module
|
||||
|
||||
Module is a container of user instances, and on the basis of which the user can create their modules. The user in his module must implement the `void builder (Scope currentScope)` method.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
```dart
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().toInstance(ApiClientMock());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Scope
|
||||
|
||||
Scope is a container that stores the entire dependency tree (scope, modules, instances).
|
||||
Through the scope, you can access the custom `instance`, for this you need to call the `resolve<T>()` method and specify the type of the object, and you can also pass additional parameters.
|
||||
|
||||
Example:
|
||||
|
||||
```dart
|
||||
// open main scope
|
||||
final rootScope = Cherrypick.openRootScope();
|
||||
|
||||
// initializing scope with a custom module
|
||||
rootScope.installModules([AppModule()]);
|
||||
|
||||
// takes custom instance
|
||||
final str = rootScope.resolve<String>();
|
||||
// or
|
||||
final str = rootScope.tryResolve<String>();
|
||||
|
||||
// close main scope
|
||||
Cherrypick.closeRootScope();
|
||||
```
|
||||
|
||||
## Example app
|
||||
|
||||
|
||||
```dart
|
||||
import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().withName("apiClientMock").toInstance(ApiClientMock());
|
||||
bind<ApiClient>().withName("apiClientImpl").toInstance(ApiClientImpl());
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureModule extends Module {
|
||||
bool isMock;
|
||||
|
||||
FeatureModule({required this.isMock});
|
||||
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<DataRepository>()
|
||||
.withName("networkRepo")
|
||||
.toProvide(
|
||||
() => NetworkDataRepository(
|
||||
currentScope.resolve<ApiClient>(
|
||||
named: isMock ? "apiClientMock" : "apiClientImpl",
|
||||
),
|
||||
),
|
||||
)
|
||||
.singleton();
|
||||
bind<DataBloc>().toProvide(
|
||||
() => DataBloc(
|
||||
currentScope.resolve<DataRepository>(named: "networkRepo"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
final scope = openRootScope().installModules([
|
||||
AppModule(),
|
||||
]);
|
||||
|
||||
final subScope = scope
|
||||
.openSubScope("featureScope")
|
||||
.installModules([FeatureModule(isMock: true)]);
|
||||
|
||||
final dataBloc = subScope.resolve<DataBloc>();
|
||||
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<String> get data => _dataController.stream;
|
||||
StreamController<String> _dataController = new StreamController.broadcast();
|
||||
|
||||
DataBloc(this._dataRepository);
|
||||
|
||||
Future<void> fetchData() async {
|
||||
try {
|
||||
_dataController.sink.add(await _dataRepository.getData());
|
||||
} catch (e) {
|
||||
_dataController.sink.addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_dataController.close();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DataRepository {
|
||||
Future<String> getData();
|
||||
}
|
||||
|
||||
class NetworkDataRepository implements DataRepository {
|
||||
final ApiClient _apiClient;
|
||||
final _token = 'token';
|
||||
|
||||
NetworkDataRepository(this._apiClient);
|
||||
|
||||
@override
|
||||
Future<String> 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';
|
||||
}
|
||||
}
|
||||
```
|
||||
194
cherrypick/doc/quick_start_ru.md
Normal file
194
cherrypick/doc/quick_start_ru.md
Normal file
@@ -0,0 +1,194 @@
|
||||
# Быстрый старт
|
||||
|
||||
## Основные компоненты DI
|
||||
|
||||
|
||||
### Binding
|
||||
|
||||
Binding - по сути это конфигуратор для пользовательского instance, который соддержит методы для конфигурирования зависимости.
|
||||
|
||||
Есть два основных метода для инициализации пользовательского instance `toInstance()` и `toProvide()` и вспомогательных `withName()` и `singleton()`.
|
||||
|
||||
`toInstance()` - принимает готовый экземпляр
|
||||
|
||||
`toProvide()` - принимает функцию `provider` (конструктор экземпляра)
|
||||
|
||||
`withName()` - принимает строку для именования экземпляра. По этому имени можно будет извлечь instance из DI контейнера
|
||||
|
||||
`singleton()` - устанавливает флаг в Binding, который говорит DI контейнеру, что зависимость одна.
|
||||
|
||||
Пример:
|
||||
|
||||
```dart
|
||||
// инициализация экземпляра текстовой строки через метод toInstance()
|
||||
Binding<String>().toInstance("hello world");
|
||||
|
||||
// или
|
||||
|
||||
// инициализация экземпляра текстовой строки
|
||||
Binding<String>().toProvide(() => "hello world");
|
||||
|
||||
// инициализация экземпляра строки с именем
|
||||
Binding<String>().withName("my_string").toInstance("hello world");
|
||||
// или
|
||||
Binding<String>().withName("my_string").toProvide(() => "hello world");
|
||||
|
||||
// инициализация экземпляра, как сингелтон
|
||||
Binding<String>().toInstance("hello world");
|
||||
// или
|
||||
Binding<String>().toProvide(() => "hello world").singleton();
|
||||
|
||||
```
|
||||
|
||||
### Module
|
||||
|
||||
Module - это контейнер пользовательских instances, и на основе которого пользователь может создавать свои модули. Пользователь в своем модуле должен реализовать метод `void builder(Scope currentScope)`.
|
||||
|
||||
|
||||
Пример:
|
||||
|
||||
```dart
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().toInstance(ApiClientMock());
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Scope
|
||||
|
||||
Scope - это контейнер, который хранит все дерево зависимостей (scope,modules,instances).
|
||||
Через scope можно получить доступ к `instance`, для этого нужно вызвать метод `resolve<T>()` и указать тип объекта, а так же можно передать дополнительные параметры.
|
||||
|
||||
Пример:
|
||||
|
||||
```dart
|
||||
// открыть главный scope
|
||||
final rootScope = CherryPick.openRootScope();
|
||||
|
||||
// инициализация scope пользовательским модулем
|
||||
rootScope.installModules([AppModule()]);
|
||||
|
||||
// получаем экземпляр класса
|
||||
final str = rootScope.resolve<String>();
|
||||
// или
|
||||
final str = rootScope.tryResolve<String>();
|
||||
|
||||
// закрыть главный scope
|
||||
Cherrypick.closeRootScope();
|
||||
```
|
||||
|
||||
## Пример приложения
|
||||
|
||||
|
||||
```dart
|
||||
import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().withName("apiClientMock").toInstance(ApiClientMock());
|
||||
bind<ApiClient>().withName("apiClientImpl").toInstance(ApiClientImpl());
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureModule extends Module {
|
||||
bool isMock;
|
||||
|
||||
FeatureModule({required this.isMock});
|
||||
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<DataRepository>()
|
||||
.withName("networkRepo")
|
||||
.toProvide(
|
||||
() => NetworkDataRepository(
|
||||
currentScope.resolve<ApiClient>(
|
||||
named: isMock ? "apiClientMock" : "apiClientImpl",
|
||||
),
|
||||
),
|
||||
)
|
||||
.singleton();
|
||||
bind<DataBloc>().toProvide(
|
||||
() => DataBloc(
|
||||
currentScope.resolve<DataRepository>(named: "networkRepo"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
final scope = openRootScope().installModules([
|
||||
AppModule(),
|
||||
]);
|
||||
|
||||
final subScope = scope
|
||||
.openSubScope("featureScope")
|
||||
.installModules([FeatureModule(isMock: true)]);
|
||||
|
||||
final dataBloc = subScope.resolve<DataBloc>();
|
||||
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<String> get data => _dataController.stream;
|
||||
StreamController<String> _dataController = new StreamController.broadcast();
|
||||
|
||||
DataBloc(this._dataRepository);
|
||||
|
||||
Future<void> fetchData() async {
|
||||
try {
|
||||
_dataController.sink.add(await _dataRepository.getData());
|
||||
} catch (e) {
|
||||
_dataController.sink.addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_dataController.close();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DataRepository {
|
||||
Future<String> getData();
|
||||
}
|
||||
|
||||
class NetworkDataRepository implements DataRepository {
|
||||
final ApiClient _apiClient;
|
||||
final _token = 'token';
|
||||
|
||||
NetworkDataRepository(this._apiClient);
|
||||
|
||||
@override
|
||||
Future<String> 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';
|
||||
}
|
||||
}
|
||||
```
|
||||
133
cherrypick/example/README.md
Normal file
133
cherrypick/example/README.md
Normal file
@@ -0,0 +1,133 @@
|
||||
# Example
|
||||
|
||||
pubspec.yaml:
|
||||
|
||||
```yaml
|
||||
name: example
|
||||
version: 1.0.0
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
|
||||
dependencies:
|
||||
cherrypick:
|
||||
path: ../
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.16.8
|
||||
```
|
||||
|
||||
main.dart:
|
||||
|
||||
```dart
|
||||
import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:cherrypick/scope.dart';
|
||||
import 'package:cherrypick/module.dart';
|
||||
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().withName("apiClientMock").toInstance(ApiClientMock());
|
||||
bind<ApiClient>().withName("apiClientImpl").toInstance(ApiClientImpl());
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureModule extends Module {
|
||||
bool isMock;
|
||||
|
||||
FeatureModule({required this.isMock});
|
||||
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<DataRepository>()
|
||||
.withName("networkRepo")
|
||||
.toProvide(
|
||||
() => NetworkDataRepository(
|
||||
currentScope.resolve<ApiClient>(
|
||||
named: isMock ? "apiClientMock" : "apiClientImpl",
|
||||
),
|
||||
),
|
||||
)
|
||||
.singleton();
|
||||
bind<DataBloc>().toProvide(
|
||||
() => DataBloc(
|
||||
currentScope.resolve<DataRepository>(named: "networkRepo"),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
final scope = openRootScope().installModules([
|
||||
AppModule(),
|
||||
]);
|
||||
|
||||
final subScope = scope
|
||||
.openSubScope("featureScope")
|
||||
.installModules([FeatureModule(isMock: true)]);
|
||||
|
||||
final dataBloc = subScope.resolve<DataBloc>();
|
||||
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<String> get data => _dataController.stream;
|
||||
StreamController<String> _dataController = new StreamController.broadcast();
|
||||
|
||||
DataBloc(this._dataRepository);
|
||||
|
||||
Future<void> fetchData() async {
|
||||
try {
|
||||
_dataController.sink.add(await _dataRepository.getData());
|
||||
} catch (e) {
|
||||
_dataController.sink.addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_dataController.close();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DataRepository {
|
||||
Future<String> getData();
|
||||
}
|
||||
|
||||
class NetworkDataRepository implements DataRepository {
|
||||
final ApiClient _apiClient;
|
||||
final _token = 'token';
|
||||
|
||||
NetworkDataRepository(this._apiClient);
|
||||
|
||||
@override
|
||||
Future<String> 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';
|
||||
}
|
||||
}
|
||||
```
|
||||
129
cherrypick/example/bin/main.dart
Normal file
129
cherrypick/example/bin/main.dart
Normal file
@@ -0,0 +1,129 @@
|
||||
import 'dart:async';
|
||||
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
class AppModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<ApiClient>().withName('apiClientMock').toInstance(ApiClientMock());
|
||||
bind<ApiClient>().withName('apiClientImpl').toInstance(ApiClientImpl());
|
||||
}
|
||||
}
|
||||
|
||||
class FeatureModule extends Module {
|
||||
bool isMock;
|
||||
|
||||
FeatureModule({required this.isMock});
|
||||
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
bind<DataRepository>()
|
||||
.withName('networkRepo')
|
||||
.toProvide(
|
||||
() => NetworkDataRepository(
|
||||
currentScope.resolve<ApiClient>(
|
||||
named: isMock ? 'apiClientMock' : 'apiClientImpl',
|
||||
),
|
||||
),
|
||||
)
|
||||
.singleton();
|
||||
|
||||
bind<DataBloc>().toProvideWithParams(
|
||||
(param) => DataBloc(
|
||||
currentScope.resolve<DataRepository>(named: 'networkRepo'),
|
||||
param,
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() async {
|
||||
final scope = openRootScope().installModules([
|
||||
AppModule(),
|
||||
]);
|
||||
|
||||
final subScope = scope
|
||||
.openSubScope('featureScope')
|
||||
.installModules([FeatureModule(isMock: true)]);
|
||||
|
||||
final dataBloc = subScope.resolve<DataBloc>(params: 'PARAMETER');
|
||||
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<String> get data => _dataController.stream;
|
||||
final StreamController<String> _dataController = StreamController.broadcast();
|
||||
|
||||
final String param;
|
||||
|
||||
DataBloc(this._dataRepository, this.param);
|
||||
|
||||
Future<void> fetchData() async {
|
||||
try {
|
||||
_dataController.sink.add(await _dataRepository.getData(param));
|
||||
} catch (e) {
|
||||
_dataController.sink.addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_dataController.close();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DataRepository {
|
||||
Future<String> getData(String param);
|
||||
}
|
||||
|
||||
class NetworkDataRepository implements DataRepository {
|
||||
final ApiClient _apiClient;
|
||||
final _token = 'token';
|
||||
|
||||
NetworkDataRepository(this._apiClient);
|
||||
|
||||
@override
|
||||
Future<String> getData(String param) async => await _apiClient.sendRequest(
|
||||
url: 'www.google.com',
|
||||
token: _token,
|
||||
requestBody: {'type': 'data'},
|
||||
param: param);
|
||||
}
|
||||
|
||||
abstract class ApiClient {
|
||||
Future sendRequest({
|
||||
@required String url,
|
||||
String token,
|
||||
Map requestBody,
|
||||
String param,
|
||||
});
|
||||
}
|
||||
|
||||
class ApiClientMock implements ApiClient {
|
||||
@override
|
||||
Future sendRequest({
|
||||
@required String? url,
|
||||
String? token,
|
||||
Map? requestBody,
|
||||
String? param,
|
||||
}) async {
|
||||
return 'Local Data $param';
|
||||
}
|
||||
}
|
||||
|
||||
class ApiClientImpl implements ApiClient {
|
||||
@override
|
||||
Future sendRequest({
|
||||
@required String? url,
|
||||
String? token,
|
||||
Map? requestBody,
|
||||
String? param,
|
||||
}) async {
|
||||
return 'Network data $param';
|
||||
}
|
||||
}
|
||||
17
cherrypick/example/pubspec.yaml
Normal file
17
cherrypick/example/pubspec.yaml
Normal file
@@ -0,0 +1,17 @@
|
||||
name: example
|
||||
version: 1.0.0
|
||||
homepage: localhost
|
||||
publish_to: none
|
||||
|
||||
environment:
|
||||
sdk: ">=2.12.0 <3.0.0"
|
||||
|
||||
|
||||
dependencies:
|
||||
meta: ^1.3.0
|
||||
|
||||
cherrypick:
|
||||
path: ../
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.16.8
|
||||
19
cherrypick/lib/cherrypick.dart
Normal file
19
cherrypick/lib/cherrypick.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
///
|
||||
/// Copyright 2021 Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
library cherrypick;
|
||||
|
||||
export 'package:cherrypick/src/binding.dart';
|
||||
export 'package:cherrypick/src/helper.dart';
|
||||
export 'package:cherrypick/src/module.dart';
|
||||
export 'package:cherrypick/src/scope.dart';
|
||||
142
cherrypick/lib/src/binding.dart
Normal file
142
cherrypick/lib/src/binding.dart
Normal file
@@ -0,0 +1,142 @@
|
||||
///
|
||||
/// Copyright 2021 Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
enum Mode { simple, instance, providerInstance, providerInstanceWithParams }
|
||||
|
||||
typedef ProviderWithParams<T> = T Function(dynamic params);
|
||||
|
||||
/// RU: Класс Binding<T> настраивает параметры экземпляра.
|
||||
/// ENG: The Binding<T> class configures the settings for the instance.
|
||||
///
|
||||
class Binding<T> {
|
||||
late Mode _mode;
|
||||
late Type _key;
|
||||
late String _name;
|
||||
T? _instance;
|
||||
T? Function()? _provider;
|
||||
ProviderWithParams<T>? _providerWithParams;
|
||||
late bool _isSingleton = false;
|
||||
late bool _isNamed = false;
|
||||
|
||||
Binding() {
|
||||
_mode = Mode.simple;
|
||||
_key = T;
|
||||
}
|
||||
|
||||
/// RU: Метод возвращает [Mode] экземпляра.
|
||||
/// ENG: The method returns the [Mode] of the instance.
|
||||
///
|
||||
/// return [Mode]
|
||||
Mode get mode => _mode;
|
||||
|
||||
/// RU: Метод возвращает тип экземпляра.
|
||||
/// ENG: The method returns the type of the instance.
|
||||
///
|
||||
/// return [Type]
|
||||
Type get key => _key;
|
||||
|
||||
/// RU: Метод возвращает имя экземпляра.
|
||||
/// ENG: The method returns the name of the instance.
|
||||
///
|
||||
/// return [String]
|
||||
String get name => _name;
|
||||
|
||||
/// RU: Метод проверяет сингелтон экземпляр или нет.
|
||||
/// ENG: The method checks the singleton instance or not.
|
||||
///
|
||||
/// return [bool]
|
||||
bool get isSingleton => _isSingleton;
|
||||
|
||||
/// RU: Метод проверяет именован экземпляр или нет.
|
||||
/// ENG: The method checks whether the instance is named or not.
|
||||
///
|
||||
/// return [bool]
|
||||
bool get isNamed => _isNamed;
|
||||
|
||||
/// RU: Добавляет имя для экземляпя [value].
|
||||
/// ENG: Added name for instance [value].
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> withName(String name) {
|
||||
_name = name;
|
||||
_isNamed = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Инициализация экземляпяра [value].
|
||||
/// ENG: Initialization instance [value].
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toInstance(T value) {
|
||||
_mode = Mode.instance;
|
||||
_instance = value;
|
||||
_isSingleton = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Инициализация экземляпяра через провайдер [value].
|
||||
/// ENG: Initialization instance via provider [value].
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toProvide(T Function() value) {
|
||||
_mode = Mode.providerInstance;
|
||||
_provider = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Инициализация экземляпяра через провайдер [value] c динамическим параметром.
|
||||
/// ENG: Initialization instance via provider [value] with a dynamic param.
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toProvideWithParams(ProviderWithParams<T> value) {
|
||||
_mode = Mode.providerInstanceWithParams;
|
||||
_providerWithParams = value;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Инициализация экземляпяра как сингелтон [value].
|
||||
/// ENG: Initialization instance as a singelton [value].
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> singleton() {
|
||||
_isSingleton = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Поиск экземпляра.
|
||||
/// ENG: Resolve instance.
|
||||
///
|
||||
/// return [T]
|
||||
T? get instance => _instance;
|
||||
|
||||
/// RU: Поиск экземпляра.
|
||||
/// ENG: Resolve instance.
|
||||
///
|
||||
/// return [T]
|
||||
T? get provider {
|
||||
if (_isSingleton) {
|
||||
_instance ??= _provider?.call();
|
||||
return _instance;
|
||||
}
|
||||
return _provider?.call();
|
||||
}
|
||||
|
||||
/// RU: Поиск экземпляра с параметром.
|
||||
///
|
||||
/// ENG: Resolve instance with [params].
|
||||
///
|
||||
/// return [T]
|
||||
T? providerWithParams(dynamic params) {
|
||||
return _providerWithParams?.call(params);
|
||||
}
|
||||
}
|
||||
17
cherrypick/lib/src/factory.dart
Normal file
17
cherrypick/lib/src/factory.dart
Normal file
@@ -0,0 +1,17 @@
|
||||
///
|
||||
/// Copyright 2021 Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
import 'package:cherrypick/src/scope.dart';
|
||||
|
||||
abstract class Factory<T> {
|
||||
T createInstance(Scope scope);
|
||||
}
|
||||
105
cherrypick/lib/src/helper.dart
Normal file
105
cherrypick/lib/src/helper.dart
Normal file
@@ -0,0 +1,105 @@
|
||||
///
|
||||
/// Copyright 2021 Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
import 'package:cherrypick/src/scope.dart';
|
||||
import 'package:meta/meta.dart';
|
||||
|
||||
Scope? _rootScope;
|
||||
|
||||
class CherryPick {
|
||||
/// RU: Метод открывает главный [Scope].
|
||||
/// ENG: The method opens the main [Scope].
|
||||
///
|
||||
/// return
|
||||
static Scope openRootScope() {
|
||||
_rootScope ??= Scope(null);
|
||||
return _rootScope!;
|
||||
}
|
||||
|
||||
/// RU: Метод закрывает главный [Scope].
|
||||
/// ENG: The method close the main [Scope].
|
||||
///
|
||||
///
|
||||
static void closeRootScope() {
|
||||
if (_rootScope != null) {
|
||||
_rootScope = null;
|
||||
}
|
||||
}
|
||||
|
||||
/// RU: Метод открывает дочерний [Scope].
|
||||
/// ENG: The method open the child [Scope].
|
||||
///
|
||||
/// Дочерний [Scope] открывается с [scopeName]
|
||||
/// Child [Scope] open with [scopeName]
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// final String scopeName = 'firstScope.secondScope';
|
||||
/// final subScope = CherryPick.openScope(scopeName);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
@experimental
|
||||
static Scope openScope({String scopeName = '', String separator = '.'}) {
|
||||
if (scopeName.isEmpty) {
|
||||
return openRootScope();
|
||||
}
|
||||
|
||||
final nameParts = scopeName.split(separator);
|
||||
if (nameParts.isEmpty) {
|
||||
throw Exception('Can not open sub scope because scopeName can not split');
|
||||
}
|
||||
|
||||
return nameParts.fold(
|
||||
openRootScope(),
|
||||
(Scope previousValue, String element) =>
|
||||
previousValue.openSubScope(element));
|
||||
}
|
||||
|
||||
/// RU: Метод открывает дочерний [Scope].
|
||||
/// ENG: The method open the child [Scope].
|
||||
///
|
||||
/// Дочерний [Scope] открывается с [scopeName]
|
||||
/// Child [Scope] open with [scopeName]
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// final String scopeName = 'firstScope.secondScope';
|
||||
/// final subScope = CherryPick.closeScope(scopeName);
|
||||
/// ```
|
||||
///
|
||||
///
|
||||
@experimental
|
||||
static void closeScope({String scopeName = '', String separator = '.'}) {
|
||||
if (scopeName.isEmpty) {
|
||||
closeRootScope();
|
||||
}
|
||||
|
||||
final nameParts = scopeName.split(separator);
|
||||
if (nameParts.isEmpty) {
|
||||
throw Exception(
|
||||
'Can not close sub scope because scopeName can not split');
|
||||
}
|
||||
|
||||
if (nameParts.length > 1) {
|
||||
final lastPart = nameParts.removeLast();
|
||||
|
||||
final scope = nameParts.fold(
|
||||
openRootScope(),
|
||||
(Scope previousValue, String element) =>
|
||||
previousValue.openSubScope(element));
|
||||
scope.closeSubScope(lastPart);
|
||||
} else {
|
||||
openRootScope().closeSubScope(nameParts[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
53
cherrypick/lib/src/module.dart
Normal file
53
cherrypick/lib/src/module.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
///
|
||||
/// Copyright 2021 Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:cherrypick/src/binding.dart';
|
||||
import 'package:cherrypick/src/scope.dart';
|
||||
|
||||
/// RU: Класс Module является основой для пользовательских модулей.
|
||||
/// Этот класс нужен для инициализации [Scope].
|
||||
///
|
||||
/// RU: The Module class is the basis for custom modules.
|
||||
/// This class is needed to initialize [Scope].
|
||||
///
|
||||
abstract class Module {
|
||||
final Set<Binding> _bindingSet = HashSet();
|
||||
|
||||
/// RU: Метод добавляет в коллекцию модуля [Binding] экземпляр.
|
||||
///
|
||||
/// ENG: The method adds an instance to the collection of the [Binding] module.
|
||||
///
|
||||
/// return [Binding<T>]
|
||||
Binding<T> bind<T>() {
|
||||
final binding = Binding<T>();
|
||||
_bindingSet.add(binding);
|
||||
return binding;
|
||||
}
|
||||
|
||||
/// RU: Метод возвращает коллекцию [Binding] экземпляров.
|
||||
///
|
||||
/// ENG: The method returns a collection of [Binding] instances.
|
||||
///
|
||||
/// return [Set<Binding>]
|
||||
Set<Binding> get bindingSet => _bindingSet;
|
||||
|
||||
/// RU: Абстрактный метод для инициализации пользовательских экземпляров.
|
||||
/// В этом методе осуществляется конфигурация зависимостей.
|
||||
///
|
||||
/// ENG: Abstract method for initializing custom instances.
|
||||
/// This method configures dependencies.
|
||||
///
|
||||
/// return [void]
|
||||
void builder(Scope currentScope);
|
||||
}
|
||||
135
cherrypick/lib/src/scope.dart
Normal file
135
cherrypick/lib/src/scope.dart
Normal file
@@ -0,0 +1,135 @@
|
||||
///
|
||||
/// Copyright 2021 Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
import 'dart:collection';
|
||||
|
||||
import 'package:cherrypick/src/binding.dart';
|
||||
import 'package:cherrypick/src/module.dart';
|
||||
|
||||
Scope openRootScope() => Scope(null);
|
||||
|
||||
class Scope {
|
||||
final Scope? _parentScope;
|
||||
|
||||
/// RU: Метод возвращает родительский [Scope].
|
||||
///
|
||||
/// ENG: The method returns the parent [Scope].
|
||||
///
|
||||
/// return [Scope]
|
||||
Scope? get parentScope => _parentScope;
|
||||
|
||||
final Map<String, Scope> _scopeMap = HashMap();
|
||||
|
||||
Scope(this._parentScope);
|
||||
|
||||
final Set<Module> _modulesList = HashSet();
|
||||
|
||||
/// RU: Метод открывает дочерний (дополнительный) [Scope].
|
||||
///
|
||||
/// ENG: The method opens child (additional) [Scope].
|
||||
///
|
||||
/// return [Scope]
|
||||
Scope openSubScope(String name) {
|
||||
if (!_scopeMap.containsKey(name)) {
|
||||
_scopeMap[name] = Scope(this);
|
||||
}
|
||||
return _scopeMap[name]!;
|
||||
}
|
||||
|
||||
/// RU: Метод закрывает дочерний (дополнительный) [Scope].
|
||||
///
|
||||
/// ENG: The method closes child (additional) [Scope].
|
||||
///
|
||||
/// return [Scope]
|
||||
void closeSubScope(String name) {
|
||||
_scopeMap.remove(name);
|
||||
}
|
||||
|
||||
/// RU: Метод инициализирует пользовательские модули в [Scope].
|
||||
///
|
||||
/// ENG: The method initializes custom modules in [Scope].
|
||||
///
|
||||
/// return [Scope]
|
||||
Scope installModules(List<Module> modules) {
|
||||
_modulesList.addAll(modules);
|
||||
for (var module in modules) {
|
||||
module.builder(this);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Метод удаляет пользовательские модули из [Scope].
|
||||
///
|
||||
/// ENG: This method removes custom modules from [Scope].
|
||||
///
|
||||
/// return [Scope]
|
||||
Scope dropModules() {
|
||||
// [AlexeyYuPopkov](https://github.com/AlexeyYuPopkov) Thank you for the [Removed exception "ConcurrentModificationError"](https://github.com/pese-git/cherrypick/pull/2)
|
||||
_modulesList.clear();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Возвращает найденную зависимость, определенную параметром типа [T].
|
||||
/// Выдает [StateError], если зависимость не может быть разрешена.
|
||||
/// Если вы хотите получить [null], если зависимость не может быть найдена,
|
||||
/// то используйте вместо этого [tryResolve]
|
||||
/// return - возвращает объект типа [T] или [StateError]
|
||||
///
|
||||
/// ENG: Returns the found dependency specified by the type parameter [T].
|
||||
/// Throws [StateError] if the dependency cannot be resolved.
|
||||
/// If you want to get [null] if the dependency cannot be found then use [tryResolve] instead
|
||||
/// return - returns an object of type [T] or [StateError]
|
||||
///
|
||||
T resolve<T>({String? named, dynamic params}) {
|
||||
var resolved = tryResolve<T>(named: named, params: params);
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
} else {
|
||||
throw StateError(
|
||||
'Can\'t resolve dependency `$T`. Maybe you forget register it?');
|
||||
}
|
||||
}
|
||||
|
||||
/// RU: Возвращает найденную зависимость типа [T] или null, если она не может быть найдена.
|
||||
/// ENG: Returns found dependency of type [T] or null if it cannot be found.
|
||||
///
|
||||
T? tryResolve<T>({String? named, dynamic params}) {
|
||||
// 1 Поиск зависимости по всем модулям текущего скоупа
|
||||
if (_modulesList.isNotEmpty) {
|
||||
for (var module in _modulesList) {
|
||||
for (var binding in module.bindingSet) {
|
||||
if (binding.key == T &&
|
||||
((!binding.isNamed && named == null) ||
|
||||
(binding.isNamed && named == binding.name))) {
|
||||
switch (binding.mode) {
|
||||
case Mode.instance:
|
||||
return binding.instance;
|
||||
case Mode.providerInstance:
|
||||
return binding.provider;
|
||||
case Mode.providerInstanceWithParams:
|
||||
if (params == null) {
|
||||
throw StateError('Param is null. Maybe you forget pass it');
|
||||
}
|
||||
return binding.providerWithParams(params);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2 Поиск зависимостей в родительском скоупе
|
||||
return _parentScope?.tryResolve(named: named);
|
||||
}
|
||||
}
|
||||
21
cherrypick/pubspec.yaml
Normal file
21
cherrypick/pubspec.yaml
Normal file
@@ -0,0 +1,21 @@
|
||||
name: cherrypick
|
||||
description: Cherrypick is a small dependency injection (DI) library for dart/flutter projects.
|
||||
version: 2.0.1
|
||||
homepage: https://pese-git.github.io/cherrypick-site/
|
||||
documentation: https://github.com/pese-git/cherrypick/wiki
|
||||
repository: https://github.com/pese-git/cherrypick
|
||||
issue_tracker: https://github.com/pese-git/cherrypick/issues
|
||||
|
||||
environment:
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
meta: ^1.3.0
|
||||
|
||||
dev_dependencies:
|
||||
#pedantic: ^1.11.0
|
||||
|
||||
test: ^1.17.2
|
||||
|
||||
mockito: ^5.0.6
|
||||
lints: ^2.1.0
|
||||
296
cherrypick/test/src/binding_test.dart
Normal file
296
cherrypick/test/src/binding_test.dart
Normal file
@@ -0,0 +1,296 @@
|
||||
import 'package:cherrypick/src/binding.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('Check instance.', () {
|
||||
group('Without name.', () {
|
||||
test('Binding resolves null', () {
|
||||
final binding = Binding<int>();
|
||||
expect(binding.instance, null);
|
||||
});
|
||||
|
||||
test('Binding check mode', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>().toInstance(expectedValue);
|
||||
|
||||
expect(binding.mode, Mode.instance);
|
||||
});
|
||||
|
||||
test('Binding check singleton', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>().toInstance(expectedValue);
|
||||
|
||||
expect(binding.isSingleton, true);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>().toInstance(expectedValue);
|
||||
|
||||
expect(binding.instance, expectedValue);
|
||||
});
|
||||
|
||||
test('Binding resolves value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>().toInstance(expectedValue);
|
||||
expect(binding.instance, expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
group('With name.', () {
|
||||
test('Binding resolves null', () {
|
||||
final binding = Binding<int>().withName('expectedValue');
|
||||
expect(binding.instance, null);
|
||||
});
|
||||
|
||||
test('Binding check mode', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().withName('expectedValue').toInstance(expectedValue);
|
||||
|
||||
expect(binding.mode, Mode.instance);
|
||||
});
|
||||
|
||||
test('Binding check key', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().withName('expectedValue').toInstance(expectedValue);
|
||||
|
||||
expect(binding.key, int);
|
||||
});
|
||||
|
||||
test('Binding check singleton', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().withName('expectedValue').toInstance(expectedValue);
|
||||
|
||||
expect(binding.isSingleton, true);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().withName('expectedValue').toInstance(expectedValue);
|
||||
|
||||
expect(binding.instance, expectedValue);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().withName('expectedValue').toInstance(expectedValue);
|
||||
|
||||
expect(binding.name, 'expectedValue');
|
||||
});
|
||||
|
||||
test('Binding resolves value', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().withName('expectedValue').toInstance(expectedValue);
|
||||
expect(binding.instance, expectedValue);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('Check provide.', () {
|
||||
group('Without name.', () {
|
||||
test('Binding resolves null', () {
|
||||
final binding = Binding<int>();
|
||||
expect(binding.provider, null);
|
||||
});
|
||||
|
||||
test('Binding check mode', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>().toProvide(() => expectedValue);
|
||||
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
});
|
||||
|
||||
test('Binding check singleton', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>().toProvide(() => expectedValue);
|
||||
|
||||
expect(binding.isSingleton, false);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>().toProvide(() => expectedValue);
|
||||
|
||||
expect(binding.provider, expectedValue);
|
||||
});
|
||||
|
||||
test('Binding resolves value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>().toProvide(() => expectedValue);
|
||||
expect(binding.provider, expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
group('With name.', () {
|
||||
test('Binding resolves null', () {
|
||||
final binding = Binding<int>().withName('expectedValue');
|
||||
expect(binding.provider, null);
|
||||
});
|
||||
|
||||
test('Binding check mode', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue);
|
||||
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
});
|
||||
|
||||
test('Binding check key', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue);
|
||||
|
||||
expect(binding.key, int);
|
||||
});
|
||||
|
||||
test('Binding check singleton', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue);
|
||||
|
||||
expect(binding.isSingleton, false);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue);
|
||||
|
||||
expect(binding.provider, expectedValue);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue);
|
||||
|
||||
expect(binding.name, 'expectedValue');
|
||||
});
|
||||
|
||||
test('Binding resolves value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue);
|
||||
expect(binding.provider, expectedValue);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
group('Check singleton provide.', () {
|
||||
group('Without name.', () {
|
||||
test('Binding resolves null', () {
|
||||
final binding = Binding<int>().singleton();
|
||||
expect(binding.provider, null);
|
||||
});
|
||||
|
||||
test('Binding check mode', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().toProvide(() => expectedValue).singleton();
|
||||
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
});
|
||||
|
||||
test('Binding check singleton', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().toProvide(() => expectedValue).singleton();
|
||||
|
||||
expect(binding.isSingleton, true);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().toProvide(() => expectedValue).singleton();
|
||||
|
||||
expect(binding.provider, expectedValue);
|
||||
});
|
||||
|
||||
test('Binding resolves value', () {
|
||||
final expectedValue = 5;
|
||||
final binding =
|
||||
Binding<int>().toProvide(() => expectedValue).singleton();
|
||||
expect(binding.provider, expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
group('With name.', () {
|
||||
test('Binding resolves null', () {
|
||||
final binding = Binding<int>().withName('expectedValue').singleton();
|
||||
expect(binding.provider, null);
|
||||
});
|
||||
|
||||
test('Binding check mode', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue)
|
||||
.singleton();
|
||||
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
});
|
||||
|
||||
test('Binding check key', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue)
|
||||
.singleton();
|
||||
|
||||
expect(binding.key, int);
|
||||
});
|
||||
|
||||
test('Binding check singleton', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue)
|
||||
.singleton();
|
||||
|
||||
expect(binding.isSingleton, true);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue)
|
||||
.singleton();
|
||||
|
||||
expect(binding.provider, expectedValue);
|
||||
});
|
||||
|
||||
test('Binding check value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue)
|
||||
.singleton();
|
||||
|
||||
expect(binding.name, 'expectedValue');
|
||||
});
|
||||
|
||||
test('Binding resolves value', () {
|
||||
final expectedValue = 5;
|
||||
final binding = Binding<int>()
|
||||
.withName('expectedValue')
|
||||
.toProvide(() => expectedValue)
|
||||
.singleton();
|
||||
expect(binding.provider, expectedValue);
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
91
cherrypick/test/src/scope_test.dart
Normal file
91
cherrypick/test/src/scope_test.dart
Normal file
@@ -0,0 +1,91 @@
|
||||
import 'package:cherrypick/src/module.dart';
|
||||
import 'package:cherrypick/src/scope.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('Without parent scope.', () {
|
||||
test('Parent scope is null.', () {
|
||||
final scope = Scope(null);
|
||||
expect(scope.parentScope, null);
|
||||
});
|
||||
|
||||
test('Open sub scope.', () {
|
||||
final scope = Scope(null);
|
||||
final subScope = scope.openSubScope('subScope');
|
||||
expect(scope.openSubScope('subScope'), subScope);
|
||||
});
|
||||
|
||||
test("Container throws state error if the value can't be resolved", () {
|
||||
final scope = Scope(null);
|
||||
expect(() => scope.resolve<String>(), throwsA(isA<StateError>()));
|
||||
});
|
||||
|
||||
test('Container resolves value after adding a dependency', () {
|
||||
final expectedValue = 'test string';
|
||||
final scope = Scope(null)
|
||||
.installModules([TestModule<String>(value: expectedValue)]);
|
||||
expect(scope.resolve<String>(), expectedValue);
|
||||
});
|
||||
});
|
||||
|
||||
group('With parent scope.', () {
|
||||
/*
|
||||
test(
|
||||
"Container bind() throws state error (if it's parent already has a resolver)",
|
||||
() {
|
||||
final parentScope = new Scope(null)
|
||||
.installModules([TestModule<String>(value: "string one")]);
|
||||
final scope = new Scope(parentScope);
|
||||
|
||||
expect(
|
||||
() => scope.installModules([TestModule<String>(value: "string two")]),
|
||||
throwsA(isA<StateError>()));
|
||||
});
|
||||
*/
|
||||
test('Container resolve() returns a value from parent container.', () {
|
||||
final expectedValue = 5;
|
||||
final parentScope = Scope(null);
|
||||
final scope = Scope(parentScope);
|
||||
|
||||
parentScope.installModules([TestModule<int>(value: expectedValue)]);
|
||||
|
||||
expect(scope.resolve<int>(), expectedValue);
|
||||
});
|
||||
|
||||
test('Container resolve() returns a several value from parent container.',
|
||||
() {
|
||||
final expectedIntValue = 5;
|
||||
final expectedStringValue = 'Hello world';
|
||||
final parentScope = Scope(null).installModules([
|
||||
TestModule<int>(value: expectedIntValue),
|
||||
TestModule<String>(value: expectedStringValue)
|
||||
]);
|
||||
final scope = Scope(parentScope);
|
||||
|
||||
expect(scope.resolve<int>(), expectedIntValue);
|
||||
expect(scope.resolve<String>(), expectedStringValue);
|
||||
});
|
||||
|
||||
test("Container resolve() throws a state error if parent hasn't value too.",
|
||||
() {
|
||||
final parentScope = Scope(null);
|
||||
final scope = Scope(parentScope);
|
||||
expect(() => scope.resolve<int>(), throwsA(isA<StateError>()));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class TestModule<T> extends Module {
|
||||
final T value;
|
||||
final String? name;
|
||||
|
||||
TestModule({required this.value, this.name});
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
if (name == null) {
|
||||
bind<T>().toInstance(value);
|
||||
} else {
|
||||
bind<T>().withName(name!).toInstance(value);
|
||||
}
|
||||
}
|
||||
}
|
||||
32
cherrypick_flutter/.gitignore
vendored
Normal file
32
cherrypick_flutter/.gitignore
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
# Miscellaneous
|
||||
*.class
|
||||
*.log
|
||||
*.pyc
|
||||
*.swp
|
||||
.DS_Store
|
||||
.atom/
|
||||
.buildlog/
|
||||
.history
|
||||
.svn/
|
||||
migrate_working_dir/
|
||||
|
||||
# IntelliJ related
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea/
|
||||
|
||||
# The .vscode folder contains launch configuration and tasks you configure in
|
||||
# VS Code which you may wish to be included in version control, so this line
|
||||
# is commented out by default.
|
||||
#.vscode/
|
||||
|
||||
# Flutter/Dart/Pub related
|
||||
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||
/pubspec.lock
|
||||
**/doc/api/
|
||||
.dart_tool/
|
||||
build/
|
||||
|
||||
|
||||
pubspec_overrides.yaml
|
||||
10
cherrypick_flutter/.metadata
Normal file
10
cherrypick_flutter/.metadata
Normal file
@@ -0,0 +1,10 @@
|
||||
# This file tracks properties of this Flutter project.
|
||||
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||
#
|
||||
# This file should be version controlled and should not be manually edited.
|
||||
|
||||
version:
|
||||
revision: "4cf269e36de2573851eaef3c763994f8f9be494d"
|
||||
channel: "stable"
|
||||
|
||||
project_type: package
|
||||
13
cherrypick_flutter/CHANGELOG.md
Normal file
13
cherrypick_flutter/CHANGELOG.md
Normal file
@@ -0,0 +1,13 @@
|
||||
## 1.1.0
|
||||
|
||||
- **FIX**: update description.
|
||||
- **FIX**: update gitignore.
|
||||
- **FEAT**: modify api in CherryPickProvider.
|
||||
|
||||
## 1.0.1
|
||||
|
||||
- Update a dependency to the latest release.
|
||||
|
||||
## 0.0.1
|
||||
|
||||
* TODO: Describe initial release.
|
||||
201
cherrypick_flutter/LICENSE
Normal file
201
cherrypick_flutter/LICENSE
Normal file
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
90
cherrypick_flutter/README.md
Normal file
90
cherrypick_flutter/README.md
Normal file
@@ -0,0 +1,90 @@
|
||||
# CherryPick Flutter
|
||||
|
||||
`cherrypick_flutter` is a Flutter library that allows access to the root scope through the context using `CherryPickProvider`. This package is designed to provide a simple and convenient way to interact with the root scope within the widget tree.
|
||||
|
||||
## Installation
|
||||
|
||||
Add `cherrypick_flutter` to your `pubspec.yaml`:
|
||||
|
||||
```yaml
|
||||
dependencies:
|
||||
cherrypick_flutter: ^1.0.0
|
||||
```
|
||||
|
||||
Then run `flutter pub get` to install the package.
|
||||
|
||||
## Usage
|
||||
|
||||
### Importing the Package
|
||||
|
||||
To start using `cherrypick_flutter`, import it into your Dart code:
|
||||
|
||||
```dart
|
||||
import 'package:cherrypick_flutter/cherrypick_flutter.dart';
|
||||
```
|
||||
|
||||
### Providing State with `CherryPickProvider`
|
||||
|
||||
Use `CherryPickProvider` to wrap the part of the widget tree that requires access to the provided state.
|
||||
|
||||
```dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:cherrypick_flutter/cherrypick_flutter.dart';
|
||||
|
||||
void main() {
|
||||
runApp(
|
||||
CherryPickProvider(
|
||||
rootScope: yourRootScopeInstance,
|
||||
child: MyApp(),
|
||||
),
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
### Accessing State
|
||||
|
||||
To access the state provided by `CherryPickProvider`, use the `of` method. This is typically done in the `build` method of your widgets.
|
||||
|
||||
```dart
|
||||
class MyWidget extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final rootScope = CherryPickProvider.of(context).rootScope;
|
||||
|
||||
return Text('Current state: ${rootScope.someStateValue}');
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Updating State
|
||||
|
||||
The `CherryPickProvider` will automatically update its dependents when its state changes. Ensure to override the `updateShouldNotify` method to specify when notifications should occur, as shown in the provided implementation.
|
||||
|
||||
## Example
|
||||
|
||||
Here is a simple example showing how to implement and use the `CherryPickProvider`.
|
||||
|
||||
```dart
|
||||
class MyApp extends StatelessWidget {
|
||||
const MyApp({super.key});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final rootScope = CherryPickProvider.of(context).rootScope;
|
||||
|
||||
return MaterialApp.router(
|
||||
routerDelegate: rootScope.resolve<AppRouter>().delegate(),
|
||||
routeInformationParser:
|
||||
rootScope.resolve<AppRouter>().defaultRouteParser(),
|
||||
);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Contributing
|
||||
|
||||
We welcome contributions from the community. Please open issues and pull requests if you have ideas for improvements.
|
||||
|
||||
## License
|
||||
|
||||
This project is licensed under the Apache License 2.0.
|
||||
4
cherrypick_flutter/analysis_options.yaml
Normal file
4
cherrypick_flutter/analysis_options.yaml
Normal file
@@ -0,0 +1,4 @@
|
||||
include: package:flutter_lints/flutter.yaml
|
||||
|
||||
# Additional information about this file can be found at
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
15
cherrypick_flutter/lib/cherrypick_flutter.dart
Normal file
15
cherrypick_flutter/lib/cherrypick_flutter.dart
Normal file
@@ -0,0 +1,15 @@
|
||||
///
|
||||
/// Copyright 2021 Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
library cherrypick_flutter;
|
||||
|
||||
export 'src/cherrypick_provider.dart';
|
||||
44
cherrypick_flutter/lib/src/cherrypick_provider.dart
Normal file
44
cherrypick_flutter/lib/src/cherrypick_provider.dart
Normal file
@@ -0,0 +1,44 @@
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
import 'package:flutter/widgets.dart';
|
||||
|
||||
///
|
||||
/// Copyright 2021 Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
/// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
/// you may not use this file except in compliance with the License.
|
||||
/// You may obtain a copy of the License at
|
||||
/// http://www.apache.org/licenses/LICENSE-2.0
|
||||
/// Unless required by applicable law or agreed to in writing, software
|
||||
/// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
/// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
/// See the License for the specific language governing permissions and
|
||||
/// limitations under the License.
|
||||
///
|
||||
|
||||
final class CherryPickProvider extends InheritedWidget {
|
||||
Scope openRootScope() => CherryPick.openRootScope();
|
||||
|
||||
Scope openSubScope({String scopeName = '', String separator = '.'}) =>
|
||||
CherryPick.openScope(scopeName: scopeName, separator: separator);
|
||||
|
||||
// Constructor for CherryPickProvider. Initializes with a required rootScope and child widget.
|
||||
const CherryPickProvider({
|
||||
super.key,
|
||||
required super.child,
|
||||
});
|
||||
|
||||
// Method to access the nearest CherryPickProvider instance from the context
|
||||
static CherryPickProvider of(BuildContext context) {
|
||||
// Looks up the widget tree for an instance of CherryPickProvider
|
||||
final CherryPickProvider? result =
|
||||
context.dependOnInheritedWidgetOfExactType<CherryPickProvider>();
|
||||
// Assert to ensure a CherryPickProvider is present in the context
|
||||
assert(result != null, 'No CherryPickProvider found in context');
|
||||
return result!;
|
||||
}
|
||||
|
||||
// Determines whether the widget should notify dependents when it changes
|
||||
@override
|
||||
bool updateShouldNotify(CherryPickProvider oldWidget) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
58
cherrypick_flutter/pubspec.yaml
Normal file
58
cherrypick_flutter/pubspec.yaml
Normal file
@@ -0,0 +1,58 @@
|
||||
name: cherrypick_flutter
|
||||
description: "Flutter library that allows access to the root scope through the context using `CherryPickProvider`."
|
||||
version: 1.1.0
|
||||
homepage: https://pese-git.github.io/cherrypick-site/
|
||||
documentation: https://github.com/pese-git/cherrypick/wiki
|
||||
repository: https://github.com/pese-git/cherrypick
|
||||
issue_tracker: https://github.com/pese-git/cherrypick/issues
|
||||
|
||||
environment:
|
||||
sdk: ^3.5.2
|
||||
flutter: ">=1.17.0"
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
cherrypick: ^2.0.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^4.0.0
|
||||
|
||||
# For information on the generic Dart part of this file, see the
|
||||
# following page: https://dart.dev/tools/pub/pubspec
|
||||
|
||||
# The following section is specific to Flutter packages.
|
||||
flutter:
|
||||
|
||||
# To add assets to your package, add an assets section, like this:
|
||||
# assets:
|
||||
# - images/a_dot_burr.jpeg
|
||||
# - images/a_dot_ham.jpeg
|
||||
#
|
||||
# For details regarding assets in packages, see
|
||||
# https://flutter.dev/to/asset-from-package
|
||||
#
|
||||
# An image asset can refer to one or more resolution-specific "variants", see
|
||||
# https://flutter.dev/to/resolution-aware-images
|
||||
|
||||
# To add custom fonts to your package, add a fonts section here,
|
||||
# in this "flutter" section. Each entry in this list should have a
|
||||
# "family" key with the font family name, and a "fonts" key with a
|
||||
# list giving the asset and other descriptors for the font. For
|
||||
# example:
|
||||
# fonts:
|
||||
# - family: Schyler
|
||||
# fonts:
|
||||
# - asset: fonts/Schyler-Regular.ttf
|
||||
# - asset: fonts/Schyler-Italic.ttf
|
||||
# style: italic
|
||||
# - family: Trajan Pro
|
||||
# fonts:
|
||||
# - asset: fonts/TrajanPro.ttf
|
||||
# - asset: fonts/TrajanPro_Bold.ttf
|
||||
# weight: 700
|
||||
#
|
||||
# For details regarding fonts in packages, see
|
||||
# https://flutter.dev/to/font-from-package
|
||||
7
cherrypick_flutter/test/cherrypick_flutter_test.dart
Normal file
7
cherrypick_flutter/test/cherrypick_flutter_test.dart
Normal file
@@ -0,0 +1,7 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
|
||||
void main() {
|
||||
test('adds one to input values', () {
|
||||
expect(1, 1);
|
||||
});
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
import 'dart:async';
|
||||
import 'package:meta/meta.dart';
|
||||
import 'package:dart_di/dart_di.dart';
|
||||
|
||||
void main() async {
|
||||
final dataModule = new DiContainer()
|
||||
..bind<ApiClient>().toValue(new ApiClientMock())
|
||||
..bind<DataRepository>()
|
||||
.toFactory1<ApiClient>((c) => new NetworkDataRepository(c))
|
||||
..bind<DataBloc>().toFactory1<DataRepository>((s) => new DataBloc(s));
|
||||
|
||||
final dataBloc = dataModule.resolve<DataBloc>();
|
||||
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<String> get data => _dataController.stream;
|
||||
StreamController<String> _dataController = new StreamController.broadcast();
|
||||
|
||||
DataBloc(this._dataRepository);
|
||||
|
||||
Future<void> fetchData() async {
|
||||
try {
|
||||
_dataController.sink.add(await _dataRepository.getData());
|
||||
} catch (e) {
|
||||
_dataController.sink.addError(e);
|
||||
}
|
||||
}
|
||||
|
||||
void dispose() {
|
||||
_dataController.close();
|
||||
}
|
||||
}
|
||||
|
||||
abstract class DataRepository {
|
||||
Future<String> getData();
|
||||
}
|
||||
|
||||
class NetworkDataRepository implements DataRepository {
|
||||
final ApiClient _apiClient;
|
||||
final _token = 'token';
|
||||
|
||||
NetworkDataRepository(this._apiClient);
|
||||
|
||||
@override
|
||||
Future<String> 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';
|
||||
}
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
name: example
|
||||
version: 1.0.0
|
||||
author: "Sergey Penkovsky <sergey.penkovsky@gmail.com>"
|
||||
homepage:
|
||||
|
||||
environment:
|
||||
sdk: ">=2.2.0 <3.0.0"
|
||||
|
||||
|
||||
dependencies:
|
||||
dart_di:
|
||||
path: ../
|
||||
|
||||
dev_dependencies:
|
||||
@@ -1,5 +0,0 @@
|
||||
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';
|
||||
@@ -1,75 +0,0 @@
|
||||
import 'package:dart_di/resolvers/resolving_context.dart';
|
||||
|
||||
/**
|
||||
* Контейнер - это объект, которой хранит все резолверы зависимостей.
|
||||
*/
|
||||
class DiContainer {
|
||||
final DiContainer _parent;
|
||||
|
||||
final _resolvers = <Type, ResolvingContext>{};
|
||||
|
||||
DiContainer([this._parent]);
|
||||
|
||||
/**
|
||||
* Добавляет resolver зависимостей типа [T] в контейнер.
|
||||
* Обратите внимание, что перезапись значений внутри одного контейнера запрещена.
|
||||
* @return - возвращает [ResolvingContext] или [StateError]
|
||||
*/
|
||||
ResolvingContext<T> bind<T>() {
|
||||
var context = ResolvingContext<T>(this);
|
||||
if (hasInTree<T>()) {
|
||||
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<T>() {
|
||||
var resolved = tryResolve<T>();
|
||||
if (resolved != null) {
|
||||
return resolved;
|
||||
} else {
|
||||
throw StateError(
|
||||
'Can\'t resolve dependency `$T`. Maybe you forget register it?');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает разрешенную зависимость типа [T] или null, если она не может быть разрешена.
|
||||
*/
|
||||
T tryResolve<T>() {
|
||||
var resolver = _resolvers[T];
|
||||
if (resolver != null) {
|
||||
return resolver.resolve();
|
||||
} else {
|
||||
return _parent?.tryResolve<T>();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает true, если у этого контейнера есть средство разрешения зависимостей для типа [T].
|
||||
* Если вы хотите проверить его для всего дерева контейнеров, используйте вместо него [hasInTree].
|
||||
* @return - возвращает булево значение
|
||||
*/
|
||||
bool has<T>() {
|
||||
return _resolvers.containsKey(T);
|
||||
}
|
||||
|
||||
/**
|
||||
* Возвращает true, если контейнер или его родители содержат средство разрешения зависимостей для типа [T].
|
||||
* Если вы хотите проверить его только для этого контейнера, используйте вместо него [has].
|
||||
* @return - возвращает булево значение
|
||||
*/
|
||||
bool hasInTree<T>() {
|
||||
return has<T>() || (_parent != null && _parent.hasInTree<T>());
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:dart_di/resolvers/resolver.dart';
|
||||
|
||||
/**
|
||||
* Разрешает зависимость для фабричной функции
|
||||
*/
|
||||
class FactoryResolver<T> extends Resolver<T> {
|
||||
final T Function() _factory;
|
||||
|
||||
FactoryResolver(this._factory);
|
||||
|
||||
@override
|
||||
T resolve() {
|
||||
return _factory();
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
/**
|
||||
* Resolver - это абстракция, которая определяет,
|
||||
* как контейнер будет разрешать зависимость
|
||||
*/
|
||||
abstract class Resolver<T> {
|
||||
/**
|
||||
* Разрешает зависимость типа [T]
|
||||
* @return - возвращает объект типа [T]
|
||||
*/
|
||||
T resolve();
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
/// Resolvers определяет, как разрешить зависимость для контейнера
|
||||
library resolvers;
|
||||
|
||||
export 'resolver.dart';
|
||||
export 'factory_resolver.dart';
|
||||
export 'value_resolver.dart';
|
||||
export 'singelton_resolver.dart';
|
||||
@@ -1,170 +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<T> extends Resolver {
|
||||
/// Корневой резолвер
|
||||
Resolver<T> get resolver => _resolver;
|
||||
|
||||
DiContainer _container;
|
||||
|
||||
Resolver _resolver;
|
||||
|
||||
ResolvingContext(this._container);
|
||||
|
||||
/**
|
||||
* Разрешает зависимость типа [T]
|
||||
* @return - возвращает объект типа [T]
|
||||
*/
|
||||
@override
|
||||
T resolve() {
|
||||
_verify();
|
||||
return _resolver?.resolve();
|
||||
}
|
||||
|
||||
/**
|
||||
* Добавляет резолвер в качестве корневого резолвера
|
||||
* С помощью этого метода вы можете добавить любой
|
||||
* пользовательский резолвер
|
||||
*/
|
||||
ResolvingContext<T> toResolver<TImpl extends T>(Resolver<TImpl> resolver) {
|
||||
_resolver = resolver;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать резолвер значения
|
||||
*/
|
||||
ResolvingContext<T> toValue<TImpl extends T>(T value) {
|
||||
Resolver<TImpl> resolver = ValueResolver(value);
|
||||
return toResolver<TImpl>(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Преобразователь в сингелтон
|
||||
*/
|
||||
ResolvingContext<T> asSingleton() {
|
||||
return toResolver(SingletonResolver<T>(resolver));
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver без каких-либо зависимостей
|
||||
*/
|
||||
ResolvingContext<T> toFactory<TImpl extends T>(TImpl Function() factory) {
|
||||
Resolver<TImpl> resolver = FactoryResolver<TImpl>(factory);
|
||||
return toResolver(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver с 1 зависимостью от контейнера
|
||||
*/
|
||||
ResolvingContext<T> toFactory1<T1>(T Function(T1) factory) {
|
||||
Resolver<T> resolver =
|
||||
FactoryResolver<T>(() => factory(_container.resolve<T1>()));
|
||||
return toResolver(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver с 2 зависимостями от контейнера
|
||||
*/
|
||||
ResolvingContext<T> toFactory2<T1, T2>(T Function(T1, T2) factory) {
|
||||
Resolver<T> resolver = FactoryResolver<T>(
|
||||
() => factory(_container.resolve<T1>(), _container.resolve<T2>()));
|
||||
return toResolver(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver с 3 зависимостями от контейнера
|
||||
*/
|
||||
ResolvingContext<T> toFactory3<T1, T2, T3>(T Function(T1, T2, T3) factory) {
|
||||
Resolver<T> resolver = FactoryResolver<T>(() => factory(
|
||||
_container.resolve<T1>(),
|
||||
_container.resolve<T2>(),
|
||||
_container.resolve<T3>()));
|
||||
return toResolver(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver с 4 зависимостями от контейнера
|
||||
*/
|
||||
ResolvingContext<T> toFactory4<T1, T2, T3, T4>(
|
||||
T Function(T1, T2, T3, T4) factory) {
|
||||
Resolver<T> resolver = FactoryResolver<T>(() => factory(
|
||||
_container.resolve<T1>(),
|
||||
_container.resolve<T2>(),
|
||||
_container.resolve<T3>(),
|
||||
_container.resolve<T4>()));
|
||||
return toResolver(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver с 5 зависимостями от контейнера
|
||||
*/
|
||||
ResolvingContext<T> toFactory5<T1, T2, T3, T4, T5>(
|
||||
T Function(T1, T2, T3, T4, T5) factory) {
|
||||
Resolver<T> resolver = FactoryResolver<T>(() => factory(
|
||||
_container.resolve<T1>(),
|
||||
_container.resolve<T2>(),
|
||||
_container.resolve<T3>(),
|
||||
_container.resolve<T4>(),
|
||||
_container.resolve<T5>()));
|
||||
return toResolver(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver с 6 зависимостями от контейнера
|
||||
*/
|
||||
ResolvingContext<T> toFactory6<T1, T2, T3, T4, T5, T6>(
|
||||
T Function(T1, T2, T3, T4, T5, T6) factory) {
|
||||
Resolver<T> resolver = FactoryResolver<T>(() => factory(
|
||||
_container.resolve<T1>(),
|
||||
_container.resolve<T2>(),
|
||||
_container.resolve<T3>(),
|
||||
_container.resolve<T4>(),
|
||||
_container.resolve<T5>(),
|
||||
_container.resolve<T6>()));
|
||||
return toResolver(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver с 7 зависимостями от контейнера
|
||||
*/
|
||||
ResolvingContext<T> toFactory7<T1, T2, T3, T4, T5, T6, T7>(
|
||||
T Function(T1, T2, T3, T4, T5, T6, T7) factory) {
|
||||
Resolver<T> resolver = FactoryResolver<T>(() => factory(
|
||||
_container.resolve<T1>(),
|
||||
_container.resolve<T2>(),
|
||||
_container.resolve<T3>(),
|
||||
_container.resolve<T4>(),
|
||||
_container.resolve<T5>(),
|
||||
_container.resolve<T6>(),
|
||||
_container.resolve<T7>()));
|
||||
return toResolver(resolver);
|
||||
}
|
||||
|
||||
/**
|
||||
* Создать фабричный resolver с 8 зависимостями от контейнера
|
||||
*/
|
||||
ResolvingContext<T> toFactory8<T1, T2, T3, T4, T5, T6, T7, T8>(
|
||||
T Function(T1, T2, T3, T4, T5, T6, T7, T8) factory) {
|
||||
Resolver<T> resolver = FactoryResolver<T>(() => factory(
|
||||
_container.resolve<T1>(),
|
||||
_container.resolve<T2>(),
|
||||
_container.resolve<T3>(),
|
||||
_container.resolve<T4>(),
|
||||
_container.resolve<T5>(),
|
||||
_container.resolve<T6>(),
|
||||
_container.resolve<T7>(),
|
||||
_container.resolve<T8>()));
|
||||
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()");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
import 'package:dart_di/resolvers/resolver.dart';
|
||||
|
||||
class SingletonResolver<T> extends Resolver<T> {
|
||||
Resolver<T> _decoratedResolver;
|
||||
T _value = null;
|
||||
|
||||
SingletonResolver(this._decoratedResolver);
|
||||
|
||||
@override
|
||||
T resolve() {
|
||||
if (_value == null) {
|
||||
_value = _decoratedResolver.resolve();
|
||||
}
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
@@ -1,15 +0,0 @@
|
||||
import 'package:dart_di/resolvers/resolver.dart';
|
||||
|
||||
/**
|
||||
* Разрешает зависимость для значения
|
||||
*/
|
||||
class ValueResolver<T> extends Resolver<T> {
|
||||
T _value;
|
||||
|
||||
ValueResolver(this._value);
|
||||
|
||||
@override
|
||||
T resolve() {
|
||||
return _value;
|
||||
}
|
||||
}
|
||||
15
melos.yaml
Normal file
15
melos.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
name: cherrypick_workspace
|
||||
|
||||
sdkPath: .fvm/flutter_sdk
|
||||
|
||||
packages:
|
||||
- cherrypick
|
||||
- cherrypick_flutter
|
||||
|
||||
scripts:
|
||||
analyze:
|
||||
run: |
|
||||
flutter analyze
|
||||
format:
|
||||
run: |
|
||||
flutter format
|
||||
626
pubspec.lock
Normal file
626
pubspec.lock
Normal file
@@ -0,0 +1,626 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_fe_analyzer_shared:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _fe_analyzer_shared
|
||||
sha256: "45cfa8471b89fb6643fe9bf51bd7931a76b8f5ec2d65de4fb176dba8d4f22c77"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "73.0.0"
|
||||
_macros:
|
||||
dependency: transitive
|
||||
description: dart
|
||||
source: sdk
|
||||
version: "0.3.2"
|
||||
analyzer:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: analyzer
|
||||
sha256: "4959fec185fe70cce007c57e9ab6983101dbe593d2bf8bbfb4453aaec0cf470a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.8.0"
|
||||
ansi_styles:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ansi_styles
|
||||
sha256: "9c656cc12b3c27b17dd982b2cc5c0cfdfbdabd7bc8f3ae5e8542d9867b47ce8a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.2+1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
build:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: build
|
||||
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
built_collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_collection
|
||||
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
built_value:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: built_value
|
||||
sha256: ea90e81dc4a25a043d9bee692d20ed6d1c4a1662a28c03a96417446c093ed6b4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.9.5"
|
||||
charcode:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: charcode
|
||||
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
checked_yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: checked_yaml
|
||||
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.3"
|
||||
cli_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_config
|
||||
sha256: ac20a183a07002b700f0c25e61b7ee46b23c309d76ab7b7640a028f18e4d99ec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.0"
|
||||
cli_launcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_launcher
|
||||
sha256: "5e7e0282b79e8642edd6510ee468ae2976d847a0a29b3916e85f5fa1bfe24005"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.1"
|
||||
cli_util:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: cli_util
|
||||
sha256: ff6785f7e9e3c38ac98b2fb035701789de90154024a75b6cb926445e83197d1c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.2"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_builder:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_builder
|
||||
sha256: "0ec10bf4a89e4c613960bf1e8b42c64127021740fb21640c29c909826a5eea3e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.10.1"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
conventional_commit:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: conventional_commit
|
||||
sha256: dec15ad1118f029c618651a4359eb9135d8b88f761aa24e4016d061cd45948f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.0+1"
|
||||
convert:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: convert
|
||||
sha256: b30acd5944035672bc15c6b7a8b47d773e41e2f17de064350988c5d02adb1c68
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.2"
|
||||
coverage:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: coverage
|
||||
sha256: "802bd084fb82e55df091ec8ad1553a7331b61c08251eef19a508b6f3f3a9858d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.13.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: "1e445881f28f22d6140f181e07737b22f1e099a5e1ff94b0af2f9e4a463f4855"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.6"
|
||||
dart_style:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: dart_style
|
||||
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.7"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
frontend_server_client:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: frontend_server_client
|
||||
sha256: f64a0333a82f30b0cca061bc3d143813a486dc086b574bfb233b7c1372427694
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.0.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
graphs:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: graphs
|
||||
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.2"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: fe7ab022b76f3034adc518fb6ea04a82387620e19977665ea18d30a1cf43442f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
http_multi_server:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_multi_server
|
||||
sha256: aa6199f908078bb1c5efb8d8638d4ae191aac11b311132c3ef48ce352fb52ef8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.2"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.19.0"
|
||||
io:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: io
|
||||
sha256: dfd5a80599cf0165756e3181807ed3e77daf6dd4137caaad72d0b7931597650b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.5"
|
||||
js:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: js
|
||||
sha256: c1b2e9b5ea78c45e1a0788d29606ba27dc5f71f019f32ca5140f61ef071838cf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.1"
|
||||
json_annotation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: json_annotation
|
||||
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.9.0"
|
||||
lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: lints
|
||||
sha256: "0a217c6c989d21039f1498c3ed9f3ed71b354e69873f13a8dfc3c9fe76f1b452"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
macros:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: macros
|
||||
sha256: "0acaed5d6b7eab89f63350bccd82119e6c602df0f391260d0e32b5e23db79536"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.1.2-main.4"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
melos:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: melos
|
||||
sha256: "3f3ab3f902843d1e5a1b1a4dd39a4aca8ba1056f2d32fd8995210fa2843f646f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.3.2"
|
||||
meta:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: meta
|
||||
sha256: e3641ec5d63ebf0d9b41bd43201a66e3fc79a65db5f61fc181f04cd27aab950c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.16.0"
|
||||
mime:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mime
|
||||
sha256: "41a20518f0cb1256669420fdba0cd90d21561e560ac240f26ef8322e45bb7ed6"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
mockito:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: mockito
|
||||
sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.4.4"
|
||||
mustache_template:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: mustache_template
|
||||
sha256: a46e26f91445bfb0b60519be280555b06792460b27b19e2b19ad5b9740df5d1c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
node_preamble:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: node_preamble
|
||||
sha256: "6e7eac89047ab8a8d26cf16127b5ed26de65209847630400f9aefd7cd5c730db"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.2"
|
||||
package_config:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: package_config
|
||||
sha256: f096c55ebb7deb7e384101542bfba8c52696c1b56fca2eb62827989ef2353bbc
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
pool:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pool
|
||||
sha256: "20fe868b6314b322ea036ba325e6fc0711a22948856475e2c2b6306e8ab39c2a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.1"
|
||||
process:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: process
|
||||
sha256: "107d8be718f120bbba9dcd1e95e3bd325b1b4a4f07db64154635ba03f2567a0d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.3"
|
||||
prompts:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: prompts
|
||||
sha256: "3773b845e85a849f01e793c4fc18a45d52d7783b4cb6c0569fad19f9d0a774a1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.0"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
pub_updater:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_updater
|
||||
sha256: "54e8dc865349059ebe7f163d6acce7c89eb958b8047e6d6e80ce93b13d7c9e60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.0"
|
||||
pubspec_parse:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pubspec_parse
|
||||
sha256: "81876843eb50dc2e1e5b151792c9a985c5ed2536914115ed04e9c8528f6647b0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
shelf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf
|
||||
sha256: e7dd780a7ffb623c57850b33f43309312fc863fb6aa3d276a754bb299839ef12
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.2"
|
||||
shelf_packages_handler:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_packages_handler
|
||||
sha256: "89f967eca29607c933ba9571d838be31d67f53f6e4ee15147d5dc2934fee1b1e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
shelf_static:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_static
|
||||
sha256: c87c3875f91262785dade62d135760c2c69cb217ac759485334c5857ad89f6e3
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.3"
|
||||
shelf_web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shelf_web_socket
|
||||
sha256: "3632775c8e90d6c9712f883e633716432a27758216dfb61bd86a8321c0580925"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
source_gen:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_gen
|
||||
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.5.0"
|
||||
source_map_stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_map_stack_trace
|
||||
sha256: c0713a43e323c3302c2abe2a1cc89aa057a387101ebd280371d6a6c9fa68516b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
source_maps:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_maps
|
||||
sha256: "190222579a448b03896e0ca6eca5998fa810fda630c1d65e2f78b3f638f54812"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.10.13"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: test
|
||||
sha256: "301b213cd241ca982e9ba50266bd3f5bd1ea33f1455554c5abb85d1be0e2d87e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.25.15"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: fb31f383e2ee25fbbfe06b40fe21e1e458d14080e3c67e7ba0acfde4df4e0bbd
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.4"
|
||||
test_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_core
|
||||
sha256: "84d17c3486c8dfdbe5e12a50c8ae176d15e2a771b96909a9442b40173649ccaa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.6.8"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: ddfa8d30d89985b96407efce8acbdd124701f96741f2d981ca860662f1c0dc02
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.0"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "69da27e49efa56a15f8afe8f4438c4ec02eff0a117df1b22ea4aad194fe1c104"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
web_socket:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket
|
||||
sha256: bfe6f435f6ec49cb6c01da1e275ae4228719e59a6b067048c51e72d9d63bcc4b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
web_socket_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web_socket_channel
|
||||
sha256: d645757fb0f4773d602444000a8131ff5d48c9e47adfe9772652dd1a4f2d45c8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.3"
|
||||
webkit_inspection_protocol:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: webkit_inspection_protocol
|
||||
sha256: "87d3f2333bb240704cd3f1c6b5b7acd8a10e7f0bc28c28dcf14e782014f4a572"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
yaml_edit:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml_edit
|
||||
sha256: fb38626579fb345ad00e674e2af3a5c9b0cc4b9bfb8fd7f7ff322c7c9e62aef5
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.2"
|
||||
sdks:
|
||||
dart: ">=3.5.0 <4.0.0"
|
||||
24
pubspec.yaml
24
pubspec.yaml
@@ -1,16 +1,22 @@
|
||||
name: dart_di
|
||||
description: Experimental Dependency Injection library.
|
||||
version: 0.0.1
|
||||
author: Sergey Penkovsky <sergey.penkovsky@gmail.com>
|
||||
homepage:
|
||||
name: cherrypick_workspace
|
||||
description: Cherrypick is a small dependency injection (DI) library for dart/flutter projects.
|
||||
version: 2.0.0
|
||||
homepage: https://pese-git.github.io/cherrypick-site/
|
||||
documentation: https://github.com/pese-git/cherrypick/wiki
|
||||
repository: https://github.com/pese-git/cherrypick
|
||||
issue_tracker: https://github.com/pese-git/cherrypick/issues
|
||||
|
||||
environment:
|
||||
sdk: ">=2.7.0 <3.0.0"
|
||||
sdk: ">=3.0.0 <4.0.0"
|
||||
|
||||
dependencies:
|
||||
meta: ^1.1.8
|
||||
meta: ^1.3.0
|
||||
|
||||
dev_dependencies:
|
||||
test: ^1.14.3
|
||||
#pedantic: ^1.11.0
|
||||
|
||||
mockito: ^4.1.1
|
||||
test: ^1.17.2
|
||||
|
||||
mockito: ^5.0.6
|
||||
lints: ^2.1.0
|
||||
melos: ^6.3.2
|
||||
|
||||
@@ -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<T> throws state error if it\'s already has resolver',
|
||||
() {
|
||||
final container = new DiContainer();
|
||||
container.bind<int>().toResolver(_makeResolver(5));
|
||||
|
||||
expect(() => container.bind<int>().toResolver(_makeResolver(3)),
|
||||
throwsA(isA<StateError>()));
|
||||
});
|
||||
|
||||
test("Container resolves value after adding a dependency", () {
|
||||
final expectedValue = 3;
|
||||
final container = new DiContainer();
|
||||
container.bind<int>().toResolver(_makeResolver(expectedValue));
|
||||
expect(container.resolve<int>(), expectedValue);
|
||||
});
|
||||
|
||||
test("Container throws state error if the value can't be resolved", () {
|
||||
final container = DiContainer();
|
||||
expect(() => container.resolve<int>(), throwsA(isA<StateError>()));
|
||||
});
|
||||
|
||||
test("Container has() returns true if it has resolver", () {
|
||||
final expectedValue = 5;
|
||||
final container = new DiContainer();
|
||||
container.bind<int>().toResolver(_makeResolver(expectedValue));
|
||||
expect(container.has<int>(), true);
|
||||
});
|
||||
|
||||
test("Container has() returns false if it hasn't resolver", () {
|
||||
final container = new DiContainer();
|
||||
expect(container.has<int>(), false);
|
||||
});
|
||||
|
||||
test("Container hasInTree() returns true if it has resolver", () {
|
||||
final expectedValue = 5;
|
||||
final container = DiContainer();
|
||||
container.bind<int>().toResolver(_makeResolver(expectedValue));
|
||||
expect(container.hasInTree<int>(), true);
|
||||
});
|
||||
|
||||
test("Container hasInTree() returns true if it hasn`t resolver", () {
|
||||
final container = DiContainer();
|
||||
expect(container.hasInTree<int>(), 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<int>().toResolver(_makeResolver(5));
|
||||
|
||||
expect(() => container.bind<int>().toResolver(_makeResolver(3)),
|
||||
throwsA(isA<StateError>()));
|
||||
});
|
||||
|
||||
test("Container resolve() returns a value from parent container", () {
|
||||
final expectedValue = 5;
|
||||
final parentContainer = DiContainer();
|
||||
final container = DiContainer(parentContainer);
|
||||
|
||||
parentContainer.bind<int>().toResolver(_makeResolver(expectedValue));
|
||||
|
||||
expect(container.resolve<int>(), 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<int>().toResolver(_makeResolver(expectedIntValue));
|
||||
parentContainer
|
||||
.bind<String>()
|
||||
.toResolver(_makeResolver(expectedStringValue));
|
||||
|
||||
expect(container.resolve<int>(), expectedIntValue);
|
||||
expect(container.resolve<String>(), expectedStringValue);
|
||||
});
|
||||
|
||||
test("Container resolve() throws a state error if parent hasn't value too",
|
||||
() {
|
||||
final parentContainer = DiContainer();
|
||||
final container = DiContainer(parentContainer);
|
||||
expect(() => container.resolve<int>(), throwsA(isA<StateError>()));
|
||||
});
|
||||
|
||||
test("Container has() returns false if parent has a resolver", () {
|
||||
final parentContainer = DiContainer();
|
||||
final container = DiContainer(parentContainer);
|
||||
|
||||
parentContainer.bind<int>().toResolver(_makeResolver(5));
|
||||
|
||||
expect(container.has<int>(), false);
|
||||
});
|
||||
|
||||
test("Container has() returns false if parent hasn't a resolver", () {
|
||||
final parentContainer = DiContainer();
|
||||
final container = DiContainer(parentContainer);
|
||||
|
||||
expect(container.has<int>(), false);
|
||||
});
|
||||
|
||||
test("Container hasInTree() returns true if parent has a resolver", () {
|
||||
final parentContainer = DiContainer();
|
||||
final container = DiContainer(parentContainer);
|
||||
|
||||
parentContainer.bind<int>().toResolver(_makeResolver(5));
|
||||
|
||||
expect(container.hasInTree<int>(), true);
|
||||
});
|
||||
|
||||
test("Test asSingelton", () {
|
||||
final expectedIntValue = 10;
|
||||
final containerA = DiContainer();
|
||||
final containerB = DiContainer(containerA);
|
||||
|
||||
containerA.bind<int>().toValue(expectedIntValue).asSingleton();
|
||||
|
||||
expect(containerB.resolve<int>(), expectedIntValue);
|
||||
});
|
||||
|
||||
test("Child container can resolve parent container's value", () {
|
||||
final containerA = DiContainer();
|
||||
final a = AA();
|
||||
containerA.bind<A>().toValue(a);
|
||||
|
||||
final containerB = DiContainer(containerA);
|
||||
final containerC = DiContainer(containerB);
|
||||
expect(containerC.resolve<A>(), a);
|
||||
});
|
||||
});
|
||||
|
||||
test("Bind to the factory resolves with value", () {
|
||||
final container = DiContainer();
|
||||
final a = AA();
|
||||
container.bind<A>().toFactory(() => a);
|
||||
|
||||
expect(container.resolve<A>(), a);
|
||||
});
|
||||
|
||||
test("Bind to the factory resolves with value", () {
|
||||
final container = DiContainer();
|
||||
final a = AA();
|
||||
container.bind<A>().toValue(a);
|
||||
container.bind<DependOnA>().toFactory1<A>((a) => DependOnA(a));
|
||||
|
||||
expect(container.resolve<DependOnA>().a, a);
|
||||
});
|
||||
|
||||
test("Bind to the factory resolves with 2 value", () {
|
||||
final container = DiContainer();
|
||||
final a = AA();
|
||||
final b = BB();
|
||||
container.bind<A>().toValue(a);
|
||||
container.bind<B>().toValue(b);
|
||||
container.bind<DependOnAB>().toFactory2<A, B>((a, b) => DependOnAB(a, b));
|
||||
|
||||
expect(container.resolve<DependOnAB>().a, a);
|
||||
expect(container.resolve<DependOnAB>().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<A>().toValue(a);
|
||||
container.bind<B>().toValue(b);
|
||||
container.bind<C>().toValue(c);
|
||||
container
|
||||
.bind<DependOnABC>()
|
||||
.toFactory3<A, B, C>((a, b, c) => DependOnABC(a, b, c));
|
||||
|
||||
expect(container.resolve<DependOnABC>().a, a);
|
||||
expect(container.resolve<DependOnABC>().b, b);
|
||||
expect(container.resolve<DependOnABC>().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<A>().toValue(a);
|
||||
container.bind<B>().toValue(b);
|
||||
container.bind<C>().toValue(c);
|
||||
container.bind<D>().toValue(d);
|
||||
container
|
||||
.bind<DependOnABCD>()
|
||||
.toFactory4<A, B, C, D>((a, b, c, d) => DependOnABCD(a, b, c, d));
|
||||
|
||||
expect(container.resolve<DependOnABCD>().a, a);
|
||||
expect(container.resolve<DependOnABCD>().b, b);
|
||||
expect(container.resolve<DependOnABCD>().c, c);
|
||||
expect(container.resolve<DependOnABCD>().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<A>().toValue(a);
|
||||
container.bind<B>().toValue(b);
|
||||
container.bind<C>().toValue(c);
|
||||
container.bind<D>().toValue(d);
|
||||
container.bind<E>().toValue(e);
|
||||
container.bind<DependOnABCDE>().toFactory5<A, B, C, D, E>(
|
||||
(a, b, c, d, e) => DependOnABCDE(a, b, c, d, e));
|
||||
|
||||
expect(container.resolve<DependOnABCDE>().a, a);
|
||||
expect(container.resolve<DependOnABCDE>().b, b);
|
||||
expect(container.resolve<DependOnABCDE>().c, c);
|
||||
expect(container.resolve<DependOnABCDE>().d, d);
|
||||
expect(container.resolve<DependOnABCDE>().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<A>().toValue(a);
|
||||
container.bind<B>().toValue(b);
|
||||
container.bind<C>().toValue(c);
|
||||
container.bind<D>().toValue(d);
|
||||
container.bind<E>().toValue(e);
|
||||
container.bind<F>().toValue(f);
|
||||
container.bind<DependOnABCDEF>().toFactory6<A, B, C, D, E, F>(
|
||||
(a, b, c, d, e, f) => DependOnABCDEF(a, b, c, d, e, f));
|
||||
|
||||
expect(container.resolve<DependOnABCDEF>().a, a);
|
||||
expect(container.resolve<DependOnABCDEF>().b, b);
|
||||
expect(container.resolve<DependOnABCDEF>().c, c);
|
||||
expect(container.resolve<DependOnABCDEF>().d, d);
|
||||
expect(container.resolve<DependOnABCDEF>().e, e);
|
||||
expect(container.resolve<DependOnABCDEF>().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<A>().toValue(a);
|
||||
container.bind<B>().toValue(b);
|
||||
container.bind<C>().toValue(c);
|
||||
container.bind<D>().toValue(d);
|
||||
container.bind<E>().toValue(e);
|
||||
container.bind<F>().toValue(f);
|
||||
container.bind<G>().toValue(g);
|
||||
container.bind<DependOnABCDEFG>().toFactory7<A, B, C, D, E, F, G>(
|
||||
(a, b, c, d, e, f, g) => DependOnABCDEFG(a, b, c, d, e, f, g));
|
||||
|
||||
expect(container.resolve<DependOnABCDEFG>().a, a);
|
||||
expect(container.resolve<DependOnABCDEFG>().b, b);
|
||||
expect(container.resolve<DependOnABCDEFG>().c, c);
|
||||
expect(container.resolve<DependOnABCDEFG>().d, d);
|
||||
expect(container.resolve<DependOnABCDEFG>().e, e);
|
||||
expect(container.resolve<DependOnABCDEFG>().f, f);
|
||||
expect(container.resolve<DependOnABCDEFG>().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<A>().toValue(a);
|
||||
container.bind<B>().toValue(b);
|
||||
container.bind<C>().toValue(c);
|
||||
container.bind<D>().toValue(d);
|
||||
container.bind<E>().toValue(e);
|
||||
container.bind<F>().toValue(f);
|
||||
container.bind<G>().toValue(g);
|
||||
container.bind<H>().toValue(h);
|
||||
container.bind<DependOnABCDEFGH>().toFactory8<A, B, C, D, E, F, G, H>(
|
||||
(a, b, c, d, e, f, g, h) => DependOnABCDEFGH(a, b, c, d, e, f, g, h));
|
||||
|
||||
expect(container.resolve<DependOnABCDEFGH>().a, a);
|
||||
expect(container.resolve<DependOnABCDEFGH>().b, b);
|
||||
expect(container.resolve<DependOnABCDEFGH>().c, c);
|
||||
expect(container.resolve<DependOnABCDEFGH>().d, d);
|
||||
expect(container.resolve<DependOnABCDEFGH>().e, e);
|
||||
expect(container.resolve<DependOnABCDEFGH>().f, f);
|
||||
expect(container.resolve<DependOnABCDEFGH>().g, g);
|
||||
expect(container.resolve<DependOnABCDEFGH>().h, h);
|
||||
});
|
||||
}
|
||||
|
||||
ResolverMock<T> _makeResolver<T>(T expectedValue) {
|
||||
final resolverMock = new ResolverMock<T>();
|
||||
when(resolverMock.resolve()).thenReturn(expectedValue);
|
||||
return resolverMock;
|
||||
}
|
||||
|
||||
class ResolverMock<T> extends Mock implements Resolver<T> {}
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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 {}
|
||||
@@ -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 {}
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user