Update copyrights

This commit is contained in:
Mike Bierlee 2016-01-06 20:18:35 +01:00
parent 4be0e28bba
commit c35e494dfe
17 changed files with 2114 additions and 2114 deletions

View file

@ -1,19 +1,19 @@
Copyright (c) 2014-2015 Mike Bierlee Copyright (c) 2014-2016 Mike Bierlee
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions: furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software. all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. THE SOFTWARE.

View file

@ -1,7 +1,7 @@
Poodinis Dependency Injection Framework Poodinis Dependency Injection Framework
======================================= =======================================
Version 6.0.0 Version 6.0.0
Copyright 2014-2015 Mike Bierlee Copyright 2014-2016 Mike Bierlee
Licensed under the terms of the MIT license - See [LICENSE.txt](LICENSE.txt) Licensed under the terms of the MIT license - See [LICENSE.txt](LICENSE.txt)
Master: [![Build Status](https://api.travis-ci.org/mbierlee/poodinis.png?branch=master)](https://travis-ci.org/mbierlee/poodinis) - Dev: [![Build Status](https://api.travis-ci.org/mbierlee/poodinis.png?branch=develop)](https://travis-ci.org/mbierlee/poodinis) Master: [![Build Status](https://api.travis-ci.org/mbierlee/poodinis.png?branch=master)](https://travis-ci.org/mbierlee/poodinis) - Dev: [![Build Status](https://api.travis-ci.org/mbierlee/poodinis.png?branch=develop)](https://travis-ci.org/mbierlee/poodinis)

View file

@ -3,7 +3,7 @@
"description" : "A dependency injection framework with support for autowiring.", "description" : "A dependency injection framework with support for autowiring.",
"homepage": "http://lostmoment.com/open-source/poodinis", "homepage": "http://lostmoment.com/open-source/poodinis",
"authors": ["Mike Bierlee"], "authors": ["Mike Bierlee"],
"copyright": "Copyright 2015 Mike Bierlee", "copyright": "Copyright 2014-2016 Mike Bierlee",
"license": "MIT", "license": "MIT",
"configurations": [ "configurations": [
{ {

View file

@ -1,50 +1,50 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2016 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; import poodinis;
import std.random; import std.random;
import std.digest.md; import std.digest.md;
import std.stdio; import std.stdio;
import std.conv; import std.conv;
class SuperSecurityDevice { class SuperSecurityDevice {
private int seed; private int seed;
public this() { public this() {
auto randomGenerator = Random(unpredictableSeed); auto randomGenerator = Random(unpredictableSeed);
seed = uniform(0, 999, randomGenerator); seed = uniform(0, 999, randomGenerator);
} }
public string getPassword() { public string getPassword() {
return to!string(seed) ~ "t1m3sp13!!:"; return to!string(seed) ~ "t1m3sp13!!:";
} }
} }
class SecurityManager { class SecurityManager {
@Autowire @Autowire
public SuperSecurityDevice levelOneSecurity; public SuperSecurityDevice levelOneSecurity;
@Autowire @Autowire
@AssignNewInstance @AssignNewInstance
public SuperSecurityDevice levelTwoSecurity; public SuperSecurityDevice levelTwoSecurity;
} }
void main() { void main() {
auto dependencies = DependencyContainer.getInstance(); auto dependencies = DependencyContainer.getInstance();
dependencies.register!SuperSecurityDevice; // Registered with the default "Single instance" scope dependencies.register!SuperSecurityDevice; // Registered with the default "Single instance" scope
dependencies.register!SecurityManager; dependencies.register!SecurityManager;
auto manager = dependencies.resolve!SecurityManager; auto manager = dependencies.resolve!SecurityManager;
writeln("Password for user one: " ~ manager.levelOneSecurity.getPassword()); writeln("Password for user one: " ~ manager.levelOneSecurity.getPassword());
writeln("Password for user two: " ~ manager.levelTwoSecurity.getPassword()); writeln("Password for user two: " ~ manager.levelTwoSecurity.getPassword());
if (manager.levelOneSecurity is manager.levelTwoSecurity) { if (manager.levelOneSecurity is manager.levelTwoSecurity) {
writeln("SECURITY BREACH!!!!!"); writeln("SECURITY BREACH!!!!!");
} }
} }

View file

@ -1,68 +1,68 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2016 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; import poodinis;
import std.stdio; import std.stdio;
class TownSquare { class TownSquare {
@Autowire @Autowire
public MarketStall marketStall; public MarketStall marketStall;
public void makeSound() { public void makeSound() {
marketStall.announceGoodsForSale(); marketStall.announceGoodsForSale();
} }
} }
interface Goods { interface Goods {
public string getGoodsName(); public string getGoodsName();
} }
class Fish : Goods { class Fish : Goods {
public override string getGoodsName() { public override string getGoodsName() {
return "Fish"; return "Fish";
} }
} }
class MarketStall { class MarketStall {
private Goods goods; private Goods goods;
this(Goods goods) { this(Goods goods) {
this.goods = goods; this.goods = goods;
} }
public void announceGoodsForSale() { public void announceGoodsForSale() {
writeln(goods.getGoodsName() ~ " for sale!"); writeln(goods.getGoodsName() ~ " for sale!");
} }
} }
class ExampleApplicationContext : ApplicationContext { class ExampleApplicationContext : ApplicationContext {
@Autowire @Autowire
public Goods goods; public Goods goods;
public override void registerDependencies(shared(DependencyContainer) container) { public override void registerDependencies(shared(DependencyContainer) container) {
container.register!(Goods, Fish); container.register!(Goods, Fish);
container.register!TownSquare; container.register!TownSquare;
} }
@Component @Component
public MarketStall marketStall() { public MarketStall marketStall() {
return new MarketStall(goods); return new MarketStall(goods);
} }
} }
void main() { void main() {
auto container = DependencyContainer.getInstance(); auto container = DependencyContainer.getInstance();
container.registerContext!ExampleApplicationContext; container.registerContext!ExampleApplicationContext;
auto townSquare = container.resolve!TownSquare; auto townSquare = container.resolve!TownSquare;
townSquare.makeSound(); townSquare.makeSound();
} }

View file

@ -1,54 +1,54 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2016 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; import poodinis;
import std.stdio; import std.stdio;
interface Pie { interface Pie {
public void eat(); public void eat();
} }
class BlueBerryPie : Pie { class BlueBerryPie : Pie {
public override void eat() { public override void eat() {
writeln("Nom nom nom. I like this one!"); writeln("Nom nom nom. I like this one!");
} }
} }
class ApplePie : Pie { class ApplePie : Pie {
public override void eat() { public override void eat() {
writeln("Nom nom nom. These aren't real apples..."); writeln("Nom nom nom. These aren't real apples...");
} }
} }
class CardboardBoxPie : Pie { class CardboardBoxPie : Pie {
public override void eat() { public override void eat() {
writeln("Nom nom nom. This... is not a pie."); writeln("Nom nom nom. This... is not a pie.");
} }
} }
class PieEater { class PieEater {
@Autowire @Autowire
public Pie[] pies; public Pie[] pies;
public void eatThemAll() { public void eatThemAll() {
foreach(pie ; pies) { foreach(pie ; pies) {
pie.eat(); pie.eat();
} }
} }
} }
void main() { void main() {
auto dependencies = DependencyContainer.getInstance(); auto dependencies = DependencyContainer.getInstance();
dependencies.register!(Pie, BlueBerryPie); dependencies.register!(Pie, BlueBerryPie);
dependencies.register!(Pie, ApplePie); dependencies.register!(Pie, ApplePie);
dependencies.register!(Pie, CardboardBoxPie); dependencies.register!(Pie, CardboardBoxPie);
dependencies.register!(PieEater); dependencies.register!(PieEater);
auto eater = dependencies.resolve!PieEater; auto eater = dependencies.resolve!PieEater;
eater.eatThemAll(); eater.eatThemAll();
} }

View file

@ -1,57 +1,57 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2016 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; import poodinis;
import std.stdio; import std.stdio;
interface Engine { interface Engine {
public void engage(); public void engage();
} }
class FuelEngine : Engine { class FuelEngine : Engine {
public void engage() { public void engage() {
writeln("VROOOOOOM!"); writeln("VROOOOOOM!");
} }
} }
class ElectricEngine : Engine { class ElectricEngine : Engine {
public void engage() { public void engage() {
writeln("hummmmmmmm...."); writeln("hummmmmmmm....");
} }
} }
class HybridCar { class HybridCar {
alias KilometersPerHour = int; alias KilometersPerHour = int;
@Autowire!FuelEngine @Autowire!FuelEngine
public Engine fuelEngine; public Engine fuelEngine;
@Autowire!ElectricEngine @Autowire!ElectricEngine
public Engine electricEngine; public Engine electricEngine;
public void moveAtSpeed(KilometersPerHour speed) { public void moveAtSpeed(KilometersPerHour speed) {
if (speed <= 45) { if (speed <= 45) {
electricEngine.engage(); electricEngine.engage();
} else { } else {
fuelEngine.engage(); fuelEngine.engage();
} }
} }
} }
void main() { void main() {
auto dependencies = DependencyContainer.getInstance(); auto dependencies = DependencyContainer.getInstance();
dependencies.register!HybridCar; dependencies.register!HybridCar;
dependencies.register!(Engine, FuelEngine); dependencies.register!(Engine, FuelEngine);
dependencies.register!(Engine, ElectricEngine); dependencies.register!(Engine, ElectricEngine);
auto car = dependencies.resolve!HybridCar; auto car = dependencies.resolve!HybridCar;
car.moveAtSpeed(10); // Should print "hummmmmmmm...." car.moveAtSpeed(10); // Should print "hummmmmmmm...."
car.moveAtSpeed(50); // Should print "VROOOOOOM!" car.moveAtSpeed(50); // Should print "VROOOOOOM!"
} }

View file

@ -1,24 +1,24 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2016 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; import poodinis;
interface Database{}; interface Database{};
class RelationalDatabase : Database {} class RelationalDatabase : Database {}
class DataWriter { class DataWriter {
@Autowire @Autowire
public Database database; // Automatically injected when class is resolved public Database database; // Automatically injected when class is resolved
} }
void main() { void main() {
auto dependencies = DependencyContainer.getInstance(); auto dependencies = DependencyContainer.getInstance();
dependencies.register!DataWriter; dependencies.register!DataWriter;
dependencies.register!(Database, RelationalDatabase); dependencies.register!(Database, RelationalDatabase);
auto writer = dependencies.resolve!DataWriter; auto writer = dependencies.resolve!DataWriter;
} }

View file

@ -1,206 +1,206 @@
/** /**
* 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-2016 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;
import poodinis.container; import poodinis.container;
import poodinis.registration; import poodinis.registration;
import std.exception; import std.exception;
import std.stdio; import std.stdio;
import std.string; import std.string;
import std.traits; import std.traits;
import std.range; import std.range;
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;
}; };
/** /**
* UDA for annotating class members to be autowired with a new instance regardless of their registration scope. * UDA for annotating class members to be autowired with a new instance regardless of their registration scope.
* *
* Examples: * Examples:
*--- *---
* class Car { * class Car {
* @Autowire * @Autowire
* @AssignNewInstance * @AssignNewInstance
* public Antenna antenna; * public Antenna antenna;
* } * }
*--- *---
* antenna will always be assigned a new instance of class Antenna. * antenna will always be assigned a new instance of class Antenna.
*/ */
struct AssignNewInstance {} struct AssignNewInstance {}
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)(shared(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);
} }
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 printDebugAutowiringArray(TypeInfo superTypeInfo, TypeInfo instanceType, void* instanceAddress, string member) { private void printDebugAutowiringArray(TypeInfo superTypeInfo, TypeInfo instanceType, void* instanceAddress, string member) {
writeln(format("DEBUG: Autowired all registered instances of super type %s to [%s@%s].%s", superTypeInfo, instanceType, instanceAddress, member)); writeln(format("DEBUG: Autowired all registered instances of super type %s to [%s@%s].%s", superTypeInfo, instanceType, instanceAddress, member));
} }
private void autowireMember(string member, Type)(shared(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)) {
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));
enum assignNewInstance = hasUDA!(__traits(getMember, instance, member), AssignNewInstance); enum assignNewInstance = hasUDA!(__traits(getMember, instance, member), AssignNewInstance);
static if (isDynamicArray!MemberType) { static if (isDynamicArray!MemberType) {
alias MemberElementType = ElementType!MemberType; alias MemberElementType = ElementType!MemberType;
auto instances = container.resolveAll!MemberElementType; auto instances = container.resolveAll!MemberElementType;
__traits(getMember, instance, member) = instances; __traits(getMember, instance, member) = instances;
debug(poodinisVerbose) { debug(poodinisVerbose) {
printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member); printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member);
} }
} else { } else {
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 = createOrResolveInstance!(MemberType, QualifierType, assignNewInstance)(container); qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType, assignNewInstance)(container);
debug(poodinisVerbose) { debug(poodinisVerbose) {
qualifiedInstanceType = typeid(QualifierType); qualifiedInstanceType = typeid(QualifierType);
} }
} else { } else {
qualifiedInstance = createOrResolveInstance!(MemberType, MemberType, assignNewInstance)(container); qualifiedInstance = createOrResolveInstance!(MemberType, MemberType, assignNewInstance)(container);
} }
__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;
} }
} }
} }
} }
private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) { private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) {
static if (createNew) { static if (createNew) {
auto instanceFactory = new InstanceFactory(typeid(MemberType), CreatesSingleton.no, null); auto instanceFactory = new InstanceFactory(typeid(MemberType), CreatesSingleton.no, null);
return cast(MemberType) instanceFactory.getInstance(); return cast(MemberType) instanceFactory.getInstance();
} else { } else {
return container.resolve!(MemberType, QualifierType); return container.resolve!(MemberType, QualifierType);
} }
} }
/** /**
* 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 shared(DependencyContainer) container; private shared(DependencyContainer) container;
public this(TypeInfo registeredType, shared(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));
} }
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;
} }

View file

@ -1,361 +1,361 @@
/** /**
* Contains the implementation of the dependency container. * Contains the implementation of the 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-2016 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.container; module poodinis.container;
import std.string; import std.string;
import std.algorithm; import std.algorithm;
import std.concurrency; import std.concurrency;
debug { debug {
import std.stdio; import std.stdio;
} }
import poodinis.registration; import poodinis.registration;
import poodinis.autowire; import poodinis.autowire;
import poodinis.context; import poodinis.context;
/** /**
* Exception thrown when errors occur while resolving a type in a dependency container. * Exception thrown when errors occur while resolving a type in a dependency container.
*/ */
class ResolveException : Exception { class ResolveException : Exception {
this(string message, TypeInfo resolveType) { this(string message, TypeInfo resolveType) {
super(format("Exception while resolving type %s: %s", resolveType.toString(), message)); super(format("Exception while resolving type %s: %s", resolveType.toString(), message));
} }
} }
/** /**
* Exception thrown when errors occur while registering a type in a dependency container. * Exception thrown when errors occur while registering a type in a dependency container.
*/ */
class RegistrationException : Exception { class RegistrationException : Exception {
this(string message, TypeInfo registrationType) { this(string message, TypeInfo registrationType) {
super(format("Exception while registering type %s: %s", registrationType.toString(), message)); super(format("Exception while registering type %s: %s", registrationType.toString(), message));
} }
} }
/** /**
* Options which influence the process of registering dependencies * Options which influence the process of registering dependencies
*/ */
public enum RegistrationOption { public enum RegistrationOption {
/** /**
* Prevent a concrete type being registered on itself. With this option you will always need * Prevent a concrete type being registered on itself. With this option you will always need
* to use the supertype as the type of the dependency. * to use the supertype as the type of the dependency.
*/ */
DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION
} }
/** /**
* The dependency container maintains all dependencies registered with it. * The dependency container maintains all dependencies registered with it.
* *
* Dependencies registered by a container can be resolved as long as they are still registered with the container. * Dependencies registered by a container can be resolved as long as they are still registered with the container.
* Upon resolving a dependency, an instance is fetched according to a specific scope which dictates how instances of * Upon resolving a dependency, an instance is fetched according to a specific scope which dictates how instances of
* dependencies are created. Resolved dependencies will be autowired before being returned. * dependencies are created. Resolved dependencies will be autowired before being returned.
* *
* 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.
*/ */
synchronized class DependencyContainer { synchronized class DependencyContainer {
private Registration[][TypeInfo] registrations; private Registration[][TypeInfo] registrations;
private Registration[] autowireStack; private Registration[] autowireStack;
/** /**
* Register a dependency by concrete class type. * Register a dependency by concrete class type.
* *
* A dependency registered by concrete class type can only be resolved by concrete class type. * A dependency registered by concrete class type can only be resolved by concrete class type.
* No qualifiers can be used when resolving dependencies which are registered by concrete type. * No qualifiers can be used when resolving dependencies which are registered by concrete type.
* *
* The default registration scope is "single instance" scope. * The default registration scope is "single instance" scope.
* *
* Returns: * Returns:
* A registration is returned which can be used to change the registration scope. * A registration is returned which can be used to change the registration scope.
* *
* Examples: * Examples:
* Register and resolve a class by concrete type: * Register and resolve a class by concrete type:
* --- * ---
* class Cat : Animal { ... } * class Cat : Animal { ... }
* container.register!Cat; * container.register!Cat;
* --- * ---
* *
* See_Also: singleInstance, newInstance, existingInstance * See_Also: singleInstance, newInstance, existingInstance
*/ */
public Registration register(ConcreteType)() { public Registration register(ConcreteType)() {
return register!(ConcreteType, ConcreteType)(); return register!(ConcreteType, ConcreteType)();
} }
/** /**
* Register a dependency by super type. * Register a dependency by super type.
* *
* A dependency registered by super type can only be resolved by super type. A qualifier is typically * A dependency registered by super type can only be resolved by super type. A qualifier is typically
* used to resolve dependencies registered by super type. * used to resolve dependencies registered by super type.
* *
* The default registration scope is "single instance" scope. * The default registration scope is "single instance" scope.
* *
* Examples: * Examples:
* Register and resolve by super type * Register and resolve by super type
* --- * ---
* class Cat : Animal { ... } * class Cat : Animal { ... }
* container.register!(Animal, Cat); * container.register!(Animal, Cat);
* --- * ---
* *
* See_Also: singleInstance, newInstance, existingInstance, RegistrationOptions * See_Also: singleInstance, newInstance, existingInstance, RegistrationOptions
*/ */
public Registration register(SuperType, ConcreteType : SuperType, RegistrationOptionsTuple...)(RegistrationOptionsTuple options) { public Registration register(SuperType, ConcreteType : SuperType, RegistrationOptionsTuple...)(RegistrationOptionsTuple options) {
TypeInfo registeredType = typeid(SuperType); TypeInfo registeredType = typeid(SuperType);
TypeInfo_Class concreteType = typeid(ConcreteType); TypeInfo_Class concreteType = typeid(ConcreteType);
debug(poodinisVerbose) { debug(poodinisVerbose) {
writeln(format("DEBUG: Register type %s (as %s)", concreteType.toString(), registeredType.toString())); writeln(format("DEBUG: Register type %s (as %s)", concreteType.toString(), registeredType.toString()));
} }
auto existingRegistration = getExistingRegistration(registeredType, concreteType); auto existingRegistration = getExistingRegistration(registeredType, concreteType);
if (existingRegistration) { if (existingRegistration) {
return existingRegistration; return existingRegistration;
} }
auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, this); auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, this);
newRegistration.singleInstance(); newRegistration.singleInstance();
if (!hasOption(options, RegistrationOption.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION)) { if (!hasOption(options, RegistrationOption.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION)) {
static if (!is(SuperType == ConcreteType)) { static if (!is(SuperType == ConcreteType)) {
auto concreteTypeRegistration = register!ConcreteType; auto concreteTypeRegistration = register!ConcreteType;
concreteTypeRegistration.linkTo(newRegistration); concreteTypeRegistration.linkTo(newRegistration);
} }
} }
registrations[registeredType] ~= cast(shared(Registration)) newRegistration; registrations[registeredType] ~= cast(shared(Registration)) newRegistration;
return newRegistration; return newRegistration;
} }
private bool hasOption(RegistrationOptionsTuple...)(RegistrationOptionsTuple options, RegistrationOption option) { private bool hasOption(RegistrationOptionsTuple...)(RegistrationOptionsTuple options, RegistrationOption option) {
foreach(presentOption ; options) { foreach(presentOption ; options) {
if (presentOption == option) { if (presentOption == option) {
return true; return true;
} }
} }
return false; return false;
} }
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(cast(Registration[]) *existingCandidates, qualifierType); return getRegistration(cast(Registration[]) *existingCandidates, qualifierType);
} }
return null; return null;
} }
private Registration getRegistration(Registration[] candidates, TypeInfo concreteType) { private Registration getRegistration(Registration[] candidates, TypeInfo concreteType) {
foreach(existingRegistration ; candidates) { foreach(existingRegistration ; candidates) {
if (existingRegistration.instanceType == concreteType) { if (existingRegistration.instanceType == concreteType) {
return existingRegistration; return existingRegistration;
} }
} }
return null; return null;
} }
/** /**
* Resolve dependencies. * Resolve dependencies.
* *
* Dependencies can only resolved using this method if they are registered by concrete type or the only * Dependencies can only resolved using this method if they are registered by concrete type or the only
* concrete type registered by super type. * concrete type registered by super type.
* *
* Resolved dependencies are automatically autowired before being returned. * Resolved dependencies are automatically autowired before being returned.
* *
* Returns: * Returns:
* An instance is returned which is created according to the registration scope with which they are registered. * An instance is returned which is created according to the registration scope with which they are registered.
* *
* Throws: * Throws:
* ResolveException when type is not registered. * ResolveException when type is not registered.
* *
* Examples: * Examples:
* Resolve dependencies registered by super type and concrete type: * Resolve dependencies registered by super type and concrete type:
* --- * ---
* class Cat : Animal { ... } * class Cat : Animal { ... }
* class Dog : Animal { ... } * class Dog : Animal { ... }
* *
* container.register!(Animal, Cat); * container.register!(Animal, Cat);
* container.register!Dog; * container.register!Dog;
* *
* container.resolve!Animal; * container.resolve!Animal;
* container.resolve!Dog; * container.resolve!Dog;
* --- * ---
* You cannot resolve a dependency when it is registered by multiple super types: * You cannot resolve a dependency when it is registered by multiple super types:
* --- * ---
* class Cat : Animal { ... } * class Cat : Animal { ... }
* class Dog : Animal { ... } * class Dog : Animal { ... }
* *
* container.register!(Animal, Cat); * container.register!(Animal, Cat);
* container.register!(Animal, Dog); * container.register!(Animal, Dog);
* *
* container.resolve!Animal; // Error: multiple candidates for type "Animal" * container.resolve!Animal; // Error: multiple candidates for type "Animal"
* container.resolve!Dog; // Error: No type is registered by concrete type "Dog", only by super type "Animal" * container.resolve!Dog; // Error: No type is registered by concrete type "Dog", only by super type "Animal"
* --- * ---
* You need to use the resolve method which allows you to specify a qualifier. * You need to use the resolve method which allows you to specify a qualifier.
*/ */
public RegistrationType resolve(RegistrationType)() { public RegistrationType resolve(RegistrationType)() {
return resolve!(RegistrationType, RegistrationType)(); return resolve!(RegistrationType, RegistrationType)();
} }
/** /**
* Resolve dependencies using a qualifier. * Resolve dependencies using a qualifier.
* *
* Dependencies can only resolved using this method if they are registered by super type. * Dependencies can only resolved using this method if they are registered by super type.
* *
* Resolved dependencies are automatically autowired before being returned. * Resolved dependencies are automatically autowired before being returned.
* *
* Returns: * Returns:
* An instance is returned which is created according to the registration scope with which they are registered. * An instance is returned which is created according to the registration scope with which they are registered.
* *
* Throws: * Throws:
* ResolveException when type is not registered or there are multiple candidates available for type. * ResolveException when type is not registered or there are multiple candidates available for type.
* *
* Examples: * Examples:
* Resolve dependencies registered by super type: * Resolve dependencies registered by super type:
* --- * ---
* class Cat : Animal { ... } * class Cat : Animal { ... }
* class Dog : Animal { ... } * class Dog : Animal { ... }
* *
* container.register!(Animal, Cat); * container.register!(Animal, Cat);
* container.register!(Animal, Dog); * container.register!(Animal, Dog);
* *
* container.resolve!(Animal, Cat); * container.resolve!(Animal, Cat);
* container.resolve!(Animal, Dog); * container.resolve!(Animal, Dog);
* --- * ---
*/ */
public QualifierType resolve(RegistrationType, QualifierType : RegistrationType)() { public QualifierType resolve(RegistrationType, QualifierType : RegistrationType)() {
TypeInfo resolveType = typeid(RegistrationType); TypeInfo resolveType = typeid(RegistrationType);
TypeInfo qualifierType = typeid(QualifierType); TypeInfo qualifierType = typeid(QualifierType);
debug(poodinisVerbose) { debug(poodinisVerbose) {
writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString()); writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString());
} }
auto candidates = resolveType in registrations; auto candidates = resolveType in registrations;
if (!candidates) { if (!candidates) {
throw new ResolveException("Type not registered.", resolveType); throw new ResolveException("Type not registered.", resolveType);
} }
Registration registration = getQualifiedRegistration(resolveType, qualifierType, cast(Registration[]) *candidates); Registration registration = getQualifiedRegistration(resolveType, qualifierType, cast(Registration[]) *candidates);
return resolveAutowiredInstance!QualifierType(registration); return resolveAutowiredInstance!QualifierType(registration);
} }
private QualifierType resolveAutowiredInstance(QualifierType)(Registration registration) { private QualifierType resolveAutowiredInstance(QualifierType)(Registration registration) {
QualifierType instance; QualifierType instance;
if (!(cast(Registration[]) autowireStack).canFind(registration)) { if (!(cast(Registration[]) autowireStack).canFind(registration)) {
autowireStack ~= cast(shared(Registration)) registration; autowireStack ~= cast(shared(Registration)) registration;
instance = cast(QualifierType) registration.getInstance(new AutowireInstantiationContext()); instance = cast(QualifierType) registration.getInstance(new AutowireInstantiationContext());
autowireStack = autowireStack[0 .. $-1]; autowireStack = autowireStack[0 .. $-1];
} else { } else {
auto autowireContext = new AutowireInstantiationContext(); auto autowireContext = new AutowireInstantiationContext();
autowireContext.autowireInstance = false; autowireContext.autowireInstance = false;
instance = cast(QualifierType) registration.getInstance(autowireContext); instance = cast(QualifierType) registration.getInstance(autowireContext);
} }
return instance; return instance;
} }
/** /**
* Resolve all dependencies registered to a super type. * Resolve all dependencies registered to a super type.
* *
* Returns: * Returns:
* An array of autowired instances is returned. The order is undetermined. * An array of autowired instances is returned. The order is undetermined.
* *
* Examples: * Examples:
* --- * ---
* class Cat : Animal { ... } * class Cat : Animal { ... }
* class Dog : Animal { ... } * class Dog : Animal { ... }
* *
* container.register!(Animal, Cat); * container.register!(Animal, Cat);
* container.register!(Animal, Dog); * container.register!(Animal, Dog);
* *
* Animal[] animals = container.resolveAll!Animal; * Animal[] animals = container.resolveAll!Animal;
* --- * ---
*/ */
public RegistrationType[] resolveAll(RegistrationType)() { public RegistrationType[] resolveAll(RegistrationType)() {
RegistrationType[] instances; RegistrationType[] instances;
TypeInfo resolveType = typeid(RegistrationType); TypeInfo resolveType = typeid(RegistrationType);
auto qualifiedRegistrations = resolveType in registrations; auto qualifiedRegistrations = resolveType in registrations;
if (!qualifiedRegistrations) { if (!qualifiedRegistrations) {
throw new ResolveException("Type not registered.", resolveType); throw new ResolveException("Type not registered.", resolveType);
} }
foreach(registration ; cast(Registration[]) *qualifiedRegistrations) { foreach(registration ; cast(Registration[]) *qualifiedRegistrations) {
instances ~= resolveAutowiredInstance!RegistrationType(registration); instances ~= resolveAutowiredInstance!RegistrationType(registration);
} }
return instances; return instances;
} }
private Registration getQualifiedRegistration(TypeInfo resolveType, TypeInfo qualifierType, Registration[] candidates) { private Registration getQualifiedRegistration(TypeInfo resolveType, TypeInfo qualifierType, Registration[] candidates) {
if (resolveType == qualifierType) { if (resolveType == qualifierType) {
if (candidates.length > 1) { if (candidates.length > 1) {
string candidateList = candidates.toConcreteTypeListString(); string candidateList = candidates.toConcreteTypeListString();
throw new ResolveException("Multiple qualified candidates available: " ~ candidateList ~ ". Please use a qualifier.", resolveType); throw new ResolveException("Multiple qualified candidates available: " ~ candidateList ~ ". Please use a qualifier.", resolveType);
} }
return candidates[0]; return candidates[0];
} }
return getRegistration(candidates, qualifierType); return getRegistration(candidates, qualifierType);
} }
/** /**
* Clears all dependency registrations managed by this container. * Clears all dependency registrations managed by this container.
*/ */
public void clearAllRegistrations() { public void clearAllRegistrations() {
registrations.destroy(); registrations.destroy();
} }
/** /**
* Removes a registered dependency by type. * Removes a registered dependency by type.
* *
* A dependency can be removed either by super type or concrete type, depending on how they are registered. * A dependency can be removed either by super type or concrete type, depending on how they are registered.
* *
* Examples: * Examples:
* --- * ---
* container.removeRegistration!Animal; * container.removeRegistration!Animal;
* --- * ---
*/ */
public void removeRegistration(RegistrationType)() { public void removeRegistration(RegistrationType)() {
registrations.remove(typeid(RegistrationType)); registrations.remove(typeid(RegistrationType));
} }
/** /**
* Register dependencies through an application context. * Register dependencies through an application context.
* *
* An application context allows you to fine-tune dependency set-up and instantiation. * An application context allows you to fine-tune dependency set-up and instantiation.
* It is mostly used for dependencies which come from an external library or when you don't * It is mostly used for dependencies which come from an external library or when you don't
* want to use annotations to set-up dependencies in your classes. * want to use annotations to set-up dependencies in your classes.
*/ */
public void registerContext(Context : ApplicationContext)() { public void registerContext(Context : ApplicationContext)() {
auto context = new Context(); auto context = new Context();
context.registerDependencies(this); context.registerDependencies(this);
context.registerContextComponents(this); context.registerContextComponents(this);
this.register!(ApplicationContext, Context)().existingInstance(context); this.register!(ApplicationContext, Context)().existingInstance(context);
autowire(this, context); autowire(this, context);
} }
/** /**
* Returns a global singleton instance of a dependency container. * Returns a global singleton instance of a dependency container.
*/ */
public static shared(DependencyContainer) getInstance() { public static shared(DependencyContainer) getInstance() {
static shared DependencyContainer instance; static shared DependencyContainer instance;
if (instance is null) { if (instance is null) {
instance = new DependencyContainer(); instance = new DependencyContainer();
} }
return instance; return instance;
} }
} }

