mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-24 13:47:24 +00:00
feat(i18n): add initial Russian localization for documentation and site config
- Added full Russian translations for all main documentation sections () into . - Sections translated include: key features, installation, getting started, all core concepts, advanced features, API reference, FAQ, links, additional modules, contributing, and license. - Updated to ensure language switching is available and Russian locale is active. - Each Russian file preserves the structure and formatting of the original Markdown, with machine-aided draft translation for immediate use. - Lays the groundwork for UI language switching (en/ru) and enables further manual translation refinement and review.
This commit is contained in:
@@ -112,16 +112,8 @@ const config: Config = {
|
|||||||
title: 'Community',
|
title: 'Community',
|
||||||
items: [
|
items: [
|
||||||
{
|
{
|
||||||
label: 'Stack Overflow',
|
label: 'Telegram',
|
||||||
href: 'https://stackoverflow.com/questions/tagged/docusaurus',
|
href: 'https://t.me/+22IVT0vqXBg1NDdi',
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'Discord',
|
|
||||||
href: 'https://discordapp.com/invite/docusaurus',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
label: 'X',
|
|
||||||
href: 'https://x.com/docusaurus',
|
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 9
|
||||||
|
---
|
||||||
|
|
||||||
|
# Дополнительные модули
|
||||||
|
|
||||||
|
CherryPick предоставляет набор официальных доп. модулей для расширенных и специфичных сценариев:
|
||||||
|
|
||||||
|
| Имя модуля | Описание |
|
||||||
|
|--------------------------|--------------------------------------------------------------|
|
||||||
|
| [**cherrypick_annotations**](https://pub.dev/packages/cherrypick_annotations) | Dart-аннотации для лаконичного DI и генерации кода. |
|
||||||
|
| [**cherrypick_generator**](https://pub.dev/packages/cherrypick_generator) | Генератор кода для автосборки DI-привязок по аннотациям. |
|
||||||
|
| [**cherrypick_flutter**](https://pub.dev/packages/cherrypick_flutter) | Интеграция DI с Flutter: виджеты-провайдеры, injection. |
|
||||||
|
| [**talker_cherrypick_logger**](https://pub.dev/packages/talker_cherrypick_logger) | Продвинутый логгер событий DI CherryPick с интеграцией в [Talker](https://pub.dev/packages/talker). Позволяет визуализировать состояние, ошибки и автоматически отслеживать DI прямо в UI и консоли. |
|
||||||
@@ -0,0 +1,71 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Обнаружение циклических зависимостей
|
||||||
|
|
||||||
|
CherryPick может обнаруживать циклические зависимости в вашей DI-конфигурации, помогая избежать бесконечных циклов и сложных для отладки ошибок.
|
||||||
|
|
||||||
|
## Как использовать:
|
||||||
|
|
||||||
|
### 1. Включите обнаружение во время разработки
|
||||||
|
|
||||||
|
**Локально (в рамках одного скоупа):**
|
||||||
|
```dart
|
||||||
|
final scope = CherryPick.openSafeRootScope(); // Локальное обнаружение включено по умолчанию
|
||||||
|
// или для существующего скоупа:
|
||||||
|
scope.enableCycleDetection();
|
||||||
|
```
|
||||||
|
|
||||||
|
**Глобально (между скоупами):**
|
||||||
|
```dart
|
||||||
|
CherryPick.enableGlobalCrossScopeCycleDetection();
|
||||||
|
final rootScope = CherryPick.openGlobalSafeRootScope();
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Пример ошибки
|
||||||
|
|
||||||
|
Если вы объявите взаимозависимые сервисы:
|
||||||
|
```dart
|
||||||
|
class A { A(B b); }
|
||||||
|
class B { B(A a); }
|
||||||
|
|
||||||
|
scope.installModules([
|
||||||
|
Module((bind) {
|
||||||
|
bind<A>().to((s) => A(s.resolve<B>()));
|
||||||
|
bind<B>().to((s) => B(s.resolve<A>()));
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
scope.resolve<A>(); // выбросит CircularDependencyException
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Общая рекомендация
|
||||||
|
|
||||||
|
- **Включайте обнаружение** всегда в debug и тестовой среде для максимальной безопасности.
|
||||||
|
- **Отключайте обнаружение** в production после завершения тестирования, ради производительности.
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:flutter/foundation.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
if (kDebugMode) {
|
||||||
|
CherryPick.enableGlobalCycleDetection();
|
||||||
|
CherryPick.enableGlobalCrossScopeCycleDetection();
|
||||||
|
}
|
||||||
|
runApp(MyApp());
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Отладка и обработка ошибок
|
||||||
|
|
||||||
|
При обнаружении будет выброшено исключение `CircularDependencyException` с цепочкой зависимостей:
|
||||||
|
```dart
|
||||||
|
try {
|
||||||
|
scope.resolve<MyService>();
|
||||||
|
} on CircularDependencyException catch (e) {
|
||||||
|
print('Цепочка зависимостей: ${e.dependencyChain}');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Подробнее:** смотрите [cycle_detection.ru.md](doc/cycle_detection.ru.md)
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Иерархические подскоупы
|
||||||
|
|
||||||
|
CherryPick поддерживает иерархическую структуру скоупов, что позволяет строить сложные и модульные графы зависимостей для профессиональных архитектур приложений. Каждый подскоуп наследует зависимости родителя и позволяет переопределять их локально.
|
||||||
|
|
||||||
|
## Основные моменты
|
||||||
|
|
||||||
|
- **Подскоупы** — дочерние скоупы, открываемые от любого существующего (в том числе root).
|
||||||
|
- Зависимости подскоупа перекрывают родительские при разрешении.
|
||||||
|
- Если зависимость не найдена в подскоупе, CherryPick ищет её выше по иерархии.
|
||||||
|
- Подскоупы могут иметь собственные модули, "жизненный цикл", Disposable-объекты.
|
||||||
|
- Можно делать вложенность любой глубины для фич, компонентов и т.д.
|
||||||
|
|
||||||
|
## Пример
|
||||||
|
|
||||||
|
```dart
|
||||||
|
final rootScope = CherryPick.openRootScope();
|
||||||
|
rootScope.installModules([AppModule()]);
|
||||||
|
|
||||||
|
// Открыть подскоуп для функции/страницы
|
||||||
|
final userFeatureScope = rootScope.openSubScope('userFeature');
|
||||||
|
userFeatureScope.installModules([UserFeatureModule()]);
|
||||||
|
|
||||||
|
// В userFeatureScope сперва ищет в своей области
|
||||||
|
final userService = userFeatureScope.resolve<UserService>();
|
||||||
|
|
||||||
|
// Если не нашлось — идёт в rootScope
|
||||||
|
final sharedService = userFeatureScope.resolve<SharedService>();
|
||||||
|
|
||||||
|
// Подскоупы можно вкладывать друг в друга сколь угодно глубоко
|
||||||
|
final dialogScope = userFeatureScope.openSubScope('dialog');
|
||||||
|
dialogScope.installModules([DialogModule()]);
|
||||||
|
final dialogManager = dialogScope.resolve<DialogManager>();
|
||||||
|
```
|
||||||
|
|
||||||
|
## Применение
|
||||||
|
|
||||||
|
- Модульная изоляция частей/экранов с собственными зависимостями
|
||||||
|
- Переопределение сервисов для конкретных сценариев/навигации
|
||||||
|
- Управление жизнью и освобождением ресурсов по группам
|
||||||
|
|
||||||
|
**Совет:** Всегда закрывайте подскоупы, когда они больше не нужны, чтобы освободить ресурсы.
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Логирование
|
||||||
|
|
||||||
|
CherryPick позволяет логировать все события и ошибки DI с помощью расширяемого observer-механизма.
|
||||||
|
|
||||||
|
## Кастомные Observer'ы
|
||||||
|
|
||||||
|
Вы можете передавать свою реализацию `CherryPickObserver` в root- или любой подскоуп.
|
||||||
|
Это позволяет централизовать и настраивать логирование, направлять логи в консоль, файл, сторонние сервисы или системы как [Talker](https://pub.dev/packages/talker).
|
||||||
|
|
||||||
|
### Пример: вывод всех событий в консоль
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:cherrypick/cherrypick.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
// Встроенный PrintCherryPickObserver для консоли
|
||||||
|
final observer = PrintCherryPickObserver();
|
||||||
|
final scope = CherryPick.openRootScope(observer: observer);
|
||||||
|
// Все события и ошибки DI будут выведены!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Пример: расширенное логирование через Talker
|
||||||
|
|
||||||
|
Для более гибкого логирования или UI-оверлеев можно использовать observer наподобие [talker_cherrypick_logger](../talker_cherrypick_logger):
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:cherrypick/cherrypick.dart';
|
||||||
|
import 'package:talker/talker.dart';
|
||||||
|
import 'package:talker_cherrypick_logger/talker_cherrypick_logger.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
final talker = Talker();
|
||||||
|
final observer = TalkerCherryPickObserver(talker);
|
||||||
|
CherryPick.openRootScope(observer: observer);
|
||||||
|
// Все события попадают в Talker!
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Поведение по умолчанию
|
||||||
|
- По умолчанию логирование "тихое" (SilentCherryPickObserver) для production — нет вывода без observer'а.
|
||||||
|
- Можно назначить observer для любого скоупа.
|
||||||
|
|
||||||
|
## Возможности Observer'а
|
||||||
|
|
||||||
|
- Регистрация зависимостей
|
||||||
|
- Получение/создание/удаление экземпляров
|
||||||
|
- Установка/удаление модулей
|
||||||
|
- Открытие/закрытие скоупов
|
||||||
|
- Кэш-хиты/мимо
|
||||||
|
- Обнаружение циклов
|
||||||
|
- Диагностика, предупреждения, ошибки
|
||||||
|
|
||||||
|
## Когда применять
|
||||||
|
|
||||||
|
- Подробное логирование в dev/test окружениях
|
||||||
|
- Передача логов в основную систему/аналитику
|
||||||
|
- Отладка и профилирование DI
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
# Улучшения производительности
|
||||||
|
|
||||||
|
> **Примечание по производительности:**
|
||||||
|
> **Начиная с версии 3.0.0**, CherryPick использует Map-индексатор для поиска зависимостей. Это означает, что вызовы `resolve<T>()` и связанные методы работают за O(1) независимо от количества модулей/биндингов в скоупе. Ранее библиотека просматривала все модули/биндинги, что могло замедлять DI в крупных проектах.
|
||||||
|
>
|
||||||
|
> Эта оптимизация полностью внутренняя: интерфейс библиотеки и пользовательский код не изменились, но производительность заметно выросла на больших графах зависимостей.
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 10
|
||||||
|
---
|
||||||
|
|
||||||
|
# Вклад в проект
|
||||||
|
|
||||||
|
Вкладывайтесь! Открывайте задачи или отправляйте pull request'ы на [GitHub](https://github.com/pese-git/cherrypick).
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Привязка (Binding)
|
||||||
|
|
||||||
|
**Binding** — это конфигурация, которая определяет, как создавать или предоставлять конкретную зависимость. Binding поддерживает:
|
||||||
|
|
||||||
|
* Прямое присваивание экземпляра (`toInstance()`, `toInstanceAsync()`)
|
||||||
|
* Ленивые провайдеры (синхронные/асинхронные функции)
|
||||||
|
* Провайдеры с поддержкой динамических параметров
|
||||||
|
* Именованные экземпляры для получения по строковому ключу
|
||||||
|
* Необязательное управление жизненным циклом синглтона
|
||||||
|
|
||||||
|
## Пример
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// Прямое создание экземпляра
|
||||||
|
Binding<String>().toInstance("Hello world");
|
||||||
|
|
||||||
|
// Асинхронное создание экземпляра
|
||||||
|
Binding<String>().toInstanceAsync(Future.value("Hello world"));
|
||||||
|
|
||||||
|
// Ленивое создание экземпляра через фабрику (sync)
|
||||||
|
Binding<String>().toProvide(() => "Hello world");
|
||||||
|
|
||||||
|
// Ленивое создание экземпляра через фабрику (async)
|
||||||
|
Binding<String>().toProvideAsync(() async => "Hello async world");
|
||||||
|
|
||||||
|
// Экземпляр с параметрами (sync)
|
||||||
|
Binding<String>().toProvideWithParams((params) => "Hello $params");
|
||||||
|
|
||||||
|
// Экземпляр с параметрами (async)
|
||||||
|
Binding<String>().toProvideAsyncWithParams((params) async => "Hello $params");
|
||||||
|
|
||||||
|
// Именованный экземпляр для получения по имени
|
||||||
|
Binding<String>().toProvide(() => "Hello world").withName("my_string");
|
||||||
|
|
||||||
|
// Синглтон (один экземпляр внутри скоупа)
|
||||||
|
Binding<String>().toProvide(() => "Hello world").singleton();
|
||||||
|
```
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
# Disposable
|
||||||
|
|
||||||
|
CherryPick может автоматически очищать любые зависимости, реализующие интерфейс `Disposable`. Это упрощает управление ресурсами (контроллеры, потоки, сокеты, файлы и др.) — особенно при закрытии скоупа или приложения.
|
||||||
|
|
||||||
|
Если вы регистрируете объект, реализующий `Disposable`, как синглтон или через DI-контейнер, CherryPick вызовет его метод `dispose()` при закрытии или очистке скоупа.
|
||||||
|
|
||||||
|
## Основные моменты
|
||||||
|
- Поддерживаются синхронная и асинхронная очистка (dispose может возвращать `void` или `Future`).
|
||||||
|
- Все объекты `Disposable` из текущего скоупа и подскоупов будут удалены в правильном порядке.
|
||||||
|
- Предотвращает утечки ресурсов и обеспечивает корректную очистку.
|
||||||
|
- Не нужно вручную связывать очистку — просто реализуйте интерфейс.
|
||||||
|
|
||||||
|
## Минимальный синхронный пример
|
||||||
|
```dart
|
||||||
|
class CacheManager implements Disposable {
|
||||||
|
void dispose() {
|
||||||
|
cache.clear();
|
||||||
|
print('CacheManager удалён!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final scope = CherryPick.openRootScope();
|
||||||
|
scope.installModules([
|
||||||
|
Module((bind) => bind<CacheManager>().toProvide(() => CacheManager()).singleton()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
// ...спустя время
|
||||||
|
await CherryPick.closeRootScope(); // выведет: CacheManager удалён!
|
||||||
|
```
|
||||||
|
|
||||||
|
## Асинхронный пример
|
||||||
|
```dart
|
||||||
|
class MyServiceWithSocket implements Disposable {
|
||||||
|
@override
|
||||||
|
Future<void> dispose() async {
|
||||||
|
await socket.close();
|
||||||
|
print('Socket закрыт!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
scope.installModules([
|
||||||
|
Module((bind) => bind<MyServiceWithSocket>().toProvide(() => MyServiceWithSocket()).singleton()),
|
||||||
|
]);
|
||||||
|
|
||||||
|
await CherryPick.closeRootScope(); // дождётся завершения async очистки
|
||||||
|
```
|
||||||
|
|
||||||
|
**Совет:** Всегда вызывайте `await CherryPick.closeRootScope()` или `await scope.closeSubScope(key)` в вашем shutdown/teardown-коде для гарантированной очистки ресурсов.
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Модуль
|
||||||
|
|
||||||
|
**Модуль** — это логическая точка сбора для привязок (bindings), предназначенная для группирования и инициализации связанных зависимостей. Реализуйте метод `builder`, чтобы определить, как зависимости будут связываться внутри скоупа.
|
||||||
|
|
||||||
|
## Пример
|
||||||
|
|
||||||
|
```dart
|
||||||
|
class AppModule extends Module {
|
||||||
|
@override
|
||||||
|
void builder(Scope currentScope) {
|
||||||
|
bind<ApiClient>().toInstance(ApiClientMock());
|
||||||
|
bind<String>().toProvide(() => "Hello world!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Скоуп (Scope)
|
||||||
|
|
||||||
|
**Scope** управляет деревом модулей и экземпляров зависимостей. Скоупы могут быть вложенными (иерархия родитель-дочерний), обеспечивая модульную компоновку приложения и возможность переопределения зависимостей для отдельных контекстов.
|
||||||
|
|
||||||
|
Обычно вы работаете с корневым скоупом, но при необходимости можете создавать именованные подскоупы.
|
||||||
|
|
||||||
|
## Пример
|
||||||
|
|
||||||
|
```dart
|
||||||
|
// Открыть основной/корневой скоуп
|
||||||
|
final rootScope = CherryPick.openRootScope();
|
||||||
|
|
||||||
|
// Установить пользовательский модуль
|
||||||
|
rootScope.installModules([AppModule()]);
|
||||||
|
|
||||||
|
// Получить зависимость синхронно
|
||||||
|
final str = rootScope.resolve<String>();
|
||||||
|
|
||||||
|
// Получить зависимость асинхронно
|
||||||
|
final result = await rootScope.resolveAsync<String>();
|
||||||
|
|
||||||
|
// Рекомендуется: закрывать корневой скоуп и высвобождать все ресурсы
|
||||||
|
await CherryPick.closeRootScope();
|
||||||
|
|
||||||
|
// Либо вручную вызвать dispose на любом скоупе, которым вы управляете индивидуально
|
||||||
|
// await rootScope.dispose();
|
||||||
|
```
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 4
|
||||||
|
---
|
||||||
|
|
||||||
|
# API разрешения зависимостей
|
||||||
|
|
||||||
|
- `resolve<T>()` — получает экземпляр зависимости или выбрасывает исключение, если не найдено.
|
||||||
|
- `resolveAsync<T>()` — асинхронный вариант для зависимостей с асинхронной инициализацией.
|
||||||
|
- `tryResolve<T>()` — возвращает `null`, если не найдено (синхронно).
|
||||||
|
- `tryResolveAsync<T>()` — возвращает `null` асинхронно, если не найдено.
|
||||||
|
|
||||||
|
Поддерживает:
|
||||||
|
- Синхронные и асинхронные зависимости
|
||||||
|
- Именованные зависимости
|
||||||
|
- Провайдеры с runtime-параметрами или без них
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 8
|
||||||
|
---
|
||||||
|
|
||||||
|
# Ссылки на документацию
|
||||||
|
|
||||||
|
* Обнаружение циклических зависимостей [(En)](doc/cycle_detection.en.md)[(Ru)](doc/cycle_detection.ru.md)
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 6
|
||||||
|
---
|
||||||
|
|
||||||
|
# Пример приложения
|
||||||
|
|
||||||
|
Ниже приведён полный пример с модулями, подскоупами, асинхронными провайдерами и разрешением зависимостей.
|
||||||
|
|
||||||
|
```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 {
|
||||||
|
final bool isMock;
|
||||||
|
FeatureModule({required this.isMock});
|
||||||
|
@override
|
||||||
|
void builder(Scope currentScope) {
|
||||||
|
// Асинхронный провайдер DataRepository с выбором зависимости по имени
|
||||||
|
bind<DataRepository>()
|
||||||
|
.withName("networkRepo")
|
||||||
|
.toProvideAsync(() async {
|
||||||
|
final client = await Future.delayed(
|
||||||
|
Duration(milliseconds: 100),
|
||||||
|
() => currentScope.resolve<ApiClient>(
|
||||||
|
named: isMock ? "apiClientMock" : "apiClientImpl",
|
||||||
|
),
|
||||||
|
);
|
||||||
|
return NetworkDataRepository(client);
|
||||||
|
})
|
||||||
|
.singleton();
|
||||||
|
|
||||||
|
// Вызов асинхронного провайдера для DataBloc
|
||||||
|
bind<DataBloc>().toProvideAsync(
|
||||||
|
() async {
|
||||||
|
final repo = await currentScope.resolveAsync<DataRepository>(
|
||||||
|
named: "networkRepo");
|
||||||
|
return DataBloc(repo);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() async {
|
||||||
|
final scope = CherryPick.openRootScope().installModules([AppModule()]);
|
||||||
|
final featureScope = scope.openSubScope("featureScope")
|
||||||
|
..installModules([FeatureModule(isMock: true)]);
|
||||||
|
|
||||||
|
final dataBloc = await featureScope.resolveAsync<DataBloc>();
|
||||||
|
dataBloc.data.listen(
|
||||||
|
(d) => print('Получены данные: $d'),
|
||||||
|
onError: (e) => print('Ошибка: $e'),
|
||||||
|
onDone: () => print('DONE'),
|
||||||
|
);
|
||||||
|
|
||||||
|
await dataBloc.fetchData();
|
||||||
|
}
|
||||||
|
|
||||||
|
class DataBloc {
|
||||||
|
final DataRepository _dataRepository;
|
||||||
|
Stream<String> get data => _dataController.stream;
|
||||||
|
final StreamController<String> _dataController = 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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 7
|
||||||
|
---
|
||||||
|
|
||||||
|
# Часто задаваемые вопросы
|
||||||
|
|
||||||
|
### В: Нужно ли использовать `await` с CherryPick.closeRootScope(), CherryPick.closeScope() или scope.dispose(), если у меня нет Disposable-сервисов?
|
||||||
|
|
||||||
|
**О:**
|
||||||
|
Да! Даже если ваши сервисы сейчас не реализуют `Disposable`, всегда используйте `await` при закрытии скоупов. Если вы позже добавите очистку ресурсов (реализовав dispose()), CherryPick всё обработает автоматически и ваш код останется без изменений. Это гарантирует надежное освобождение ресурсов для любого сценария.
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 3
|
||||||
|
---
|
||||||
|
|
||||||
|
# Быстрый старт
|
||||||
|
|
||||||
|
Минимальный пример регистрации и получения зависимости:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:cherrypick/cherrypick.dart';
|
||||||
|
|
||||||
|
class AppModule extends Module {
|
||||||
|
@override
|
||||||
|
void builder(Scope currentScope) {
|
||||||
|
bind<ApiClient>().toInstance(ApiClientMock());
|
||||||
|
bind<String>().toProvide(() => "Hello, CherryPick!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final rootScope = CherryPick.openRootScope();
|
||||||
|
rootScope.installModules([AppModule()]);
|
||||||
|
|
||||||
|
final greeting = rootScope.resolve<String>();
|
||||||
|
print(greeting); // напечатает: Hello, CherryPick!
|
||||||
|
|
||||||
|
await CherryPick.closeRootScope();
|
||||||
|
```
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 2
|
||||||
|
---
|
||||||
|
|
||||||
|
# Установка
|
||||||
|
|
||||||
|
Добавьте в ваш `pubspec.yaml`:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
dependencies:
|
||||||
|
cherrypick: ^<latest_version>
|
||||||
|
```
|
||||||
|
|
||||||
|
Затем выполните команду:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
dart pub get
|
||||||
|
```
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 1
|
||||||
|
---
|
||||||
|
|
||||||
|
# Ключевые возможности
|
||||||
|
|
||||||
|
- Главный скоуп и именованные подскоупы
|
||||||
|
- Привязка и разрешение экземпляров по имени
|
||||||
|
- Асинхронные и синхронные провайдеры
|
||||||
|
- Провайдеры с поддержкой параметров времени выполнения
|
||||||
|
- Управление жизненным циклом синглтонов
|
||||||
|
- Модульная и иерархическая композиция
|
||||||
|
- Null-safe разрешение зависимостей (tryResolve/tryResolveAsync)
|
||||||
|
- Обнаружение циклических зависимостей (локально и глобально)
|
||||||
|
- Подробное логирование состояния и действий DI-контейнера
|
||||||
|
- Автоматическое освобождение ресурсов для всех зарегистрированных Disposable зависимостей
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 11
|
||||||
|
---
|
||||||
|
|
||||||
|
# Лицензия
|
||||||
|
|
||||||
|
Проект распространяется под [лицензией Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0).
|
||||||
|
|
||||||
|
**Важно:** При отсутствии специальных договорённостей поставляется "КАК ЕСТЬ", без каких-либо гарантий. Подробности см. в самой лицензии.
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
---
|
||||||
|
sidebar_position: 5
|
||||||
|
---
|
||||||
|
|
||||||
|
# Использование аннотаций и генерация кода
|
||||||
|
|
||||||
|
CherryPick предоставляет продвинутую эргономику и безопасный DI благодаря **аннотациям Dart** и генерации кода. Это позволяет избавить вас от рутины — просто аннотируйте классы, поля и модули, запускайте генератор и используйте полностью автосвязанный DI!
|
||||||
|
|
||||||
|
## Как это работает
|
||||||
|
|
||||||
|
1. **Аннотируйте** сервисы, провайдеры и поля с помощью `cherrypick_annotations`.
|
||||||
|
2. **Генерируйте** код с помощью `cherrypick_generator` и `build_runner`.
|
||||||
|
3. **Используйте** автосгенерированные модули и миксины для автоматического внедрения.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Поддерживаемые аннотации
|
||||||
|
|
||||||
|
| Аннотация | Target | Описание |
|
||||||
|
|---------------------|---------------|--------------------------------------------------------------|
|
||||||
|
| `@injectable()` | класс | Включает автоподстановку полей (генерируется mixin) |
|
||||||
|
| `@inject()` | поле | Автоподстановка через DI (работает с @injectable) |
|
||||||
|
| `@module()` | класс | DI-модуль: методы — провайдеры и сервисы |
|
||||||
|
| `@provide` | метод | Регистрирует как DI-провайдер (можно с параметрами) |
|
||||||
|
| `@instance` | метод/класс | Регистрирует новый экземпляр (на каждый resolve, factory) |
|
||||||
|
| `@singleton` | метод/класс | Регистрация как синглтон (один экземпляр на скоуп) |
|
||||||
|
| `@named` | поле/параметр | Использование именованных экземпляров для внедрения/resolve |
|
||||||
|
| `@scope` | поле/параметр | Внедрение/resolve из другого (именованного) скоупа |
|
||||||
|
| `@params` | параметр | Добавляет user-defined параметры во время resolve |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Пример Field Injection
|
||||||
|
|
||||||
|
```dart
|
||||||
|
import 'package:cherrypick_annotations/cherrypick_annotations.dart';
|
||||||
|
|
||||||
|
@injectable()
|
||||||
|
class ProfilePage with _\$ProfilePage {
|
||||||
|
@inject()
|
||||||
|
late final AuthService auth;
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
@scope('profile')
|
||||||
|
late final ProfileManager manager;
|
||||||
|
|
||||||
|
@inject()
|
||||||
|
@named('admin')
|
||||||
|
late final UserService adminUserService;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- После запуска build_runner миксин `_ProfilePage` будет сгенерирован для внедрения.
|
||||||
|
- Вызовите `myProfilePage.injectFields();` чтобы все зависимости были внедрены автоматически.
|
||||||
|
|
||||||
|
## Пример модуля/провайдера
|
||||||
|
|
||||||
|
```dart
|
||||||
|
@module()
|
||||||
|
abstract class AppModule {
|
||||||
|
@singleton
|
||||||
|
AuthService provideAuth(Api api) => AuthService(api);
|
||||||
|
|
||||||
|
@named('logging')
|
||||||
|
@provide
|
||||||
|
Future<Logger> provideLogger(@params Map<String, dynamic> args) async => ...;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Шаги использования
|
||||||
|
|
||||||
|
1. Добавьте зависимости в `pubspec.yaml`.
|
||||||
|
2. Аннотируйте классы и модули.
|
||||||
|
3. Генерируйте код командой build_runner.
|
||||||
|
4. Регистрируйте модули и используйте автосвязь.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Расширенные возможности
|
||||||
|
|
||||||
|
- Используйте `@named` для внедрения по ключу.
|
||||||
|
- Используйте `@scope` для внедрения из разных скоупов.
|
||||||
|
- Используйте `@params` для передачи runtime-параметров.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Советы и FAQ
|
||||||
|
|
||||||
|
- После изменений в DI-коде запускайте build_runner заново.
|
||||||
|
- Не редактируйте `.g.dart` вручную.
|
||||||
|
- Ошибки некорректных аннотаций определяются автоматически.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Ссылки
|
||||||
|
|
||||||
|
- [Подробнее про аннотации (en)](doc/annotations_en.md)
|
||||||
|
- [cherrypick_annotations/README.md](../cherrypick_annotations/README.md)
|
||||||
|
- [cherrypick_generator/README.md](../cherrypick_generator/README.md)
|
||||||
|
- Полный пример: [`examples/postly`](../examples/postly)
|
||||||
Reference in New Issue
Block a user