refactor: adding parameter override support all test passing
This commit is contained in:
parent
d920180845
commit
81bb7bdd5e
2 changed files with 178 additions and 9 deletions
|
@ -37,6 +37,9 @@ class Container {
|
|||
/// The container's refreshing instances
|
||||
final Set<Type> _refreshing = {};
|
||||
|
||||
/// The container's parameter override stack
|
||||
final List<Map<String, dynamic>> _parameterStack = [];
|
||||
|
||||
/// The container's contextual bindings
|
||||
final Map<Type, Map<Type, dynamic>> _contextual = {};
|
||||
|
||||
|
@ -318,15 +321,23 @@ class Container {
|
|||
var named = <String, Object>{};
|
||||
|
||||
for (var param in constructor.parameters) {
|
||||
if (param.type.reflectedType == String) {
|
||||
positional.add('test.log'); // Default filename for FileLogger
|
||||
} else {
|
||||
var value = make(param.type.reflectedType);
|
||||
// Check for parameter override
|
||||
var override = getParameterOverride(param.name);
|
||||
if (override != null) {
|
||||
if (param.isNamed) {
|
||||
named[param.name] = value;
|
||||
named[param.name] = override;
|
||||
} else {
|
||||
positional.add(value);
|
||||
positional.add(override);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// No override, resolve normally
|
||||
var value = make(param.type.reflectedType);
|
||||
if (param.isNamed) {
|
||||
named[param.name] = value;
|
||||
} else {
|
||||
positional.add(value);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,7 +402,10 @@ class Container {
|
|||
_fireAfterResolvingCallbacks(instance);
|
||||
return instance as T;
|
||||
} else if (search._factories.containsKey(resolvedType)) {
|
||||
var instance = search._factories[resolvedType]!(this);
|
||||
// For factory bindings, wrap the factory call in withParameters
|
||||
var instance = withParameters(_parameterStack.lastOrNull ?? {}, () {
|
||||
return search!._factories[resolvedType]!(this);
|
||||
});
|
||||
instance = _applyExtenders(resolvedType, instance);
|
||||
_fireResolvingCallbacks(instance);
|
||||
_fireAfterResolvingCallbacks(instance);
|
||||
|
@ -450,8 +464,19 @@ class Container {
|
|||
_buildStack.add(t2);
|
||||
try {
|
||||
for (var param in constructor.parameters) {
|
||||
var value = make(param.type.reflectedType);
|
||||
// Check for parameter override
|
||||
var override = getParameterOverride(param.name);
|
||||
if (override != null) {
|
||||
if (param.isNamed) {
|
||||
named[param.name] = override;
|
||||
} else {
|
||||
positional.add(override);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// No override, resolve normally
|
||||
var value = make(param.type.reflectedType);
|
||||
if (param.isNamed) {
|
||||
named[param.name] = value;
|
||||
} else {
|
||||
|
@ -511,7 +536,11 @@ class Container {
|
|||
throw StateError('This container already has a factory for $t2.');
|
||||
}
|
||||
|
||||
_factories[t2] = f;
|
||||
// Wrap factory in parameter override handler
|
||||
_factories[t2] = (container) {
|
||||
return container.withParameters(
|
||||
_parameterStack.lastOrNull ?? {}, () => f(container));
|
||||
};
|
||||
return f;
|
||||
}
|
||||
|
||||
|
@ -951,6 +980,37 @@ class Container {
|
|||
}
|
||||
}
|
||||
|
||||
/// Push parameter overrides onto the stack.
|
||||
///
|
||||
/// These parameters will be used when resolving dependencies until they are popped.
|
||||
/// ```dart
|
||||
/// container.withParameters({'filename': 'custom.log'}, () {
|
||||
/// var logger = container.make<Logger>();
|
||||
/// });
|
||||
/// ```
|
||||
T withParameters<T>(Map<String, dynamic> parameters, T Function() callback) {
|
||||
_parameterStack.add(parameters);
|
||||
try {
|
||||
return callback();
|
||||
} finally {
|
||||
_parameterStack.removeLast();
|
||||
}
|
||||
}
|
||||
|
||||
/// Get an override value for a parameter if one exists.
|
||||
///
|
||||
/// This method is used internally by the container to resolve parameter overrides,
|
||||
/// but is also exposed for use in factory functions.
|
||||
dynamic getParameterOverride(String name) {
|
||||
for (var i = _parameterStack.length - 1; i >= 0; i--) {
|
||||
var parameters = _parameterStack[i];
|
||||
if (parameters.containsKey(name)) {
|
||||
return parameters[name];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// Check if we're in danger of a circular dependency.
|
||||
void _checkCircularDependency(Type type) {
|
||||
if (_buildStack.contains(type)) {
|
||||
|
|
109
incubation/container/container/test/parameter_override_test.dart
Normal file
109
incubation/container/container/test/parameter_override_test.dart
Normal file
|
@ -0,0 +1,109 @@
|
|||
import 'package:platformed_container/container.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
class FileLogger {
|
||||
final String filename;
|
||||
FileLogger(this.filename);
|
||||
}
|
||||
|
||||
class MockReflector extends Reflector {
|
||||
@override
|
||||
String? getName(Symbol symbol) => null;
|
||||
|
||||
@override
|
||||
ReflectedClass? reflectClass(Type clazz) => null;
|
||||
|
||||
@override
|
||||
ReflectedType? reflectType(Type type) => null;
|
||||
|
||||
@override
|
||||
ReflectedInstance? reflectInstance(Object? instance) => null;
|
||||
|
||||
@override
|
||||
ReflectedFunction? reflectFunction(Function function) => null;
|
||||
|
||||
@override
|
||||
ReflectedType reflectFutureOf(Type type) => throw UnimplementedError();
|
||||
}
|
||||
|
||||
void main() {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container(MockReflector());
|
||||
});
|
||||
|
||||
group('Parameter Override Tests', () {
|
||||
test('can override constructor parameters', () {
|
||||
container.registerFactory<FileLogger>((c) {
|
||||
var filename =
|
||||
c.getParameterOverride('filename') as String? ?? 'default.log';
|
||||
return FileLogger(filename);
|
||||
});
|
||||
|
||||
var logger = container.withParameters(
|
||||
{'filename': 'custom.log'}, () => container.make<FileLogger>());
|
||||
|
||||
expect(logger.filename, equals('custom.log'));
|
||||
});
|
||||
|
||||
test('parameter overrides are scoped', () {
|
||||
container.registerFactory<FileLogger>((c) {
|
||||
var filename =
|
||||
c.getParameterOverride('filename') as String? ?? 'default.log';
|
||||
return FileLogger(filename);
|
||||
});
|
||||
|
||||
var customLogger = container.withParameters(
|
||||
{'filename': 'custom.log'}, () => container.make<FileLogger>());
|
||||
|
||||
var defaultLogger = container.make<FileLogger>();
|
||||
|
||||
expect(customLogger.filename, equals('custom.log'));
|
||||
expect(defaultLogger.filename, equals('default.log'));
|
||||
});
|
||||
|
||||
test('nested parameter overrides', () {
|
||||
container.registerFactory<FileLogger>((c) {
|
||||
var filename =
|
||||
c.getParameterOverride('filename') as String? ?? 'default.log';
|
||||
return FileLogger(filename);
|
||||
});
|
||||
|
||||
var logger = container.withParameters(
|
||||
{'filename': 'outer.log'},
|
||||
() => container.withParameters(
|
||||
{'filename': 'inner.log'}, () => container.make<FileLogger>()));
|
||||
|
||||
expect(logger.filename, equals('inner.log'));
|
||||
});
|
||||
|
||||
test('parameter overrides in child container', () {
|
||||
container.registerFactory<FileLogger>((c) {
|
||||
var filename =
|
||||
c.getParameterOverride('filename') as String? ?? 'default.log';
|
||||
return FileLogger(filename);
|
||||
});
|
||||
var child = container.createChild();
|
||||
|
||||
var logger = child.withParameters(
|
||||
{'filename': 'custom.log'}, () => child.make<FileLogger>());
|
||||
|
||||
expect(logger.filename, equals('custom.log'));
|
||||
});
|
||||
|
||||
test('parameter overrides with multiple parameters', () {
|
||||
container.registerFactory<FileLogger>((c) {
|
||||
var filename =
|
||||
c.getParameterOverride('filename') as String? ?? 'default.log';
|
||||
return FileLogger(filename);
|
||||
});
|
||||
|
||||
var logger = container.withParameters(
|
||||
{'filename': 'custom.log', 'level': 'debug', 'maxSize': 1024},
|
||||
() => container.make<FileLogger>());
|
||||
|
||||
expect(logger.filename, equals('custom.log'));
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue