refactor: refactored laravel features into container test 3 84 pass

This commit is contained in:
Patrick Stewart 2024-12-26 21:47:41 -07:00
parent d690877afc
commit f3b0fac943
3 changed files with 80 additions and 8 deletions

View file

@ -268,6 +268,10 @@ class Container {
// Check for contextual binding
var contextualConcrete = _getContextualConcrete(t2);
if (contextualConcrete == null && _hasContextualBinding(t2)) {
throw BindingResolutionException(
'No implementation was provided for contextual binding of $t2');
}
if (contextualConcrete != null) {
dynamic instance;
if (contextualConcrete is Function) {
@ -382,6 +386,31 @@ class Container {
// Use reflection to create instance
var reflectedType = reflector.reflectType(t2);
if (reflectedType == null) {
throw BindingResolutionException('No binding was found for $t2');
}
// Check if we have all required dependencies
if (reflectedType is ReflectedClass) {
bool isDefault(String name) {
return name.isEmpty || name == reflectedType.name;
}
var constructor = reflectedType.constructors.firstWhere(
(c) => isDefault(c.name),
orElse: (() => throw BindingResolutionException(
'${reflectedType.name} has no default constructor, and therefore cannot be instantiated.')));
// Check if we can resolve all constructor parameters
for (var param in constructor.parameters) {
var paramType = param.type.reflectedType;
if (!has(paramType) && reflector.reflectType(paramType) == null) {
throw BindingResolutionException(
'No binding was found for $paramType required by $t2');
}
}
}
var positional = [];
var named = <String, Object>{};
@ -730,6 +759,24 @@ class Container {
return null;
}
/// Check if a type has a contextual binding.
bool _hasContextualBinding(Type type) {
if (_buildStack.isEmpty) return false;
// Check current container's contextual bindings
Container? search = this;
while (search != null) {
var building = _buildStack.last;
var contextMap = search._contextual[building];
if (contextMap != null && contextMap.containsKey(type)) {
return true;
}
search = search._parent;
}
return false;
}
/// Check if we're in danger of a circular dependency.
void _checkCircularDependency(Type type) {
if (_buildStack.contains(type)) {

View file

@ -45,12 +45,19 @@ class ContextualImplementationBuilder {
final Type abstract;
/// Creates a new contextual implementation builder
ContextualImplementationBuilder(this.container, this.concrete, this.abstract);
ContextualImplementationBuilder(
this.container, this.concrete, this.abstract) {
// Register an empty binding by default
for (var concreteType in concrete) {
container.addContextualBinding(concreteType, abstract, null);
}
}
/// Specify the implementation that should be used
void give<T>() {
for (var concreteType in concrete) {
container.addContextualBinding(concreteType, abstract, T);
container.addContextualBinding(
concreteType, abstract, (Container c) => c.make<T>());
}
}

View file

@ -42,7 +42,10 @@ void main() {
container.registerSingleton<Logger>(ConsoleLogger());
// Register contextual binding
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
container
.when(LoggerClient)
.needs<Logger>()
.giveFactory((c) => FileLogger('test.log'));
// The default binding should be used here
var logger = container.make<Logger>();
@ -56,8 +59,14 @@ void main() {
test('multiple contextual bindings work independently', () {
container.registerSingleton<Logger>(ConsoleLogger());
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
container.when(SpecialLoggerClient).needs<Logger>().give<ConsoleLogger>();
container
.when(LoggerClient)
.needs<Logger>()
.giveFactory((c) => FileLogger('test.log'));
container
.when(SpecialLoggerClient)
.needs<Logger>()
.giveFactory((c) => ConsoleLogger());
var client1 = container.make<LoggerClient>();
var client2 = container.make<SpecialLoggerClient>();
@ -80,7 +89,10 @@ void main() {
});
test('contextual binding throws when implementation not found', () {
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
container
.when(LoggerClient)
.needs<Logger>()
.giveFactory((c) => FileLogger('test.log'));
expect(
() => container.make<LoggerClient>(),
@ -90,7 +102,10 @@ void main() {
test('contextual bindings are inherited by child containers', () {
container.registerSingleton<Logger>(ConsoleLogger());
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
container
.when(LoggerClient)
.needs<Logger>()
.giveFactory((c) => FileLogger('test.log'));
var childContainer = container.createChild();
var client = childContainer.make<LoggerClient>();
@ -100,7 +115,10 @@ void main() {
test('child container can override parent contextual binding', () {
container.registerSingleton<Logger>(ConsoleLogger());
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
container
.when(LoggerClient)
.needs<Logger>()
.giveFactory((c) => FileLogger('test.log'));
var childContainer = container.createChild();
childContainer