From 1166d2811a7f6480a86aaeae3872723043760ef7 Mon Sep 17 00:00:00 2001 From: Mike Bierlee Date: Sat, 13 Dec 2014 22:06:11 +0100 Subject: [PATCH] Add @Qualifier UDA for qualifying members typed by supertype --- README.md | 8 ++++++ source/poodinis/autowire.d | 51 ++++++++++++++++++++++++++------- test/poodinis/autowiretest.d | 55 ++++++++++++++++++++++++++++++++---- 3 files changed, 98 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 8c58e46..648e400 100644 --- a/README.md +++ b/README.md @@ -130,6 +130,14 @@ container.register!(Color, Blue); container.register!(Color, Red); auto blueInstance = container.resolve!(Color, Blue); ``` +If you want to autowire a type registered to multiple concrete types, use the @Qualifier UDA: +```d +class BluePaint { + @Autowire + @Qualifier!Blue + public Color color; +} +``` If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a ResolveException is throw stating that there are multiple candidates for the type to be resolved. Known issues diff --git a/source/poodinis/autowire.d b/source/poodinis/autowire.d index f31541a..a5ace16 100644 --- a/source/poodinis/autowire.d +++ b/source/poodinis/autowire.d @@ -18,30 +18,61 @@ debug { class Autowire{}; +struct Qualifier(T) { + T qualifier; +} + alias Autowired = Autowire; public void autowire(Type)(DependencyContainer container, Type instance) { + // For the love of god, refactor this! + debug { - auto memberType = typeid(Type); + auto instanceType = typeid(Type); auto instanceAddress = &instance; - writeln(format("DEBUG: Autowiring members of [%s@%s]", memberType, instanceAddress)); + writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress)); } foreach (member ; __traits(allMembers, Type)) { - static if(__traits(compiles, __traits( getMember, Type, member )) && __traits(compiles, __traits(getAttributes, __traits(getMember, Type, member )))) { - foreach(attribute; __traits(getAttributes, __traits(getMember, Type, member))) { - static if (is(attribute : Autowire)) { + static if(__traits(compiles, __traits(getMember, Type, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, Type, member)))) { + foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, Type, member))) { + static if (is(autowireAttribute : Autowire)) { if (__traits(getMember, instance, member) is null) { alias TypeTuple!(__traits(getMember, instance, member)) memberReference; - auto autowirableInstance = container.resolve!(typeof(memberReference)); + alias MemberType = typeof(memberReference)[0]; debug { - auto autowirableType = typeid(typeof(memberReference[0])); - auto autowireableAddress = &autowirableInstance; - writeln(format("DEBUG: Autowire instance [%s@%s] to [%s@%s].%s", autowirableType, autowireableAddress, memberType, instanceAddress, member)); + string qualifiedInstanceTypeString = typeid(MemberType).toString; } - __traits(getMember, instance, member) = autowirableInstance; + MemberType qualifiedInstance; + auto resolvedThroughQualifier = false; + foreach (qualifierAttribute; __traits(getAttributes, __traits(getMember, Type, member))) { + static if (is(qualifierAttribute == Qualifier!T, T)) { + alias QualifierType = typeof(qualifierAttribute.qualifier); + qualifiedInstance = container.resolve!(typeof(memberReference), QualifierType); + + debug { + qualifiedInstanceTypeString = typeid(QualifierType).toString; + } + + resolvedThroughQualifier = true; + break; + } + } + + if (!resolvedThroughQualifier) { + qualifiedInstance = container.resolve!(typeof(memberReference)); + } + + __traits(getMember, instance, member) = qualifiedInstance; + + debug { + auto qualifiedInstanceAddress = &qualifiedInstance; + writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", qualifiedInstanceTypeString, qualifiedInstanceAddress, instanceType, instanceAddress, member)); + } } + + break; } } } diff --git a/test/poodinis/autowiretest.d b/test/poodinis/autowiretest.d index 2b9ce0c..4b1815f 100644 --- a/test/poodinis/autowiretest.d +++ b/test/poodinis/autowiretest.d @@ -10,8 +10,7 @@ import poodinis.autowire; import std.exception; version(unittest) { - class ComponentA { - } + class ComponentA {} class ComponentB { public @Autowire ComponentA componentA; @@ -21,11 +20,9 @@ version(unittest) { } } - interface InterfaceA { - } + interface InterfaceA {} - class ComponentC : InterfaceA { - } + class ComponentC : InterfaceA {} class ComponentD { public @Autowire InterfaceA componentC = null; @@ -62,6 +59,24 @@ version(unittest) { } } + class ComponentX : InterfaceA {} + + class MonkeyShine { + @Autowire + @Qualifier!ComponentX + public InterfaceA component; + } + + class BootstrapBootstrap { + @Autowire + @Qualifier!ComponentX + public InterfaceA componentX; + + @Autowire + @Qualifier!ComponentC + public InterfaceA componentC; + } + // Test autowiring concrete type to existing instance unittest { auto container = new DependencyContainer(); @@ -132,4 +147,32 @@ version(unittest) { assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed"); } + + // Test autowire class with qualifier + unittest { + auto container = new DependencyContainer(); + container.register!(InterfaceA, ComponentC); + container.register!(InterfaceA, ComponentX); + auto componentX = container.resolve!(InterfaceA, ComponentX); + + auto monkeyShine = new MonkeyShine(); + container.autowire(monkeyShine); + + assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed"); + } + + // Test autowire class with multiple qualifiers + unittest { + auto container = new DependencyContainer(); + container.register!(InterfaceA, ComponentC); + container.register!(InterfaceA, ComponentX); + auto componentC = container.resolve!(InterfaceA, ComponentC); + auto componentX = container.resolve!(InterfaceA, ComponentX); + + auto bootstrapBootstrap = new BootstrapBootstrap(); + container.autowire(bootstrapBootstrap); + + assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed"); + assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed"); + } } \ No newline at end of file