mirror of
https://github.com/pese-git/cherrypick.git
synced 2026-01-24 13:47:24 +00:00
Merge pull request #13 from pese-git/impr/binding_resolver
Refactored Binding by extracting BindingResolver to improve code structure and simplify the Binding class.
This commit is contained in:
@@ -17,12 +17,12 @@ All scenarios use the public API capabilities of cherrypick (scope, module, bind
|
||||
|
||||
| Scenario | RunTime (μs) |
|
||||
|----------------------------------------------------|---------------|
|
||||
| RegisterAndResolve | 0.3407 |
|
||||
| ChainSingleton (A->B->C, singleton) | 0.3777 |
|
||||
| ChainFactory (A->B->C, factory) | 0.9688 |
|
||||
| NamedResolve (by name) | 0.3878 |
|
||||
| AsyncChain (A->B->C, async) | 1.8006 |
|
||||
| ScopeOverride (child overrides parent) | 0.3477 |
|
||||
| RegisterAndResolve | 0.4574 |
|
||||
| ChainSingleton (A->B->C, singleton) | 0.3759 |
|
||||
| ChainFactory (A->B->C, factory) | 1.3783 |
|
||||
| NamedResolve (by name) | 0.5193 |
|
||||
| AsyncChain (A->B->C, async) | 0.5985 |
|
||||
| ScopeOverride (child overrides parent) | 0.3611 |
|
||||
|
||||
## How to run
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@
|
||||
|
||||
| Сценарий | RunTime (мкс) |
|
||||
|----------------------------------------------------|--------------|
|
||||
| RegisterAndResolve | 0.3407 |
|
||||
| ChainSingleton (A->B->C, singleton) | 0.3777 |
|
||||
| ChainFactory (A->B->C, factory) | 0.9688 |
|
||||
| NamedResolve (by name) | 0.3878 |
|
||||
| AsyncChain (A->B->C, async) | 1.8006 |
|
||||
| ScopeOverride (child overrides parent) | 0.3477 |
|
||||
| RegisterAndResolve | 0.4574 |
|
||||
| ChainSingleton (A->B->C, singleton) | 0.3759 |
|
||||
| ChainFactory (A->B->C, factory) | 1.3783 |
|
||||
| NamedResolve (by name) | 0.5193 |
|
||||
| AsyncChain (A->B->C, async) | 0.5985 |
|
||||
| ScopeOverride (child overrides parent) | 0.3611 |
|
||||
|
||||
## Как запускать
|
||||
|
||||
|
||||
@@ -17,19 +17,28 @@ class FeatureModule extends Module {
|
||||
@override
|
||||
void builder(Scope currentScope) {
|
||||
// Using toProvideAsync for async initialization
|
||||
bind<DataRepository>().withName("networkRepo").toProvideAsync(() async {
|
||||
bind<DataRepository>()
|
||||
.withName("networkRepo")
|
||||
.toProvideWithParams((params) async {
|
||||
print('REPO PARAMS: $params');
|
||||
|
||||
final client = await Future.delayed(
|
||||
Duration(milliseconds: 100),
|
||||
() => currentScope.resolve<ApiClient>(
|
||||
named: isMock ? "apiClientMock" : "apiClientImpl"));
|
||||
Duration(milliseconds: 1000),
|
||||
() => currentScope.resolve<ApiClient>(
|
||||
named: isMock ? "apiClientMock" : "apiClientImpl",
|
||||
),
|
||||
);
|
||||
|
||||
return NetworkDataRepository(client);
|
||||
}).singleton();
|
||||
|
||||
// Asynchronous initialization of DataBloc
|
||||
bind<DataBloc>().toProvideAsync(
|
||||
bind<DataBloc>().toProvide(
|
||||
() async {
|
||||
final repo = await currentScope.resolveAsync<DataRepository>(
|
||||
named: "networkRepo");
|
||||
named: "networkRepo",
|
||||
params: 'Some params',
|
||||
);
|
||||
return DataBloc(repo);
|
||||
},
|
||||
);
|
||||
@@ -38,18 +47,19 @@ class FeatureModule extends Module {
|
||||
|
||||
Future<void> main() async {
|
||||
try {
|
||||
final scope = openRootScope().installModules([
|
||||
AppModule(),
|
||||
]);
|
||||
final scope = openRootScope().installModules([AppModule()]);
|
||||
|
||||
final subScope = scope
|
||||
.openSubScope("featureScope")
|
||||
.installModules([FeatureModule(isMock: true)]);
|
||||
|
||||
// Asynchronous instance resolution
|
||||
final dataBloc = await subScope.resolveAsync<DataBloc>();
|
||||
dataBloc.data.listen((d) => print('Received data: $d'),
|
||||
onError: (e) => print('Error: $e'), onDone: () => print('DONE'));
|
||||
// Asynchronous instance resolution
|
||||
final dataBloc = await subScope.resolveAsync<DataBloc>();
|
||||
dataBloc.data.listen(
|
||||
(d) => print('Received data: $d'),
|
||||
onError: (e) => print('Error: $e'),
|
||||
onDone: () => print('DONE'),
|
||||
);
|
||||
|
||||
await dataBloc.fetchData();
|
||||
} catch (e) {
|
||||
|
||||
@@ -13,6 +13,7 @@ library;
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
export 'package:cherrypick/src/binding_resolver.dart';
|
||||
export 'package:cherrypick/src/binding.dart';
|
||||
export 'package:cherrypick/src/cycle_detector.dart';
|
||||
export 'package:cherrypick/src/global_cycle_detector.dart';
|
||||
|
||||
@@ -11,45 +11,21 @@
|
||||
// limitations under the License.
|
||||
//
|
||||
|
||||
enum Mode { simple, instance, providerInstance, providerInstanceWithParams }
|
||||
|
||||
typedef Provider<T> = T? Function();
|
||||
|
||||
typedef ProviderWithParams<T> = T Function(dynamic params);
|
||||
|
||||
typedef AsyncProvider<T> = Future<T> Function();
|
||||
|
||||
typedef AsyncProviderWithParams<T> = Future<T> Function(dynamic params);
|
||||
import 'package:cherrypick/src/binding_resolver.dart';
|
||||
|
||||
/// 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;
|
||||
Future<T>? _instanceAsync;
|
||||
Provider<T>? _provider;
|
||||
ProviderWithParams<T>? _providerWithParams;
|
||||
String? _name;
|
||||
|
||||
AsyncProvider<T>? asyncProvider;
|
||||
AsyncProviderWithParams<T>? asyncProviderWithParams;
|
||||
|
||||
late bool _isSingleton = false;
|
||||
late bool _isNamed = false;
|
||||
BindingResolver<T>? _resolver;
|
||||
|
||||
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.
|
||||
///
|
||||
@@ -60,19 +36,21 @@ class Binding<T> {
|
||||
/// 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;
|
||||
String? get name => _name;
|
||||
|
||||
/// RU: Метод проверяет именован экземпляр или нет.
|
||||
/// ENG: The method checks whether the instance is named or not.
|
||||
///
|
||||
/// return [bool]
|
||||
bool get isNamed => _isNamed;
|
||||
bool get isNamed => _name != null;
|
||||
|
||||
/// RU: Метод проверяет сингелтон экземпляр или нет.
|
||||
/// ENG: The method checks the singleton instance or not.
|
||||
///
|
||||
/// return [bool]
|
||||
bool get isSingleton => _resolver?.isSingleton ?? false;
|
||||
|
||||
BindingResolver<T>? get resolver => _resolver;
|
||||
|
||||
/// RU: Добавляет имя для экземляпя [value].
|
||||
/// ENG: Added name for instance [value].
|
||||
@@ -80,7 +58,6 @@ class Binding<T> {
|
||||
/// return [Binding]
|
||||
Binding<T> withName(String name) {
|
||||
_name = name;
|
||||
_isNamed = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -88,21 +65,9 @@ class Binding<T> {
|
||||
/// ENG: Initialization instance [value].
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toInstance(T value) {
|
||||
_mode = Mode.instance;
|
||||
_instance = value;
|
||||
_isSingleton = true;
|
||||
return this;
|
||||
}
|
||||
Binding<T> toInstance(Instance<T> value) {
|
||||
_resolver = InstanceResolver<T>(value);
|
||||
|
||||
/// RU: Инициализация экземляпяра [value].
|
||||
/// ENG: Initialization instance [value].
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toInstanceAsync(Future<T> value) {
|
||||
_mode = Mode.instance;
|
||||
_instanceAsync = value;
|
||||
_isSingleton = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -111,18 +76,8 @@ class Binding<T> {
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toProvide(Provider<T> value) {
|
||||
_mode = Mode.providerInstance;
|
||||
_provider = value;
|
||||
return this;
|
||||
}
|
||||
_resolver = ProviderResolver<T>((_) => value.call(), withParams: false);
|
||||
|
||||
/// RU: Инициализация экземляпяра через провайдер [value].
|
||||
/// ENG: Initialization instance via provider [value].
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toProvideAsync(AsyncProvider<T> provider) {
|
||||
_mode = Mode.providerInstance;
|
||||
asyncProvider = provider;
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -131,19 +86,24 @@ class Binding<T> {
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toProvideWithParams(ProviderWithParams<T> value) {
|
||||
_mode = Mode.providerInstanceWithParams;
|
||||
_providerWithParams = value;
|
||||
_resolver = ProviderResolver<T>(value, withParams: true);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Инициализация экземляра через асинхронный провайдер [value] с динамическим параметром.
|
||||
/// ENG: Initializes the instance via async provider [value] with a dynamic param.
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> toProvideAsyncWithParams(AsyncProviderWithParams<T> provider) {
|
||||
_mode = Mode.providerInstanceWithParams;
|
||||
asyncProviderWithParams = provider;
|
||||
return this;
|
||||
@Deprecated('Use toInstance instead of toInstanceAsync')
|
||||
Binding<T> toInstanceAsync(Instance<T> value) {
|
||||
return this.toInstance(value);
|
||||
}
|
||||
|
||||
@Deprecated('Use toProvide instead of toProvideAsync')
|
||||
Binding<T> toProvideAsync(Provider<T> value) {
|
||||
return this.toProvide(value);
|
||||
}
|
||||
|
||||
@Deprecated('Use toProvideWithParams instead of toProvideAsyncWithParams')
|
||||
Binding<T> toProvideAsyncWithParams(ProviderWithParams<T> value) {
|
||||
return this.toProvideWithParams(value);
|
||||
}
|
||||
|
||||
/// RU: Инициализация экземляпяра как сингелтон [value].
|
||||
@@ -151,40 +111,16 @@ class Binding<T> {
|
||||
///
|
||||
/// return [Binding]
|
||||
Binding<T> singleton() {
|
||||
_isSingleton = true;
|
||||
_resolver?.toSingleton();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/// RU: Поиск экземпляра.
|
||||
/// ENG: Resolve instance.
|
||||
///
|
||||
/// return [T]
|
||||
T? get instance => _instance;
|
||||
|
||||
/// RU: Поиск экземпляра.
|
||||
/// ENG: Resolve instance.
|
||||
///
|
||||
/// return [T]
|
||||
Future<T>? get instanceAsync => _instanceAsync;
|
||||
|
||||
/// RU: Поиск экземпляра.
|
||||
/// ENG: Resolve instance.
|
||||
///
|
||||
/// return [T]
|
||||
T? get provider {
|
||||
if (_isSingleton) {
|
||||
_instance ??= _provider?.call();
|
||||
return _instance;
|
||||
}
|
||||
return _provider?.call();
|
||||
T? resolveSync([dynamic params]) {
|
||||
return resolver?.resolveSync(params);
|
||||
}
|
||||
|
||||
/// RU: Поиск экземпляра с параметром.
|
||||
///
|
||||
/// ENG: Resolve instance with [params].
|
||||
///
|
||||
/// return [T]
|
||||
T? providerWithParams(dynamic params) {
|
||||
return _providerWithParams?.call(params);
|
||||
Future<T>? resolveAsync([dynamic params]) {
|
||||
return resolver?.resolveAsync(params);
|
||||
}
|
||||
}
|
||||
|
||||
129
cherrypick/lib/src/binding_resolver.dart
Normal file
129
cherrypick/lib/src/binding_resolver.dart
Normal file
@@ -0,0 +1,129 @@
|
||||
import 'dart:async';
|
||||
|
||||
typedef Instance<T> = FutureOr<T>;
|
||||
|
||||
/// RU: Синхронный или асинхронный провайдер без параметров, возвращающий [T] или [Future<T>].
|
||||
/// ENG: Synchronous or asynchronous provider without parameters, returning [T] or [Future<T>].
|
||||
typedef Provider<T> = FutureOr<T> Function();
|
||||
|
||||
/// RU: Провайдер с динамическим параметром, возвращающий [T] или [Future<T>] в зависимости от реализации.
|
||||
/// ENG: Provider with dynamic parameter, returning [T] or [Future<T>] depending on implementation.
|
||||
typedef ProviderWithParams<T> = FutureOr<T> Function(dynamic);
|
||||
|
||||
/// RU: Абстрактный интерфейс для классов, которые разрешают зависимости типа [T].
|
||||
/// ENG: Abstract interface for classes that resolve dependencies of type [T].
|
||||
abstract class BindingResolver<T> {
|
||||
/// RU: Синхронное разрешение зависимости с параметром [params].
|
||||
/// ENG: Synchronous resolution of the dependency with [params].
|
||||
T? resolveSync([dynamic params]);
|
||||
|
||||
/// RU: Асинхронное разрешение зависимости с параметром [params].
|
||||
/// ENG: Asynchronous resolution of the dependency with [params].
|
||||
Future<T>? resolveAsync([dynamic params]);
|
||||
|
||||
/// RU: Помечает текущий резолвер как синглтон — результат будет закеширован.
|
||||
/// ENG: Marks this resolver as singleton — result will be cached.
|
||||
void toSingleton();
|
||||
|
||||
bool get isSingleton;
|
||||
}
|
||||
|
||||
/// RU: Резолвер, оборачивающий конкретный экземпляр [T] (или Future<T>), без вызова провайдера.
|
||||
/// ENG: Resolver that wraps a concrete instance of [T] (or Future<T>), without provider invocation.
|
||||
class InstanceResolver<T> implements BindingResolver<T> {
|
||||
final Instance<T> _instance;
|
||||
|
||||
/// RU: Создаёт резолвер, оборачивающий значение [instance].
|
||||
/// ENG: Creates a resolver that wraps the given [instance].
|
||||
InstanceResolver(this._instance);
|
||||
|
||||
@override
|
||||
T resolveSync([_]) {
|
||||
if (_instance is T) return _instance;
|
||||
throw StateError(
|
||||
'Instance $_instance is Future; '
|
||||
'use resolveAsync() instead',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<T> resolveAsync([_]) {
|
||||
if (_instance is Future<T>) return _instance;
|
||||
|
||||
return Future.value(_instance);
|
||||
}
|
||||
|
||||
@override
|
||||
void toSingleton() {}
|
||||
|
||||
@override
|
||||
bool get isSingleton => true;
|
||||
}
|
||||
|
||||
/// RU: Резолвер, оборачивающий провайдер, с возможностью синглтон-кеширования.
|
||||
/// ENG: Resolver that wraps a provider, with optional singleton caching.
|
||||
class ProviderResolver<T> implements BindingResolver<T> {
|
||||
final ProviderWithParams<T> _provider;
|
||||
final bool _withParams;
|
||||
|
||||
FutureOr<T>? _cache;
|
||||
bool _singleton = false;
|
||||
|
||||
/// RU: Создаёт резолвер из произвольной функции [raw], поддерживающей ноль или один параметр.
|
||||
/// ENG: Creates a resolver from arbitrary function [raw], supporting zero or one parameter.
|
||||
ProviderResolver(
|
||||
ProviderWithParams<T> provider, {
|
||||
required bool withParams,
|
||||
}) : _provider = provider,
|
||||
_withParams = withParams;
|
||||
|
||||
@override
|
||||
T resolveSync([dynamic params]) {
|
||||
_checkParams(params);
|
||||
|
||||
final result = _cache ?? _provider(params);
|
||||
|
||||
if (result is T) {
|
||||
if (_singleton) {
|
||||
_cache ??= result;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
throw StateError(
|
||||
'Provider [$_provider] return Future<$T>. Use resolveAsync() instead.',
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<T> resolveAsync([dynamic params]) {
|
||||
_checkParams(params);
|
||||
|
||||
final result = _cache ?? _provider(params);
|
||||
final target = result is Future<T> ? result : Future<T>.value(result);
|
||||
|
||||
if (_singleton) {
|
||||
_cache ??= target;
|
||||
}
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
@override
|
||||
void toSingleton() {
|
||||
_singleton = true;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isSingleton => _singleton;
|
||||
|
||||
/// RU: Проверяет, был ли передан параметр, если провайдер требует его.
|
||||
/// ENG: Checks if parameter is passed when the provider expects it.
|
||||
void _checkParams(dynamic params) {
|
||||
if (_withParams && params == null) {
|
||||
throw StateError(
|
||||
'[$T] Params is null. Maybe you forgot to pass it?',
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -13,9 +13,9 @@
|
||||
import 'dart:collection';
|
||||
import 'dart:math';
|
||||
|
||||
import 'package:cherrypick/src/binding.dart';
|
||||
import 'package:cherrypick/src/cycle_detector.dart';
|
||||
import 'package:cherrypick/src/global_cycle_detector.dart';
|
||||
import 'package:cherrypick/src/binding_resolver.dart';
|
||||
import 'package:cherrypick/src/module.dart';
|
||||
|
||||
Scope openRootScope() => Scope(null);
|
||||
@@ -176,33 +176,12 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
|
||||
/// RU: Внутренний метод для разрешения зависимостей без проверки циклических зависимостей.
|
||||
/// ENG: Internal method for dependency resolution without circular dependency checking.
|
||||
T? _tryResolveInternal<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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
final resolver = _findBindingResolver<T>(named);
|
||||
|
||||
// 2 Поиск зависимостей в родительском скоупе
|
||||
return _parentScope?._tryResolveInternal(named: named, params: params);
|
||||
// 1 Поиск зависимости по всем модулям текущего скоупа
|
||||
return resolver?.resolveSync(params) ??
|
||||
// 2 Поиск зависимостей в родительском скоупе
|
||||
_parentScope?.tryResolve(named: named, params: params);
|
||||
}
|
||||
|
||||
/// RU: Асинхронно возвращает найденную зависимость, определенную параметром типа [T].
|
||||
@@ -266,30 +245,25 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
|
||||
/// RU: Внутренний метод для асинхронного разрешения зависимостей без проверки циклических зависимостей.
|
||||
/// ENG: Internal method for async dependency resolution without circular dependency checking.
|
||||
Future<T?> _tryResolveAsyncInternal<T>({String? named, dynamic params}) async {
|
||||
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))) {
|
||||
if (binding.instanceAsync != null) {
|
||||
return await binding.instanceAsync;
|
||||
}
|
||||
final resolver = _findBindingResolver<T>(named);
|
||||
|
||||
if (binding.asyncProvider != null) {
|
||||
return await binding.asyncProvider?.call();
|
||||
}
|
||||
// 1 Поиск зависимости по всем модулям текущего скоупа
|
||||
return resolver?.resolveAsync(params) ??
|
||||
// 2 Поиск зависимостей в родительском скоупе
|
||||
_parentScope?.tryResolveAsync(named: named, params: params);
|
||||
}
|
||||
|
||||
if (binding.asyncProviderWithParams != null) {
|
||||
if (params == null) {
|
||||
throw StateError('Param is null. Maybe you forget pass it');
|
||||
}
|
||||
return await binding.asyncProviderWithParams!(params);
|
||||
}
|
||||
}
|
||||
BindingResolver<T>? _findBindingResolver<T>(String? named) {
|
||||
for (var module in _modulesList) {
|
||||
for (var binding in module.bindingSet) {
|
||||
if (binding.key == T &&
|
||||
((!binding.isNamed && named == null) ||
|
||||
(binding.isNamed && named == binding.name))) {
|
||||
return binding.resolver as BindingResolver<T>?;
|
||||
}
|
||||
}
|
||||
}
|
||||
return _parentScope?._tryResolveAsyncInternal(named: named, params: params);
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import 'package:cherrypick/src/binding.dart';
|
||||
import 'package:cherrypick/cherrypick.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
@@ -7,12 +7,12 @@ void main() {
|
||||
group('Without name', () {
|
||||
test('Returns null by default', () {
|
||||
final binding = Binding<int>();
|
||||
expect(binding.instance, null);
|
||||
expect(binding.resolver, null);
|
||||
});
|
||||
|
||||
test('Sets mode to instance', () {
|
||||
final binding = Binding<int>().toInstance(5);
|
||||
expect(binding.mode, Mode.instance);
|
||||
expect(binding.resolver, isA<InstanceResolver<int>>());
|
||||
});
|
||||
|
||||
test('isSingleton is true', () {
|
||||
@@ -22,19 +22,19 @@ void main() {
|
||||
|
||||
test('Stores value', () {
|
||||
final binding = Binding<int>().toInstance(5);
|
||||
expect(binding.instance, 5);
|
||||
expect(binding.resolver?.resolveSync(), 5);
|
||||
});
|
||||
});
|
||||
|
||||
group('With name', () {
|
||||
test('Returns null by default', () {
|
||||
final binding = Binding<int>().withName('n');
|
||||
expect(binding.instance, null);
|
||||
expect(binding.resolver, null);
|
||||
});
|
||||
|
||||
test('Sets mode to instance', () {
|
||||
final binding = Binding<int>().withName('n').toInstance(5);
|
||||
expect(binding.mode, Mode.instance);
|
||||
expect(binding.resolver, isA<InstanceResolver<int>>());
|
||||
});
|
||||
|
||||
test('Sets key', () {
|
||||
@@ -49,7 +49,7 @@ void main() {
|
||||
|
||||
test('Stores value', () {
|
||||
final binding = Binding<int>().withName('n').toInstance(5);
|
||||
expect(binding.instance, 5);
|
||||
expect(binding.resolver?.resolveSync(), 5);
|
||||
});
|
||||
|
||||
test('Sets name', () {
|
||||
@@ -60,45 +60,39 @@ void main() {
|
||||
|
||||
test('Multiple toInstance calls change value', () {
|
||||
final binding = Binding<int>().toInstance(1).toInstance(2);
|
||||
expect(binding.instance, 2);
|
||||
expect(binding.resolver?.resolveSync(), 2);
|
||||
});
|
||||
});
|
||||
|
||||
// --- Instance binding (asynchronous) ---
|
||||
group('Async Instance Binding (toInstanceAsync)', () {
|
||||
test('Resolves instanceAsync with expected value', () async {
|
||||
final binding = Binding<int>().toInstanceAsync(Future.value(42));
|
||||
expect(await binding.instanceAsync, 42);
|
||||
});
|
||||
|
||||
test('Does not affect instance', () {
|
||||
final binding = Binding<int>().toInstanceAsync(Future.value(5));
|
||||
expect(binding.instance, null);
|
||||
final binding = Binding<int>().toInstance(Future.value(42));
|
||||
expect(await binding.resolveAsync(), 42);
|
||||
});
|
||||
|
||||
test('Sets mode to instance', () {
|
||||
final binding = Binding<int>().toInstanceAsync(Future.value(5));
|
||||
expect(binding.mode, Mode.instance);
|
||||
final binding = Binding<int>().toInstance(Future.value(5));
|
||||
expect(binding.resolver, isA<InstanceResolver<int>>());
|
||||
});
|
||||
|
||||
test('isSingleton is true after toInstanceAsync', () {
|
||||
final binding = Binding<int>().toInstanceAsync(Future.value(5));
|
||||
final binding = Binding<int>().toInstance(Future.value(5));
|
||||
expect(binding.isSingleton, isTrue);
|
||||
});
|
||||
|
||||
test('Composes with withName', () async {
|
||||
final binding = Binding<int>()
|
||||
.withName('asyncValue')
|
||||
.toInstanceAsync(Future.value(7));
|
||||
final binding =
|
||||
Binding<int>().withName('asyncValue').toInstance(Future.value(7));
|
||||
expect(binding.isNamed, isTrue);
|
||||
expect(binding.name, 'asyncValue');
|
||||
expect(await binding.instanceAsync, 7);
|
||||
expect(await binding.resolveAsync(), 7);
|
||||
});
|
||||
|
||||
test('Keeps value after multiple awaits', () async {
|
||||
final binding = Binding<int>().toInstanceAsync(Future.value(123));
|
||||
final result1 = await binding.instanceAsync;
|
||||
final result2 = await binding.instanceAsync;
|
||||
final binding = Binding<int>().toInstance(Future.value(123));
|
||||
final result1 = await binding.resolveAsync();
|
||||
final result2 = await binding.resolveAsync();
|
||||
expect(result1, equals(result2));
|
||||
});
|
||||
});
|
||||
@@ -108,12 +102,12 @@ void main() {
|
||||
group('Without name', () {
|
||||
test('Returns null by default', () {
|
||||
final binding = Binding<int>();
|
||||
expect(binding.provider, null);
|
||||
expect(binding.resolver, null);
|
||||
});
|
||||
|
||||
test('Sets mode to providerInstance', () {
|
||||
final binding = Binding<int>().toProvide(() => 5);
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
expect(binding.resolver, isA<ProviderResolver<int>>());
|
||||
});
|
||||
|
||||
test('isSingleton is false by default', () {
|
||||
@@ -123,19 +117,19 @@ void main() {
|
||||
|
||||
test('Returns provided value', () {
|
||||
final binding = Binding<int>().toProvide(() => 5);
|
||||
expect(binding.provider, 5);
|
||||
expect(binding.resolveSync(), 5);
|
||||
});
|
||||
});
|
||||
|
||||
group('With name', () {
|
||||
test('Returns null by default', () {
|
||||
final binding = Binding<int>().withName('n');
|
||||
expect(binding.provider, null);
|
||||
expect(binding.resolver, null);
|
||||
});
|
||||
|
||||
test('Sets mode to providerInstance', () {
|
||||
final binding = Binding<int>().withName('n').toProvide(() => 5);
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
expect(binding.resolver, isA<ProviderResolver<int>>());
|
||||
});
|
||||
|
||||
test('Sets key', () {
|
||||
@@ -150,7 +144,7 @@ void main() {
|
||||
|
||||
test('Returns provided value', () {
|
||||
final binding = Binding<int>().withName('n').toProvide(() => 5);
|
||||
expect(binding.provider, 5);
|
||||
expect(binding.resolveSync(), 5);
|
||||
});
|
||||
|
||||
test('Sets name', () {
|
||||
@@ -163,14 +157,14 @@ void main() {
|
||||
// --- Async provider binding ---
|
||||
group('Async Provider Binding', () {
|
||||
test('Resolves asyncProvider value', () async {
|
||||
final binding = Binding<int>().toProvideAsync(() async => 5);
|
||||
expect(await binding.asyncProvider?.call(), 5);
|
||||
final binding = Binding<int>().toProvide(() async => 5);
|
||||
expect(await binding.resolveAsync(), 5);
|
||||
});
|
||||
|
||||
test('Resolves asyncProviderWithParams value', () async {
|
||||
final binding = Binding<int>()
|
||||
.toProvideAsyncWithParams((param) async => 5 + (param as int));
|
||||
expect(await binding.asyncProviderWithParams?.call(3), 8);
|
||||
.toProvideWithParams((param) async => 5 + (param as int));
|
||||
expect(await binding.resolveAsync(3), 8);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -179,12 +173,7 @@ void main() {
|
||||
group('Without name', () {
|
||||
test('Returns null if no provider set', () {
|
||||
final binding = Binding<int>().singleton();
|
||||
expect(binding.provider, null);
|
||||
});
|
||||
|
||||
test('Sets mode to providerInstance', () {
|
||||
final binding = Binding<int>().toProvide(() => 5).singleton();
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
expect(binding.resolver, null);
|
||||
});
|
||||
|
||||
test('isSingleton is true', () {
|
||||
@@ -194,7 +183,7 @@ void main() {
|
||||
|
||||
test('Returns singleton value', () {
|
||||
final binding = Binding<int>().toProvide(() => 5).singleton();
|
||||
expect(binding.provider, 5);
|
||||
expect(binding.resolveSync(), 5);
|
||||
});
|
||||
|
||||
test('Returns same value each call and provider only called once', () {
|
||||
@@ -204,8 +193,8 @@ void main() {
|
||||
return counter;
|
||||
}).singleton();
|
||||
|
||||
final first = binding.provider;
|
||||
final second = binding.provider;
|
||||
final first = binding.resolveSync();
|
||||
final second = binding.resolveSync();
|
||||
expect(first, equals(second));
|
||||
expect(counter, 1);
|
||||
});
|
||||
@@ -214,13 +203,7 @@ void main() {
|
||||
group('With name', () {
|
||||
test('Returns null if no provider set', () {
|
||||
final binding = Binding<int>().withName('n').singleton();
|
||||
expect(binding.provider, null);
|
||||
});
|
||||
|
||||
test('Sets mode to providerInstance', () {
|
||||
final binding =
|
||||
Binding<int>().withName('n').toProvide(() => 5).singleton();
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
expect(binding.resolver, null);
|
||||
});
|
||||
|
||||
test('Sets key', () {
|
||||
@@ -238,7 +221,7 @@ void main() {
|
||||
test('Returns singleton value', () {
|
||||
final binding =
|
||||
Binding<int>().withName('n').toProvide(() => 5).singleton();
|
||||
expect(binding.provider, 5);
|
||||
expect(binding.resolveSync(), 5);
|
||||
});
|
||||
|
||||
test('Sets name', () {
|
||||
@@ -247,12 +230,6 @@ void main() {
|
||||
expect(binding.name, 'n');
|
||||
});
|
||||
});
|
||||
|
||||
test('Chained withName and singleton preserves mode', () {
|
||||
final binding =
|
||||
Binding<int>().toProvide(() => 3).withName("named").singleton();
|
||||
expect(binding.mode, Mode.providerInstance);
|
||||
});
|
||||
});
|
||||
|
||||
// --- WithName / Named binding, isNamed, edge-cases ---
|
||||
@@ -265,7 +242,7 @@ void main() {
|
||||
|
||||
test('providerWithParams returns null if not set', () {
|
||||
final binding = Binding<int>();
|
||||
expect(binding.providerWithParams(123), null);
|
||||
expect(binding.resolveSync(123), null);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ void main() {
|
||||
final scope = Scope(null)
|
||||
..installModules([
|
||||
_InlineModule((m, s) {
|
||||
m.bind<String>().toInstanceAsync(Future.value('async value'));
|
||||
m.bind<String>().toInstance(Future.value('async value'));
|
||||
}),
|
||||
]);
|
||||
expect(await scope.resolveAsync<String>(), "async value");
|
||||
@@ -137,7 +137,7 @@ void main() {
|
||||
final scope = Scope(null)
|
||||
..installModules([
|
||||
_InlineModule((m, s) {
|
||||
m.bind<int>().toProvideAsync(() async => 7);
|
||||
m.bind<int>().toProvide(() async => 7);
|
||||
}),
|
||||
]);
|
||||
expect(await scope.resolveAsync<int>(), 7);
|
||||
@@ -147,7 +147,7 @@ void main() {
|
||||
final scope = Scope(null)
|
||||
..installModules([
|
||||
_InlineModule((m, s) {
|
||||
m.bind<int>().toProvideAsyncWithParams((x) async => (x as int) * 3);
|
||||
m.bind<int>().toProvideWithParams((x) async => (x as int) * 3);
|
||||
}),
|
||||
]);
|
||||
expect(await scope.resolveAsync<int>(params: 2), 6);
|
||||
|
||||
Reference in New Issue
Block a user