Compare commits

...

78 Commits

Author SHA1 Message Date
Sergey Penkovsky
e2562d22bb chore(release): publish packages
- cherrypick@2.0.2
 - cherrypick_flutter@1.1.1
2025-05-16 12:46:16 +03:00
Sergey Penkovsky
1a6e3d0b97 Merge pull request #6 from pese-git/fix/resolve_with_params
fix: support passing params when resolving dependency recursively in …
2025-05-16 12:43:42 +03:00
yarashevich_kv
ea8ff1da83 fix: support passing params when resolving dependency recursively in parent scope. 2025-05-16 10:13:59 +03:00
Sergey Penkovsky
ff55ddb491 chore(release): publish packages
- cherrypick_flutter@1.1.0
2025-05-03 17:02:38 +03:00
Sergey Penkovsky
3d3130914a feat: modify api in CherryPickProvider 2025-05-03 17:02:13 +03:00
Sergey Penkovsky
35f7c27360 fix: update description 2025-05-02 12:13:28 +03:00
Sergey Penkovsky
36e42171b7 hotfix 2025-05-02 12:10:30 +03:00
Sergey Penkovsky
1a1fe9c4e4 fix: update gitignore 2025-05-02 12:09:25 +03:00
Sergey Penkovsky
6900b649e1 chore(release): publish packages
- cherrypick@2.0.1
 - cherrypick_flutter@1.0.1
2025-05-02 12:04:22 +03:00
Sergey Penkovsky
75222a3471 doc: write readme 2025-05-02 12:00:06 +03:00
Sergey Penkovsky
c573e9840f feat: add melos commands 2025-05-02 11:53:29 +03:00
Sergey Penkovsky
490355b145 add license info 2025-05-02 11:53:03 +03:00
Sergey Penkovsky
63cd56a696 fix: fix warning 2025-05-02 11:48:37 +03:00
Sergey Penkovsky
585385980f doc: update readme 2025-05-02 11:46:47 +03:00
Sergey Penkovsky
e1a556d193 implement cherrypick_flutter library 2025-05-02 11:42:32 +03:00
Sergey Penkovsky
f6da7568fe add melos and rebase project structure 2025-05-02 11:41:18 +03:00
Sergey Penkovsky
938f8df8b6 supported Dart 3.0 and fixed lint warnings 2023-05-22 15:58:03 +03:00
Sergey Penkovsky
f94c2df7cd updated changelog 2023-05-22 15:57:12 +03:00
Sergey Penkovsky
13c96acca5 updated build script 2023-05-22 15:48:23 +03:00
Sergey Penkovsky
c90f96708f changed build version 2023-01-27 16:04:22 +03:00
Sergey Penkovsky
b789dd0179 added meta 2023-01-27 16:03:11 +03:00
Sergey Penkovsky
a9b0ff4f36 Removed exception "ConcurrentModificationError 2023-01-27 16:02:49 +03:00
Sergey Penkovsky
21c3e83a6a added provider with params and changed build version 2023-01-04 11:17:44 +03:00
Sergey Penkovsky
a983281727 Merge pull request #4 from KlimYarosh/master
Add parameter to provider
2023-01-04 11:09:47 +03:00
Sergey Penkovsky
e0f5874621 refactored pr 2023-01-04 11:03:29 +03:00
yarashevich_kv
8c3a0df452 Add parameter to provider 2022-08-10 17:23:54 +03:00
Sergey Penkovsky
085ccb55f5 changed build version 2022-05-21 16:31:29 +03:00
Sergey Penkovsky
c91392c978 fixed pubspec 2022-05-21 16:29:41 +03:00
Sergey Penkovsky
4205993ea7 updated changelog and updated version 2021-12-10 22:08:12 +03:00
Sergey Penkovsky
58245fb665 fixed docs and code 2021-12-10 22:04:51 +03:00
Sergey Penkovsky
7a53844c7d updated readme 2021-12-10 21:53:14 +03:00
Sergey Penkovsky
73d199b012 updated readme 2021-12-10 21:52:46 +03:00
Sergey Penkovsky
75bc73d62f changed version number 2021-12-10 21:42:18 +03:00
Sergey Penkovsky
bdc8951438 rename method 2021-12-10 21:39:38 +03:00
Sergey Penkovsky
3c95bf4947 upgraded changelog 2021-10-20 10:25:55 +03:00
Sergey Penkovsky
643a830d2d changed package version 2021-10-20 10:18:46 +03:00
Sergey Penkovsky
c44abaaedb added experimental api 2021-10-20 10:17:48 +03:00
Sergey Penkovsky
e2cc712840 updated doc 2021-10-20 09:17:10 +03:00
Sergey Penkovsky
c49c9012ac refactored code 2021-10-20 09:15:51 +03:00
Sergey Penkovsky
4cb210d0c2 changed build version and updated changelog 2021-04-30 16:43:42 +03:00
Sergey Penkovsky
8f2ae95b8e fixed initialization error for singeltone provider 2021-04-30 16:42:39 +03:00
Sergey Penkovsky
276d6bfb12 changed build version and updated changelog 2021-04-29 10:04:34 +03:00
Sergey Penkovsky
0e37d7f222 fixed cide analizer warnings 2021-04-29 10:02:32 +03:00
Sergey Penkovsky
5ea3744961 changed build version 2021-04-28 09:34:17 +03:00
Sergey Penkovsky
37c676cefa fixed warnings 2021-04-28 09:31:49 +03:00
Sergey Penkovsky
8a9fb1d55c updated libs, fixed warnings 2021-04-28 09:30:32 +03:00
Sergey Penkovsky
8f86662c9b changed build version 2021-04-28 08:22:08 +03:00
Sergey Penkovsky
dcdfce41db added readme for example 2021-04-28 08:20:45 +03:00
Sergey Penkovsky
c2f2577cc6 fixed description 2021-04-28 08:01:42 +03:00
Sergey Penkovsky
93f431ce93 Update quick_start_ru.md 2021-04-27 13:06:44 +03:00
Sergey Penkovsky
5462f9da07 updated pubspec 2021-04-27 06:17:54 +03:00
Sergey Penkovsky
ac30908f2d update version 2021-04-27 06:15:34 +03:00
Sergey Penkovsky
4cfca7c063 update version 2021-04-27 06:14:47 +03:00
Sergey Penkovsky
5afb8bda35 fixed link to documentation 2021-04-27 06:13:30 +03:00
Sergey Penkovsky
86c58191e5 Merge branch 'renamed_package' into 'master'
renamed package