View file

@ -1,68 +1,68 @@
/** /**
* Contains the implementation of application context setup. * Contains the implementation of application context setup.
* *
* 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-2016 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.context; module poodinis.context;
import poodinis.container; import poodinis.container;
import poodinis.registration; import poodinis.registration;
import std.traits; import std.traits;
class ApplicationContext { class ApplicationContext {
public void registerDependencies(shared(DependencyContainer) container) {} public void registerDependencies(shared(DependencyContainer) container) {}
} }
/** /**
* A component annotation is used for specifying which factory methods produce components in * A component annotation is used for specifying which factory methods produce components in
* an application context. * an application context.
*/ */
struct Component {} struct Component {}
/** /**
* This annotation allows you to specify by which super type the component should be registered. This * This annotation allows you to specify by which super type the component should be registered. This
* enables you to use type-qualified alternatives for dependencies. * enables you to use type-qualified alternatives for dependencies.
*/ */
struct RegisterByType(Type) { struct RegisterByType(Type) {
Type type; Type type;
} }
/** /**
* Components with the prototype registration will be scoped as dependencies which will created * Components with the prototype registration will be scoped as dependencies which will created
* new instances every time they are resolved. The factory method will be called repeatedly. * new instances every time they are resolved. The factory method will be called repeatedly.
*/ */
struct Prototype {} struct Prototype {}
public void registerContextComponents(ApplicationContextType : ApplicationContext)(ApplicationContextType context, shared(DependencyContainer) container) { public void registerContextComponents(ApplicationContextType : ApplicationContext)(ApplicationContextType context, shared(DependencyContainer) container) {
foreach (member ; __traits(allMembers, ApplicationContextType)) { foreach (member ; __traits(allMembers, ApplicationContextType)) {
static if (hasUDA!(__traits(getMember, context, member), Component)) { static if (hasUDA!(__traits(getMember, context, member), Component)) {
auto factoryMethod = &__traits(getMember, context, member); auto factoryMethod = &__traits(getMember, context, member);
Registration registration = null; Registration registration = null;
auto createsSingleton = CreatesSingleton.yes; auto createsSingleton = CreatesSingleton.yes;
foreach(attribute; __traits(getAttributes, __traits(getMember, context, member))) { foreach(attribute; __traits(getAttributes, __traits(getMember, context, member))) {
static if (is(attribute == RegisterByType!T, T)) { static if (is(attribute == RegisterByType!T, T)) {
registration = container.register!(typeof(attribute.type), ReturnType!factoryMethod); registration = container.register!(typeof(attribute.type), ReturnType!factoryMethod);
} else static if (__traits(isSame, attribute, Prototype)) { } else static if (__traits(isSame, attribute, Prototype)) {
createsSingleton = CreatesSingleton.no; createsSingleton = CreatesSingleton.no;
} }
} }
if (registration is null) { if (registration is null) {
registration = container.register!(ReturnType!factoryMethod); registration = container.register!(ReturnType!factoryMethod);
} }
registration.instanceFactory = new InstanceFactory(registration.instanceType, createsSingleton, null, factoryMethod); registration.instanceFactory = new InstanceFactory(registration.instanceType, createsSingleton, null, factoryMethod);
} }
} }
} }

