From 4faf7f2e47035070e48f185a158b6b564ca44226 Mon Sep 17 00:00:00 2001 From: Mike Bierlee Date: Tue, 24 Aug 2021 23:17:26 +0300 Subject: [PATCH] Fix chained registration scopes getting rid of initializedBy's factory method --- source/poodinis/container.d | 5 ++- source/poodinis/registration.d | 62 +++++++++++++++++++-------- test/poodinis/autowiretest.d | 2 +- test/poodinis/registrationtest.d | 72 +++++++++++++++++++++++++++++--- 4 files changed, 114 insertions(+), 27 deletions(-) diff --git a/source/poodinis/container.d b/source/poodinis/container.d index fd549e7..dbb6a81 100644 --- a/source/poodinis/container.d +++ b/source/poodinis/container.d @@ -13,7 +13,8 @@ module poodinis.container; -import poodinis.registration : Registration, singleInstance, toConcreteTypeListString; +import poodinis.registration : Registration, singleInstance, + toConcreteTypeListString, initializeFactoryType; import poodinis.autowire : AutowiredRegistration, AutowireInstantiationContext; import poodinis.factory : ConstructorInjectingInstanceFactory; import poodinis.valueinjection : ValueInjectionException; @@ -202,7 +203,7 @@ synchronized class DependencyContainer auto instanceFactory = new ConstructorInjectingInstanceFactory!ConcreteType(this); auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, instanceFactory, this); - newRegistration.singleInstance(); + newRegistration.initializeFactoryType().singleInstance(); static if (!is(SuperType == ConcreteType)) { diff --git a/source/poodinis/registration.d b/source/poodinis/registration.d index 36c8f72..8323510 100644 --- a/source/poodinis/registration.d +++ b/source/poodinis/registration.d @@ -95,6 +95,29 @@ class Registration } } +private InstanceFactoryParameters copyFactoryParameters(Registration registration) +{ + return registration.instanceFactory.factoryParameters; +} + +private void setFactoryParameters(Registration registration, InstanceFactoryParameters newParameters) +{ + registration.instanceFactory.factoryParameters = newParameters; +} + +/** + * Sets the registration's instance factory type the same as the registration's. + * + * This is not a registration scope. Typically used by Poodinis internally only. + */ +public Registration initializeFactoryType(Registration registration) +{ + auto params = registration.copyFactoryParameters(); + params.instanceType = registration.instanceType; + registration.setFactoryParameters(params); + return registration; +} + /** * Scopes registrations to return the same instance every time a given registration is resolved. * @@ -102,8 +125,9 @@ class Registration */ public Registration singleInstance(Registration registration) { - registration.instanceFactory.factoryParameters = InstanceFactoryParameters( - registration.instanceType, CreatesSingleton.yes); + auto params = registration.copyFactoryParameters(); + params.createsSingleton = CreatesSingleton.yes; + registration.setFactoryParameters(params); return registration; } @@ -112,8 +136,10 @@ public Registration singleInstance(Registration registration) */ public Registration newInstance(Registration registration) { - registration.instanceFactory.factoryParameters = InstanceFactoryParameters( - registration.instanceType, CreatesSingleton.no); + auto params = registration.copyFactoryParameters(); + params.createsSingleton = CreatesSingleton.no; + params.existingInstance = null; + registration.setFactoryParameters(params); return registration; } @@ -122,22 +148,23 @@ public Registration newInstance(Registration registration) */ public Registration existingInstance(Registration registration, Object instance) { - registration.instanceFactory.factoryParameters = InstanceFactoryParameters( - registration.instanceType, CreatesSingleton.yes, instance); + auto params = registration.copyFactoryParameters(); + params.createsSingleton = CreatesSingleton.yes; + params.existingInstance = instance; + registration.setFactoryParameters(params); return registration; } /** * Scopes registrations to create new instances using the given initializer delegate. */ -public Registration initializedBy(T)(Registration registration, T delegate() initializer) - if(is(T == class) || is(T == interface)) +public Registration initializedBy(T)(Registration registration, T delegate() initializer) + if (is(T == class) || is(T == interface)) { - registration.instanceFactory.factoryParameters = InstanceFactoryParameters( - registration.instanceType, CreatesSingleton.no, null, { - return cast(Object) initializer(); - }); - + auto params = registration.copyFactoryParameters(); + params.createsSingleton = CreatesSingleton.no; + params.factoryMethod = () => cast(Object) initializer(); + registration.setFactoryParameters(params); return registration; } @@ -146,11 +173,10 @@ public Registration initializedBy(T)(Registration registration, T delegate() ini */ public Registration initializedOnceBy(T : Object)(Registration registration, T delegate() initializer) { - registration.instanceFactory.factoryParameters = InstanceFactoryParameters( - registration.instanceType, CreatesSingleton.yes, null, { - return cast(Object) initializer(); - }); - + auto params = registration.copyFactoryParameters(); + params.createsSingleton = CreatesSingleton.yes; + params.factoryMethod = () => cast(Object) initializer(); + registration.setFactoryParameters(params); return registration; } diff --git a/test/poodinis/autowiretest.d b/test/poodinis/autowiretest.d index ec2e070..a70253b 100644 --- a/test/poodinis/autowiretest.d +++ b/test/poodinis/autowiretest.d @@ -129,7 +129,7 @@ version (unittest) container.register!ComponentA; auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), - new InstanceFactory(), container).singleInstance(); + new InstanceFactory(), container).initializeFactoryType().singleInstance(); auto instance = cast(ComponentB) registration.getInstance( new AutowireInstantiationContext()); diff --git a/test/poodinis/registrationtest.d b/test/poodinis/registrationtest.d index bf8f095..7bdec0a 100644 --- a/test/poodinis/registrationtest.d +++ b/test/poodinis/registrationtest.d @@ -24,7 +24,7 @@ version (unittest) unittest { Registration registration = new Registration(null, typeid(TestType), - new InstanceFactory(), null); + new InstanceFactory(), null).initializeFactoryType(); auto chainedRegistration = registration.singleInstance(); auto instance1 = registration.getInstance(); auto instance2 = registration.getInstance(); @@ -38,7 +38,7 @@ version (unittest) unittest { Registration registration = new Registration(null, typeid(TestType), - new InstanceFactory(), null); + new InstanceFactory(), null).initializeFactoryType(); auto chainedRegistration = registration.newInstance(); auto instance1 = registration.getInstance(); auto instance2 = registration.getInstance(); @@ -55,7 +55,7 @@ version (unittest) auto expectedInstance = new TestType(); auto chainedRegistration = registration.existingInstance(expectedInstance); auto actualInstance = registration.getInstance(); - assert(expectedInstance is expectedInstance, + assert(expectedInstance is actualInstance, "Registration with existing instance scope did not return the same instance"); assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set"); @@ -65,10 +65,11 @@ version (unittest) unittest { Registration firstRegistration = new Registration(typeid(TestInterface), - typeid(TestImplementation), new InstanceFactory(), null).singleInstance(); + typeid(TestImplementation), new InstanceFactory(), null).initializeFactoryType() + .singleInstance(); Registration secondRegistration = new Registration(typeid(TestImplementation), - typeid(TestImplementation), new InstanceFactory(), null).singleInstance() - .linkTo(firstRegistration); + typeid(TestImplementation), new InstanceFactory(), null).initializeFactoryType() + .singleInstance().linkTo(firstRegistration); auto firstInstance = firstRegistration.getInstance(); auto secondInstance = secondRegistration.getInstance(); @@ -76,4 +77,63 @@ version (unittest) assert(firstInstance is secondInstance); } + // Test custom factory method via initializedBy + unittest + { + Registration registration = new Registration(typeid(TestInterface), + typeid(TestImplementation), new InstanceFactory(), null); + + registration.initializedBy({ + auto instance = new TestImplementation(); + instance.someContent = "createdbyinitializer"; + return instance; + }); + + TestImplementation instanceOne = cast(TestImplementation) registration.getInstance(); + TestImplementation instanceTwo = cast(TestImplementation) registration.getInstance(); + assert(instanceOne.someContent == "createdbyinitializer"); + assert(instanceTwo.someContent == "createdbyinitializer"); + assert(instanceOne !is instanceTwo); + } + + // Test custom factory method via initializedOnceBy + unittest + { + Registration registration = new Registration(typeid(TestInterface), + typeid(TestImplementation), new InstanceFactory(), null); + + registration.initializedOnceBy({ + auto instance = new TestImplementation(); + instance.someContent = "createdbyinitializer"; + return instance; + }); + + TestImplementation instanceOne = cast(TestImplementation) registration.getInstance(); + TestImplementation instanceTwo = cast(TestImplementation) registration.getInstance(); + assert(instanceOne.someContent == "createdbyinitializer"); + assert(instanceTwo.someContent == "createdbyinitializer"); + assert(instanceOne is instanceTwo); + } + + // Test chaining single/new instance scope to initializedBy will not overwrite the factory method. + unittest + { + Registration registration = new Registration(typeid(TestInterface), + typeid(TestImplementation), new InstanceFactory(), null); + + registration.initializedBy({ + auto instance = new TestImplementation(); + instance.someContent = "createdbyinitializer"; + return instance; + }); + + registration.singleInstance(); + + TestImplementation instanceOne = cast(TestImplementation) registration.getInstance(); + TestImplementation instanceTwo = cast(TestImplementation) registration.getInstance(); + assert(instanceOne.someContent == "createdbyinitializer"); + assert(instanceTwo.someContent == "createdbyinitializer"); + assert(instanceOne is instanceTwo); + } + }