mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 04:04:01 +01:00
Update copyrights
This commit is contained in:
parent
4be0e28bba
commit
c35e494dfe
38
LICENSE.txt
38
LICENSE.txt
|
@ -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.
|
||||
|
|
|
@ -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)
|
||||
|
|
2
dub.json
2
dub.json
|
@ -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": [
|
||||
{
|
||||
|
|
|
@ -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!!!!!");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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!"
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {}
|
||||
|
|
|
@ -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
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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!");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue