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

View file

@ -1,7 +1,7 @@
Poodinis Dependency Injection Framework
=======================================
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)
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.",
"homepage": "http://lostmoment.com/open-source/poodinis",
"authors": ["Mike Bierlee"],
"copyright": "Copyright 2015 Mike Bierlee",
"copyright": "Copyright 2014-2016 Mike Bierlee",
"license": "MIT",
"configurations": [
{

View file

@ -1,50 +1,50 @@
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.random;
import std.digest.md;
import std.stdio;
import std.conv;
class SuperSecurityDevice {
private int seed;
public this() {
auto randomGenerator = Random(unpredictableSeed);
seed = uniform(0, 999, randomGenerator);
}
public string getPassword() {
return to!string(seed) ~ "t1m3sp13!!:";
}
}
class SecurityManager {
@Autowire
public SuperSecurityDevice levelOneSecurity;
@Autowire
@AssignNewInstance
public SuperSecurityDevice levelTwoSecurity;
}
void main() {
auto dependencies = DependencyContainer.getInstance();
dependencies.register!SuperSecurityDevice; // Registered with the default "Single instance" scope
dependencies.register!SecurityManager;
auto manager = dependencies.resolve!SecurityManager;
writeln("Password for user one: " ~ manager.levelOneSecurity.getPassword());
writeln("Password for user two: " ~ manager.levelTwoSecurity.getPassword());
if (manager.levelOneSecurity is manager.levelTwoSecurity) {
writeln("SECURITY BREACH!!!!!");
}
}
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.random;
import std.digest.md;
import std.stdio;
import std.conv;
class SuperSecurityDevice {
private int seed;
public this() {
auto randomGenerator = Random(unpredictableSeed);
seed = uniform(0, 999, randomGenerator);
}
public string getPassword() {
return to!string(seed) ~ "t1m3sp13!!:";
}
}
class SecurityManager {
@Autowire
public SuperSecurityDevice levelOneSecurity;
@Autowire
@AssignNewInstance
public SuperSecurityDevice levelTwoSecurity;
}
void main() {
auto dependencies = DependencyContainer.getInstance();
dependencies.register!SuperSecurityDevice; // Registered with the default "Single instance" scope
dependencies.register!SecurityManager;
auto manager = dependencies.resolve!SecurityManager;
writeln("Password for user one: " ~ manager.levelOneSecurity.getPassword());
writeln("Password for user two: " ~ manager.levelTwoSecurity.getPassword());
if (manager.levelOneSecurity is manager.levelTwoSecurity) {
writeln("SECURITY BREACH!!!!!");
}
}

View file

@ -1,68 +1,68 @@
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.stdio;
class TownSquare {
@Autowire
public MarketStall marketStall;
public void makeSound() {
marketStall.announceGoodsForSale();
}
}
interface Goods {
public string getGoodsName();
}
class Fish : Goods {
public override string getGoodsName() {
return "Fish";
}
}
class MarketStall {
private Goods goods;
this(Goods goods) {
this.goods = goods;
}
public void announceGoodsForSale() {
writeln(goods.getGoodsName() ~ " for sale!");
}
}
class ExampleApplicationContext : ApplicationContext {
@Autowire
public Goods goods;
public override void registerDependencies(shared(DependencyContainer) container) {
container.register!(Goods, Fish);
container.register!TownSquare;
}
@Component
public MarketStall marketStall() {
return new MarketStall(goods);
}
}
void main() {
auto container = DependencyContainer.getInstance();
container.registerContext!ExampleApplicationContext;
auto townSquare = container.resolve!TownSquare;
townSquare.makeSound();
}
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.stdio;
class TownSquare {
@Autowire
public MarketStall marketStall;
public void makeSound() {
marketStall.announceGoodsForSale();
}
}
interface Goods {
public string getGoodsName();
}
class Fish : Goods {
public override string getGoodsName() {
return "Fish";
}
}
class MarketStall {
private Goods goods;
this(Goods goods) {
this.goods = goods;
}
public void announceGoodsForSale() {
writeln(goods.getGoodsName() ~ " for sale!");
}
}
class ExampleApplicationContext : ApplicationContext {
@Autowire
public Goods goods;
public override void registerDependencies(shared(DependencyContainer) container) {
container.register!(Goods, Fish);
container.register!TownSquare;
}
@Component
public MarketStall marketStall() {
return new MarketStall(goods);
}
}
void main() {
auto container = DependencyContainer.getInstance();
container.registerContext!ExampleApplicationContext;
auto townSquare = container.resolve!TownSquare;
townSquare.makeSound();
}

View file

@ -1,54 +1,54 @@
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.stdio;
interface Pie {
public void eat();
}
class BlueBerryPie : Pie {
public override void eat() {
writeln("Nom nom nom. I like this one!");
}
}
class ApplePie : Pie {
public override void eat() {
writeln("Nom nom nom. These aren't real apples...");
}
}
class CardboardBoxPie : Pie {
public override void eat() {
writeln("Nom nom nom. This... is not a pie.");
}
}
class PieEater {
@Autowire
public Pie[] pies;
public void eatThemAll() {
foreach(pie ; pies) {
pie.eat();
}
}
}
void main() {
auto dependencies = DependencyContainer.getInstance();
dependencies.register!(Pie, BlueBerryPie);
dependencies.register!(Pie, ApplePie);
dependencies.register!(Pie, CardboardBoxPie);
dependencies.register!(PieEater);
auto eater = dependencies.resolve!PieEater;
eater.eatThemAll();
}
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.stdio;
interface Pie {
public void eat();
}
class BlueBerryPie : Pie {
public override void eat() {
writeln("Nom nom nom. I like this one!");
}
}
class ApplePie : Pie {
public override void eat() {
writeln("Nom nom nom. These aren't real apples...");
}
}
class CardboardBoxPie : Pie {
public override void eat() {
writeln("Nom nom nom. This... is not a pie.");
}
}
class PieEater {
@Autowire
public Pie[] pies;
public void eatThemAll() {
foreach(pie ; pies) {
pie.eat();
}
}
}
void main() {
auto dependencies = DependencyContainer.getInstance();
dependencies.register!(Pie, BlueBerryPie);
dependencies.register!(Pie, ApplePie);
dependencies.register!(Pie, CardboardBoxPie);
dependencies.register!(PieEater);
auto eater = dependencies.resolve!PieEater;
eater.eatThemAll();
}

View file

@ -1,57 +1,57 @@
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.stdio;
interface Engine {
public void engage();
}
class FuelEngine : Engine {
public void engage() {
writeln("VROOOOOOM!");
}
}
class ElectricEngine : Engine {
public void engage() {
writeln("hummmmmmmm....");
}
}
class HybridCar {
alias KilometersPerHour = int;
@Autowire!FuelEngine
public Engine fuelEngine;
@Autowire!ElectricEngine
public Engine electricEngine;
public void moveAtSpeed(KilometersPerHour speed) {
if (speed <= 45) {
electricEngine.engage();
} else {
fuelEngine.engage();
}
}
}
void main() {
auto dependencies = DependencyContainer.getInstance();
dependencies.register!HybridCar;
dependencies.register!(Engine, FuelEngine);
dependencies.register!(Engine, ElectricEngine);
auto car = dependencies.resolve!HybridCar;
car.moveAtSpeed(10); // Should print "hummmmmmmm...."
car.moveAtSpeed(50); // Should print "VROOOOOOM!"
}
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.stdio;
interface Engine {
public void engage();
}
class FuelEngine : Engine {
public void engage() {
writeln("VROOOOOOM!");
}
}
class ElectricEngine : Engine {
public void engage() {
writeln("hummmmmmmm....");
}
}
class HybridCar {
alias KilometersPerHour = int;
@Autowire!FuelEngine
public Engine fuelEngine;
@Autowire!ElectricEngine
public Engine electricEngine;
public void moveAtSpeed(KilometersPerHour speed) {
if (speed <= 45) {
electricEngine.engage();
} else {
fuelEngine.engage();
}
}
}
void main() {
auto dependencies = DependencyContainer.getInstance();
dependencies.register!HybridCar;
dependencies.register!(Engine, FuelEngine);
dependencies.register!(Engine, ElectricEngine);
auto car = dependencies.resolve!HybridCar;
car.moveAtSpeed(10); // Should print "hummmmmmmm...."
car.moveAtSpeed(50); // Should print "VROOOOOOM!"
}

View file

@ -1,24 +1,24 @@
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
interface Database{};
class RelationalDatabase : Database {}
class DataWriter {
@Autowire
public Database database; // Automatically injected when class is resolved
}
void main() {
auto dependencies = DependencyContainer.getInstance();
dependencies.register!DataWriter;
dependencies.register!(Database, RelationalDatabase);
auto writer = dependencies.resolve!DataWriter;
}
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
interface Database{};
class RelationalDatabase : Database {}
class DataWriter {
@Autowire
public Database database; // Automatically injected when class is resolved
}
void main() {
auto dependencies = DependencyContainer.getInstance();
dependencies.register!DataWriter;
dependencies.register!(Database, RelationalDatabase);
auto writer = dependencies.resolve!DataWriter;
}

View file

@ -1,206 +1,206 @@
/**
* Contains functionality for autowiring dependencies using a dependency container.
*
* 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
* managed by a dependency container.
*
* Part of the Poodinis Dependency Injection framework.
*
* Authors:
* Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2015 Mike Bierlee
* 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.
*/
module poodinis.autowire;
import poodinis.container;
import poodinis.registration;
import std.exception;
import std.stdio;
import std.string;
import std.traits;
import std.range;
struct UseMemberType {};
/**
* 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
* 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.
*
* Examples:
* Annotate member of class to be autowired:
* ---
* class Car {
* @Autowire
* public Engine engine;
* }
* ---
*
* Annotate member of class with qualifier:
* ---
* class FuelEngine : Engine { ... }
* class ElectricEngine : Engine { ... }
*
* class HybridCar {
* @Autowire!FuelEngine
* public Engine fuelEngine;
*
* @Autowire!ElectricEngine
* public Engine electricEngine;
* }
* ---
* 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"
* will also be autowired because the autowire mechanism knows that member "fuelEngine" is an instance of "FuelEngine"
*/
struct Autowire(QualifierType = UseMemberType) {
QualifierType qualifier;
};
/**
* UDA for annotating class members to be autowired with a new instance regardless of their registration scope.
*
* Examples:
*---
* class Car {
* @Autowire
* @AssignNewInstance
* public Antenna antenna;
* }
*---
* antenna will always be assigned a new instance of class Antenna.
*/
struct AssignNewInstance {}
private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) {
writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress));
}
/**
* 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 members are resolved using the given container. Qualifiers are used to determine the type of class to
* resolve for any member of instance.
*
* Note that private members will not be autowired because the autowiring mechanism is not able to by-pass
* member visibility protection.
*
* See_Also: Autowire
*/
public void autowire(Type)(shared(DependencyContainer) container, Type instance) {
debug(poodinisVerbose) {
printDebugAutowiredInstance(typeid(Type), &instance);
}
foreach (member ; __traits(allMembers, Type)) {
autowireMember!member(container, instance);
}
}
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));
}
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));
}
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)))) {
foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, instance, member))) {
static if (__traits(isSame, autowireAttribute, Autowire) || is(autowireAttribute == Autowire!T, T)) {
if (__traits(getMember, instance, member) is null) {
alias MemberType = typeof(__traits(getMember, instance, member));
enum assignNewInstance = hasUDA!(__traits(getMember, instance, member), AssignNewInstance);
static if (isDynamicArray!MemberType) {
alias MemberElementType = ElementType!MemberType;
auto instances = container.resolveAll!MemberElementType;
__traits(getMember, instance, member) = instances;
debug(poodinisVerbose) {
printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member);
}
} else {
debug(poodinisVerbose) {
TypeInfo qualifiedInstanceType = typeid(MemberType);
}
MemberType qualifiedInstance;
static if (is(autowireAttribute == Autowire!T, T) && !is(autowireAttribute.qualifier == UseMemberType)) {
alias QualifierType = typeof(autowireAttribute.qualifier);
qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType, assignNewInstance)(container);
debug(poodinisVerbose) {
qualifiedInstanceType = typeid(QualifierType);
}
} else {
qualifiedInstance = createOrResolveInstance!(MemberType, MemberType, assignNewInstance)(container);
}
__traits(getMember, instance, member) = qualifiedInstance;
debug(poodinisVerbose) {
printDebugAutowiringCandidate(qualifiedInstanceType, &qualifiedInstance, typeid(Type), &instance, member);
}
}
}
break;
}
}
}
}
private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) {
static if (createNew) {
auto instanceFactory = new InstanceFactory(typeid(MemberType), CreatesSingleton.no, null);
return cast(MemberType) instanceFactory.getInstance();
} else {
return container.resolve!(MemberType, QualifierType);
}
}
/**
* Autowire the given instance using the globally available dependency container.
*
* See_Also: DependencyContainer
*/
public void globalAutowire(Type)(Type instance) {
DependencyContainer.getInstance().autowire(instance);
}
class AutowiredRegistration(RegistrationType : Object) : Registration {
private 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.");
this.container = container;
super(registeredType, typeid(RegistrationType));
}
public override Object getInstance(InstantiationContext context = new AutowireInstantiationContext()) {
RegistrationType instance = cast(RegistrationType) super.getInstance(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!");
if (autowireContext.autowireInstance) {
container.autowire(instance);
}
return instance;
}
}
class AutowireInstantiationContext : InstantiationContext {
public bool autowireInstance = true;
}
/**
* Contains functionality for autowiring dependencies using a dependency container.
*
* 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
* managed by a dependency container.
*
* Part of the Poodinis Dependency Injection framework.
*
* Authors:
* Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2016 Mike Bierlee
* 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.
*/
module poodinis.autowire;
import poodinis.container;
import poodinis.registration;
import std.exception;
import std.stdio;
import std.string;
import std.traits;
import std.range;
struct UseMemberType {};
/**
* 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
* 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.
*
* Examples:
* Annotate member of class to be autowired:
* ---
* class Car {
* @Autowire
* public Engine engine;
* }
* ---
*
* Annotate member of class with qualifier:
* ---
* class FuelEngine : Engine { ... }
* class ElectricEngine : Engine { ... }
*
* class HybridCar {
* @Autowire!FuelEngine
* public Engine fuelEngine;
*
* @Autowire!ElectricEngine
* public Engine electricEngine;
* }
* ---
* 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"
* will also be autowired because the autowire mechanism knows that member "fuelEngine" is an instance of "FuelEngine"
*/
struct Autowire(QualifierType = UseMemberType) {
QualifierType qualifier;
};
/**
* UDA for annotating class members to be autowired with a new instance regardless of their registration scope.
*
* Examples:
*---
* class Car {
* @Autowire
* @AssignNewInstance
* public Antenna antenna;
* }
*---
* antenna will always be assigned a new instance of class Antenna.
*/
struct AssignNewInstance {}
private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) {
writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress));
}
/**
* 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 members are resolved using the given container. Qualifiers are used to determine the type of class to
* resolve for any member of instance.
*
* Note that private members will not be autowired because the autowiring mechanism is not able to by-pass
* member visibility protection.
*
* See_Also: Autowire
*/
public void autowire(Type)(shared(DependencyContainer) container, Type instance) {
debug(poodinisVerbose) {
printDebugAutowiredInstance(typeid(Type), &instance);
}
foreach (member ; __traits(allMembers, Type)) {
autowireMember!member(container, instance);
}
}
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));
}
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));
}
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)))) {
foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, instance, member))) {
static if (__traits(isSame, autowireAttribute, Autowire) || is(autowireAttribute == Autowire!T, T)) {
if (__traits(getMember, instance, member) is null) {
alias MemberType = typeof(__traits(getMember, instance, member));
enum assignNewInstance = hasUDA!(__traits(getMember, instance, member), AssignNewInstance);
static if (isDynamicArray!MemberType) {
alias MemberElementType = ElementType!MemberType;
auto instances = container.resolveAll!MemberElementType;
__traits(getMember, instance, member) = instances;
debug(poodinisVerbose) {
printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member);
}
} else {
debug(poodinisVerbose) {
TypeInfo qualifiedInstanceType = typeid(MemberType);
}
MemberType qualifiedInstance;
static if (is(autowireAttribute == Autowire!T, T) && !is(autowireAttribute.qualifier == UseMemberType)) {
alias QualifierType = typeof(autowireAttribute.qualifier);
qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType, assignNewInstance)(container);
debug(poodinisVerbose) {
qualifiedInstanceType = typeid(QualifierType);
}
} else {
qualifiedInstance = createOrResolveInstance!(MemberType, MemberType, assignNewInstance)(container);
}
__traits(getMember, instance, member) = qualifiedInstance;
debug(poodinisVerbose) {
printDebugAutowiringCandidate(qualifiedInstanceType, &qualifiedInstance, typeid(Type), &instance, member);
}
}
}
break;
}
}
}
}
private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) {
static if (createNew) {
auto instanceFactory = new InstanceFactory(typeid(MemberType), CreatesSingleton.no, null);
return cast(MemberType) instanceFactory.getInstance();
} else {
return container.resolve!(MemberType, QualifierType);
}
}
/**
* Autowire the given instance using the globally available dependency container.
*
* See_Also: DependencyContainer
*/
public void globalAutowire(Type)(Type instance) {
DependencyContainer.getInstance().autowire(instance);
}
class AutowiredRegistration(RegistrationType : Object) : Registration {
private 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.");
this.container = container;
super(registeredType, typeid(RegistrationType));
}
public override Object getInstance(InstantiationContext context = new AutowireInstantiationContext()) {
RegistrationType instance = cast(RegistrationType) super.getInstance(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!");
if (autowireContext.autowireInstance) {
container.autowire(instance);
}
return instance;
}
}
class AutowireInstantiationContext : InstantiationContext {
public bool autowireInstance = true;
}