See merge request pese/dart_di!2
2021-04-26 11:58:32 +00:00
Sergey Penkovsky
f3b1ee84b2 renamed package 2021-04-26 14:56:09 +03:00
Sergey Penkovsky
b6a4b86b19 fixed pubspec 2021-04-26 14:19:46 +03:00
Sergey Penkovsky
a20d153c1a fixed pubspec 2021-04-26 14:15:15 +03:00
Sergey Penkovsky
b621865e82 fixed pubspec 2021-04-26 14:11:52 +03:00
Sergey Penkovsky
4f127751d8 added home site 2021-04-26 14:10:03 +03:00
Sergey Penkovsky
1fb6db6dec Merge branch 'next' into 'master'
Next

See merge request pese/dart_di!1
2021-04-26 07:49:43 +00:00
Sergey Penkovsky
565fb3e682 added license header for src. Added changelog 2021-04-26 10:47:52 +03:00
Sergey Penkovsky
de404d4ee1 refactored di library. 2021-04-26 10:47:52 +03:00
Sergey Penkovsky
35879380d0 fixed binding and writed unit tests 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
bab560a856 fixed binding and writed unit tests 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
98f12c5eb7 refactored code and implemented unit tests 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
2568414a1b implemented doc 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
1ddbb74e3f added documentation 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
e102b15022 added documents 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
ec75ad9172 added documentation 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
ed0c2fae53 added documentation 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
4302d733ba added documentation 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
c9ddc2ffa8 fixed resolve method 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
b2b66bdcfd fixed example 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
6a2d86c83c implemented expiremental di with new api 2021-04-26 10:47:16 +03:00
Sergey Penkovsky
2d6fdbe04c Update README.md 2021-04-07 14:57:43 +00:00
Sergey Penkovsky
35a9478446 upgraded code for nullsafety 2021-03-27 19:50:47 +03:00
Sergey Penkovsky
0e3c5037fb Добавить .gitlab-ci.yml 2020-11-12 19:27:11 +00:00
53 changed files with 3390 additions and 1173 deletions

