Finish refactoring for now

It's still not perfect, but traits are real difficult to work with.
This commit is contained in:
Mike Bierlee 2015-03-21 14:25:55 +01:00
parent d1e3366549
commit a5f6a1dc32

View file

@ -1,166 +1,165 @@
/** /**
* Contains functionality for autowiring dependencies using a dependency container. * Contains functionality for autowiring dependencies using a dependency container.
* *
* This module is used in a dependency container for autowiring dependencies when resolving them. * This module is used in a dependency container for autowiring dependencies when resolving them.
* You typically only need this module if you want inject dependencies into a class instance not * You typically only need this module if you want inject dependencies into a class instance not
* managed by a dependency container. * managed by a dependency container.
* *
* Part of the Poodinis Dependency Injection framework. * Part of the Poodinis Dependency Injection framework.
* *
* Authors: * Authors:
* Mike Bierlee, m.bierlee@lostmoment.com * Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2015 Mike Bierlee * Copyright: 2014-2015 Mike Bierlee
* License: * License:
* 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.
*/ */
module poodinis.autowire; module poodinis.autowire;
public import poodinis.container; public import poodinis.container;
import std.typetuple; import std.typetuple;
import std.exception; import std.exception;
import std.stdio; import std.stdio;
import std.string; import std.string;
struct UseMemberType {}; struct UseMemberType {};
/** /**
* UDA for annotating class members as candidates for autowiring. * UDA for annotating class members as candidates for autowiring.
* *
* Optionally a template parameter can be supplied to specify the type of a qualified class. The qualified type * Optionally a template parameter can be supplied to specify the type of a qualified class. The qualified type
* of a concrete class is used to autowire members declared by supertype. If no qualifier is supplied, the type * of a concrete class is used to autowire members declared by supertype. If no qualifier is supplied, the type
* of the member is used as qualifier. * of the member is used as qualifier.
* *
* Examples: * Examples:
* Annotate member of class to be autowired: * Annotate member of class to be autowired:
* --- * ---
* class Car { * class Car {
* @Autowire * @Autowire
* public Engine engine; * public Engine engine;
* } * }
* --- * ---
* *
* Annotate member of class with qualifier: * Annotate member of class with qualifier:
* --- * ---
* class FuelEngine : Engine { ... } * class FuelEngine : Engine { ... }
* class ElectricEngine : Engine { ... } * class ElectricEngine : Engine { ... }
* *
* class HybridCar { * class HybridCar {
* @Autowire!FuelEngine * @Autowire!FuelEngine
* public Engine fuelEngine; * public Engine fuelEngine;
* *
* @Autowire!ElectricEngine * @Autowire!ElectricEngine
* public Engine electricEngine; * public Engine electricEngine;
* } * }
* --- * ---
* The members of an instance of "HybridCar" will now be autowired properly, because the autowire mechanism will * The members of an instance of "HybridCar" will now be autowired properly, because the autowire mechanism will
* autowire member "fuelEngine" as if it's of type "FuelEngine". This means that the members of instance "fuelEngine" * autowire member "fuelEngine" as if it's of type "FuelEngine". This means that the members of instance "fuelEngine"
* will also be autowired because the autowire mechanism knows that member "fuelEngine" is an instance of "FuelEngine" * will also be autowired because the autowire mechanism knows that member "fuelEngine" is an instance of "FuelEngine"
*/ */
struct Autowire(QualifierType = UseMemberType) { struct Autowire(QualifierType = UseMemberType) {
QualifierType qualifier; QualifierType qualifier;
}; };
private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) { private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) {
writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress)); writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress));
} }
/** /**
* Autowires members of a given instance using dependencies registered in the given container. * Autowires members of a given instance using dependencies registered in the given container.
* *
* All public members of the given instance, which are annotated using the "Autowire" UDA, are autowired. * All public members of the given instance, which are annotated using the "Autowire" UDA, are autowired.
* All members are resolved using the given container. Qualifiers are used to determine the type of class to * All members are resolved using the given container. Qualifiers are used to determine the type of class to
* resolve for any member of instance. * resolve for any member of instance.
* *
* Note that private members will not be autowired because the autowiring mechanism is not able to by-pass * Note that private members will not be autowired because the autowiring mechanism is not able to by-pass
* member visibility protection. * member visibility protection.
* *
* See_Also: Autowire * See_Also: Autowire
*/ */
public void autowire(Type)(DependencyContainer container, Type instance) { public void autowire(Type)(DependencyContainer container, Type instance) {
debug(poodinisVerbose) { debug(poodinisVerbose) {
printDebugAutowiredInstance(typeid(Type), &instance); printDebugAutowiredInstance(typeid(Type), &instance);
} }
foreach (member ; __traits(allMembers, Type)) { foreach (member ; __traits(allMembers, Type)) {
autowireMember!member(container, instance); autowireMember!member(container, instance);
} }
} }
private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType, void* candidateInstanceAddress, TypeInfo instanceType, void* instanceAddress, string member) { private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType, void* candidateInstanceAddress, TypeInfo instanceType, void* instanceAddress, string member) {
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)(DependencyContainer container, Type instance) {
// For the love of god, refactor this! <-- Doing it, bro! 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)) { if (__traits(getMember, instance, member) is null) {
if (__traits(getMember, instance, member) is null) { alias MemberType = typeof(__traits(getMember, instance, member));
alias MemberType = typeof(__traits(getMember, instance, member));
debug(poodinisVerbose) {
debug(poodinisVerbose) { TypeInfo qualifiedInstanceType = typeid(MemberType);
TypeInfo qualifiedInstanceType = typeid(MemberType); }
}
MemberType qualifiedInstance;
MemberType qualifiedInstance; static if (is(autowireAttribute == Autowire!T, T) && !is(autowireAttribute.qualifier == UseMemberType)) {
static if (is(autowireAttribute == Autowire!T, T) && !is(autowireAttribute.qualifier == UseMemberType)) { alias QualifierType = typeof(autowireAttribute.qualifier);
alias QualifierType = typeof(autowireAttribute.qualifier); qualifiedInstance = container.resolve!(MemberType, QualifierType);
qualifiedInstance = container.resolve!(MemberType, QualifierType); debug(poodinisVerbose) {
debug(poodinisVerbose) { qualifiedInstanceType = typeid(QualifierType);
qualifiedInstanceType = typeid(QualifierType); }
} } else {
} else { qualifiedInstance = container.resolve!(MemberType);
qualifiedInstance = container.resolve!(MemberType); }
}
__traits(getMember, instance, member) = qualifiedInstance;
__traits(getMember, instance, member) = qualifiedInstance;
debug(poodinisVerbose) {
debug(poodinisVerbose) { printDebugAutowiringCandidate(qualifiedInstanceType, &qualifiedInstance, typeid(Type), &instance, member);
printDebugAutowiringCandidate(qualifiedInstanceType, &qualifiedInstance, typeid(Type), &instance, member); }
} }
}
break;
break; }
} }
} }
} }
}
/**
/** * Autowire the given instance using the globally available dependency container.
* Autowire the given instance using the globally available dependency container. *
* * See_Also: DependencyContainer
* See_Also: DependencyContainer */
*/ public void globalAutowire(Type)(Type instance) {
public void globalAutowire(Type)(Type instance) { DependencyContainer.getInstance().autowire(instance);
DependencyContainer.getInstance().autowire(instance); }
}
class AutowiredRegistration(RegistrationType : Object) : Registration {
class AutowiredRegistration(RegistrationType : Object) : Registration { private DependencyContainer container;
private DependencyContainer container;
public this(TypeInfo registeredType, DependencyContainer container) {
public this(TypeInfo registeredType, 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)); }
}
public override Object getInstance(InstantiationContext context = new AutowireInstantiationContext()) {
public override Object getInstance(InstantiationContext context = new AutowireInstantiationContext()) { RegistrationType instance = cast(RegistrationType) super.getInstance(context);
RegistrationType instance = cast(RegistrationType) super.getInstance(context);
AutowireInstantiationContext autowireContext = cast(AutowireInstantiationContext) context;
AutowireInstantiationContext autowireContext = cast(AutowireInstantiationContext) context; enforce(!(autowireContext is null), "Given instantiation context type could not be cast to an AutowireInstantiationContext. If you relied on using the default assigned context: make sure you're calling getInstance() on an instance of type AutowiredRegistration!");
enforce(!(autowireContext is null), "Given instantiation context type could not be cast to an AutowireInstantiationContext. If you relied on using the default assigned context: make sure you're calling getInstance() on an instance of type AutowiredRegistration!"); if (autowireContext.autowireInstance) {
if (autowireContext.autowireInstance) { container.autowire(instance);
container.autowire(instance); }
}
return instance;
return instance; }
}
}
}
class AutowireInstantiationContext : InstantiationContext {
class AutowireInstantiationContext : InstantiationContext { public bool autowireInstance = true;
public bool autowireInstance = true; }
}