View file

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

View file

@ -1,68 +1,68 @@
/**
* Contains the implementation of application context setup.
*
* Part of the Poodinis Dependency Injection framework.
*
* Authors:
* Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2015 Mike Bierlee
* 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.
*/
module poodinis.context;
import poodinis.container;
import poodinis.registration;
import std.traits;
class ApplicationContext {
public void registerDependencies(shared(DependencyContainer) container) {}
}
/**
* A component annotation is used for specifying which factory methods produce components in
* an application context.
*/
struct Component {}
/**
* 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.
*/
struct RegisterByType(Type) {
Type type;
}
/**
* 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.
*/
struct Prototype {}
public void registerContextComponents(ApplicationContextType : ApplicationContext)(ApplicationContextType context, shared(DependencyContainer) container) {
foreach (member ; __traits(allMembers, ApplicationContextType)) {
static if (hasUDA!(__traits(getMember, context, member), Component)) {
auto factoryMethod = &__traits(getMember, context, member);
Registration registration = null;
auto createsSingleton = CreatesSingleton.yes;
foreach(attribute; __traits(getAttributes, __traits(getMember, context, member))) {
static if (is(attribute == RegisterByType!T, T)) {
registration = container.register!(typeof(attribute.type), ReturnType!factoryMethod);
} else static if (__traits(isSame, attribute, Prototype)) {
createsSingleton = CreatesSingleton.no;
}
}
if (registration is null) {
registration = container.register!(ReturnType!factoryMethod);
}
registration.instanceFactory = new InstanceFactory(registration.instanceType, createsSingleton, null, factoryMethod);
}
}
}
/**
* Contains the implementation of application context setup.
*
* Part of the Poodinis Dependency Injection framework.
*
* Authors:
* Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2016 Mike Bierlee
* 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.
*/
module poodinis.context;
import poodinis.container;
import poodinis.registration;
import std.traits;
class ApplicationContext {
public void registerDependencies(shared(DependencyContainer) container) {}
}
/**
* A component annotation is used for specifying which factory methods produce components in
* an application context.
*/
struct Component {}
/**
* 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.
*/
struct RegisterByType(Type) {
Type type;
}
/**
* 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.
*/
struct Prototype {}
public void registerContextComponents(ApplicationContextType : ApplicationContext)(ApplicationContextType context, shared(DependencyContainer) container) {
foreach (member ; __traits(allMembers, ApplicationContextType)) {
static if (hasUDA!(__traits(getMember, context, member), Component)) {
auto factoryMethod = &__traits(getMember, context, member);
Registration registration = null;
auto createsSingleton = CreatesSingleton.yes;
foreach(attribute; __traits(getAttributes, __traits(getMember, context, member))) {
static if (is(attribute == RegisterByType!T, T)) {
registration = container.register!(typeof(attribute.type), ReturnType!factoryMethod);
} else static if (__traits(isSame, attribute, Prototype)) {
createsSingleton = CreatesSingleton.no;
}
}
if (registration is null) {
registration = container.register!(ReturnType!factoryMethod);
}
registration.instanceFactory = new InstanceFactory(registration.instanceType, createsSingleton, null, factoryMethod);
}
}
}

View file

@ -1,17 +1,17 @@
/**
* Package module for the Poodinis Dependency Injection framework.
*
* Authors:
* Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2015 Mike Bierlee
* 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.
*/
module poodinis;
public import poodinis.autowire;
public import poodinis.container;
public import poodinis.registration;
public import poodinis.context;
/**
* Package module for the Poodinis Dependency Injection framework.
*
* Authors:
* Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2016 Mike Bierlee
* 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.
*/
module poodinis;
public import poodinis.autowire;
public import poodinis.container;
public import poodinis.registration;
public import poodinis.context;

View file

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

View file

@ -1,191 +1,191 @@
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.exception;
version(unittest) {
class ComponentA {}
class ComponentB {
public @Autowire ComponentA componentA;
}
interface InterfaceA {}
class ComponentC : InterfaceA {}
class ComponentD {
public @Autowire InterfaceA componentC = null;
}
class DummyAttribute{};
class ComponentE {
@DummyAttribute
public ComponentC componentC;
}
class ComponentDeclarationCocktail {
alias noomer = int;
@Autowire
public ComponentA componentA;
public void doesNothing() {
}
~this(){
}
}
class ComponentX : InterfaceA {}
class MonkeyShine {
@Autowire!ComponentX
public InterfaceA component;
}
class BootstrapBootstrap {
@Autowire!ComponentX
public InterfaceA componentX;
@Autowire!ComponentC
public InterfaceA componentC;
}
class LordOfTheComponents {
@Autowire
public InterfaceA[] components;
}
class ComponentCharlie {
@Autowire
@AssignNewInstance
public ComponentA componentA;
}
// Test autowiring concrete type to existing instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA;
auto componentB = new ComponentB();
container.autowire!(ComponentB)(componentB);
assert(componentB !is null, "Autowirable dependency failed to autowire");
}
// Test autowiring interface type to existing instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
auto componentD = new ComponentD();
container.autowire!(ComponentD)(componentD);
assert(componentD.componentC !is null, "Autowirable dependency failed to autowire");
}
// Test autowiring will only happen once
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC).newInstance();
auto componentD = new ComponentD();
container.autowire!(ComponentD)(componentD);
auto expectedComponent = componentD.componentC;
container.autowire!(ComponentD)(componentD);
auto actualComponent = componentD.componentC;
assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance");
}
// Test autowiring unregistered type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto componentD = new ComponentD();
assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException");
}
// Test autowiring member with non-autowire attribute does not autowire
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto componentE = new ComponentE();
container.autowire!ComponentE(componentE);
assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire");
}
// Test autowire class with alias declaration
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA;
auto componentDeclarationCocktail = new ComponentDeclarationCocktail();
container.autowire(componentDeclarationCocktail);
assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed");
}
// Test autowire class with qualifier
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto componentX = container.resolve!(InterfaceA, ComponentX);
auto monkeyShine = new MonkeyShine();
container.autowire(monkeyShine);
assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed");
}
// Test autowire class with multiple qualifiers
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto componentC = container.resolve!(InterfaceA, ComponentC);
auto componentX = container.resolve!(InterfaceA, ComponentX);
auto bootstrapBootstrap = new BootstrapBootstrap();
container.autowire(bootstrapBootstrap);
assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed");
assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed");
}
// Test getting instance from autowired registration will autowire instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA;
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance();
auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext());
assert(instance.componentA !is null);
}
// Test autowiring a dynamic array with all qualified types
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto lord = new LordOfTheComponents();
container.autowire(lord);
assert(lord.components.length == 2, "Dynamic array was not autowired");
}
// Test autowiring new instance of singleinstance registration with newInstance UDA
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA;
auto regularComponentA = container.resolve!ComponentA;
auto charlie = new ComponentCharlie();
container.autowire(charlie);
assert(charlie.componentA !is regularComponentA, "Autowiring class with AssignNewInstance did not yield a different instance");
}
}
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.exception;
version(unittest) {
class ComponentA {}
class ComponentB {
public @Autowire ComponentA componentA;
}
interface InterfaceA {}
class ComponentC : InterfaceA {}
class ComponentD {
public @Autowire InterfaceA componentC = null;
}
class DummyAttribute{};
class ComponentE {
@DummyAttribute
public ComponentC componentC;
}
class ComponentDeclarationCocktail {
alias noomer = int;
@Autowire
public ComponentA componentA;
public void doesNothing() {
}
~this(){
}
}
class ComponentX : InterfaceA {}
class MonkeyShine {
@Autowire!ComponentX
public InterfaceA component;
}
class BootstrapBootstrap {
@Autowire!ComponentX
public InterfaceA componentX;
@Autowire!ComponentC
public InterfaceA componentC;
}
class LordOfTheComponents {
@Autowire
public InterfaceA[] components;
}
class ComponentCharlie {
@Autowire
@AssignNewInstance
public ComponentA componentA;
}
// Test autowiring concrete type to existing instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA;
auto componentB = new ComponentB();
container.autowire!(ComponentB)(componentB);
assert(componentB !is null, "Autowirable dependency failed to autowire");
}
// Test autowiring interface type to existing instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
auto componentD = new ComponentD();
container.autowire!(ComponentD)(componentD);
assert(componentD.componentC !is null, "Autowirable dependency failed to autowire");
}
// Test autowiring will only happen once
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC).newInstance();
auto componentD = new ComponentD();
container.autowire!(ComponentD)(componentD);
auto expectedComponent = componentD.componentC;
container.autowire!(ComponentD)(componentD);
auto actualComponent = componentD.componentC;
assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance");
}
// Test autowiring unregistered type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto componentD = new ComponentD();
assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException");
}
// Test autowiring member with non-autowire attribute does not autowire
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto componentE = new ComponentE();
container.autowire!ComponentE(componentE);
assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire");
}
// Test autowire class with alias declaration
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA;
auto componentDeclarationCocktail = new ComponentDeclarationCocktail();
container.autowire(componentDeclarationCocktail);
assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed");
}
// Test autowire class with qualifier
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto componentX = container.resolve!(InterfaceA, ComponentX);
auto monkeyShine = new MonkeyShine();
container.autowire(monkeyShine);
assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed");
}
// Test autowire class with multiple qualifiers
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto componentC = container.resolve!(InterfaceA, ComponentC);
auto componentX = container.resolve!(InterfaceA, ComponentX);
auto bootstrapBootstrap = new BootstrapBootstrap();
container.autowire(bootstrapBootstrap);
assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed");
assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed");
}
// Test getting instance from autowired registration will autowire instance
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA;
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance();
auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext());
assert(instance.componentA !is null);
}
// Test autowiring a dynamic array with all qualified types
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX);
auto lord = new LordOfTheComponents();
container.autowire(lord);
assert(lord.components.length == 2, "Dynamic array was not autowired");
}
// Test autowiring new instance of singleinstance registration with newInstance UDA
unittest {
shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA;
auto regularComponentA = container.resolve!ComponentA;
auto charlie = new ComponentCharlie();
container.autowire(charlie);
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
* Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.exception;
version(unittest) {
interface Fruit {
string getShape();
}
interface Animal {
string getYell();
}
class Banana {
public string color;
this(string color) {
this.color = color;
}
}
class Apple {}
class Pear : Fruit {
public override string getShape() {
return "Pear shaped";
}
}
class Rabbit : Animal {
public override string getYell() {
return "Squeeeeeel";
}
}
class Wolf : Animal {
public override string getYell() {
return "Wooooooooooo";
}
}
class PieChart {}
class TestContext : ApplicationContext {
@Component
public Banana banana() {
return new Banana("Yellow");
}
public Apple apple() {
return new Apple();
}
@Component
@RegisterByType!Fruit
public Pear pear() {
return new Pear();
}
@Component
@RegisterByType!Animal
public Rabbit rabbit() {
return new Rabbit();
}
@Component
@RegisterByType!Animal
public Wolf wolf() {
return new Wolf();
}
@Component
@Prototype
public PieChart pieChart() {
return new PieChart();
}
}
//Test register component registrations from context
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto bananaInstance = container.resolve!Banana;
assert(bananaInstance.color == "Yellow");
}
//Test non-annotated methods are not registered
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
assertThrown!ResolveException(container.resolve!Apple);
}
//Test register component by base type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto instance = container.resolve!Fruit;
assert(instance.getShape() == "Pear shaped");
}
//Test register components with multiple candidates
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto rabbit = container.resolve!(Animal, Rabbit);
assert(rabbit.getYell() == "Squeeeeeel");
auto wolf = container.resolve!(Animal, Wolf);
assert(wolf.getYell() == "Wooooooooooo");
}
//Test register component as prototype
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto firstInstance = container.resolve!PieChart;
auto secondInstance = container.resolve!PieChart;
assert(firstInstance !is null && secondInstance !is null);
assert(firstInstance !is secondInstance);
}
}
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.exception;
version(unittest) {
interface Fruit {
string getShape();
}
interface Animal {
string getYell();
}
class Banana {
public string color;
this(string color) {
this.color = color;
}
}
class Apple {}
class Pear : Fruit {
public override string getShape() {
return "Pear shaped";
}
}
class Rabbit : Animal {
public override string getYell() {
return "Squeeeeeel";
}
}
class Wolf : Animal {
public override string getYell() {
return "Wooooooooooo";
}
}
class PieChart {}
class TestContext : ApplicationContext {
@Component
public Banana banana() {
return new Banana("Yellow");
}
public Apple apple() {
return new Apple();
}
@Component
@RegisterByType!Fruit
public Pear pear() {
return new Pear();
}
@Component
@RegisterByType!Animal
public Rabbit rabbit() {
return new Rabbit();
}
@Component
@RegisterByType!Animal
public Wolf wolf() {
return new Wolf();
}
@Component
@Prototype
public PieChart pieChart() {
return new PieChart();
}
}
//Test register component registrations from context
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto bananaInstance = container.resolve!Banana;
assert(bananaInstance.color == "Yellow");
}
//Test non-annotated methods are not registered
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
assertThrown!ResolveException(container.resolve!Apple);
}
//Test register component by base type
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto instance = container.resolve!Fruit;
assert(instance.getShape() == "Pear shaped");
}
//Test register components with multiple candidates
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto rabbit = container.resolve!(Animal, Rabbit);
assert(rabbit.getYell() == "Squeeeeeel");
auto wolf = container.resolve!(Animal, Wolf);
assert(wolf.getYell() == "Wooooooooooo");
}
//Test register component as prototype
unittest {
shared(DependencyContainer) container = new DependencyContainer();
auto context = new TestContext();
context.registerContextComponents(container);
auto firstInstance = container.resolve!PieChart;
auto secondInstance = container.resolve!PieChart;
assert(firstInstance !is null && secondInstance !is null);
assert(firstInstance !is secondInstance);
}
}

