refactor: refactored laravel features into container test 3 84 pass
This commit is contained in:
parent
d690877afc
commit
f3b0fac943
3 changed files with 80 additions and 8 deletions
|
@ -268,6 +268,10 @@ class Container {
|
||||||
|
|
||||||
// Check for contextual binding
|
// Check for contextual binding
|
||||||
var contextualConcrete = _getContextualConcrete(t2);
|
var contextualConcrete = _getContextualConcrete(t2);
|
||||||
|
if (contextualConcrete == null && _hasContextualBinding(t2)) {
|
||||||
|
throw BindingResolutionException(
|
||||||
|
'No implementation was provided for contextual binding of $t2');
|
||||||
|
}
|
||||||
if (contextualConcrete != null) {
|
if (contextualConcrete != null) {
|
||||||
dynamic instance;
|
dynamic instance;
|
||||||
if (contextualConcrete is Function) {
|
if (contextualConcrete is Function) {
|
||||||
|
@ -382,6 +386,31 @@ class Container {
|
||||||
|
|
||||||
// Use reflection to create instance
|
// Use reflection to create instance
|
||||||
var reflectedType = reflector.reflectType(t2);
|
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 positional = [];
|
||||||
var named = <String, Object>{};
|
var named = <String, Object>{};
|
||||||
|
|
||||||
|
@ -730,6 +759,24 @@ class Container {
|
||||||
return null;
|
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.
|
/// Check if we're in danger of a circular dependency.
|
||||||
void _checkCircularDependency(Type type) {
|
void _checkCircularDependency(Type type) {
|
||||||
if (_buildStack.contains(type)) {
|
if (_buildStack.contains(type)) {
|
||||||
|
|
|
@ -45,12 +45,19 @@ class ContextualImplementationBuilder {
|
||||||
final Type abstract;
|
final Type abstract;
|
||||||
|
|
||||||
/// Creates a new contextual implementation builder
|
/// 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
|
/// Specify the implementation that should be used
|
||||||
void give<T>() {
|
void give<T>() {
|
||||||
for (var concreteType in concrete) {
|
for (var concreteType in concrete) {
|
||||||
container.addContextualBinding(concreteType, abstract, T);
|
container.addContextualBinding(
|
||||||
|
concreteType, abstract, (Container c) => c.make<T>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,10 @@ void main() {
|
||||||
container.registerSingleton<Logger>(ConsoleLogger());
|
container.registerSingleton<Logger>(ConsoleLogger());
|
||||||
|
|
||||||
// Register contextual binding
|
// 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
|
// The default binding should be used here
|
||||||
var logger = container.make<Logger>();
|
var logger = container.make<Logger>();
|
||||||
|
@ -56,8 +59,14 @@ void main() {
|
||||||
test('multiple contextual bindings work independently', () {
|
test('multiple contextual bindings work independently', () {
|
||||||
container.registerSingleton<Logger>(ConsoleLogger());
|
container.registerSingleton<Logger>(ConsoleLogger());
|
||||||
|
|
||||||
container.when(LoggerClient).needs<Logger>().give<FileLogger>();
|
container
|
||||||
container.when(SpecialLoggerClient).needs<Logger>().give<ConsoleLogger>();
|
.when(LoggerClient)
|
||||||
|
.needs<Logger>()
|
||||||
|
.giveFactory((c) => FileLogger('test.log'));
|
||||||
|
container
|
||||||
|
.when(SpecialLoggerClient)
|
||||||
|
.needs<Logger>()
|
||||||
|
.giveFactory((c) => ConsoleLogger());
|
||||||
|
|
||||||
var client1 = container.make<LoggerClient>();
|
var client1 = container.make<LoggerClient>();
|
||||||
var client2 = container.make<SpecialLoggerClient>();
|
var client2 = container.make<SpecialLoggerClient>();
|
||||||
|
@ -80,7 +89,10 @@ void main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
test('contextual binding throws when implementation not found', () {
|
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(
|
expect(
|
||||||
() => container.make<LoggerClient>(),
|
() => container.make<LoggerClient>(),
|
||||||
|
@ -90,7 +102,10 @@ void main() {
|
||||||
|
|
||||||
test('contextual bindings are inherited by child containers', () {
|
test('contextual bindings are inherited by child containers', () {
|
||||||
container.registerSingleton<Logger>(ConsoleLogger());
|
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 childContainer = container.createChild();
|
||||||
var client = childContainer.make<LoggerClient>();
|
var client = childContainer.make<LoggerClient>();
|
||||||
|
@ -100,7 +115,10 @@ void main() {
|
||||||
|
|
||||||
test('child container can override parent contextual binding', () {
|
test('child container can override parent contextual binding', () {
|
||||||
container.registerSingleton<Logger>(ConsoleLogger());
|
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 childContainer = container.createChild();
|
||||||
childContainer
|
childContainer
|
||||||
|
|
Loading…
Reference in a new issue