View file

@ -1,17 +1,17 @@
/** /**
* Package module for the Poodinis Dependency Injection framework. * Package module for 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-2016 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; module poodinis;
public import poodinis.autowire; public import poodinis.autowire;
public import poodinis.container; public import poodinis.container;
public import poodinis.registration; public import poodinis.registration;
public import poodinis.context; public import poodinis.context;

View file

@ -1,145 +1,145 @@
/** /**
* This module contains objects for defining and scoping dependency registrations. * This module contains objects for defining and scoping dependency registrations.
* *
* 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-2016 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.registration; module poodinis.registration;
import std.typecons; import std.typecons;
import std.exception; import std.exception;
debug { debug {
import std.stdio; import std.stdio;
import std.string; import std.string;
} }
class InstanceCreationException : Exception { class InstanceCreationException : Exception {
this(string message, string file = __FILE__, size_t line = __LINE__) { this(string message, string file = __FILE__, size_t line = __LINE__) {
super(message, file, line); super(message, file, line);
} }
} }
class Registration { class Registration {
private TypeInfo _registeredType = null; private TypeInfo _registeredType = null;
private TypeInfo_Class _instanceType = null; private TypeInfo_Class _instanceType = null;
private Registration linkedRegistration; private Registration linkedRegistration;
public @property registeredType() { public @property registeredType() {
return _registeredType; return _registeredType;
} }
public @property instanceType() { public @property instanceType() {
return _instanceType; return _instanceType;
} }
public InstanceFactory instanceFactory = null; public InstanceFactory instanceFactory = null;
this(TypeInfo registeredType, TypeInfo_Class instanceType) { this(TypeInfo registeredType, TypeInfo_Class instanceType) {
this._registeredType = registeredType; this._registeredType = registeredType;
this._instanceType = instanceType; this._instanceType = instanceType;
} }
public Object getInstance(InstantiationContext context = new InstantiationContext()) { public Object getInstance(InstantiationContext context = new InstantiationContext()) {
if (linkedRegistration !is null) { if (linkedRegistration !is null) {
return linkedRegistration.getInstance(context); return linkedRegistration.getInstance(context);
} }
if (instanceFactory is null) { if (instanceFactory is null) {
throw new InstanceCreationException("No instance factory defined for registration of type " ~ registeredType.toString()); throw new InstanceCreationException("No instance factory defined for registration of type " ~ registeredType.toString());
} }
return instanceFactory.getInstance(); return instanceFactory.getInstance();
} }
public Registration linkTo(Registration registration) { public Registration linkTo(Registration registration) {
this.linkedRegistration = registration; this.linkedRegistration = registration;
return this; return this;
} }
} }
alias CreatesSingleton = Flag!"CreatesSingleton"; alias CreatesSingleton = Flag!"CreatesSingleton";
alias InstanceFactoryMethod = Object delegate(); alias InstanceFactoryMethod = Object delegate();
class InstanceFactory { class InstanceFactory {
private TypeInfo_Class instanceType = null; private TypeInfo_Class instanceType = null;
private Object instance = null; private Object instance = null;
private CreatesSingleton createsSingleton; private CreatesSingleton createsSingleton;
private InstanceFactoryMethod factoryMethod; private InstanceFactoryMethod factoryMethod;
this(TypeInfo_Class instanceType, CreatesSingleton createsSingleton = CreatesSingleton.yes, Object existingInstance = null, InstanceFactoryMethod factoryMethod = null) { this(TypeInfo_Class instanceType, CreatesSingleton createsSingleton = CreatesSingleton.yes, Object existingInstance = null, InstanceFactoryMethod factoryMethod = null) {
this.instanceType = instanceType; this.instanceType = instanceType;
this.createsSingleton = existingInstance !is null ? CreatesSingleton.yes : createsSingleton; this.createsSingleton = existingInstance !is null ? CreatesSingleton.yes : createsSingleton;
this.instance = existingInstance; this.instance = existingInstance;
this.factoryMethod = factoryMethod !is null ? factoryMethod : &this.createInstance; this.factoryMethod = factoryMethod !is null ? factoryMethod : &this.createInstance;
} }
public Object getInstance() { public Object getInstance() {
if (createsSingleton && instance !is null) { if (createsSingleton && instance !is null) {
debug(poodinisVerbose) { debug(poodinisVerbose) {
writeln(format("DEBUG: Existing instance returned of type %s", instanceType.toString())); writeln(format("DEBUG: Existing instance returned of type %s", instanceType.toString()));
} }
return instance; return instance;
} }
debug(poodinisVerbose) { debug(poodinisVerbose) {
writeln(format("DEBUG: Creating new instance of type %s", instanceType.toString())); writeln(format("DEBUG: Creating new instance of type %s", instanceType.toString()));
} }
instance = factoryMethod(); instance = factoryMethod();
return instance; return instance;
} }
private Object createInstance() { private Object createInstance() {
enforce!InstanceCreationException(instanceType, "Instance type is not defined, cannot create instance without knowing its type."); enforce!InstanceCreationException(instanceType, "Instance type is not defined, cannot create instance without knowing its type.");
return instanceType.create(); return instanceType.create();
} }
} }
/** /**
* Scopes registrations to return the same instance every time a given registration is resolved. * Scopes registrations to return the same instance every time a given registration is resolved.
* *
* Effectively makes the given registration a singleton. * Effectively makes the given registration a singleton.
*/ */
public Registration singleInstance(Registration registration) { public Registration singleInstance(Registration registration) {
registration.instanceFactory = new InstanceFactory(registration.instanceType, CreatesSingleton.yes, null); registration.instanceFactory = new InstanceFactory(registration.instanceType, CreatesSingleton.yes, null);
return registration; return registration;
} }
/** /**
* Scopes registrations to return a new instance every time the given registration is resolved. * Scopes registrations to return a new instance every time the given registration is resolved.
*/ */
public Registration newInstance(Registration registration) { public Registration newInstance(Registration registration) {
registration.instanceFactory = new InstanceFactory(registration.instanceType, CreatesSingleton.no, null); registration.instanceFactory = new InstanceFactory(registration.instanceType, CreatesSingleton.no, null);
return registration; return registration;
} }
/** /**
* Scopes registrations to return the given instance every time the given registration is resolved. * Scopes registrations to return the given instance every time the given registration is resolved.
*/ */
public Registration existingInstance(Registration registration, Object instance) { public Registration existingInstance(Registration registration, Object instance) {
registration.instanceFactory = new InstanceFactory(registration.instanceType, CreatesSingleton.yes, instance); registration.instanceFactory = new InstanceFactory(registration.instanceType, CreatesSingleton.yes, instance);
return registration; return registration;
} }
public string toConcreteTypeListString(Registration[] registrations) { public string toConcreteTypeListString(Registration[] registrations) {
auto concreteTypeListString = ""; auto concreteTypeListString = "";
foreach (registration ; registrations) { foreach (registration ; registrations) {
if (concreteTypeListString.length > 0) { if (concreteTypeListString.length > 0) {
concreteTypeListString ~= ", "; concreteTypeListString ~= ", ";
} }
concreteTypeListString ~= registration.instanceType.toString(); concreteTypeListString ~= registration.instanceType.toString();
} }
return concreteTypeListString; return concreteTypeListString;
} }
class InstantiationContext {} class InstantiationContext {}

View file

@ -1,191 +1,191 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2016 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; import poodinis;
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;
} }
interface InterfaceA {} interface InterfaceA {}
class ComponentC : InterfaceA {} class ComponentC : InterfaceA {}
class ComponentD { class ComponentD {
public @Autowire InterfaceA componentC = null; public @Autowire InterfaceA componentC = 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;
} }
class LordOfTheComponents { class LordOfTheComponents {
@Autowire @Autowire
public InterfaceA[] components; public InterfaceA[] components;
} }
class ComponentCharlie { class ComponentCharlie {
@Autowire @Autowire
@AssignNewInstance @AssignNewInstance
public ComponentA componentA; public ComponentA componentA;
} }
// Test autowiring concrete type to existing instance // Test autowiring concrete type to existing instance
unittest { unittest {
shared(DependencyContainer) 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 !is null, "Autowirable dependency failed to autowire"); assert(componentB !is null, "Autowirable dependency failed to autowire");
} }
// Test autowiring interface type to existing instance // Test autowiring interface type to existing instance
unittest { unittest {
shared(DependencyContainer) 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.componentC !is null, "Autowirable dependency failed to autowire"); assert(componentD.componentC !is null, "Autowirable dependency failed to autowire");
} }
// Test autowiring will only happen once // Test autowiring will only happen once
unittest { unittest {
shared(DependencyContainer) 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 {
shared(DependencyContainer) 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 {
shared(DependencyContainer) 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 {
shared(DependencyContainer) 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 {
shared(DependencyContainer) 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 {
shared(DependencyContainer) 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 {
shared(DependencyContainer) 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.componentA !is null); assert(instance.componentA !is null);
} }
// Test autowiring a dynamic array with all qualified types // Test autowiring a dynamic array with all qualified types
unittest { unittest {
shared(DependencyContainer) 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 lord = new LordOfTheComponents(); auto lord = new LordOfTheComponents();
container.autowire(lord); container.autowire(lord);
assert(lord.components.length == 2, "Dynamic array was not autowired"); assert(lord.components.length == 2, "Dynamic array was not autowired");
} }
// Test autowiring new instance of singleinstance registration with newInstance UDA // Test autowiring new instance of singleinstance registration with newInstance UDA
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA; container.register!ComponentA;
auto regularComponentA = container.resolve!ComponentA; auto regularComponentA = container.resolve!ComponentA;
auto charlie = new ComponentCharlie(); auto charlie = new ComponentCharlie();
container.autowire(charlie); container.autowire(charlie);
assert(charlie.componentA !is regularComponentA, "Autowiring class with AssignNewInstance did not yield a different instance"); assert(charlie.componentA !is regularComponentA, "Autowiring class with AssignNewInstance did not yield a different instance");
} }
} }

File diff suppressed because it is too large Load diff

View file

@ -1,141 +1,141 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2016 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; import poodinis;
import std.exception; import std.exception;
version(unittest) { version(unittest) {
interface Fruit { interface Fruit {
string getShape(); string getShape();
} }
interface Animal { interface Animal {
string getYell(); string getYell();
} }
class Banana { class Banana {
public string color; public string color;
this(string color) { this(string color) {
this.color = color; this.color = color;
} }
} }
class Apple {} class Apple {}
class Pear : Fruit { class Pear : Fruit {
public override string getShape() { public override string getShape() {
return "Pear shaped"; return "Pear shaped";
} }
} }
class Rabbit : Animal { class Rabbit : Animal {
public override string getYell() { public override string getYell() {
return "Squeeeeeel"; return "Squeeeeeel";
} }
} }
class Wolf : Animal { class Wolf : Animal {
public override string getYell() { public override string getYell() {
return "Wooooooooooo"; return "Wooooooooooo";
} }
} }
class PieChart {} class PieChart {}
class TestContext : ApplicationContext { class TestContext : ApplicationContext {
@Component @Component
public Banana banana() { public Banana banana() {
return new Banana("Yellow"); return new Banana("Yellow");
} }
public Apple apple() { public Apple apple() {
return new Apple(); return new Apple();
} }
@Component @Component
@RegisterByType!Fruit @RegisterByType!Fruit
public Pear pear() { public Pear pear() {
return new Pear(); return new Pear();
} }
@Component @Component
@RegisterByType!Animal @RegisterByType!Animal
public Rabbit rabbit() { public Rabbit rabbit() {
return new Rabbit(); return new Rabbit();
} }
@Component @Component
@RegisterByType!Animal @RegisterByType!Animal
public Wolf wolf() { public Wolf wolf() {
return new Wolf(); return new Wolf();
} }
@Component @Component
@Prototype @Prototype
public PieChart pieChart() { public PieChart pieChart() {
return new PieChart(); return new PieChart();
} }
} }
//Test register component registrations from context //Test register component registrations from context
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext(); auto context = new TestContext();
context.registerContextComponents(container); context.registerContextComponents(container);
auto bananaInstance = container.resolve!Banana; auto bananaInstance = container.resolve!Banana;
assert(bananaInstance.color == "Yellow"); assert(bananaInstance.color == "Yellow");
} }
//Test non-annotated methods are not registered //Test non-annotated methods are not registered
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext(); auto context = new TestContext();
context.registerContextComponents(container); context.registerContextComponents(container);
assertThrown!ResolveException(container.resolve!Apple); assertThrown!ResolveException(container.resolve!Apple);
} }
//Test register component by base type //Test register component by base type
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext(); auto context = new TestContext();
context.registerContextComponents(container); context.registerContextComponents(container);
auto instance = container.resolve!Fruit; auto instance = container.resolve!Fruit;
assert(instance.getShape() == "Pear shaped"); assert(instance.getShape() == "Pear shaped");
} }
//Test register components with multiple candidates //Test register components with multiple candidates
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext(); auto context = new TestContext();
context.registerContextComponents(container); context.registerContextComponents(container);
auto rabbit = container.resolve!(Animal, Rabbit); auto rabbit = container.resolve!(Animal, Rabbit);
assert(rabbit.getYell() == "Squeeeeeel"); assert(rabbit.getYell() == "Squeeeeeel");
auto wolf = container.resolve!(Animal, Wolf); auto wolf = container.resolve!(Animal, Wolf);
assert(wolf.getYell() == "Wooooooooooo"); assert(wolf.getYell() == "Wooooooooooo");
} }
//Test register component as prototype //Test register component as prototype
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext(); auto context = new TestContext();
context.registerContextComponents(container); context.registerContextComponents(container);
auto firstInstance = container.resolve!PieChart; auto firstInstance = container.resolve!PieChart;
auto secondInstance = container.resolve!PieChart; auto secondInstance = container.resolve!PieChart;
assert(firstInstance !is null && secondInstance !is null); assert(firstInstance !is null && secondInstance !is null);
assert(firstInstance !is secondInstance); assert(firstInstance !is secondInstance);
} }
} }

