mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 04:04:01 +01:00
Synchronize DependencyContainer
The implication is that dependency containers are now always shared data. In the sense of application context, this is acceptable.
This commit is contained in:
parent
8d268846ed
commit
9860624148
|
@ -79,7 +79,7 @@ private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAd
|
||||||
*
|
*
|
||||||
* See_Also: Autowire
|
* See_Also: Autowire
|
||||||
*/
|
*/
|
||||||
public void autowire(Type)(DependencyContainer container, Type instance) {
|
public void autowire(Type)(shared(DependencyContainer) container, Type instance) {
|
||||||
debug(poodinisVerbose) {
|
debug(poodinisVerbose) {
|
||||||
printDebugAutowiredInstance(typeid(Type), &instance);
|
printDebugAutowiredInstance(typeid(Type), &instance);
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType, void*
|
||||||
writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType, candidateInstanceAddress, instanceType, instanceAddress, member));
|
writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType, candidateInstanceAddress, instanceType, instanceAddress, member));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void autowireMember(string member, Type)(DependencyContainer container, Type instance) {
|
private void autowireMember(string member, Type)(shared(DependencyContainer) container, Type instance) {
|
||||||
static if(__traits(compiles, __traits(getMember, instance, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, instance, member)))) {
|
static if(__traits(compiles, __traits(getMember, instance, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, instance, member)))) {
|
||||||
foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, instance, member))) {
|
foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, instance, member))) {
|
||||||
static if (__traits(isSame, autowireAttribute, Autowire) || is(autowireAttribute == Autowire!T, T)) {
|
static if (__traits(isSame, autowireAttribute, Autowire) || is(autowireAttribute == Autowire!T, T)) {
|
||||||
|
@ -138,9 +138,9 @@ public void globalAutowire(Type)(Type instance) {
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutowiredRegistration(RegistrationType : Object) : Registration {
|
class AutowiredRegistration(RegistrationType : Object) : Registration {
|
||||||
private DependencyContainer container;
|
private shared(DependencyContainer) container;
|
||||||
|
|
||||||
public this(TypeInfo registeredType, DependencyContainer container) {
|
public this(TypeInfo registeredType, shared(DependencyContainer) container) {
|
||||||
enforce(!(container is null), "Argument 'container' is null. Autowired registrations need to autowire using a container.");
|
enforce(!(container is null), "Argument 'container' is null. Autowired registrations need to autowire using a container.");
|
||||||
this.container = container;
|
this.container = container;
|
||||||
super(registeredType, typeid(RegistrationType));
|
super(registeredType, typeid(RegistrationType));
|
||||||
|
|
|
@ -44,7 +44,7 @@ class ResolveException : Exception {
|
||||||
* In most cases you want to use a global singleton dependency container provided by getInstance() to manage all dependencies.
|
* In most cases you want to use a global singleton dependency container provided by getInstance() to manage all dependencies.
|
||||||
* You can still create new instances of this class for exceptional situations.
|
* You can still create new instances of this class for exceptional situations.
|
||||||
*/
|
*/
|
||||||
class DependencyContainer {
|
synchronized class DependencyContainer {
|
||||||
private Registration[][TypeInfo] registrations;
|
private Registration[][TypeInfo] registrations;
|
||||||
|
|
||||||
private Registration[] autowireStack;
|
private Registration[] autowireStack;
|
||||||
|
@ -111,16 +111,16 @@ class DependencyContainer {
|
||||||
return existingRegistration;
|
return existingRegistration;
|
||||||
}
|
}
|
||||||
|
|
||||||
AutowiredRegistration!ConcreteType newRegistration = new AutowiredRegistration!ConcreteType(registeredType, this);
|
auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, this);
|
||||||
newRegistration.singleInstance();
|
newRegistration.singleInstance();
|
||||||
registrations[registeredType] ~= newRegistration;
|
registrations[registeredType] ~= cast(shared(Registration)) newRegistration;
|
||||||
return newRegistration;
|
return newRegistration;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType) {
|
private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType) {
|
||||||
auto existingCandidates = registrationType in registrations;
|
auto existingCandidates = registrationType in registrations;
|
||||||
if (existingCandidates) {
|
if (existingCandidates) {
|
||||||
return getRegistration(*existingCandidates, qualifierType);
|
return getRegistration(cast(Registration[]) *existingCandidates, qualifierType);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
|
@ -218,13 +218,13 @@ class DependencyContainer {
|
||||||
throw new ResolveException("Type not registered.", resolveType);
|
throw new ResolveException("Type not registered.", resolveType);
|
||||||
}
|
}
|
||||||
|
|
||||||
Registration registration = getQualifiedRegistration(resolveType, qualifierType, *candidates);
|
Registration registration = getQualifiedRegistration(resolveType, qualifierType, cast(Registration[]) *candidates);
|
||||||
QualifierType instance;
|
QualifierType instance;
|
||||||
|
|
||||||
if (!autowireStack.canFind(registration)) {
|
if (!(cast(Registration[]) autowireStack).canFind(registration)) {
|
||||||
autowireStack ~= registration;
|
autowireStack ~= cast(shared(Registration)) registration;
|
||||||
instance = cast(QualifierType) registration.getInstance(new AutowireInstantiationContext());
|
instance = cast(QualifierType) registration.getInstance(new AutowireInstantiationContext());
|
||||||
autowireStack.popBack();
|
autowireStack = autowireStack[0 .. $-1];
|
||||||
} else {
|
} else {
|
||||||
auto autowireContext = new AutowireInstantiationContext();
|
auto autowireContext = new AutowireInstantiationContext();
|
||||||
autowireContext.autowireInstance = false;
|
autowireContext.autowireInstance = false;
|
||||||
|
|
|
@ -1,164 +1,164 @@
|
||||||
/**
|
/**
|
||||||
* Poodinis Dependency Injection Framework
|
* Poodinis Dependency Injection Framework
|
||||||
* Copyright 2014-2015 Mike Bierlee
|
* Copyright 2014-2015 Mike Bierlee
|
||||||
* This software is licensed under the terms of the MIT license.
|
* This software is licensed under the terms of the MIT license.
|
||||||
* The full terms of the license can be found in the LICENSE file.
|
* The full terms of the license can be found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import poodinis.autowire;
|
import poodinis.autowire;
|
||||||
|
|
||||||
import std.exception;
|
import std.exception;
|
||||||
|
|
||||||
version(unittest) {
|
version(unittest) {
|
||||||
class ComponentA {}
|
class ComponentA {}
|
||||||
|
|
||||||
class ComponentB {
|
class ComponentB {
|
||||||
public @Autowire ComponentA componentA;
|
public @Autowire ComponentA componentA;
|
||||||
|
|
||||||
public bool componentIsNull() {
|
public bool componentIsNull() {
|
||||||
return componentA is null;
|
return componentA is null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InterfaceA {}
|
interface InterfaceA {}
|
||||||
|
|
||||||
class ComponentC : InterfaceA {}
|
class ComponentC : InterfaceA {}
|
||||||
|
|
||||||
class ComponentD {
|
class ComponentD {
|
||||||
public @Autowire InterfaceA componentC = null;
|
public @Autowire InterfaceA componentC = null;
|
||||||
|
|
||||||
public bool componentIsNull() {
|
public bool componentIsNull() {
|
||||||
return componentC is null;
|
return componentC is null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DummyAttribute{};
|
class DummyAttribute{};
|
||||||
|
|
||||||
class ComponentE {
|
class ComponentE {
|
||||||
@DummyAttribute
|
@DummyAttribute
|
||||||
public ComponentC componentC;
|
public ComponentC componentC;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComponentDeclarationCocktail {
|
class ComponentDeclarationCocktail {
|
||||||
alias noomer = int;
|
alias noomer = int;
|
||||||
|
|
||||||
@Autowire
|
@Autowire
|
||||||
public ComponentA componentA;
|
public ComponentA componentA;
|
||||||
|
|
||||||
public void doesNothing() {
|
public void doesNothing() {
|
||||||
}
|
}
|
||||||
|
|
||||||
~this(){
|
~this(){
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComponentX : InterfaceA {}
|
class ComponentX : InterfaceA {}
|
||||||
|
|
||||||
class MonkeyShine {
|
class MonkeyShine {
|
||||||
@Autowire!ComponentX
|
@Autowire!ComponentX
|
||||||
public InterfaceA component;
|
public InterfaceA component;
|
||||||
}
|
}
|
||||||
|
|
||||||
class BootstrapBootstrap {
|
class BootstrapBootstrap {
|
||||||
@Autowire!ComponentX
|
@Autowire!ComponentX
|
||||||
public InterfaceA componentX;
|
public InterfaceA componentX;
|
||||||
|
|
||||||
@Autowire!ComponentC
|
@Autowire!ComponentC
|
||||||
public InterfaceA componentC;
|
public InterfaceA componentC;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring concrete type to existing instance
|
// Test autowiring concrete type to existing instance
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!ComponentA;
|
container.register!ComponentA;
|
||||||
auto componentB = new ComponentB();
|
auto componentB = new ComponentB();
|
||||||
container.autowire!(ComponentB)(componentB);
|
container.autowire!(ComponentB)(componentB);
|
||||||
assert(!componentB.componentIsNull(), "Autowirable dependency failed to autowire");
|
assert(!componentB.componentIsNull(), "Autowirable dependency failed to autowire");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring interface type to existing instance
|
// Test autowiring interface type to existing instance
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(InterfaceA, ComponentC);
|
container.register!(InterfaceA, ComponentC);
|
||||||
auto componentD = new ComponentD();
|
auto componentD = new ComponentD();
|
||||||
container.autowire!(ComponentD)(componentD);
|
container.autowire!(ComponentD)(componentD);
|
||||||
assert(!componentD.componentIsNull(), "Autowirable dependency failed to autowire");
|
assert(!componentD.componentIsNull(), "Autowirable dependency failed to autowire");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring will only happen once
|
// Test autowiring will only happen once
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(InterfaceA, ComponentC).newInstance();
|
container.register!(InterfaceA, ComponentC).newInstance();
|
||||||
auto componentD = new ComponentD();
|
auto componentD = new ComponentD();
|
||||||
container.autowire!(ComponentD)(componentD);
|
container.autowire!(ComponentD)(componentD);
|
||||||
auto expectedComponent = componentD.componentC;
|
auto expectedComponent = componentD.componentC;
|
||||||
container.autowire!(ComponentD)(componentD);
|
container.autowire!(ComponentD)(componentD);
|
||||||
auto actualComponent = componentD.componentC;
|
auto actualComponent = componentD.componentC;
|
||||||
assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance");
|
assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring unregistered type
|
// Test autowiring unregistered type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
auto componentD = new ComponentD();
|
auto componentD = new ComponentD();
|
||||||
assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException");
|
assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring member with non-autowire attribute does not autowire
|
// Test autowiring member with non-autowire attribute does not autowire
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
auto componentE = new ComponentE();
|
auto componentE = new ComponentE();
|
||||||
container.autowire!ComponentE(componentE);
|
container.autowire!ComponentE(componentE);
|
||||||
assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire");
|
assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowire class with alias declaration
|
// Test autowire class with alias declaration
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!ComponentA;
|
container.register!ComponentA;
|
||||||
auto componentDeclarationCocktail = new ComponentDeclarationCocktail();
|
auto componentDeclarationCocktail = new ComponentDeclarationCocktail();
|
||||||
|
|
||||||
container.autowire(componentDeclarationCocktail);
|
container.autowire(componentDeclarationCocktail);
|
||||||
|
|
||||||
assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed");
|
assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowire class with qualifier
|
// Test autowire class with qualifier
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(InterfaceA, ComponentC);
|
container.register!(InterfaceA, ComponentC);
|
||||||
container.register!(InterfaceA, ComponentX);
|
container.register!(InterfaceA, ComponentX);
|
||||||
auto componentX = container.resolve!(InterfaceA, ComponentX);
|
auto componentX = container.resolve!(InterfaceA, ComponentX);
|
||||||
|
|
||||||
auto monkeyShine = new MonkeyShine();
|
auto monkeyShine = new MonkeyShine();
|
||||||
container.autowire(monkeyShine);
|
container.autowire(monkeyShine);
|
||||||
|
|
||||||
assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed");
|
assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowire class with multiple qualifiers
|
// Test autowire class with multiple qualifiers
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(InterfaceA, ComponentC);
|
container.register!(InterfaceA, ComponentC);
|
||||||
container.register!(InterfaceA, ComponentX);
|
container.register!(InterfaceA, ComponentX);
|
||||||
auto componentC = container.resolve!(InterfaceA, ComponentC);
|
auto componentC = container.resolve!(InterfaceA, ComponentC);
|
||||||
auto componentX = container.resolve!(InterfaceA, ComponentX);
|
auto componentX = container.resolve!(InterfaceA, ComponentX);
|
||||||
|
|
||||||
auto bootstrapBootstrap = new BootstrapBootstrap();
|
auto bootstrapBootstrap = new BootstrapBootstrap();
|
||||||
container.autowire(bootstrapBootstrap);
|
container.autowire(bootstrapBootstrap);
|
||||||
|
|
||||||
assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed");
|
assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed");
|
||||||
assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed");
|
assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test getting instance from autowired registration will autowire instance
|
// Test getting instance from autowired registration will autowire instance
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!ComponentA;
|
container.register!ComponentA;
|
||||||
|
|
||||||
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance();
|
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance();
|
||||||
auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext());
|
auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext());
|
||||||
|
|
||||||
assert(!instance.componentIsNull());
|
assert(!instance.componentIsNull());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,372 +1,372 @@
|
||||||
/**
|
/**
|
||||||
* Poodinis Dependency Injection Framework
|
* Poodinis Dependency Injection Framework
|
||||||
* Copyright 2014-2015 Mike Bierlee
|
* Copyright 2014-2015 Mike Bierlee
|
||||||
* This software is licensed under the terms of the MIT license.
|
* This software is licensed under the terms of the MIT license.
|
||||||
* The full terms of the license can be found in the LICENSE file.
|
* The full terms of the license can be found in the LICENSE file.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import poodinis.container;
|
import poodinis.container;
|
||||||
|
|
||||||
import std.exception;
|
import std.exception;
|
||||||
|
|
||||||
version(unittest) {
|
version(unittest) {
|
||||||
interface TestInterface {
|
interface TestInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestClass : TestInterface {
|
class TestClass : TestInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
class UnrelatedClass{
|
class UnrelatedClass{
|
||||||
}
|
}
|
||||||
|
|
||||||
class FailOnCreationClass {
|
class FailOnCreationClass {
|
||||||
this() {
|
this() {
|
||||||
throw new Exception("This class should not be instantiated");
|
throw new Exception("This class should not be instantiated");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class AutowiredClass {
|
class AutowiredClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComponentClass {
|
class ComponentClass {
|
||||||
@Autowire
|
@Autowire
|
||||||
public AutowiredClass autowiredClass;
|
public AutowiredClass autowiredClass;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComponentCat {
|
class ComponentCat {
|
||||||
@Autowire
|
@Autowire
|
||||||
public ComponentMouse mouse;
|
public ComponentMouse mouse;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ComponentMouse {
|
class ComponentMouse {
|
||||||
@Autowire
|
@Autowire
|
||||||
public ComponentCat cat;
|
public ComponentCat cat;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Eenie {
|
class Eenie {
|
||||||
@Autowire
|
@Autowire
|
||||||
public Meenie meenie;
|
public Meenie meenie;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Meenie {
|
class Meenie {
|
||||||
@Autowire
|
@Autowire
|
||||||
public Moe moe;
|
public Moe moe;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Moe {
|
class Moe {
|
||||||
@Autowire
|
@Autowire
|
||||||
public Eenie eenie;
|
public Eenie eenie;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Ittie {
|
class Ittie {
|
||||||
@Autowire
|
@Autowire
|
||||||
public Bittie bittie;
|
public Bittie bittie;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Bittie {
|
class Bittie {
|
||||||
@Autowire
|
@Autowire
|
||||||
public Banana banana;
|
public Banana banana;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Banana {
|
class Banana {
|
||||||
@Autowire
|
@Autowire
|
||||||
public Bittie bittie;
|
public Bittie bittie;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SuperInterface {
|
interface SuperInterface {
|
||||||
}
|
}
|
||||||
|
|
||||||
class SuperImplementation : SuperInterface {
|
class SuperImplementation : SuperInterface {
|
||||||
@Autowire
|
@Autowire
|
||||||
public Banana banana;
|
public Banana banana;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Color {
|
interface Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Blue : Color {
|
class Blue : Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Red : Color {
|
class Red : Color {
|
||||||
}
|
}
|
||||||
|
|
||||||
class Spiders {
|
class Spiders {
|
||||||
@Autowire
|
@Autowire
|
||||||
public TestInterface testMember;
|
public TestInterface testMember;
|
||||||
}
|
}
|
||||||
|
|
||||||
class Recursive {
|
class Recursive {
|
||||||
@Autowire
|
@Autowire
|
||||||
public Recursive recursive;
|
public Recursive recursive;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test register concrete type
|
// Test register concrete type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
auto registration = container.register!(TestClass)();
|
auto registration = container.register!(TestClass)();
|
||||||
assert(registration.registeredType == typeid(TestClass), "Type of registered type not the same");
|
assert(registration.registeredType == typeid(TestClass), "Type of registered type not the same");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test resolve registered type
|
// Test resolve registered type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(TestClass)();
|
container.register!(TestClass)();
|
||||||
TestClass actualInstance = container.resolve!(TestClass)();
|
TestClass actualInstance = container.resolve!(TestClass)();
|
||||||
assert(actualInstance !is null, "Resolved type is null");
|
assert(actualInstance !is null, "Resolved type is null");
|
||||||
assert(cast(TestClass) actualInstance, "Resolved class is not the same type as expected");
|
assert(cast(TestClass) actualInstance, "Resolved class is not the same type as expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test register interface
|
// Test register interface
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(TestInterface, TestClass)();
|
container.register!(TestInterface, TestClass)();
|
||||||
TestInterface actualInstance = container.resolve!(TestInterface)();
|
TestInterface actualInstance = container.resolve!(TestInterface)();
|
||||||
assert(actualInstance !is null, "Resolved type is null");
|
assert(actualInstance !is null, "Resolved type is null");
|
||||||
assert(cast(TestInterface) actualInstance, "Resolved class is not the same type as expected");
|
assert(cast(TestInterface) actualInstance, "Resolved class is not the same type as expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test resolve non-registered type
|
// Test resolve non-registered type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving non-registered type does not fail");
|
assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving non-registered type does not fail");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test clear registrations
|
// Test clear registrations
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(TestClass)();
|
container.register!(TestClass)();
|
||||||
container.clearAllRegistrations();
|
container.clearAllRegistrations();
|
||||||
assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving cleared type does not fail");
|
assertThrown!ResolveException(container.resolve!(TestClass)(), "Resolving cleared type does not fail");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test get singleton of container
|
// Test get singleton of container
|
||||||
unittest {
|
unittest {
|
||||||
auto instance1 = DependencyContainer.getInstance();
|
auto instance1 = DependencyContainer.getInstance();
|
||||||
auto instance2 = DependencyContainer.getInstance();
|
auto instance2 = DependencyContainer.getInstance();
|
||||||
assert(instance1 is instance2, "getInstance does not return the same instance");
|
assert(instance1 is instance2, "getInstance does not return the same instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test resolve single instance for type
|
// Test resolve single instance for type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(TestClass)().singleInstance();
|
container.register!(TestClass)().singleInstance();
|
||||||
auto instance1 = container.resolve!(TestClass);
|
auto instance1 = container.resolve!(TestClass);
|
||||||
auto instance2 = container.resolve!(TestClass);
|
auto instance2 = container.resolve!(TestClass);
|
||||||
assert(instance1 is instance2, "Resolved instance from single instance scope is not the each time it is resolved");
|
assert(instance1 is instance2, "Resolved instance from single instance scope is not the each time it is resolved");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test resolve new instance for type
|
// Test resolve new instance for type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(TestClass)().newInstance();
|
container.register!(TestClass)().newInstance();
|
||||||
auto instance1 = container.resolve!(TestClass);
|
auto instance1 = container.resolve!(TestClass);
|
||||||
auto instance2 = container.resolve!(TestClass);
|
auto instance2 = container.resolve!(TestClass);
|
||||||
assert(instance1 !is instance2, "Resolved instance from new instance scope is the same each time it is resolved");
|
assert(instance1 !is instance2, "Resolved instance from new instance scope is the same each time it is resolved");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test resolve existing instance for type
|
// Test resolve existing instance for type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
auto expectedInstance = new TestClass();
|
auto expectedInstance = new TestClass();
|
||||||
container.register!(TestClass)().existingInstance(expectedInstance);
|
container.register!(TestClass)().existingInstance(expectedInstance);
|
||||||
auto actualInstance = container.resolve!(TestClass);
|
auto actualInstance = container.resolve!(TestClass);
|
||||||
assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance");
|
assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowire resolved instances
|
// Test autowire resolved instances
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!AutowiredClass;
|
container.register!AutowiredClass;
|
||||||
container.register!ComponentClass;
|
container.register!ComponentClass;
|
||||||
auto componentInstance = container.resolve!ComponentClass;
|
auto componentInstance = container.resolve!ComponentClass;
|
||||||
auto autowiredInstance = container.resolve!AutowiredClass;
|
auto autowiredInstance = container.resolve!AutowiredClass;
|
||||||
assert(componentInstance.autowiredClass is autowiredInstance, "Member is not autowired upon resolving");
|
assert(componentInstance.autowiredClass is autowiredInstance, "Member is not autowired upon resolving");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test circular autowiring
|
// Test circular autowiring
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!ComponentMouse;
|
container.register!ComponentMouse;
|
||||||
container.register!ComponentCat;
|
container.register!ComponentCat;
|
||||||
auto mouse = container.resolve!ComponentMouse;
|
auto mouse = container.resolve!ComponentMouse;
|
||||||
auto cat = container.resolve!ComponentCat;
|
auto cat = container.resolve!ComponentCat;
|
||||||
assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat, "Circular dependencies should be autowirable");
|
assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat, "Circular dependencies should be autowirable");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test remove registration
|
// Test remove registration
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!TestClass;
|
container.register!TestClass;
|
||||||
container.removeRegistration!TestClass;
|
container.removeRegistration!TestClass;
|
||||||
assertThrown!ResolveException(container.resolve!TestClass);
|
assertThrown!ResolveException(container.resolve!TestClass);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring does not autowire member where instance is non-null
|
// Test autowiring does not autowire member where instance is non-null
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
auto existingA = new AutowiredClass();
|
auto existingA = new AutowiredClass();
|
||||||
auto existingB = new ComponentClass();
|
auto existingB = new ComponentClass();
|
||||||
existingB.autowiredClass = existingA;
|
existingB.autowiredClass = existingA;
|
||||||
|
|
||||||
container.register!AutowiredClass;
|
container.register!AutowiredClass;
|
||||||
container.register!(ComponentClass).existingInstance(existingB);
|
container.register!(ComponentClass).existingInstance(existingB);
|
||||||
auto resolvedA = container.resolve!AutowiredClass;
|
auto resolvedA = container.resolve!AutowiredClass;
|
||||||
auto resolvedB = container.resolve!ComponentClass;
|
auto resolvedB = container.resolve!ComponentClass;
|
||||||
|
|
||||||
assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA, "Autowiring shouldn't rewire member when it is already wired to an instance");
|
assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA, "Autowiring shouldn't rewire member when it is already wired to an instance");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring circular dependency by third-degree
|
// Test autowiring circular dependency by third-degree
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!Eenie;
|
container.register!Eenie;
|
||||||
container.register!Meenie;
|
container.register!Meenie;
|
||||||
container.register!Moe;
|
container.register!Moe;
|
||||||
|
|
||||||
auto eenie = container.resolve!Eenie;
|
auto eenie = container.resolve!Eenie;
|
||||||
|
|
||||||
assert(eenie.meenie.moe.eenie is eenie, "Autowiring third-degree circular dependency failed");
|
assert(eenie.meenie.moe.eenie is eenie, "Autowiring third-degree circular dependency failed");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring deep circular dependencies
|
// Test autowiring deep circular dependencies
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!Ittie;
|
container.register!Ittie;
|
||||||
container.register!Bittie;
|
container.register!Bittie;
|
||||||
container.register!Banana;
|
container.register!Banana;
|
||||||
|
|
||||||
auto ittie = container.resolve!Ittie;
|
auto ittie = container.resolve!Ittie;
|
||||||
|
|
||||||
assert(ittie.bittie is ittie.bittie.banana.bittie, "Autowiring deep dependencies failed.");
|
assert(ittie.bittie is ittie.bittie.banana.bittie, "Autowiring deep dependencies failed.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time
|
// Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(Ittie).newInstance();
|
container.register!(Ittie).newInstance();
|
||||||
container.register!(Bittie).newInstance();
|
container.register!(Bittie).newInstance();
|
||||||
container.register!(Banana).newInstance();
|
container.register!(Banana).newInstance();
|
||||||
|
|
||||||
auto ittie = container.resolve!Ittie;
|
auto ittie = container.resolve!Ittie;
|
||||||
|
|
||||||
assert(ittie.bittie.banana.bittie.banana is null, "Autowiring deep dependencies with newInstance scope autowired a reoccuring type.");
|
assert(ittie.bittie.banana.bittie.banana is null, "Autowiring deep dependencies with newInstance scope autowired a reoccuring type.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowiring type registered by interface
|
// Test autowiring type registered by interface
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!Banana;
|
container.register!Banana;
|
||||||
container.register!Bittie;
|
container.register!Bittie;
|
||||||
container.register!(SuperInterface, SuperImplementation);
|
container.register!(SuperInterface, SuperImplementation);
|
||||||
|
|
||||||
SuperImplementation superInstance = cast(SuperImplementation) container.resolve!SuperInterface;
|
SuperImplementation superInstance = cast(SuperImplementation) container.resolve!SuperInterface;
|
||||||
|
|
||||||
assert(!(superInstance.banana is null), "Instance which was resolved by interface type was not autowired.");
|
assert(!(superInstance.banana is null), "Instance which was resolved by interface type was not autowired.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test reusing a container after clearing all registrations
|
// Test reusing a container after clearing all registrations
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!Banana;
|
container.register!Banana;
|
||||||
container.clearAllRegistrations();
|
container.clearAllRegistrations();
|
||||||
try {
|
try {
|
||||||
container.resolve!Banana;
|
container.resolve!Banana;
|
||||||
} catch (ResolveException e) {
|
} catch (ResolveException e) {
|
||||||
container.register!Banana;
|
container.register!Banana;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test register multiple concrete classess to same interface type
|
// Test register multiple concrete classess to same interface type
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(Color, Blue);
|
container.register!(Color, Blue);
|
||||||
container.register!(Color, Red);
|
container.register!(Color, Red);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test removing all registrations for type with multiple registrations.
|
// Test removing all registrations for type with multiple registrations.
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(Color, Blue);
|
container.register!(Color, Blue);
|
||||||
container.register!(Color, Red);
|
container.register!(Color, Red);
|
||||||
container.removeRegistration!Color;
|
container.removeRegistration!Color;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test registering same registration again
|
// Test registering same registration again
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
auto firstRegistration = container.register!(Color, Blue);
|
auto firstRegistration = container.register!(Color, Blue);
|
||||||
auto secondRegistration = container.register!(Color, Blue);
|
auto secondRegistration = container.register!(Color, Blue);
|
||||||
|
|
||||||
assert(firstRegistration is secondRegistration, "First registration is not the same as the second of equal types");
|
assert(firstRegistration is secondRegistration, "First registration is not the same as the second of equal types");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test resolve registration with multiple qualifiers
|
// Test resolve registration with multiple qualifiers
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(Color, Blue);
|
container.register!(Color, Blue);
|
||||||
container.register!(Color, Red);
|
container.register!(Color, Red);
|
||||||
try {
|
try {
|
||||||
container.resolve!Color;
|
container.resolve!Color;
|
||||||
} catch (ResolveException e) {
|
} catch (ResolveException e) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
assert(false);
|
assert(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test resolve registration with multiple qualifiers using a qualifier
|
// Test resolve registration with multiple qualifiers using a qualifier
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!(Color, Blue);
|
container.register!(Color, Blue);
|
||||||
container.register!(Color, Red);
|
container.register!(Color, Red);
|
||||||
auto blueInstance = container.resolve!(Color, Blue);
|
auto blueInstance = container.resolve!(Color, Blue);
|
||||||
auto redInstance = container.resolve!(Color, Red);
|
auto redInstance = container.resolve!(Color, Red);
|
||||||
|
|
||||||
assert(blueInstance !is redInstance, "Resolving type with multiple, different registrations yielded the same instance");
|
assert(blueInstance !is redInstance, "Resolving type with multiple, different registrations yielded the same instance");
|
||||||
assert(blueInstance !is null, "Resolved blue instance to null");
|
assert(blueInstance !is null, "Resolved blue instance to null");
|
||||||
assert(redInstance !is null, "Resolved red instance to null");
|
assert(redInstance !is null, "Resolved red instance to null");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test autowire of unqualified member typed by interface.
|
// Test autowire of unqualified member typed by interface.
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!Spiders;
|
container.register!Spiders;
|
||||||
container.register!(TestInterface, TestClass);
|
container.register!(TestInterface, TestClass);
|
||||||
|
|
||||||
auto instance = container.resolve!Spiders;
|
auto instance = container.resolve!Spiders;
|
||||||
|
|
||||||
assert(!(instance is null), "Container failed to autowire member by interface");
|
assert(!(instance is null), "Container failed to autowire member by interface");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register existing registration
|
// Register existing registration
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
|
|
||||||
auto firstRegistration = container.register!TestClass;
|
auto firstRegistration = container.register!TestClass;
|
||||||
auto secondRegistration = container.register!TestClass;
|
auto secondRegistration = container.register!TestClass;
|
||||||
|
|
||||||
assert(firstRegistration is secondRegistration, "Registering the same registration twice registers the dependencies twice.");
|
assert(firstRegistration is secondRegistration, "Registering the same registration twice registers the dependencies twice.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Register existing registration by supertype
|
// Register existing registration by supertype
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
|
|
||||||
auto firstRegistration = container.register!(TestInterface, TestClass);
|
auto firstRegistration = container.register!(TestInterface, TestClass);
|
||||||
auto secondRegistration = container.register!(TestInterface, TestClass);
|
auto secondRegistration = container.register!(TestInterface, TestClass);
|
||||||
|
|
||||||
assert(firstRegistration is secondRegistration, "Registering the same registration by super type twice registers the dependencies twice.");
|
assert(firstRegistration is secondRegistration, "Registering the same registration by super type twice registers the dependencies twice.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Resolve dependency depending on itself
|
// Resolve dependency depending on itself
|
||||||
unittest {
|
unittest {
|
||||||
auto container = new DependencyContainer();
|
shared(DependencyContainer) container = new DependencyContainer();
|
||||||
container.register!Recursive;
|
container.register!Recursive;
|
||||||
|
|
||||||
auto instance = container.resolve!Recursive;
|
auto instance = container.resolve!Recursive;
|
||||||
|
|
||||||
assert(instance.recursive is instance, "Resolving dependency that depends on itself fails.");
|
assert(instance.recursive is instance, "Resolving dependency that depends on itself fails.");
|
||||||
assert(instance.recursive.recursive is instance, "Resolving dependency that depends on itself fails.");
|
assert(instance.recursive.recursive is instance, "Resolving dependency that depends on itself fails.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue