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:
Sergey Penkovsky
2025-08-01 11:01:30 +03:00
committed by GitHub
9 changed files with 262 additions and 235 deletions

View File

@@ -17,12 +17,12 @@ All scenarios use the public API capabilities of cherrypick (scope, module, bind
| Scenario | RunTime (μs) | | Scenario | RunTime (μs) |
|----------------------------------------------------|---------------| |----------------------------------------------------|---------------|
| RegisterAndResolve | 0.3407 | | RegisterAndResolve | 0.4574 |
| ChainSingleton (A->B->C, singleton) | 0.3777 | | ChainSingleton (A->B->C, singleton) | 0.3759 |
| ChainFactory (A->B->C, factory) | 0.9688 | | ChainFactory (A->B->C, factory) | 1.3783 |
| NamedResolve (by name) | 0.3878 | | NamedResolve (by name) | 0.5193 |
| AsyncChain (A->B->C, async) | 1.8006 | | AsyncChain (A->B->C, async) | 0.5985 |
| ScopeOverride (child overrides parent) | 0.3477 | | ScopeOverride (child overrides parent) | 0.3611 |
## How to run ## How to run

View File

@@ -17,12 +17,12 @@
| Сценарий | RunTime (мкс) | | Сценарий | RunTime (мкс) |
|----------------------------------------------------|--------------| |----------------------------------------------------|--------------|
| RegisterAndResolve | 0.3407 | | RegisterAndResolve | 0.4574 |
| ChainSingleton (A->B->C, singleton) | 0.3777 | | ChainSingleton (A->B->C, singleton) | 0.3759 |
| ChainFactory (A->B->C, factory) | 0.9688 | | ChainFactory (A->B->C, factory) | 1.3783 |
| NamedResolve (by name) | 0.3878 | | NamedResolve (by name) | 0.5193 |
| AsyncChain (A->B->C, async) | 1.8006 | | AsyncChain (A->B->C, async) | 0.5985 |
| ScopeOverride (child overrides parent) | 0.3477 | | ScopeOverride (child overrides parent) | 0.3611 |
## Как запускать ## Как запускать

View File

@@ -17,19 +17,28 @@ class FeatureModule extends Module {
@override @override
void builder(Scope currentScope) { void builder(Scope currentScope) {
// Using toProvideAsync for async initialization // 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( final client = await Future.delayed(
Duration(milliseconds: 100), Duration(milliseconds: 1000),
() => currentScope.resolve<ApiClient>( () => currentScope.resolve<ApiClient>(
named: isMock ? "apiClientMock" : "apiClientImpl")); named: isMock ? "apiClientMock" : "apiClientImpl",
),
);
return NetworkDataRepository(client); return NetworkDataRepository(client);
}).singleton(); }).singleton();
// Asynchronous initialization of DataBloc // Asynchronous initialization of DataBloc
bind<DataBloc>().toProvideAsync( bind<DataBloc>().toProvide(
() async { () async {
final repo = await currentScope.resolveAsync<DataRepository>( final repo = await currentScope.resolveAsync<DataRepository>(
named: "networkRepo"); named: "networkRepo",
params: 'Some params',
);
return DataBloc(repo); return DataBloc(repo);
}, },
); );
@@ -38,9 +47,7 @@ class FeatureModule extends Module {
Future<void> main() async { Future<void> main() async {
try { try {
final scope = openRootScope().installModules([ final scope = openRootScope().installModules([AppModule()]);
AppModule(),
]);
final subScope = scope final subScope = scope
.openSubScope("featureScope") .openSubScope("featureScope")
@@ -48,8 +55,11 @@ Future<void> main() async {
// Asynchronous instance resolution // Asynchronous instance resolution
final dataBloc = await subScope.resolveAsync<DataBloc>(); final dataBloc = await subScope.resolveAsync<DataBloc>();
dataBloc.data.listen((d) => print('Received data: $d'), dataBloc.data.listen(
onError: (e) => print('Error: $e'), onDone: () => print('DONE')); (d) => print('Received data: $d'),
onError: (e) => print('Error: $e'),
onDone: () => print('DONE'),
);
await dataBloc.fetchData(); await dataBloc.fetchData();
} catch (e) { } catch (e) {

View File

@@ -13,6 +13,7 @@ library;
// limitations under the License. // limitations under the License.
// //
export 'package:cherrypick/src/binding_resolver.dart';
export 'package:cherrypick/src/binding.dart'; export 'package:cherrypick/src/binding.dart';
export 'package:cherrypick/src/cycle_detector.dart'; export 'package:cherrypick/src/cycle_detector.dart';
export 'package:cherrypick/src/global_cycle_detector.dart'; export 'package:cherrypick/src/global_cycle_detector.dart';

View File

@@ -11,45 +11,21 @@
// limitations under the License. // limitations under the License.
// //
enum Mode { simple, instance, providerInstance, providerInstanceWithParams } import 'package:cherrypick/src/binding_resolver.dart';
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);
/// RU: Класс Binding<T> настраивает параметры экземпляра. /// RU: Класс Binding<T> настраивает параметры экземпляра.
/// ENG: The Binding<T> class configures the settings for the instance. /// ENG: The Binding<T> class configures the settings for the instance.
/// ///
class Binding<T> { class Binding<T> {
late Mode _mode;
late Type _key; late Type _key;
late String _name; String? _name;
T? _instance;
Future<T>? _instanceAsync;
Provider<T>? _provider;
ProviderWithParams<T>? _providerWithParams;
AsyncProvider<T>? asyncProvider; BindingResolver<T>? _resolver;
AsyncProviderWithParams<T>? asyncProviderWithParams;
late bool _isSingleton = false;
late bool _isNamed = false;
Binding() { Binding() {
_mode = Mode.simple;
_key = T; _key = T;
} }
/// RU: Метод возвращает [Mode] экземпляра.
/// ENG: The method returns the [Mode] of the instance.
///
/// return [Mode]
Mode get mode => _mode;
/// RU: Метод возвращает тип экземпляра. /// RU: Метод возвращает тип экземпляра.
/// ENG: The method returns the type of the instance. /// 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. /// ENG: The method returns the name of the instance.
/// ///
/// return [String] /// return [String]
String get name => _name; String? get name => _name;
/// RU: Метод проверяет сингелтон экземпляр или нет.
/// ENG: The method checks the singleton instance or not.
///
/// return [bool]
bool get isSingleton => _isSingleton;
/// RU: Метод проверяет именован экземпляр или нет. /// RU: Метод проверяет именован экземпляр или нет.
/// ENG: The method checks whether the instance is named or not. /// ENG: The method checks whether the instance is named or not.
/// ///
/// return [bool] /// 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]. /// RU: Добавляет имя для экземляпя [value].
/// ENG: Added name for instance [value]. /// ENG: Added name for instance [value].
@@ -80,7 +58,6 @@ class Binding<T> {
/// return [Binding] /// return [Binding]
Binding<T> withName(String name) { Binding<T> withName(String name) {
_name = name; _name = name;
_isNamed = true;
return this; return this;
} }
@@ -88,21 +65,9 @@ class Binding<T> {
/// ENG: Initialization instance [value]. /// ENG: Initialization instance [value].
/// ///
/// return [Binding] /// return [Binding]
Binding<T> toInstance(T value) { Binding<T> toInstance(Instance<T> value) {
_mode = Mode.instance; _resolver = InstanceResolver<T>(value);
_instance = value;
_isSingleton = true;
return this;
}
/// RU: Инициализация экземляпяра [value].
/// ENG: Initialization instance [value].
///
/// return [Binding]
Binding<T> toInstanceAsync(Future<T> value) {
_mode = Mode.instance;
_instanceAsync = value;
_isSingleton = true;
return this; return this;
} }
@@ -111,18 +76,8 @@ class Binding<T> {
/// ///
/// return [Binding] /// return [Binding]
Binding<T> toProvide(Provider<T> value) { Binding<T> toProvide(Provider<T> value) {
_mode = Mode.providerInstance; _resolver = ProviderResolver<T>((_) => value.call(), withParams: false);
_provider = value;
return this;
}
/// RU: Инициализация экземляпяра  через провайдер [value].
/// ENG: Initialization instance via provider [value].
///
/// return [Binding]
Binding<T> toProvideAsync(AsyncProvider<T> provider) {
_mode = Mode.providerInstance;
asyncProvider = provider;
return this; return this;
} }
@@ -131,19 +86,24 @@ class Binding<T> {
/// ///
/// return [Binding] /// return [Binding]
Binding<T> toProvideWithParams(ProviderWithParams<T> value) { Binding<T> toProvideWithParams(ProviderWithParams<T> value) {
_mode = Mode.providerInstanceWithParams; _resolver = ProviderResolver<T>(value, withParams: true);
_providerWithParams = value;
return this; return this;
} }
/// RU: Инициализация экземляра через асинхронный провайдер [value] с динамическим параметром. @Deprecated('Use toInstance instead of toInstanceAsync')
/// ENG: Initializes the instance via async provider [value] with a dynamic param. Binding<T> toInstanceAsync(Instance<T> value) {
/// return this.toInstance(value);
/// return [Binding] }
Binding<T> toProvideAsyncWithParams(AsyncProviderWithParams<T> provider) {
_mode = Mode.providerInstanceWithParams; @Deprecated('Use toProvide instead of toProvideAsync')
asyncProviderWithParams = provider; Binding<T> toProvideAsync(Provider<T> value) {
return this; return this.toProvide(value);
}
@Deprecated('Use toProvideWithParams instead of toProvideAsyncWithParams')
Binding<T> toProvideAsyncWithParams(ProviderWithParams<T> value) {
return this.toProvideWithParams(value);
} }
/// RU: Инициализация экземляпяра  как сингелтон [value]. /// RU: Инициализация экземляпяра  как сингелтон [value].
@@ -151,40 +111,16 @@ class Binding<T> {
/// ///
/// return [Binding] /// return [Binding]
Binding<T> singleton() { Binding<T> singleton() {
_isSingleton = true; _resolver?.toSingleton();
return this; return this;
} }
/// RU: Поиск экземпляра. T? resolveSync([dynamic params]) {
/// ENG: Resolve instance. return resolver?.resolveSync(params);
///
/// 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();
} }
/// RU: Поиск экземпляра с параметром. Future<T>? resolveAsync([dynamic params]) {
/// return resolver?.resolveAsync(params);
/// ENG: Resolve instance with [params].
///
/// return [T]
T? providerWithParams(dynamic params) {
return _providerWithParams?.call(params);
} }
} }

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

View File

@@ -13,9 +13,9 @@
import 'dart:collection'; import 'dart:collection';
import 'dart:math'; import 'dart:math';
import 'package:cherrypick/src/binding.dart';
import 'package:cherrypick/src/cycle_detector.dart'; import 'package:cherrypick/src/cycle_detector.dart';
import 'package:cherrypick/src/global_cycle_detector.dart'; import 'package:cherrypick/src/global_cycle_detector.dart';
import 'package:cherrypick/src/binding_resolver.dart';
import 'package:cherrypick/src/module.dart'; import 'package:cherrypick/src/module.dart';
Scope openRootScope() => Scope(null); Scope openRootScope() => Scope(null);
@@ -176,33 +176,12 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
/// RU: Внутренний метод для разрешения зависимостей без проверки циклических зависимостей. /// RU: Внутренний метод для разрешения зависимостей без проверки циклических зависимостей.
/// ENG: Internal method for dependency resolution without circular dependency checking. /// ENG: Internal method for dependency resolution without circular dependency checking.
T? _tryResolveInternal<T>({String? named, dynamic params}) { T? _tryResolveInternal<T>({String? named, dynamic params}) {
// 1 Поиск зависимости по всем модулям текущего скоупа final resolver = _findBindingResolver<T>(named);
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;
}
}
}
}
}
// 1 Поиск зависимости по всем модулям текущего скоупа
return resolver?.resolveSync(params) ??
// 2 Поиск зависимостей в родительском скоупе // 2 Поиск зависимостей в родительском скоупе
return _parentScope?._tryResolveInternal(named: named, params: params); _parentScope?.tryResolve(named: named, params: params);
} }
/// RU: Асинхронно возвращает найденную зависимость, определенную параметром типа [T]. /// RU: Асинхронно возвращает найденную зависимость, определенную параметром типа [T].
@@ -266,30 +245,25 @@ class Scope with CycleDetectionMixin, GlobalCycleDetectionMixin {
/// RU: Внутренний метод для асинхронного разрешения зависимостей без проверки циклических зависимостей. /// RU: Внутренний метод для асинхронного разрешения зависимостей без проверки циклических зависимостей.
/// ENG: Internal method for async dependency resolution without circular dependency checking. /// ENG: Internal method for async dependency resolution without circular dependency checking.
Future<T?> _tryResolveAsyncInternal<T>({String? named, dynamic params}) async { Future<T?> _tryResolveAsyncInternal<T>({String? named, dynamic params}) async {
if (_modulesList.isNotEmpty) { final resolver = _findBindingResolver<T>(named);
// 1 Поиск зависимости по всем модулям текущего скоупа
return resolver?.resolveAsync(params) ??
// 2 Поиск зависимостей в родительском скоупе
_parentScope?.tryResolveAsync(named: named, params: params);
}
BindingResolver<T>? _findBindingResolver<T>(String? named) {
for (var module in _modulesList) { for (var module in _modulesList) {
for (var binding in module.bindingSet) { for (var binding in module.bindingSet) {
if (binding.key == T && if (binding.key == T &&
((!binding.isNamed && named == null) || ((!binding.isNamed && named == null) ||
(binding.isNamed && named == binding.name))) { (binding.isNamed && named == binding.name))) {
if (binding.instanceAsync != null) { return binding.resolver as BindingResolver<T>?;
return await binding.instanceAsync; }
}
} }
if (binding.asyncProvider != null) { return null;
return await binding.asyncProvider?.call();
}
if (binding.asyncProviderWithParams != null) {
if (params == null) {
throw StateError('Param is null. Maybe you forget pass it');
}
return await binding.asyncProviderWithParams!(params);
}
}
}
}
}
return _parentScope?._tryResolveAsyncInternal(named: named, params: params);
} }
} }

View File

@@ -1,4 +1,4 @@
import 'package:cherrypick/src/binding.dart'; import 'package:cherrypick/cherrypick.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
void main() { void main() {
@@ -7,12 +7,12 @@ void main() {
group('Without name', () { group('Without name', () {
test('Returns null by default', () { test('Returns null by default', () {
final binding = Binding<int>(); final binding = Binding<int>();
expect(binding.instance, null); expect(binding.resolver, null);
}); });
test('Sets mode to instance', () { test('Sets mode to instance', () {
final binding = Binding<int>().toInstance(5); final binding = Binding<int>().toInstance(5);
expect(binding.mode, Mode.instance); expect(binding.resolver, isA<InstanceResolver<int>>());
}); });
test('isSingleton is true', () { test('isSingleton is true', () {
@@ -22,19 +22,19 @@ void main() {
test('Stores value', () { test('Stores value', () {
final binding = Binding<int>().toInstance(5); final binding = Binding<int>().toInstance(5);
expect(binding.instance, 5); expect(binding.resolver?.resolveSync(), 5);
}); });
}); });
group('With name', () { group('With name', () {
test('Returns null by default', () { test('Returns null by default', () {
final binding = Binding<int>().withName('n'); final binding = Binding<int>().withName('n');
expect(binding.instance, null); expect(binding.resolver, null);
}); });
test('Sets mode to instance', () { test('Sets mode to instance', () {
final binding = Binding<int>().withName('n').toInstance(5); final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.mode, Mode.instance); expect(binding.resolver, isA<InstanceResolver<int>>());
}); });
test('Sets key', () { test('Sets key', () {
@@ -49,7 +49,7 @@ void main() {
test('Stores value', () { test('Stores value', () {
final binding = Binding<int>().withName('n').toInstance(5); final binding = Binding<int>().withName('n').toInstance(5);
expect(binding.instance, 5); expect(binding.resolver?.resolveSync(), 5);
}); });
test('Sets name', () { test('Sets name', () {
@@ -60,45 +60,39 @@ void main() {
test('Multiple toInstance calls change value', () { test('Multiple toInstance calls change value', () {
final binding = Binding<int>().toInstance(1).toInstance(2); final binding = Binding<int>().toInstance(1).toInstance(2);
expect(binding.instance, 2); expect(binding.resolver?.resolveSync(), 2);
}); });
}); });
// --- Instance binding (asynchronous) --- // --- Instance binding (asynchronous) ---
group('Async Instance Binding (toInstanceAsync)', () { group('Async Instance Binding (toInstanceAsync)', () {
test('Resolves instanceAsync with expected value', () async { test('Resolves instanceAsync with expected value', () async {
final binding = Binding<int>().toInstanceAsync(Future.value(42)); final binding = Binding<int>().toInstance(Future.value(42));
expect(await binding.instanceAsync, 42); expect(await binding.resolveAsync(), 42);
});
test('Does not affect instance', () {
final binding = Binding<int>().toInstanceAsync(Future.value(5));
expect(binding.instance, null);
}); });
test('Sets mode to instance', () { test('Sets mode to instance', () {
final binding = Binding<int>().toInstanceAsync(Future.value(5)); final binding = Binding<int>().toInstance(Future.value(5));
expect(binding.mode, Mode.instance); expect(binding.resolver, isA<InstanceResolver<int>>());
}); });
test('isSingleton is true after toInstanceAsync', () { 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); expect(binding.isSingleton, isTrue);
}); });
test('Composes with withName', () async { test('Composes with withName', () async {
final binding = Binding<int>() final binding =
.withName('asyncValue') Binding<int>().withName('asyncValue').toInstance(Future.value(7));
.toInstanceAsync(Future.value(7));
expect(binding.isNamed, isTrue); expect(binding.isNamed, isTrue);
expect(binding.name, 'asyncValue'); expect(binding.name, 'asyncValue');
expect(await binding.instanceAsync, 7); expect(await binding.resolveAsync(), 7);
}); });
test('Keeps value after multiple awaits', () async { test('Keeps value after multiple awaits', () async {
final binding = Binding<int>().toInstanceAsync(Future.value(123)); final binding = Binding<int>().toInstance(Future.value(123));
final result1 = await binding.instanceAsync; final result1 = await binding.resolveAsync();
final result2 = await binding.instanceAsync; final result2 = await binding.resolveAsync();
expect(result1, equals(result2)); expect(result1, equals(result2));
}); });
}); });
@@ -108,12 +102,12 @@ void main() {
group('Without name', () { group('Without name', () {
test('Returns null by default', () { test('Returns null by default', () {
final binding = Binding<int>(); final binding = Binding<int>();
expect(binding.provider, null); expect(binding.resolver, null);
}); });
test('Sets mode to providerInstance', () { test('Sets mode to providerInstance', () {
final binding = Binding<int>().toProvide(() => 5); final binding = Binding<int>().toProvide(() => 5);
expect(binding.mode, Mode.providerInstance); expect(binding.resolver, isA<ProviderResolver<int>>());
}); });
test('isSingleton is false by default', () { test('isSingleton is false by default', () {
@@ -123,19 +117,19 @@ void main() {
test('Returns provided value', () { test('Returns provided value', () {
final binding = Binding<int>().toProvide(() => 5); final binding = Binding<int>().toProvide(() => 5);
expect(binding.provider, 5); expect(binding.resolveSync(), 5);
}); });
}); });
group('With name', () { group('With name', () {
test('Returns null by default', () { test('Returns null by default', () {
final binding = Binding<int>().withName('n'); final binding = Binding<int>().withName('n');
expect(binding.provider, null); expect(binding.resolver, null);
}); });
test('Sets mode to providerInstance', () { test('Sets mode to providerInstance', () {
final binding = Binding<int>().withName('n').toProvide(() => 5); final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.mode, Mode.providerInstance); expect(binding.resolver, isA<ProviderResolver<int>>());
}); });
test('Sets key', () { test('Sets key', () {
@@ -150,7 +144,7 @@ void main() {
test('Returns provided value', () { test('Returns provided value', () {
final binding = Binding<int>().withName('n').toProvide(() => 5); final binding = Binding<int>().withName('n').toProvide(() => 5);
expect(binding.provider, 5); expect(binding.resolveSync(), 5);
}); });
test('Sets name', () { test('Sets name', () {
@@ -163,14 +157,14 @@ void main() {
// --- Async provider binding --- // --- Async provider binding ---
group('Async Provider Binding', () { group('Async Provider Binding', () {
test('Resolves asyncProvider value', () async { test('Resolves asyncProvider value', () async {
final binding = Binding<int>().toProvideAsync(() async => 5); final binding = Binding<int>().toProvide(() async => 5);
expect(await binding.asyncProvider?.call(), 5); expect(await binding.resolveAsync(), 5);
}); });
test('Resolves asyncProviderWithParams value', () async { test('Resolves asyncProviderWithParams value', () async {
final binding = Binding<int>() final binding = Binding<int>()
.toProvideAsyncWithParams((param) async => 5 + (param as int)); .toProvideWithParams((param) async => 5 + (param as int));
expect(await binding.asyncProviderWithParams?.call(3), 8); expect(await binding.resolveAsync(3), 8);
}); });
}); });
@@ -179,12 +173,7 @@ void main() {
group('Without name', () { group('Without name', () {
test('Returns null if no provider set', () { test('Returns null if no provider set', () {
final binding = Binding<int>().singleton(); final binding = Binding<int>().singleton();
expect(binding.provider, null); expect(binding.resolver, null);
});
test('Sets mode to providerInstance', () {
final binding = Binding<int>().toProvide(() => 5).singleton();
expect(binding.mode, Mode.providerInstance);
}); });
test('isSingleton is true', () { test('isSingleton is true', () {
@@ -194,7 +183,7 @@ void main() {
test('Returns singleton value', () { test('Returns singleton value', () {
final binding = Binding<int>().toProvide(() => 5).singleton(); 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', () { test('Returns same value each call and provider only called once', () {
@@ -204,8 +193,8 @@ void main() {
return counter; return counter;
}).singleton(); }).singleton();
final first = binding.provider; final first = binding.resolveSync();
final second = binding.provider; final second = binding.resolveSync();
expect(first, equals(second)); expect(first, equals(second));
expect(counter, 1); expect(counter, 1);
}); });
@@ -214,13 +203,7 @@ void main() {
group('With name', () { group('With name', () {
test('Returns null if no provider set', () { test('Returns null if no provider set', () {
final binding = Binding<int>().withName('n').singleton(); final binding = Binding<int>().withName('n').singleton();
expect(binding.provider, null); expect(binding.resolver, null);
});
test('Sets mode to providerInstance', () {
final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.mode, Mode.providerInstance);
}); });
test('Sets key', () { test('Sets key', () {
@@ -238,7 +221,7 @@ void main() {
test('Returns singleton value', () { test('Returns singleton value', () {
final binding = final binding =
Binding<int>().withName('n').toProvide(() => 5).singleton(); Binding<int>().withName('n').toProvide(() => 5).singleton();
expect(binding.provider, 5); expect(binding.resolveSync(), 5);
}); });
test('Sets name', () { test('Sets name', () {
@@ -247,12 +230,6 @@ void main() {
expect(binding.name, 'n'); 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 --- // --- WithName / Named binding, isNamed, edge-cases ---
@@ -265,7 +242,7 @@ void main() {
test('providerWithParams returns null if not set', () { test('providerWithParams returns null if not set', () {
final binding = Binding<int>(); final binding = Binding<int>();
expect(binding.providerWithParams(123), null); expect(binding.resolveSync(123), null);
}); });
}); });
} }

View File

@@ -127,7 +127,7 @@ void main() {
final scope = Scope(null) final scope = Scope(null)
..installModules([ ..installModules([
_InlineModule((m, s) { _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"); expect(await scope.resolveAsync<String>(), "async value");
@@ -137,7 +137,7 @@ void main() {
final scope = Scope(null) final scope = Scope(null)
..installModules([ ..installModules([
_InlineModule((m, s) { _InlineModule((m, s) {
m.bind<int>().toProvideAsync(() async => 7); m.bind<int>().toProvide(() async => 7);
}), }),
]); ]);
expect(await scope.resolveAsync<int>(), 7); expect(await scope.resolveAsync<int>(), 7);
@@ -147,7 +147,7 @@ void main() {
final scope = Scope(null) final scope = Scope(null)
..installModules([ ..installModules([
_InlineModule((m, s) { _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); expect(await scope.resolveAsync<int>(params: 2), 6);