View file

@ -1,123 +1,123 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee * Copyright 2014-2016 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; import poodinis;
import std.exception; import std.exception;
version(unittest) { version(unittest) {
class TestType {} class TestType {}
interface TestInterface {} interface TestInterface {}
class TestImplementation : TestInterface { class TestImplementation : TestInterface {
public string someContent = ""; public string someContent = "";
} }
// Test getting instance without scope defined throws exception // Test getting instance without scope defined throws exception
unittest { unittest {
Registration registration = new Registration(typeid(TestType), null); Registration registration = new Registration(typeid(TestType), null);
assertThrown!(InstanceCreationException)(registration.getInstance()); assertThrown!(InstanceCreationException)(registration.getInstance());
} }
// Test set single instance scope using scope setter // Test set single instance scope using scope setter
unittest { unittest {
Registration registration = new Registration(null, typeid(TestType)); Registration registration = new Registration(null, typeid(TestType));
auto chainedRegistration = registration.singleInstance(); auto chainedRegistration = registration.singleInstance();
auto instance1 = registration.getInstance(); auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance(); auto instance2 = registration.getInstance();
assert(instance1 is instance2, "Registration with single instance scope did not return the same instance"); assert(instance1 is instance2, "Registration with single 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"); assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set");
} }
// Test set new instance scope using scope setter // Test set new instance scope using scope setter
unittest { unittest {
Registration registration = new Registration(null, typeid(TestType)); Registration registration = new Registration(null, typeid(TestType));
auto chainedRegistration = registration.newInstance(); auto chainedRegistration = registration.newInstance();
auto instance1 = registration.getInstance(); auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance(); auto instance2 = registration.getInstance();
assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance"); assert(instance1 !is instance2, "Registration with new instance scope did not return a different instance");
assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set"); assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set");
} }
// Test set existing instance scope using scope setter // Test set existing instance scope using scope setter
unittest { unittest {
Registration registration = new Registration(null, null); Registration registration = new Registration(null, null);
auto expectedInstance = new TestType(); auto expectedInstance = new TestType();
auto chainedRegistration = registration.existingInstance(expectedInstance); auto chainedRegistration = registration.existingInstance(expectedInstance);
auto actualInstance = registration.getInstance(); auto actualInstance = registration.getInstance();
assert(expectedInstance is expectedInstance, "Registration with existing instance scope did not return the same instance"); assert(expectedInstance is expectedInstance, "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"); assert(registration is chainedRegistration, "Registration returned by scope setting is not the same as the registration being set");
} }
// Test linking registrations // Test linking registrations
unittest { unittest {
Registration firstRegistration = new Registration(typeid(TestInterface), typeid(TestImplementation)).singleInstance(); Registration firstRegistration = new Registration(typeid(TestInterface), typeid(TestImplementation)).singleInstance();
Registration secondRegistration = new Registration(typeid(TestImplementation), typeid(TestImplementation)).singleInstance().linkTo(firstRegistration); Registration secondRegistration = new Registration(typeid(TestImplementation), typeid(TestImplementation)).singleInstance().linkTo(firstRegistration);
auto firstInstance = firstRegistration.getInstance(); auto firstInstance = firstRegistration.getInstance();
auto secondInstance = secondRegistration.getInstance(); auto secondInstance = secondRegistration.getInstance();
assert(firstInstance is secondInstance); assert(firstInstance is secondInstance);
} }
// Test instance factory with singletons // Test instance factory with singletons
unittest { unittest {
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, null); auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, null);
auto instanceOne = factory.getInstance(); auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance(); auto instanceTwo = factory.getInstance();
assert(instanceOne !is null, "Created factory instance is null"); assert(instanceOne !is null, "Created factory instance is null");
assert(instanceOne is instanceTwo, "Created factory instance is not the same"); assert(instanceOne is instanceTwo, "Created factory instance is not the same");
} }
// Test instance factory with new instances // Test instance factory with new instances
unittest { unittest {
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, null); auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, null);
auto instanceOne = factory.getInstance(); auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance(); auto instanceTwo = factory.getInstance();
assert(instanceOne !is null, "Created factory instance is null"); assert(instanceOne !is null, "Created factory instance is null");
assert(instanceOne !is instanceTwo, "Created factory instance is the same"); assert(instanceOne !is instanceTwo, "Created factory instance is the same");
} }
// Test instance factory with existing instances // Test instance factory with existing instances
unittest { unittest {
auto existingInstance = new TestImplementation(); auto existingInstance = new TestImplementation();
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, existingInstance); auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, existingInstance);
auto instanceOne = factory.getInstance(); auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance(); auto instanceTwo = factory.getInstance();
assert(instanceOne is existingInstance, "Created factory instance is not the existing instance"); assert(instanceOne is existingInstance, "Created factory instance is not the existing instance");
assert(instanceTwo is existingInstance, "Created factory instance is not the existing instance when called again"); assert(instanceTwo is existingInstance, "Created factory instance is not the existing instance when called again");
} }
// Test instance factory with existing instances when setting singleton flag to "no" // Test instance factory with existing instances when setting singleton flag to "no"
unittest { unittest {
auto existingInstance = new TestImplementation(); auto existingInstance = new TestImplementation();
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, existingInstance); auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, existingInstance);
auto instance = factory.getInstance(); auto instance = factory.getInstance();
assert(instance is existingInstance, "Created factory instance is not the existing instance"); assert(instance is existingInstance, "Created factory instance is not the existing instance");
} }
// Test creating instance using custom factory method // Test creating instance using custom factory method
unittest { unittest {
Object factoryMethod() { Object factoryMethod() {
auto instance = new TestImplementation(); auto instance = new TestImplementation();
instance.someContent = "Ducks!"; instance.someContent = "Ducks!";
return instance; return instance;
} }
auto factory = new InstanceFactory(null, CreatesSingleton.yes, null, &factoryMethod); auto factory = new InstanceFactory(null, CreatesSingleton.yes, null, &factoryMethod);
auto instance = cast(TestImplementation) factory.getInstance(); auto instance = cast(TestImplementation) factory.getInstance();
assert(instance !is null, "No instance was created by factory or could not be cast to expected type"); assert(instance !is null, "No instance was created by factory or could not be cast to expected type");
assert(instance.someContent == "Ducks!"); assert(instance.someContent == "Ducks!");
} }
} }