3
.fvmrc Normal file
View File

@@ -0,0 +1,3 @@
{
"flutter": "3.24.2"
}

27
.gitignore vendored
View File

@@ -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

83
CHANGELOG.md Normal file
View File

@@ -0,0 +1,83 @@
# 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-16
### Changes
---
Packages with breaking changes:
- There are no breaking changes in this release.
Packages with other changes:
- [`cherrypick` - `v2.0.2`](#cherrypick---v202)
- [`cherrypick_flutter` - `v1.1.1`](#cherrypick_flutter---v111)
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.1.1`
---
#### `cherrypick` - `v2.0.2`
- **FIX**: support passing params when resolving dependency recursively in parent scope.
## 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.

305
README.md
View File

@@ -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
- **Binding Dependencies**: Use `Binding` to set up dependencies.
```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<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);
}
}
void dispose() {
_dataController.close();
}
}
abstract class DataRepository {
Future<String> getData();
}
class NetworkDataRepository implements DataRepository {
final ApiClient _apiClient;
final _token = 'token';
NetworkDataRepository(this._apiClient);
class AppModule extends Module {
@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';
void builder(Scope currentScope) {
bind<ApiClient>().toInstance(ApiClientMock());
}
}
```
- **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(),
));
}
```
- **Accessing Root Scope**: Use `CherryPickProvider.of(context).rootScope` to interact with the root scope in your widgets.
```dart
final rootScope = CherryPickProvider.of(context).rootScope;
```
### Example Project
Check the `example` directory for a complete demonstration of implementing CherryPick Workspace in a Flutter app.
## Features
- [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
View File

@@ -0,0 +1,4 @@
{
"flutter": "3.24.2",
"flavors": {}
}

21
cherrypick/.gitignore vendored Normal file
View 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
View 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
View File

@@ -0,0 +1 @@
Sergey Penkovsky <sergey.penkovsky@gmail.com>

58
cherrypick/CHANGELOG.md Normal file
View File

@@ -0,0 +1,58 @@
## 2.0.2
- **FIX**: support passing params when resolving dependency recursively in parent scope.
## 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
View 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
View 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)

View 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

View 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';
}
}
```

View 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';
}
}
```

View 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';
}
}
```

View 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';
}
}

View 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

View 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';

View 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);
}
}

View 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);
}

View 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]);
}
}
}

View 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);
}

View 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, params: params);
}
}

21
cherrypick/pubspec.yaml Normal file
View File

@@ -0,0 +1,21 @@
name: cherrypick
description: Cherrypick is a small dependency injection (DI) library for dart/flutter projects.
version: 2.0.2
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

View 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);
});
});
});
}

View 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
View 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

View 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

View File

@@ -0,0 +1,17 @@
## 1.1.1
- Update a dependency to the latest release.
## 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
View 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.

View 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.

View 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

View 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';

View 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;
}
}

View 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.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.5.2
flutter: ">=1.17.0"
dependencies:
flutter:
sdk: flutter
cherrypick: ^2.0.2
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

View File

@@ -0,0 +1,7 @@
import 'package:flutter_test/flutter_test.dart';
void main() {
test('adds one to input values', () {
expect(1, 1);
});
}

View File

@@ -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';
}
}

View File

@@ -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:

View File

@@ -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';

View File

@@ -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>());
}
}

View File

@@ -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();
}
}

View File

@@ -1,11 +0,0 @@
/**
* Resolver - это абстракция, которая определяет,
* как контейнер будет разрешать зависимость
*/
abstract class Resolver<T> {
/**
* Разрешает зависимость типа [T]
* @return - возвращает объект типа [T]
*/
T resolve();
}

View File

@@ -1,7 +0,0 @@
/// Resolvers определяет, как разрешить зависимость для контейнера
library resolvers;
export 'resolver.dart';
export 'factory_resolver.dart';
export 'value_resolver.dart';
export 'singelton_resolver.dart';

View File

@@ -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()");
}
}
}

View File

@@ -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;
}
}

View File

@@ -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
View 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
View 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"

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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 {}

View File

@@ -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 {}

View File

@@ -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);
});
}