From b38bccc03c5caf6f56ecc7aa7c224a41c626a4ae Mon Sep 17 00:00:00 2001 From: Mike Bierlee Date: Wed, 9 Jul 2014 23:15:05 +0200 Subject: [PATCH] Fix autowiring classes with non-symbolic unassignable members (such as aliases) --- source/poodinis/autowire.d | 26 ++-- test/poodinis/autowiretest.d | 244 +++++++++++++++++++---------------- 2 files changed, 148 insertions(+), 122 deletions(-) diff --git a/source/poodinis/autowire.d b/source/poodinis/autowire.d index 9a929da..3e6fbc1 100644 --- a/source/poodinis/autowire.d +++ b/source/poodinis/autowire.d @@ -20,19 +20,21 @@ class Autowire{}; public void autowire(Type)(Container container, Type instance) { foreach (member ; __traits(allMembers, Type)) { - foreach (attribute; mixin(`__traits(getAttributes, Type.` ~ member ~ `)`) ) { - if (is(attribute : Autowire) && __traits(getMember, instance, member) is null){ - alias TypeTuple!(__traits(getMember, instance, member)) memberReference; - auto autowirableInstance = container.resolve!(typeof(memberReference)); - debug { - auto autowirableType = typeid(typeof(memberReference[0])); - auto autowireableAddress = &autowirableInstance; - auto memberType = typeid(Type); - auto instanceAddress = &instance; - writeln(format("DEBUG: Autowire instance [%s@%s] to [%s@%s].%s", autowirableType, autowireableAddress, memberType, instanceAddress, member)); + static if(__traits(compiles, __traits( getMember, Type, member )) && __traits(compiles, __traits(getAttributes, __traits(getMember, Type, member )))) { + foreach(attribute; __traits(getAttributes, __traits(getMember, Type, member))) { + if (is(attribute : Autowire) && __traits(getMember, instance, member) is null){ + alias TypeTuple!(__traits(getMember, instance, member)) memberReference; + auto autowirableInstance = container.resolve!(typeof(memberReference)); + debug { + auto autowirableType = typeid(typeof(memberReference[0])); + auto autowireableAddress = &autowirableInstance; + auto memberType = typeid(Type); + auto instanceAddress = &instance; + writeln(format("DEBUG: Autowire instance [%s@%s] to [%s@%s].%s", autowirableType, autowireableAddress, memberType, instanceAddress, member)); + } + + __traits(getMember, instance, member) = autowirableInstance; } - - __traits(getMember, instance, member) = autowirableInstance; } } } diff --git a/test/poodinis/autowiretest.d b/test/poodinis/autowiretest.d index 7aa896d..4b6497c 100644 --- a/test/poodinis/autowiretest.d +++ b/test/poodinis/autowiretest.d @@ -1,111 +1,135 @@ -/** - * Poodinis Dependency Injection Framework - * Copyright 2014 Mike Bierlee - * This software is licensed under the terms of the MIT license. - * The full terms of the license can be found in the LICENSE file. - */ - -import poodinis.autowire; - -import std.exception; - -version(unittest) { - class ComponentA { - } - - class ComponentB { - public @Autowire ComponentA componentA; - - public bool componentIsNull() { - return componentA is null; - } - } - - interface InterfaceA { - } - - class ComponentC : InterfaceA { - } - - class ComponentD { - public @Autowire InterfaceA componentC = null; - - public bool componentIsNull() { - return componentC is null; - } - } - - class DummyAttribute{}; - - class ComponentE { - @DummyAttribute - public ComponentC componentC; - } - - class ComponentF { - @Autowire - public ComponentA componentA; - - mixin AutowireConstructor; - } - - // Test autowiring concrete type to existing instance - unittest { - auto container = new Container(); - container.register!ComponentA; - auto componentB = new ComponentB(); - container.autowire!(ComponentB)(componentB); - assert(!componentB.componentIsNull(), "Autowirable dependency failed to autowire"); - } - - // Test autowiring interface type to existing instance - unittest { - auto container = new Container(); - container.register!(InterfaceA, ComponentC); - auto componentD = new ComponentD(); - container.autowire!(ComponentD)(componentD); - assert(!componentD.componentIsNull(), "Autowirable dependency failed to autowire"); - } - - // Test autowiring will only happen once - unittest { - auto container = new Container(); - container.register!(InterfaceA, ComponentC).newInstance(); - auto componentD = new ComponentD(); - container.autowire!(ComponentD)(componentD); - auto expectedComponent = componentD.componentC; - container.autowire!(ComponentD)(componentD); - auto actualComponent = componentD.componentC; - assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance"); - } - - // Test autowiring unregistered type - unittest { - auto container = new Container(); - auto componentD = new ComponentD(); - assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException"); - } - - // Test autowiring member with non-autowire attribute does not autowire - unittest { - auto container = new Container(); - auto componentE = new ComponentE(); - container.autowire!ComponentE(componentE); - assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire"); - } - - // Test autowire in constructor - unittest { - auto container = Container.getInstance(); - container.register!ComponentA; - auto componentF = new ComponentF(); - auto autowiredComponentA = componentF.componentA; - container.register!(ComponentF).existingInstance(componentF); - assert(componentF.componentA !is null, "Constructor did not autowire component F"); - - auto resolvedComponentF = container.resolve!ComponentF; - assert(resolvedComponentF.componentA is autowiredComponentA, "Resolving instance of ComponentF rewired members"); - - container.clearAllRegistrations(); - } +/** + * Poodinis Dependency Injection Framework + * Copyright 2014 Mike Bierlee + * This software is licensed under the terms of the MIT license. + * The full terms of the license can be found in the LICENSE file. + */ + +import poodinis.autowire; + +import std.exception; + +version(unittest) { + class ComponentA { + } + + class ComponentB { + public @Autowire ComponentA componentA; + + public bool componentIsNull() { + return componentA is null; + } + } + + interface InterfaceA { + } + + class ComponentC : InterfaceA { + } + + class ComponentD { + public @Autowire InterfaceA componentC = null; + + public bool componentIsNull() { + return componentC is null; + } + } + + class DummyAttribute{}; + + class ComponentE { + @DummyAttribute + public ComponentC componentC; + } + + class ComponentF { + @Autowire + public ComponentA componentA; + + mixin AutowireConstructor; + } + + class ComponentDeclarationCocktail { + alias noomer = int; + + @Autowire + public ComponentA componentA; + + public void doesNothing() { + } + + ~this(){ + } + } + + // Test autowiring concrete type to existing instance + unittest { + auto container = new Container(); + container.register!ComponentA; + auto componentB = new ComponentB(); + container.autowire!(ComponentB)(componentB); + assert(!componentB.componentIsNull(), "Autowirable dependency failed to autowire"); + } + + // Test autowiring interface type to existing instance + unittest { + auto container = new Container(); + container.register!(InterfaceA, ComponentC); + auto componentD = new ComponentD(); + container.autowire!(ComponentD)(componentD); + assert(!componentD.componentIsNull(), "Autowirable dependency failed to autowire"); + } + + // Test autowiring will only happen once + unittest { + auto container = new Container(); + container.register!(InterfaceA, ComponentC).newInstance(); + auto componentD = new ComponentD(); + container.autowire!(ComponentD)(componentD); + auto expectedComponent = componentD.componentC; + container.autowire!(ComponentD)(componentD); + auto actualComponent = componentD.componentC; + assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance"); + } + + // Test autowiring unregistered type + unittest { + auto container = new Container(); + auto componentD = new ComponentD(); + assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException"); + } + + // Test autowiring member with non-autowire attribute does not autowire + unittest { + auto container = new Container(); + auto componentE = new ComponentE(); + container.autowire!ComponentE(componentE); + assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire"); + } + + // Test autowire in constructor + unittest { + auto container = Container.getInstance(); + container.register!ComponentA; + auto componentF = new ComponentF(); + auto autowiredComponentA = componentF.componentA; + container.register!(ComponentF).existingInstance(componentF); + assert(componentF.componentA !is null, "Constructor did not autowire component F"); + + auto resolvedComponentF = container.resolve!ComponentF; + assert(resolvedComponentF.componentA is autowiredComponentA, "Resolving instance of ComponentF rewired members"); + + container.clearAllRegistrations(); + } + + // Test autowire class with alias declaration + unittest { + auto container = new Container(); + container.register!ComponentA; + auto componentDeclarationCocktail = new ComponentDeclarationCocktail(); + + container.autowire(componentDeclarationCocktail); + + assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed"); + } } \ No newline at end of file