View file

@ -1,123 +1,123 @@
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2015 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.exception;
version(unittest) {
class TestType {}
interface TestInterface {}
class TestImplementation : TestInterface {
public string someContent = "";
}
// Test getting instance without scope defined throws exception
unittest {
Registration registration = new Registration(typeid(TestType), null);
assertThrown!(InstanceCreationException)(registration.getInstance());
}
// Test set single instance scope using scope setter
unittest {
Registration registration = new Registration(null, typeid(TestType));
auto chainedRegistration = registration.singleInstance();
auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance();
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");
}
// Test set new instance scope using scope setter
unittest {
Registration registration = new Registration(null, typeid(TestType));
auto chainedRegistration = registration.newInstance();
auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance();
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");
}
// Test set existing instance scope using scope setter
unittest {
Registration registration = new Registration(null, null);
auto expectedInstance = new TestType();
auto chainedRegistration = registration.existingInstance(expectedInstance);
auto actualInstance = registration.getInstance();
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");
}
// Test linking registrations
unittest {
Registration firstRegistration = new Registration(typeid(TestInterface), typeid(TestImplementation)).singleInstance();
Registration secondRegistration = new Registration(typeid(TestImplementation), typeid(TestImplementation)).singleInstance().linkTo(firstRegistration);
auto firstInstance = firstRegistration.getInstance();
auto secondInstance = secondRegistration.getInstance();
assert(firstInstance is secondInstance);
}
// Test instance factory with singletons
unittest {
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, null);
auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance();
assert(instanceOne !is null, "Created factory instance is null");
assert(instanceOne is instanceTwo, "Created factory instance is not the same");
}
// Test instance factory with new instances
unittest {
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, null);
auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance();
assert(instanceOne !is null, "Created factory instance is null");
assert(instanceOne !is instanceTwo, "Created factory instance is the same");
}
// Test instance factory with existing instances
unittest {
auto existingInstance = new TestImplementation();
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, existingInstance);
auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance();
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");
}
// Test instance factory with existing instances when setting singleton flag to "no"
unittest {
auto existingInstance = new TestImplementation();
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, existingInstance);
auto instance = factory.getInstance();
assert(instance is existingInstance, "Created factory instance is not the existing instance");
}
// Test creating instance using custom factory method
unittest {
Object factoryMethod() {
auto instance = new TestImplementation();
instance.someContent = "Ducks!";
return instance;
}
auto factory = new InstanceFactory(null, CreatesSingleton.yes, null, &factoryMethod);
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.someContent == "Ducks!");
}
}
/**
* Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file.
*/
import poodinis;
import std.exception;
version(unittest) {
class TestType {}
interface TestInterface {}
class TestImplementation : TestInterface {
public string someContent = "";
}
// Test getting instance without scope defined throws exception
unittest {
Registration registration = new Registration(typeid(TestType), null);
assertThrown!(InstanceCreationException)(registration.getInstance());
}
// Test set single instance scope using scope setter
unittest {
Registration registration = new Registration(null, typeid(TestType));
auto chainedRegistration = registration.singleInstance();
auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance();
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");
}
// Test set new instance scope using scope setter
unittest {
Registration registration = new Registration(null, typeid(TestType));
auto chainedRegistration = registration.newInstance();
auto instance1 = registration.getInstance();
auto instance2 = registration.getInstance();
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");
}
// Test set existing instance scope using scope setter
unittest {
Registration registration = new Registration(null, null);
auto expectedInstance = new TestType();
auto chainedRegistration = registration.existingInstance(expectedInstance);
auto actualInstance = registration.getInstance();
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");
}
// Test linking registrations
unittest {
Registration firstRegistration = new Registration(typeid(TestInterface), typeid(TestImplementation)).singleInstance();
Registration secondRegistration = new Registration(typeid(TestImplementation), typeid(TestImplementation)).singleInstance().linkTo(firstRegistration);
auto firstInstance = firstRegistration.getInstance();
auto secondInstance = secondRegistration.getInstance();
assert(firstInstance is secondInstance);
}
// Test instance factory with singletons
unittest {
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, null);
auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance();
assert(instanceOne !is null, "Created factory instance is null");
assert(instanceOne is instanceTwo, "Created factory instance is not the same");
}
// Test instance factory with new instances
unittest {
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, null);
auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance();
assert(instanceOne !is null, "Created factory instance is null");
assert(instanceOne !is instanceTwo, "Created factory instance is the same");
}
// Test instance factory with existing instances
unittest {
auto existingInstance = new TestImplementation();
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.yes, existingInstance);
auto instanceOne = factory.getInstance();
auto instanceTwo = factory.getInstance();
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");
}
// Test instance factory with existing instances when setting singleton flag to "no"
unittest {
auto existingInstance = new TestImplementation();
auto factory = new InstanceFactory(typeid(TestImplementation), CreatesSingleton.no, existingInstance);
auto instance = factory.getInstance();
assert(instance is existingInstance, "Created factory instance is not the existing instance");
}
// Test creating instance using custom factory method
unittest {
Object factoryMethod() {
auto instance = new TestImplementation();
instance.someContent = "Ducks!";
return instance;
}
auto factory = new InstanceFactory(null, CreatesSingleton.yes, null, &factoryMethod);
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.someContent == "Ducks!");
}
}