mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 04:04:01 +01:00
Autoformat code according to The D Style
This commit is contained in:
parent
2b40ab95cf
commit
dcaa4d77be
|
@ -12,47 +12,53 @@ import std.digest.md;
|
|||
import std.stdio;
|
||||
import std.conv;
|
||||
|
||||
class SecurityAuditor {
|
||||
public void submitAudit() {
|
||||
class SecurityAuditor
|
||||
{
|
||||
public void submitAudit()
|
||||
{
|
||||
writeln("Hmmmyes I have received your audit. It is.... adequate.");
|
||||
}
|
||||
}
|
||||
|
||||
class SuperSecurityDevice {
|
||||
class SuperSecurityDevice
|
||||
{
|
||||
private int seed;
|
||||
|
||||
public this() {
|
||||
public this()
|
||||
{
|
||||
auto randomGenerator = Random(unpredictableSeed);
|
||||
seed = uniform(0, 999, randomGenerator);
|
||||
}
|
||||
|
||||
public string getPassword() {
|
||||
public string getPassword()
|
||||
{
|
||||
return to!string(seed) ~ "t1m3sp13!!:";
|
||||
}
|
||||
}
|
||||
|
||||
class SecurityManager {
|
||||
@Autowire
|
||||
private SuperSecurityDevice levelOneSecurity;
|
||||
class SecurityManager
|
||||
{
|
||||
@Autowire private SuperSecurityDevice levelOneSecurity;
|
||||
|
||||
@Autowire
|
||||
@AssignNewInstance
|
||||
private SuperSecurityDevice levelTwoSecurity;
|
||||
@Autowire @AssignNewInstance private SuperSecurityDevice levelTwoSecurity;
|
||||
|
||||
@Autowire
|
||||
@OptionalDependency
|
||||
private SecurityAuditor auditor;
|
||||
@Autowire @OptionalDependency private SecurityAuditor auditor;
|
||||
|
||||
public void doAudit() {
|
||||
if (auditor !is null) {
|
||||
public void doAudit()
|
||||
{
|
||||
if (auditor !is null)
|
||||
{
|
||||
auditor.submitAudit();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln("I uh, will skip the audit for now...");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
auto dependencies = new shared DependencyContainer();
|
||||
dependencies.register!SuperSecurityDevice; // Registered with the default "Single instance" scope
|
||||
dependencies.register!SecurityManager;
|
||||
|
@ -62,9 +68,12 @@ void main() {
|
|||
writeln("Password for user one: " ~ manager.levelOneSecurity.getPassword());
|
||||
writeln("Password for user two: " ~ manager.levelTwoSecurity.getPassword());
|
||||
|
||||
if (manager.levelOneSecurity is manager.levelTwoSecurity) {
|
||||
if (manager.levelOneSecurity is manager.levelTwoSecurity)
|
||||
{
|
||||
writeln("SECURITY BREACH!!!!!"); // Should not be printed since levelTwoSecurity is a new instance.
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln("Security okay!");
|
||||
}
|
||||
|
||||
|
|
|
@ -9,57 +9,64 @@ import poodinis;
|
|||
|
||||
import std.stdio;
|
||||
|
||||
class TownSquare {
|
||||
class TownSquare
|
||||
{
|
||||
|
||||
@Autowire
|
||||
private MarketStall marketStall;
|
||||
@Autowire private MarketStall marketStall;
|
||||
|
||||
public void makeSound() {
|
||||
public void makeSound()
|
||||
{
|
||||
marketStall.announceGoodsForSale();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
interface Goods {
|
||||
interface Goods
|
||||
{
|
||||
public string getGoodsName();
|
||||
}
|
||||
|
||||
class Fish : Goods {
|
||||
public override string getGoodsName() {
|
||||
class Fish : Goods
|
||||
{
|
||||
public override string getGoodsName()
|
||||
{
|
||||
return "Fish";
|
||||
}
|
||||
}
|
||||
|
||||
class MarketStall {
|
||||
class MarketStall
|
||||
{
|
||||
private Goods goods;
|
||||
|
||||
this(Goods goods) {
|
||||
this(Goods goods)
|
||||
{
|
||||
this.goods = goods;
|
||||
}
|
||||
|
||||
public void announceGoodsForSale() {
|
||||
public void announceGoodsForSale()
|
||||
{
|
||||
writeln(goods.getGoodsName() ~ " for sale!");
|
||||
}
|
||||
}
|
||||
|
||||
class ExampleApplicationContext : ApplicationContext {
|
||||
class ExampleApplicationContext : ApplicationContext
|
||||
{
|
||||
|
||||
@Autowire
|
||||
private Goods goods;
|
||||
@Autowire private Goods goods;
|
||||
|
||||
public override void registerDependencies(shared(DependencyContainer) container) {
|
||||
public override void registerDependencies(shared(DependencyContainer) container)
|
||||
{
|
||||
container.register!(Goods, Fish);
|
||||
container.register!TownSquare;
|
||||
}
|
||||
|
||||
@Component
|
||||
public MarketStall marketStall() {
|
||||
@Component public MarketStall marketStall()
|
||||
{
|
||||
return new MarketStall(goods);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.registerContext!ExampleApplicationContext;
|
||||
|
||||
|
|
|
@ -9,40 +9,50 @@ import poodinis;
|
|||
|
||||
import std.stdio;
|
||||
|
||||
interface Pie {
|
||||
interface Pie
|
||||
{
|
||||
public void eat();
|
||||
}
|
||||
|
||||
class BlueBerryPie : Pie {
|
||||
public override void eat() {
|
||||
class BlueBerryPie : Pie
|
||||
{
|
||||
public override void eat()
|
||||
{
|
||||
writeln("Nom nom nom. I like this one!");
|
||||
}
|
||||
}
|
||||
|
||||
class ApplePie : Pie {
|
||||
public override void eat() {
|
||||
class ApplePie : Pie
|
||||
{
|
||||
public override void eat()
|
||||
{
|
||||
writeln("Nom nom nom. These aren't real apples...");
|
||||
}
|
||||
}
|
||||
|
||||
class CardboardBoxPie : Pie {
|
||||
public override void eat() {
|
||||
class CardboardBoxPie : Pie
|
||||
{
|
||||
public override void eat()
|
||||
{
|
||||
writeln("Nom nom nom. This... is not a pie.");
|
||||
}
|
||||
}
|
||||
|
||||
class PieEater {
|
||||
@Autowire
|
||||
private Pie[] pies;
|
||||
class PieEater
|
||||
{
|
||||
@Autowire private Pie[] pies;
|
||||
|
||||
public void eatThemAll() {
|
||||
foreach(pie ; pies) {
|
||||
public void eatThemAll()
|
||||
{
|
||||
foreach (pie; pies)
|
||||
{
|
||||
pie.eat();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
auto dependencies = new shared DependencyContainer();
|
||||
dependencies.register!(Pie, BlueBerryPie);
|
||||
dependencies.register!(Pie, ApplePie);
|
||||
|
|
|
@ -5,54 +5,66 @@
|
|||
* The full terms of the license can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
import std.stdio;
|
||||
import std.stdio;
|
||||
|
||||
class Scheduler {
|
||||
class Scheduler
|
||||
{
|
||||
private Calendar calendar;
|
||||
|
||||
// All parameters will autmatically be assigned when Scheduler is created.
|
||||
this(Calendar calendar) {
|
||||
this(Calendar calendar)
|
||||
{
|
||||
this.calendar = calendar;
|
||||
}
|
||||
|
||||
public void scheduleJob() {
|
||||
public void scheduleJob()
|
||||
{
|
||||
calendar.findOpenDate();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class Calendar {
|
||||
class Calendar
|
||||
{
|
||||
private HardwareClock hardwareClock;
|
||||
|
||||
// This constructor contains built-in type "int" and thus will not be used.
|
||||
this(int initialDateTimeStamp, HardwareClock hardwareClock) {
|
||||
this(int initialDateTimeStamp, HardwareClock hardwareClock)
|
||||
{
|
||||
}
|
||||
|
||||
// This constructor is chosen instead as candidate for injection when Calendar is created.
|
||||
this(HardwareClock hardwareClock) {
|
||||
this(HardwareClock hardwareClock)
|
||||
{
|
||||
this.hardwareClock = hardwareClock;
|
||||
}
|
||||
|
||||
public void findOpenDate() {
|
||||
public void findOpenDate()
|
||||
{
|
||||
hardwareClock.doThings();
|
||||
}
|
||||
}
|
||||
|
||||
class HardwareClock {
|
||||
class HardwareClock
|
||||
{
|
||||
// Parameterless constructors will halt any further selection of constructors.
|
||||
this() {}
|
||||
this()
|
||||
{
|
||||
}
|
||||
|
||||
// As a result, this constructor will not be used when HardwareClock is created.
|
||||
this(Calendar calendar) {
|
||||
this(Calendar calendar)
|
||||
{
|
||||
throw new Exception("This constructor should not be used by Poodinis");
|
||||
}
|
||||
|
||||
public void doThings() {
|
||||
public void doThings()
|
||||
{
|
||||
writeln("Things are being done!");
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
import poodinis; // Locally imported to emphasize that classes do not depend on Poodinis.
|
||||
|
||||
auto dependencies = new shared DependencyContainer();
|
||||
|
|
|
@ -9,38 +9,44 @@ import poodinis;
|
|||
|
||||
import std.stdio;
|
||||
|
||||
class ADependency {
|
||||
@PostConstruct
|
||||
public void postConstructor() {
|
||||
class ADependency
|
||||
{
|
||||
@PostConstruct public void postConstructor()
|
||||
{
|
||||
writeln("The dependency is created.");
|
||||
}
|
||||
|
||||
public void callMe() {
|
||||
public void callMe()
|
||||
{
|
||||
writeln("The dependency was called.");
|
||||
}
|
||||
}
|
||||
|
||||
class AClass {
|
||||
@Autowire
|
||||
public ADependency dependency; // Dependencies are autowired before the post-constructor is called.
|
||||
class AClass
|
||||
{
|
||||
@Autowire public ADependency dependency; // Dependencies are autowired before the post-constructor is called.
|
||||
|
||||
@PostConstruct
|
||||
public void postConstructor() {
|
||||
@PostConstruct public void postConstructor()
|
||||
{
|
||||
writeln("The class is created.");
|
||||
if (dependency !is null) {
|
||||
if (dependency !is null)
|
||||
{
|
||||
writeln("The dependency is autowired.");
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln("The dependency was NOT autowired.");
|
||||
}
|
||||
}
|
||||
|
||||
@PreDestroy
|
||||
public void preDestructor() {
|
||||
@PreDestroy public void preDestructor()
|
||||
{
|
||||
writeln("The class is no longer registered with the container.");
|
||||
}
|
||||
}
|
||||
|
||||
public void main() {
|
||||
public void main()
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(ADependency).onConstructed((Object obj) {
|
||||
writeln("ADependency constructed");
|
||||
|
|
|
@ -9,41 +9,50 @@ import poodinis;
|
|||
|
||||
import std.stdio;
|
||||
|
||||
interface Engine {
|
||||
interface Engine
|
||||
{
|
||||
public void engage();
|
||||
}
|
||||
|
||||
class FuelEngine : Engine {
|
||||
public void engage() {
|
||||
class FuelEngine : Engine
|
||||
{
|
||||
public void engage()
|
||||
{
|
||||
writeln("VROOOOOOM!");
|
||||
}
|
||||
}
|
||||
|
||||
class ElectricEngine : Engine {
|
||||
public void engage() {
|
||||
class ElectricEngine : Engine
|
||||
{
|
||||
public void engage()
|
||||
{
|
||||
writeln("hummmmmmmm....");
|
||||
}
|
||||
}
|
||||
|
||||
class HybridCar {
|
||||
class HybridCar
|
||||
{
|
||||
alias KilometersPerHour = int;
|
||||
|
||||
@Autowire!FuelEngine
|
||||
private Engine fuelEngine;
|
||||
@Autowire!FuelEngine private Engine fuelEngine;
|
||||
|
||||
@Autowire!ElectricEngine
|
||||
private Engine electricEngine;
|
||||
@Autowire!ElectricEngine private Engine electricEngine;
|
||||
|
||||
public void moveAtSpeed(KilometersPerHour speed) {
|
||||
if (speed <= 45) {
|
||||
public void moveAtSpeed(KilometersPerHour speed)
|
||||
{
|
||||
if (speed <= 45)
|
||||
{
|
||||
electricEngine.engage();
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
fuelEngine.engage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
auto dependencies = new shared DependencyContainer();
|
||||
|
||||
dependencies.register!HybridCar;
|
||||
|
|
|
@ -7,24 +7,31 @@
|
|||
|
||||
import poodinis;
|
||||
|
||||
class Driver {}
|
||||
class Driver
|
||||
{
|
||||
}
|
||||
|
||||
interface Database {};
|
||||
interface Database
|
||||
{
|
||||
}
|
||||
|
||||
class RelationalDatabase : Database {
|
||||
class RelationalDatabase : Database
|
||||
{
|
||||
private Driver driver;
|
||||
|
||||
this(Driver driver) { // Automatically injected on creation by container
|
||||
this(Driver driver)
|
||||
{ // Automatically injected on creation by container
|
||||
this.driver = driver;
|
||||
}
|
||||
}
|
||||
|
||||
class DataWriter {
|
||||
@Autowire
|
||||
private Database database; // Automatically injected when class is resolved
|
||||
class DataWriter
|
||||
{
|
||||
@Autowire private Database database; // Automatically injected when class is resolved
|
||||
}
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
auto dependencies = new shared DependencyContainer();
|
||||
dependencies.register!Driver;
|
||||
dependencies.register!DataWriter;
|
||||
|
|
|
@ -7,25 +7,28 @@
|
|||
|
||||
import poodinis;
|
||||
|
||||
class Violin {
|
||||
class Violin
|
||||
{
|
||||
}
|
||||
|
||||
interface InstrumentPlayer {
|
||||
interface InstrumentPlayer
|
||||
{
|
||||
}
|
||||
|
||||
class ViolinPlayer : InstrumentPlayer {
|
||||
class ViolinPlayer : InstrumentPlayer
|
||||
{
|
||||
// Autowired concrete types can be registered on resolve
|
||||
@Autowire
|
||||
private Violin violin;
|
||||
@Autowire private Violin violin;
|
||||
}
|
||||
|
||||
class Orchestra {
|
||||
class Orchestra
|
||||
{
|
||||
// Autowired non-concrete types can be registered on resolved, given they have a qualifier.
|
||||
@Autowire!ViolinPlayer
|
||||
private InstrumentPlayer violinPlayer;
|
||||
@Autowire!ViolinPlayer private InstrumentPlayer violinPlayer;
|
||||
}
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
auto dependencies = new shared DependencyContainer();
|
||||
|
||||
/*
|
||||
|
|
|
@ -10,9 +10,12 @@ import poodinis;
|
|||
import std.stdio;
|
||||
import std.string;
|
||||
|
||||
class IntValueInjector : ValueInjector!int {
|
||||
int get(string key) {
|
||||
switch(key) {
|
||||
class IntValueInjector : ValueInjector!int
|
||||
{
|
||||
int get(string key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case "http.port":
|
||||
return 8080;
|
||||
case "http.keep_alive":
|
||||
|
@ -23,9 +26,12 @@ class IntValueInjector : ValueInjector!int {
|
|||
}
|
||||
}
|
||||
|
||||
class StringValueInjector : ValueInjector!string {
|
||||
string get(string key) {
|
||||
switch(key) {
|
||||
class StringValueInjector : ValueInjector!string
|
||||
{
|
||||
string get(string key)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case "http.hostname":
|
||||
return "acme.org";
|
||||
default:
|
||||
|
@ -34,7 +40,8 @@ class StringValueInjector : ValueInjector!string {
|
|||
}
|
||||
}
|
||||
|
||||
class HttpServer {
|
||||
class HttpServer
|
||||
{
|
||||
|
||||
@Value("http.port")
|
||||
private int port = 80;
|
||||
|
@ -48,12 +55,15 @@ class HttpServer {
|
|||
@MandatoryValue("http.keep_alive")
|
||||
private int keepAliveTime; // A ResolveException is thrown when the value is not available, default assignments are not used.
|
||||
|
||||
public void serve() {
|
||||
writeln(format("Serving pages for %s:%s with max connection count of %s", hostName, port, maxConnections));
|
||||
public void serve()
|
||||
{
|
||||
writeln(format("Serving pages for %s:%s with max connection count of %s",
|
||||
hostName, port, maxConnections));
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
void main()
|
||||
{
|
||||
auto dependencies = new shared DependencyContainer();
|
||||
dependencies.register!(ValueInjector!int, IntValueInjector);
|
||||
dependencies.register!(ValueInjector!string, StringValueInjector);
|
||||
|
|
|
@ -20,7 +20,8 @@ module poodinis.autowire;
|
|||
import poodinis.container : DependencyContainer, PreDestroy, ResolveException, ResolveOption;
|
||||
import poodinis.registration : Registration, InstantiationContext;
|
||||
import poodinis.factory : InstanceFactory, InstanceFactoryParameters, CreatesSingleton;
|
||||
import poodinis.valueinjection : ValueInjector, ValueInjectionException, ValueNotAvailableException, Value, MandatoryValue;
|
||||
import poodinis.valueinjection : ValueInjector, ValueInjectionException,
|
||||
ValueNotAvailableException, Value, MandatoryValue;
|
||||
import poodinis.altphobos : isFunction;
|
||||
import poodinis.imports : createImportsString;
|
||||
|
||||
|
@ -29,11 +30,14 @@ import std.string : format;
|
|||
import std.traits : BaseClassesTuple, FieldNameTuple, fullyQualifiedName, hasUDA, isDynamicArray;
|
||||
import std.range : ElementType;
|
||||
|
||||
debug {
|
||||
debug
|
||||
{
|
||||
import std.stdio : writeln;
|
||||
}
|
||||
|
||||
private struct UseMemberType {}
|
||||
private struct UseMemberType
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* UDA for annotating class members as candidates for autowiring.
|
||||
|
@ -68,7 +72,8 @@ private struct UseMemberType {}
|
|||
* 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) {
|
||||
struct Autowire(QualifierType)
|
||||
{
|
||||
QualifierType qualifier;
|
||||
}
|
||||
|
||||
|
@ -77,7 +82,9 @@ struct Autowire(QualifierType) {
|
|||
* Optional dependencies will not lead to a resolveException when there is no type registered for them.
|
||||
* The member will remain null.
|
||||
*/
|
||||
struct OptionalDependency {}
|
||||
struct OptionalDependency
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* UDA for annotating class members to be autowired with a new instance regardless of their registration scope.
|
||||
|
@ -92,10 +99,14 @@ struct OptionalDependency {}
|
|||
*---
|
||||
* antenna will always be assigned a new instance of class Antenna.
|
||||
*/
|
||||
struct AssignNewInstance {}
|
||||
struct AssignNewInstance
|
||||
{
|
||||
}
|
||||
|
||||
private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) {
|
||||
debug {
|
||||
private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress)
|
||||
{
|
||||
debug
|
||||
{
|
||||
writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress));
|
||||
}
|
||||
}
|
||||
|
@ -109,135 +120,207 @@ private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAd
|
|||
*
|
||||
* See_Also: Autowire
|
||||
*/
|
||||
public void autowire(Type)(shared(DependencyContainer) container, Type instance) {
|
||||
debug(poodinisVerbose) {
|
||||
public void autowire(Type)(shared(DependencyContainer) container, Type instance)
|
||||
{
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
printDebugAutowiredInstance(typeid(Type), &instance);
|
||||
}
|
||||
|
||||
// Recurse into base class if there are more between Type and Object in the hierarchy
|
||||
static if(BaseClassesTuple!Type.length > 1)
|
||||
static if (BaseClassesTuple!Type.length > 1)
|
||||
{
|
||||
autowire!(BaseClassesTuple!Type[0])(container, instance);
|
||||
}
|
||||
|
||||
foreach(index, name; FieldNameTuple!Type) {
|
||||
foreach (index, name; FieldNameTuple!Type)
|
||||
{
|
||||
autowireMember!(name, index, Type)(container, instance);
|
||||
}
|
||||
}
|
||||
|
||||
private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType, void* candidateInstanceAddress, TypeInfo instanceType, void* instanceAddress, string member) {
|
||||
debug {
|
||||
writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType, candidateInstanceAddress, instanceType, instanceAddress, member));
|
||||
private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType,
|
||||
void* candidateInstanceAddress, TypeInfo instanceType, void* instanceAddress, string member)
|
||||
{
|
||||
debug
|
||||
{
|
||||
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) {
|
||||
debug {
|
||||
writeln(format("DEBUG: Autowired all registered instances of super type %s to [%s@%s].%s", superTypeInfo, instanceType, instanceAddress, member));
|
||||
private void printDebugAutowiringArray(TypeInfo superTypeInfo,
|
||||
TypeInfo instanceType, void* instanceAddress, string member)
|
||||
{
|
||||
debug
|
||||
{
|
||||
writeln(format("DEBUG: Autowired all registered instances of super type %s to [%s@%s].%s",
|
||||
superTypeInfo, instanceType, instanceAddress, member));
|
||||
}
|
||||
}
|
||||
|
||||
private void autowireMember(string member, size_t memberIndex, Type)(shared(DependencyContainer) container, Type instance) {
|
||||
foreach(attribute; __traits(getAttributes, Type.tupleof[memberIndex])) {
|
||||
static if (is(attribute == Autowire!T, T)) {
|
||||
private void autowireMember(string member, size_t memberIndex, Type)(
|
||||
shared(DependencyContainer) container, Type instance)
|
||||
{
|
||||
foreach (attribute; __traits(getAttributes, Type.tupleof[memberIndex]))
|
||||
{
|
||||
static if (is(attribute == Autowire!T, T))
|
||||
{
|
||||
injectInstance!(member, memberIndex, typeof(attribute.qualifier))(container, instance);
|
||||
} else static if (__traits(isSame, attribute, Autowire)) {
|
||||
}
|
||||
else static if (__traits(isSame, attribute, Autowire))
|
||||
{
|
||||
injectInstance!(member, memberIndex, UseMemberType)(container, instance);
|
||||
} else static if (is(typeof(attribute) == Value)) {
|
||||
}
|
||||
else static if (is(typeof(attribute) == Value))
|
||||
{
|
||||
enum key = attribute.key;
|
||||
injectValue!(member, memberIndex, key, false)(container, instance);
|
||||
} else static if (is(typeof(attribute) == MandatoryValue)) {
|
||||
}
|
||||
else static if (is(typeof(attribute) == MandatoryValue))
|
||||
{
|
||||
enum key = attribute.key;
|
||||
injectValue!(member, memberIndex, key, true)(container, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void injectInstance(string member, size_t memberIndex, QualifierType, Type)(shared(DependencyContainer) container, Type instance) {
|
||||
if (instance.tupleof[memberIndex] is null) {
|
||||
private void injectInstance(string member, size_t memberIndex, QualifierType, Type)(
|
||||
shared(DependencyContainer) container, Type instance)
|
||||
{
|
||||
if (instance.tupleof[memberIndex] is null)
|
||||
{
|
||||
alias MemberType = typeof(Type.tupleof[memberIndex]);
|
||||
enum isOptional = hasUDA!(Type.tupleof[memberIndex], OptionalDependency);
|
||||
|
||||
static if (isDynamicArray!MemberType) {
|
||||
injectMultipleInstances!(member, memberIndex, isOptional, MemberType)(container, instance);
|
||||
} else {
|
||||
injectSingleInstance!(member, memberIndex, isOptional, MemberType, QualifierType)(container, instance);
|
||||
static if (isDynamicArray!MemberType)
|
||||
{
|
||||
injectMultipleInstances!(member, memberIndex, isOptional, MemberType)(container,
|
||||
instance);
|
||||
}
|
||||
else
|
||||
{
|
||||
injectSingleInstance!(member, memberIndex, isOptional, MemberType, QualifierType)(container,
|
||||
instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void injectMultipleInstances(string member, size_t memberIndex, bool isOptional, MemberType, Type)(shared(DependencyContainer) container, Type instance) {
|
||||
private void injectMultipleInstances(string member, size_t memberIndex,
|
||||
bool isOptional, MemberType, Type)(shared(DependencyContainer) container, Type instance)
|
||||
{
|
||||
alias MemberElementType = ElementType!MemberType;
|
||||
static if (isOptional) {
|
||||
static if (isOptional)
|
||||
{
|
||||
auto instances = container.resolveAll!MemberElementType(ResolveOption.noResolveException);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
auto instances = container.resolveAll!MemberElementType;
|
||||
}
|
||||
|
||||
instance.tupleof[memberIndex] = instances;
|
||||
debug(poodinisVerbose) {
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member);
|
||||
}
|
||||
}
|
||||
|
||||
private void injectSingleInstance(string member, size_t memberIndex, bool isOptional, MemberType, QualifierType, Type)(shared(DependencyContainer) container, Type instance) {
|
||||
debug(poodinisVerbose) {
|
||||
private void injectSingleInstance(string member, size_t memberIndex,
|
||||
bool isOptional, MemberType, QualifierType, Type)(
|
||||
shared(DependencyContainer) container, Type instance)
|
||||
{
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
TypeInfo qualifiedInstanceType = typeid(MemberType);
|
||||
}
|
||||
|
||||
enum assignNewInstance = hasUDA!(Type.tupleof[memberIndex], AssignNewInstance);
|
||||
|
||||
MemberType qualifiedInstance;
|
||||
static if (!is(QualifierType == UseMemberType)) {
|
||||
qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType, assignNewInstance, isOptional)(container);
|
||||
debug(poodinisVerbose) {
|
||||
static if (!is(QualifierType == UseMemberType))
|
||||
{
|
||||
qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType,
|
||||
assignNewInstance, isOptional)(container);
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
qualifiedInstanceType = typeid(QualifierType);
|
||||
}
|
||||
} else {
|
||||
qualifiedInstance = createOrResolveInstance!(MemberType, MemberType, assignNewInstance, isOptional)(container);
|
||||
}
|
||||
else
|
||||
{
|
||||
qualifiedInstance = createOrResolveInstance!(MemberType, MemberType,
|
||||
assignNewInstance, isOptional)(container);
|
||||
}
|
||||
|
||||
instance.tupleof[memberIndex] = qualifiedInstance;
|
||||
|
||||
debug(poodinisVerbose) {
|
||||
printDebugAutowiringCandidate(qualifiedInstanceType, &qualifiedInstance, typeid(Type), &instance, member);
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
printDebugAutowiringCandidate(qualifiedInstanceType,
|
||||
&qualifiedInstance, typeid(Type), &instance, member);
|
||||
}
|
||||
}
|
||||
|
||||
private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew, bool isOptional)(shared(DependencyContainer) container) {
|
||||
static if (createNew) {
|
||||
private QualifierType createOrResolveInstance(MemberType, QualifierType,
|
||||
bool createNew, bool isOptional)(shared(DependencyContainer) container)
|
||||
{
|
||||
static if (createNew)
|
||||
{
|
||||
auto instanceFactory = new InstanceFactory();
|
||||
instanceFactory.factoryParameters = InstanceFactoryParameters(typeid(MemberType), CreatesSingleton.no);
|
||||
instanceFactory.factoryParameters = InstanceFactoryParameters(typeid(MemberType),
|
||||
CreatesSingleton.no);
|
||||
return cast(MemberType) instanceFactory.getInstance();
|
||||
} else {
|
||||
static if (isOptional) {
|
||||
}
|
||||
else
|
||||
{
|
||||
static if (isOptional)
|
||||
{
|
||||
return container.resolve!(MemberType, QualifierType)(ResolveOption.noResolveException);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
return container.resolve!(MemberType, QualifierType);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void injectValue(string member, size_t memberIndex, string key, bool mandatory, Type)(shared(DependencyContainer) container, Type instance) {
|
||||
private void injectValue(string member, size_t memberIndex, string key, bool mandatory, Type)(
|
||||
shared(DependencyContainer) container, Type instance)
|
||||
{
|
||||
alias MemberType = typeof(Type.tupleof[memberIndex]);
|
||||
try {
|
||||
try
|
||||
{
|
||||
auto injector = container.resolve!(ValueInjector!MemberType);
|
||||
instance.tupleof[memberIndex] = injector.get(key);
|
||||
debug(poodinisVerbose) {
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
printDebugValueInjection(typeid(Type), &instance, member, typeid(MemberType), key);
|
||||
}
|
||||
} catch (ResolveException e) {
|
||||
throw new ValueInjectionException(format("Could not inject value of type %s into %s.%s: value injector is missing for this type.", typeid(MemberType), typeid(Type), member));
|
||||
} catch (ValueNotAvailableException e) {
|
||||
static if (mandatory) {
|
||||
throw new ValueInjectionException(format("Could not inject value of type %s into %s.%s", typeid(MemberType), typeid(Type), member), e);
|
||||
}
|
||||
catch (ResolveException e)
|
||||
{
|
||||
throw new ValueInjectionException(format(
|
||||
"Could not inject value of type %s into %s.%s: value injector is missing for this type.",
|
||||
typeid(MemberType), typeid(Type), member));
|
||||
}
|
||||
catch (ValueNotAvailableException e)
|
||||
{
|
||||
static if (mandatory)
|
||||
{
|
||||
throw new ValueInjectionException(format("Could not inject value of type %s into %s.%s",
|
||||
typeid(MemberType), typeid(Type), member), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printDebugValueInjection(TypeInfo instanceType, void* instanceAddress, string member, TypeInfo valueType, string key) {
|
||||
debug {
|
||||
writeln(format("DEBUG: Injected value with key '%s' of type %s into [%s@%s].%s", key, valueType, instanceType, instanceAddress, member));
|
||||
private void printDebugValueInjection(TypeInfo instanceType,
|
||||
void* instanceAddress, string member, TypeInfo valueType, string key)
|
||||
{
|
||||
debug
|
||||
{
|
||||
writeln(format("DEBUG: Injected value with key '%s' of type %s into [%s@%s].%s",
|
||||
key, valueType, instanceType, instanceAddress, member));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -247,25 +330,33 @@ private void printDebugValueInjection(TypeInfo instanceType, void* instanceAddre
|
|||
* See_Also: DependencyContainer
|
||||
* Deprecated: Using the global container is undesired. See DependencyContainer.getInstance().
|
||||
*/
|
||||
public deprecated void globalAutowire(Type)(Type instance) {
|
||||
public deprecated void globalAutowire(Type)(Type instance)
|
||||
{
|
||||
DependencyContainer.getInstance().autowire(instance);
|
||||
}
|
||||
|
||||
class AutowiredRegistration(RegistrationType : Object) : Registration {
|
||||
class AutowiredRegistration(RegistrationType : Object) : Registration
|
||||
{
|
||||
private shared(DependencyContainer) container;
|
||||
|
||||
public this(TypeInfo registeredType, InstanceFactory instanceFactory, shared(DependencyContainer) originatingContainer) {
|
||||
public this(TypeInfo registeredType, InstanceFactory instanceFactory,
|
||||
shared(DependencyContainer) originatingContainer)
|
||||
{
|
||||
super(registeredType, typeid(RegistrationType), instanceFactory, originatingContainer);
|
||||
}
|
||||
|
||||
public override Object getInstance(InstantiationContext context = new AutowireInstantiationContext()) {
|
||||
enforce(!(originatingContainer is null), "The registration's originating container is null. There is no way to resolve autowire dependencies.");
|
||||
public override Object getInstance(
|
||||
InstantiationContext context = new AutowireInstantiationContext())
|
||||
{
|
||||
enforce(!(originatingContainer is null),
|
||||
"The registration's originating container is null. There is no way to resolve autowire dependencies.");
|
||||
|
||||
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) {
|
||||
if (autowireContext.autowireInstance)
|
||||
{
|
||||
originatingContainer.autowire(instance);
|
||||
}
|
||||
|
||||
|
@ -274,15 +365,18 @@ class AutowiredRegistration(RegistrationType : Object) : Registration {
|
|||
return instance;
|
||||
}
|
||||
|
||||
private void delegate() getPreDestructor(RegistrationType instance) {
|
||||
private void delegate() getPreDestructor(RegistrationType instance)
|
||||
{
|
||||
void delegate() preDestructor = null;
|
||||
foreach (memberName; __traits(allMembers, RegistrationType)) {
|
||||
foreach (memberName; __traits(allMembers, RegistrationType))
|
||||
{
|
||||
mixin(createImportsString!RegistrationType);
|
||||
enum QualifiedName = fullyQualifiedName!RegistrationType ~ `.` ~ memberName;
|
||||
static if (__traits(compiles, __traits(getProtection, __traits(getMember, instance, memberName)))
|
||||
&& __traits(getProtection, __traits(getMember, instance, memberName)) == "public"
|
||||
&& isFunction!(mixin(QualifiedName))
|
||||
&& hasUDA!(__traits(getMember, instance, memberName), PreDestroy)) {
|
||||
&& hasUDA!(__traits(getMember, instance, memberName), PreDestroy))
|
||||
{
|
||||
preDestructor = &__traits(getMember, instance, memberName);
|
||||
}
|
||||
}
|
||||
|
@ -291,6 +385,7 @@ class AutowiredRegistration(RegistrationType : Object) : Registration {
|
|||
}
|
||||
}
|
||||
|
||||
class AutowireInstantiationContext : InstantiationContext {
|
||||
class AutowireInstantiationContext : InstantiationContext
|
||||
{
|
||||
public bool autowireInstance = true;
|
||||
}
|
||||
|
|
|
@ -21,23 +21,27 @@ import poodinis.altphobos : isFunction;
|
|||
import poodinis.imports : createImportsString;
|
||||
|
||||
import std.string : format;
|
||||
import std.algorithm: canFind;
|
||||
import std.algorithm : canFind;
|
||||
import std.traits : fullyQualifiedName, hasUDA, BaseTypeTuple;
|
||||
import std.meta : AliasSeq;
|
||||
|
||||
debug {
|
||||
debug
|
||||
{
|
||||
import std.stdio;
|
||||
}
|
||||
|
||||
/**
|
||||
* Exception thrown when errors occur while resolving a type in a dependency container.
|
||||
*/
|
||||
class ResolveException : Exception {
|
||||
this(string message, TypeInfo resolveType) {
|
||||
class ResolveException : Exception
|
||||
{
|
||||
this(string message, TypeInfo resolveType)
|
||||
{
|
||||
super(format("Exception while resolving type %s: %s", resolveType.toString(), message));
|
||||
}
|
||||
|
||||
this(Throwable cause, TypeInfo resolveType) {
|
||||
this(Throwable cause, TypeInfo resolveType)
|
||||
{
|
||||
super(format("Exception while resolving type %s", resolveType.toString()), cause);
|
||||
}
|
||||
}
|
||||
|
@ -45,16 +49,20 @@ class ResolveException : Exception {
|
|||
/**
|
||||
* 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));
|
||||
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 {
|
||||
public enum RegistrationOption
|
||||
{
|
||||
none = 0,
|
||||
/**
|
||||
* Prevent a concrete type being registered on itself. With this option you will always need
|
||||
|
@ -66,7 +74,8 @@ public enum RegistrationOption {
|
|||
/**
|
||||
* Options which influence the process of resolving dependencies
|
||||
*/
|
||||
public enum ResolveOption {
|
||||
public enum ResolveOption
|
||||
{
|
||||
none = 0,
|
||||
/**
|
||||
* Registers the type you're trying to resolve before returning it.
|
||||
|
@ -89,7 +98,9 @@ public enum ResolveOption {
|
|||
* Multiple methods can be marked and will all be called after construction. The order in which
|
||||
* methods are called is undetermined. Methods should have the signature void(void).
|
||||
*/
|
||||
struct PostConstruct {}
|
||||
struct PostConstruct
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Methods marked with this UDA within dependencies are called before the container
|
||||
|
@ -98,7 +109,9 @@ struct PostConstruct {}
|
|||
* This method is called when removeRegistration or clearAllRegistrations is called.
|
||||
* It will also be called when the container's destructor is called.
|
||||
*/
|
||||
struct PreDestroy {}
|
||||
struct PreDestroy
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* The dependency container maintains all dependencies registered with it.
|
||||
|
@ -110,7 +123,8 @@ struct PreDestroy {}
|
|||
* 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 {
|
||||
synchronized class DependencyContainer
|
||||
{
|
||||
private Registration[][TypeInfo] registrations;
|
||||
|
||||
private Registration[] autowireStack;
|
||||
|
@ -118,7 +132,8 @@ synchronized class DependencyContainer {
|
|||
private RegistrationOption persistentRegistrationOptions;
|
||||
private ResolveOption persistentResolveOptions;
|
||||
|
||||
~this() {
|
||||
~this()
|
||||
{
|
||||
clearAllRegistrations();
|
||||
}
|
||||
|
||||
|
@ -142,7 +157,8 @@ synchronized class DependencyContainer {
|
|||
*
|
||||
* See_Also: singleInstance, newInstance, existingInstance
|
||||
*/
|
||||
public Registration register(ConcreteType)(RegistrationOption options = RegistrationOption.none) {
|
||||
public Registration register(ConcreteType)(RegistrationOption options = RegistrationOption.none)
|
||||
{
|
||||
return register!(ConcreteType, ConcreteType)(options);
|
||||
}
|
||||
|
||||
|
@ -163,27 +179,36 @@ synchronized class DependencyContainer {
|
|||
*
|
||||
* See_Also: singleInstance, newInstance, existingInstance, RegistrationOption
|
||||
*/
|
||||
public Registration register(SuperType, ConcreteType : SuperType)(
|
||||
RegistrationOption options = RegistrationOption.none) if (!is(ConcreteType == struct)) {
|
||||
public Registration register(SuperType, ConcreteType:
|
||||
SuperType)(RegistrationOption options = RegistrationOption.none)
|
||||
if (!is(ConcreteType == struct))
|
||||
{
|
||||
|
||||
TypeInfo registeredType = typeid(SuperType);
|
||||
TypeInfo_Class concreteType = typeid(ConcreteType);
|
||||
|
||||
debug(poodinisVerbose) {
|
||||
writeln(format("DEBUG: Register type %s (as %s)", concreteType.toString(), registeredType.toString()));
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
writeln(format("DEBUG: Register type %s (as %s)",
|
||||
concreteType.toString(), registeredType.toString()));
|
||||
}
|
||||
|
||||
auto existingRegistration = getExistingRegistration(registeredType, concreteType);
|
||||
if (existingRegistration) {
|
||||
if (existingRegistration)
|
||||
{
|
||||
return existingRegistration;
|
||||
}
|
||||
|
||||
auto instanceFactory = new ConstructorInjectingInstanceFactory!ConcreteType(this);
|
||||
auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType, instanceFactory, this);
|
||||
auto newRegistration = new AutowiredRegistration!ConcreteType(registeredType,
|
||||
instanceFactory, this);
|
||||
newRegistration.singleInstance();
|
||||
|
||||
static if (!is(SuperType == ConcreteType)) {
|
||||
if (!hasOption(options, persistentRegistrationOptions, RegistrationOption.doNotAddConcreteTypeRegistration)) {
|
||||
static if (!is(SuperType == ConcreteType))
|
||||
{
|
||||
if (!hasOption(options, persistentRegistrationOptions,
|
||||
RegistrationOption.doNotAddConcreteTypeRegistration))
|
||||
{
|
||||
auto concreteTypeRegistration = register!ConcreteType;
|
||||
concreteTypeRegistration.linkTo(newRegistration);
|
||||
}
|
||||
|
@ -193,36 +218,49 @@ synchronized class DependencyContainer {
|
|||
return newRegistration;
|
||||
}
|
||||
|
||||
public Registration register(SuperType, ConcreteType)(RegistrationOption options = RegistrationOption.none)
|
||||
if (!is(SuperType == ConcreteType) && !is(BaseTypeTuple!ConcreteType == AliasSeq!(Object, SuperType)) && !is(BaseTypeTuple!ConcreteType == AliasSeq!(SuperType))) {
|
||||
pragma(msg, "Cannot register dependency: ", ConcreteType, " is not derived from ", SuperType);
|
||||
public Registration register(SuperType, ConcreteType)(
|
||||
RegistrationOption options = RegistrationOption.none)
|
||||
if (!is(SuperType == ConcreteType) && !is(BaseTypeTuple!ConcreteType == AliasSeq!(Object,
|
||||
SuperType)) && !is(BaseTypeTuple!ConcreteType == AliasSeq!(SuperType)))
|
||||
{
|
||||
pragma(msg, "Cannot register dependency: ", ConcreteType,
|
||||
" is not derived from ", SuperType);
|
||||
static assert(0, "Cannot register dependency");
|
||||
}
|
||||
|
||||
private bool hasOption(OptionType)(OptionType options, OptionType persistentOptions, OptionType option) {
|
||||
private bool hasOption(OptionType)(OptionType options,
|
||||
OptionType persistentOptions, OptionType option)
|
||||
{
|
||||
return ((options | persistentOptions) & option) != 0;
|
||||
}
|
||||
|
||||
private OptionType buildFlags(OptionType)(OptionType[] options) {
|
||||
private OptionType buildFlags(OptionType)(OptionType[] options)
|
||||
{
|
||||
OptionType flags;
|
||||
foreach (option; options) {
|
||||
foreach (option; options)
|
||||
{
|
||||
flags |= option;
|
||||
}
|
||||
return flags;
|
||||
}
|
||||
|
||||
private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType) {
|
||||
private Registration getExistingRegistration(TypeInfo registrationType, TypeInfo qualifierType)
|
||||
{
|
||||
auto existingCandidates = registrationType in registrations;
|
||||
if (existingCandidates) {
|
||||
return getRegistration(cast(Registration[]) *existingCandidates, qualifierType);
|
||||
if (existingCandidates)
|
||||
{
|
||||
return getRegistration(cast(Registration[])*existingCandidates, qualifierType);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private Registration getRegistration(Registration[] candidates, TypeInfo concreteType) {
|
||||
foreach(existingRegistration ; candidates) {
|
||||
if (existingRegistration.instanceType == concreteType) {
|
||||
private Registration getRegistration(Registration[] candidates, TypeInfo concreteType)
|
||||
{
|
||||
foreach (existingRegistration; candidates)
|
||||
{
|
||||
if (existingRegistration.instanceType == concreteType)
|
||||
{
|
||||
return existingRegistration;
|
||||
}
|
||||
}
|
||||
|
@ -269,7 +307,10 @@ synchronized class DependencyContainer {
|
|||
* ---
|
||||
* You need to use the resolve method which allows you to specify a qualifier.
|
||||
*/
|
||||
public RegistrationType resolve(RegistrationType)(ResolveOption resolveOptions = ResolveOption.none) if (!is(RegistrationType == struct)) {
|
||||
public RegistrationType resolve(RegistrationType)(
|
||||
ResolveOption resolveOptions = ResolveOption.none)
|
||||
if (!is(RegistrationType == struct))
|
||||
{
|
||||
return resolve!(RegistrationType, RegistrationType)(resolveOptions);
|
||||
}
|
||||
|
||||
|
@ -299,53 +340,73 @@ synchronized class DependencyContainer {
|
|||
* container.resolve!(Animal, Dog);
|
||||
* ---
|
||||
*/
|
||||
public QualifierType resolve(RegistrationType, QualifierType : RegistrationType)(ResolveOption resolveOptions = ResolveOption.none) if (!is(QualifierType == struct)) {
|
||||
public QualifierType resolve(RegistrationType, QualifierType:
|
||||
RegistrationType)(ResolveOption resolveOptions = ResolveOption.none)
|
||||
if (!is(QualifierType == struct))
|
||||
{
|
||||
TypeInfo resolveType = typeid(RegistrationType);
|
||||
TypeInfo qualifierType = typeid(QualifierType);
|
||||
|
||||
debug(poodinisVerbose) {
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
writeln("DEBUG: Resolving type " ~ resolveType.toString() ~ " with qualifier " ~ qualifierType.toString());
|
||||
}
|
||||
|
||||
static if (__traits(compiles, new QualifierType())) {
|
||||
if (hasOption(resolveOptions, persistentResolveOptions, ResolveOption.registerBeforeResolving)) {
|
||||
static if (__traits(compiles, new QualifierType()))
|
||||
{
|
||||
if (hasOption(resolveOptions, persistentResolveOptions,
|
||||
ResolveOption.registerBeforeResolving))
|
||||
{
|
||||
register!(RegistrationType, QualifierType)();
|
||||
}
|
||||
}
|
||||
|
||||
auto candidates = resolveType in registrations;
|
||||
if (!candidates) {
|
||||
if (hasOption(resolveOptions, persistentResolveOptions, ResolveOption.noResolveException)) {
|
||||
if (!candidates)
|
||||
{
|
||||
if (hasOption(resolveOptions, persistentResolveOptions,
|
||||
ResolveOption.noResolveException))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new ResolveException("Type not registered.", resolveType);
|
||||
}
|
||||
|
||||
Registration registration = getQualifiedRegistration(resolveType, qualifierType, cast(Registration[]) *candidates);
|
||||
Registration registration = getQualifiedRegistration(resolveType,
|
||||
qualifierType, cast(Registration[])*candidates);
|
||||
|
||||
try {
|
||||
try
|
||||
{
|
||||
QualifierType newInstance = resolveAutowiredInstance!QualifierType(registration);
|
||||
callPostConstructors(newInstance);
|
||||
return newInstance;
|
||||
} catch (ValueInjectionException e) {
|
||||
}
|
||||
catch (ValueInjectionException e)
|
||||
{
|
||||
throw new ResolveException(e, resolveType);
|
||||
}
|
||||
}
|
||||
|
||||
bool isRegistered(RegistrationType)() {
|
||||
bool isRegistered(RegistrationType)()
|
||||
{
|
||||
TypeInfo typeInfo = typeid(RegistrationType);
|
||||
auto candidates = typeInfo in registrations;
|
||||
return candidates !is null;
|
||||
}
|
||||
|
||||
private QualifierType resolveAutowiredInstance(QualifierType)(Registration registration) {
|
||||
private QualifierType resolveAutowiredInstance(QualifierType)(Registration registration)
|
||||
{
|
||||
QualifierType instance;
|
||||
if (!(cast(Registration[]) autowireStack).canFind(registration)) {
|
||||
if (!(cast(Registration[]) autowireStack).canFind(registration))
|
||||
{
|
||||
autowireStack ~= cast(shared(Registration)) registration;
|
||||
instance = cast(QualifierType) registration.getInstance(new AutowireInstantiationContext());
|
||||
autowireStack = autowireStack[0 .. $-1];
|
||||
} else {
|
||||
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);
|
||||
|
@ -370,31 +431,43 @@ synchronized class DependencyContainer {
|
|||
* Animal[] animals = container.resolveAll!Animal;
|
||||
* ---
|
||||
*/
|
||||
public RegistrationType[] resolveAll(RegistrationType)(ResolveOption resolveOptions = ResolveOption.none) {
|
||||
public RegistrationType[] resolveAll(RegistrationType)(
|
||||
ResolveOption resolveOptions = ResolveOption.none)
|
||||
{
|
||||
RegistrationType[] instances;
|
||||
TypeInfo resolveType = typeid(RegistrationType);
|
||||
|
||||
auto qualifiedRegistrations = resolveType in registrations;
|
||||
if (!qualifiedRegistrations) {
|
||||
if (hasOption(resolveOptions, persistentResolveOptions, ResolveOption.noResolveException)) {
|
||||
if (!qualifiedRegistrations)
|
||||
{
|
||||
if (hasOption(resolveOptions, persistentResolveOptions,
|
||||
ResolveOption.noResolveException))
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
throw new ResolveException("Type not registered.", resolveType);
|
||||
}
|
||||
|
||||
foreach(registration ; cast(Registration[]) *qualifiedRegistrations) {
|
||||
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) {
|
||||
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);
|
||||
throw new ResolveException(
|
||||
"Multiple qualified candidates available: " ~ candidateList ~ ". Please use a qualifier.",
|
||||
resolveType);
|
||||
}
|
||||
|
||||
return candidates[0];
|
||||
|
@ -403,14 +476,17 @@ synchronized class DependencyContainer {
|
|||
return getRegistration(candidates, qualifierType);
|
||||
}
|
||||
|
||||
private void callPostConstructors(Type)(Type instance) {
|
||||
foreach (memberName; __traits(allMembers, Type)) {
|
||||
private void callPostConstructors(Type)(Type instance)
|
||||
{
|
||||
foreach (memberName; __traits(allMembers, Type))
|
||||
{
|
||||
mixin(createImportsString!Type);
|
||||
enum QualifiedName = fullyQualifiedName!Type ~ `.` ~ memberName;
|
||||
static if (__traits(compiles, __traits(getProtection, __traits(getMember, instance, memberName)))
|
||||
&& __traits(getProtection, __traits(getMember, instance, memberName)) == "public"
|
||||
&& isFunction!(mixin(QualifiedName))
|
||||
&& hasUDA!(__traits(getMember, instance, memberName), PostConstruct)) {
|
||||
&& hasUDA!(__traits(getMember, instance, memberName), PostConstruct))
|
||||
{
|
||||
__traits(getMember, instance, memberName)();
|
||||
}
|
||||
}
|
||||
|
@ -419,8 +495,10 @@ synchronized class DependencyContainer {
|
|||
/**
|
||||
* Clears all dependency registrations managed by this container.
|
||||
*/
|
||||
public void clearAllRegistrations() {
|
||||
foreach(registrationsOfType; registrations) {
|
||||
public void clearAllRegistrations()
|
||||
{
|
||||
foreach (registrationsOfType; registrations)
|
||||
{
|
||||
callPreDestructorsOfRegistrations(registrationsOfType);
|
||||
}
|
||||
registrations.destroy();
|
||||
|
@ -436,16 +514,20 @@ synchronized class DependencyContainer {
|
|||
* container.removeRegistration!Animal;
|
||||
* ---
|
||||
*/
|
||||
public void removeRegistration(RegistrationType)() {
|
||||
public void removeRegistration(RegistrationType)()
|
||||
{
|
||||
auto registrationsOfType = *(typeid(RegistrationType) in registrations);
|
||||
callPreDestructorsOfRegistrations(registrationsOfType);
|
||||
registrations.remove(typeid(RegistrationType));
|
||||
}
|
||||
|
||||
private void callPreDestructorsOfRegistrations(shared(Registration[]) registrations) {
|
||||
foreach(registration; registrations) {
|
||||
private void callPreDestructorsOfRegistrations(shared(Registration[]) registrations)
|
||||
{
|
||||
foreach (registration; registrations)
|
||||
{
|
||||
Registration unsharedRegistration = cast(Registration) registration;
|
||||
if (unsharedRegistration.preDestructor !is null) {
|
||||
if (unsharedRegistration.preDestructor !is null)
|
||||
{
|
||||
unsharedRegistration.preDestructor()();
|
||||
}
|
||||
}
|
||||
|
@ -454,28 +536,32 @@ synchronized class DependencyContainer {
|
|||
/**
|
||||
* Apply persistent registration options which will be used everytime register() is called.
|
||||
*/
|
||||
public void setPersistentRegistrationOptions(RegistrationOption options) {
|
||||
public void setPersistentRegistrationOptions(RegistrationOption options)
|
||||
{
|
||||
persistentRegistrationOptions = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets all applied persistent registration options
|
||||
*/
|
||||
public void unsetPersistentRegistrationOptions() {
|
||||
public void unsetPersistentRegistrationOptions()
|
||||
{
|
||||
persistentRegistrationOptions = RegistrationOption.none;
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply persistent resolve options which will be used everytime resolve() is called.
|
||||
*/
|
||||
public void setPersistentResolveOptions(ResolveOption options) {
|
||||
public void setPersistentResolveOptions(ResolveOption options)
|
||||
{
|
||||
persistentResolveOptions = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* Unsets all applied persistent resolve options
|
||||
*/
|
||||
public void unsetPersistentResolveOptions() {
|
||||
public void unsetPersistentResolveOptions()
|
||||
{
|
||||
persistentResolveOptions = ResolveOption.none;
|
||||
}
|
||||
|
||||
|
|
|
@ -20,21 +20,27 @@ import poodinis.autowire : autowire;
|
|||
|
||||
import std.traits : hasUDA, ReturnType;
|
||||
|
||||
class ApplicationContext {
|
||||
public void registerDependencies(shared(DependencyContainer) container) {}
|
||||
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 {}
|
||||
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) {
|
||||
struct RegisterByType(Type)
|
||||
{
|
||||
Type type;
|
||||
}
|
||||
|
||||
|
@ -42,7 +48,9 @@ struct RegisterByType(Type) {
|
|||
* Components with the prototype registration will be scoped as dependencies which will create
|
||||
* new instances every time they are resolved. The factory method will be called repeatedly.
|
||||
*/
|
||||
struct Prototype {}
|
||||
struct Prototype
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Register dependencies through an application context.
|
||||
|
@ -51,7 +59,8 @@ struct Prototype {}
|
|||
* 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)(shared(DependencyContainer) container) {
|
||||
public void registerContext(Context : ApplicationContext)(shared(DependencyContainer) container)
|
||||
{
|
||||
auto context = new Context();
|
||||
context.registerDependencies(container);
|
||||
context.registerContextComponents(container);
|
||||
|
@ -59,26 +68,38 @@ public void registerContext(Context : ApplicationContext)(shared(DependencyConta
|
|||
autowire(container, context);
|
||||
}
|
||||
|
||||
public void registerContextComponents(ApplicationContextType : ApplicationContext)(ApplicationContextType context, shared(DependencyContainer) container) {
|
||||
foreach (member ; __traits(allMembers, ApplicationContextType)) {
|
||||
static if (__traits(getProtection, __traits(getMember, context, member)) == "public" && hasUDA!(__traits(getMember, context, member), Component)) {
|
||||
public void registerContextComponents(ApplicationContextType : ApplicationContext)(
|
||||
ApplicationContextType context, shared(DependencyContainer) container)
|
||||
{
|
||||
foreach (member; __traits(allMembers, ApplicationContextType))
|
||||
{
|
||||
static if (__traits(getProtection, __traits(getMember, context,
|
||||
member)) == "public" && 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)) {
|
||||
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) {
|
||||
if (registration is null)
|
||||
{
|
||||
registration = container.register!(ReturnType!factoryMethod);
|
||||
}
|
||||
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(registration.instanceType, createsSingleton, null, factoryMethod);
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(
|
||||
registration.instanceType, createsSingleton, null, factoryMethod);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,8 @@ import std.exception : enforce;
|
|||
import std.traits : Parameters, isBuiltinType, fullyQualifiedName;
|
||||
import std.string : format;
|
||||
|
||||
debug {
|
||||
debug
|
||||
{
|
||||
import std.stdio : writeln;
|
||||
}
|
||||
|
||||
|
@ -27,34 +28,42 @@ alias CreatesSingleton = Flag!"CreatesSingleton";
|
|||
alias InstanceFactoryMethod = Object delegate();
|
||||
alias InstanceEventHandler = void delegate(Object instance);
|
||||
|
||||
class InstanceCreationException : Exception {
|
||||
this(string message, string file = __FILE__, size_t line = __LINE__) {
|
||||
class InstanceCreationException : Exception
|
||||
{
|
||||
this(string message, string file = __FILE__, size_t line = __LINE__)
|
||||
{
|
||||
super(message, file, line);
|
||||
}
|
||||
}
|
||||
|
||||
struct InstanceFactoryParameters {
|
||||
struct InstanceFactoryParameters
|
||||
{
|
||||
TypeInfo_Class instanceType;
|
||||
CreatesSingleton createsSingleton = CreatesSingleton.yes;
|
||||
Object existingInstance;
|
||||
InstanceFactoryMethod factoryMethod;
|
||||
}
|
||||
|
||||
class InstanceFactory {
|
||||
class InstanceFactory
|
||||
{
|
||||
private Object instance = null;
|
||||
private InstanceFactoryParameters _factoryParameters;
|
||||
private InstanceEventHandler _constructionHandler;
|
||||
|
||||
this() {
|
||||
this()
|
||||
{
|
||||
factoryParameters = InstanceFactoryParameters();
|
||||
}
|
||||
|
||||
public @property void factoryParameters(InstanceFactoryParameters factoryParameters) {
|
||||
if (factoryParameters.factoryMethod is null) {
|
||||
public @property void factoryParameters(InstanceFactoryParameters factoryParameters)
|
||||
{
|
||||
if (factoryParameters.factoryMethod is null)
|
||||
{
|
||||
factoryParameters.factoryMethod = &this.createInstance;
|
||||
}
|
||||
|
||||
if (factoryParameters.existingInstance !is null) {
|
||||
if (factoryParameters.existingInstance !is null)
|
||||
{
|
||||
factoryParameters.createsSingleton = CreatesSingleton.yes;
|
||||
this.instance = factoryParameters.existingInstance;
|
||||
}
|
||||
|
@ -62,73 +71,99 @@ class InstanceFactory {
|
|||
_factoryParameters = factoryParameters;
|
||||
}
|
||||
|
||||
public @property InstanceFactoryParameters factoryParameters() {
|
||||
public @property InstanceFactoryParameters factoryParameters()
|
||||
{
|
||||
return _factoryParameters;
|
||||
}
|
||||
|
||||
public Object getInstance() {
|
||||
if (_factoryParameters.createsSingleton && instance !is null) {
|
||||
debug(poodinisVerbose) {
|
||||
public Object getInstance()
|
||||
{
|
||||
if (_factoryParameters.createsSingleton && instance !is null)
|
||||
{
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
printDebugUseExistingInstance();
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
debug(poodinisVerbose) {
|
||||
debug (poodinisVerbose)
|
||||
{
|
||||
printDebugCreateNewInstance();
|
||||
}
|
||||
|
||||
instance = _factoryParameters.factoryMethod();
|
||||
if(_constructionHandler !is null) {
|
||||
if (_constructionHandler !is null)
|
||||
{
|
||||
_constructionHandler(instance);
|
||||
}
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void onConstructed(InstanceEventHandler handler) {
|
||||
void onConstructed(InstanceEventHandler handler)
|
||||
{
|
||||
_constructionHandler = handler;
|
||||
}
|
||||
|
||||
private void printDebugUseExistingInstance() {
|
||||
debug {
|
||||
if (_factoryParameters.instanceType !is null) {
|
||||
writeln(format("DEBUG: Existing instance returned of type %s", _factoryParameters.instanceType.toString()));
|
||||
} else {
|
||||
private void printDebugUseExistingInstance()
|
||||
{
|
||||
debug
|
||||
{
|
||||
if (_factoryParameters.instanceType !is null)
|
||||
{
|
||||
writeln(format("DEBUG: Existing instance returned of type %s",
|
||||
_factoryParameters.instanceType.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln("DEBUG: Existing instance returned from custom factory method");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void printDebugCreateNewInstance() {
|
||||
debug {
|
||||
if (_factoryParameters.instanceType !is null) {
|
||||
writeln(format("DEBUG: Creating new instance of type %s", _factoryParameters.instanceType.toString()));
|
||||
} else {
|
||||
private void printDebugCreateNewInstance()
|
||||
{
|
||||
debug
|
||||
{
|
||||
if (_factoryParameters.instanceType !is null)
|
||||
{
|
||||
writeln(format("DEBUG: Creating new instance of type %s",
|
||||
_factoryParameters.instanceType.toString()));
|
||||
}
|
||||
else
|
||||
{
|
||||
writeln("DEBUG: Creating new instance from custom factory method");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected Object createInstance() {
|
||||
enforce!InstanceCreationException(_factoryParameters.instanceType, "Instance type is not defined, cannot create instance without knowing its type.");
|
||||
protected Object createInstance()
|
||||
{
|
||||
enforce!InstanceCreationException(_factoryParameters.instanceType,
|
||||
"Instance type is not defined, cannot create instance without knowing its type.");
|
||||
return _factoryParameters.instanceType.create();
|
||||
}
|
||||
}
|
||||
|
||||
class ConstructorInjectingInstanceFactory(InstanceType) : InstanceFactory {
|
||||
class ConstructorInjectingInstanceFactory(InstanceType) : InstanceFactory
|
||||
{
|
||||
private shared DependencyContainer container;
|
||||
private bool isBeingInjected = false;
|
||||
|
||||
this(shared DependencyContainer container) {
|
||||
this(shared DependencyContainer container)
|
||||
{
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
private static string createArgumentList(Params...)() {
|
||||
private static string createArgumentList(Params...)()
|
||||
{
|
||||
string argumentList = "";
|
||||
foreach(param; Params) {
|
||||
if (argumentList.length > 0) {
|
||||
foreach (param; Params)
|
||||
{
|
||||
if (argumentList.length > 0)
|
||||
{
|
||||
argumentList ~= ",";
|
||||
}
|
||||
|
||||
|
@ -137,18 +172,23 @@ class ConstructorInjectingInstanceFactory(InstanceType) : InstanceFactory {
|
|||
return argumentList;
|
||||
}
|
||||
|
||||
private static string createImportList(Params...)() {
|
||||
private static string createImportList(Params...)()
|
||||
{
|
||||
string importList = "";
|
||||
foreach(param; Params) {
|
||||
foreach (param; Params)
|
||||
{
|
||||
importList ~= createImportsString!param;
|
||||
}
|
||||
return importList;
|
||||
}
|
||||
|
||||
private static bool parametersAreValid(Params...)() {
|
||||
private static bool parametersAreValid(Params...)()
|
||||
{
|
||||
bool isValid = true;
|
||||
foreach(param; Params) {
|
||||
if (isBuiltinType!param || is(param == struct)) {
|
||||
foreach (param; Params)
|
||||
{
|
||||
if (isBuiltinType!param || is(param == struct))
|
||||
{
|
||||
isValid = false;
|
||||
break;
|
||||
}
|
||||
|
@ -157,18 +197,26 @@ class ConstructorInjectingInstanceFactory(InstanceType) : InstanceFactory {
|
|||
return isValid;
|
||||
}
|
||||
|
||||
protected override Object createInstance() {
|
||||
enforce!InstanceCreationException(container, "A dependency container is not defined. Cannot perform constructor injection without one.");
|
||||
enforce!InstanceCreationException(!isBeingInjected, format("%s is already being created and injected; possible circular dependencies in constructors?", InstanceType.stringof));
|
||||
protected override Object createInstance()
|
||||
{
|
||||
enforce!InstanceCreationException(container,
|
||||
"A dependency container is not defined. Cannot perform constructor injection without one.");
|
||||
enforce!InstanceCreationException(!isBeingInjected,
|
||||
format("%s is already being created and injected; possible circular dependencies in constructors?",
|
||||
InstanceType.stringof));
|
||||
|
||||
Object instance = null;
|
||||
static if (__traits(compiles, __traits(getOverloads, InstanceType, `__ctor`))) {
|
||||
foreach(ctor ; __traits(getOverloads, InstanceType, `__ctor`)) {
|
||||
static if (parametersAreValid!(Parameters!ctor)) {
|
||||
static if (__traits(compiles, __traits(getOverloads, InstanceType, `__ctor`)))
|
||||
{
|
||||
foreach (ctor; __traits(getOverloads, InstanceType, `__ctor`))
|
||||
{
|
||||
static if (parametersAreValid!(Parameters!ctor))
|
||||
{
|
||||
isBeingInjected = true;
|
||||
mixin(createImportsString!InstanceType
|
||||
~ createImportList!(Parameters!ctor) ~ `
|
||||
instance = new ` ~ fullyQualifiedName!InstanceType ~ `(` ~ createArgumentList!(Parameters!ctor) ~ `);
|
||||
mixin(createImportsString!InstanceType ~ createImportList!(
|
||||
Parameters!ctor) ~ `
|
||||
instance = new ` ~ fullyQualifiedName!InstanceType ~ `(` ~ createArgumentList!(
|
||||
Parameters!ctor) ~ `);
|
||||
`);
|
||||
isBeingInjected = false;
|
||||
break;
|
||||
|
@ -176,11 +224,14 @@ class ConstructorInjectingInstanceFactory(InstanceType) : InstanceFactory {
|
|||
}
|
||||
}
|
||||
|
||||
if (instance is null) {
|
||||
if (instance is null)
|
||||
{
|
||||
instance = typeid(InstanceType).create();
|
||||
}
|
||||
|
||||
enforce!InstanceCreationException(instance !is null, "Unable to create instance of type" ~ InstanceType.stringof ~ ", does it have injectable constructors?");
|
||||
enforce!InstanceCreationException(instance !is null,
|
||||
"Unable to create instance of type" ~ InstanceType.stringof
|
||||
~ ", does it have injectable constructors?");
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
|
|
@ -14,11 +14,16 @@ module poodinis.imports;
|
|||
import std.meta : staticIndexOf;
|
||||
import std.traits : moduleName, TemplateArgsOf, isBuiltinType;
|
||||
|
||||
public static string createImportsString(Type, ParentTypeList...)() {
|
||||
public static string createImportsString(Type, ParentTypeList...)()
|
||||
{
|
||||
string imports = `import ` ~ moduleName!Type ~ `;`;
|
||||
static if (__traits(compiles, TemplateArgsOf!Type)) {
|
||||
foreach(TemplateArgType; TemplateArgsOf!Type) {
|
||||
static if (!isBuiltinType!TemplateArgType && staticIndexOf!(TemplateArgType, ParentTypeList) == -1) {
|
||||
static if (__traits(compiles, TemplateArgsOf!Type))
|
||||
{
|
||||
foreach (TemplateArgType; TemplateArgsOf!Type)
|
||||
{
|
||||
static if (!isBuiltinType!TemplateArgType
|
||||
&& staticIndexOf!(TemplateArgType, ParentTypeList) == -1)
|
||||
{
|
||||
imports ~= createImportsString!(TemplateArgType, ParentTypeList, Type);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -27,7 +27,8 @@ module poodinis.polyfill;
|
|||
|
||||
import std.exception;
|
||||
|
||||
static if (!__traits(compiles, basicExceptionCtors)) {
|
||||
static if (!__traits(compiles, basicExceptionCtors))
|
||||
{
|
||||
mixin template basicExceptionCtors()
|
||||
{
|
||||
/++
|
||||
|
@ -37,8 +38,7 @@ static if (!__traits(compiles, basicExceptionCtors)) {
|
|||
line = The line number where the exception occurred.
|
||||
next = The previous exception in the chain of exceptions, if any.
|
||||
+/
|
||||
this(string msg, string file = __FILE__, size_t line = __LINE__,
|
||||
Throwable next = null) @nogc @safe pure nothrow
|
||||
this(string msg, string file = __FILE__, size_t line = __LINE__, Throwable next = null) @nogc @safe pure nothrow
|
||||
{
|
||||
super(msg, file, line, next);
|
||||
}
|
||||
|
@ -50,12 +50,13 @@ static if (!__traits(compiles, basicExceptionCtors)) {
|
|||
file = The file where the exception occurred.
|
||||
line = The line number where the exception occurred.
|
||||
+/
|
||||
this(string msg, Throwable next, string file = __FILE__,
|
||||
size_t line = __LINE__) @nogc @safe pure nothrow
|
||||
this(string msg, Throwable next, string file = __FILE__, size_t line = __LINE__) @nogc @safe pure nothrow
|
||||
{
|
||||
super(msg, file, line, next);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
public import std.exception : basicExceptionCtors;
|
||||
}
|
||||
|
|
|
@ -14,9 +14,11 @@
|
|||
module poodinis.registration;
|
||||
|
||||
import poodinis.container : DependencyContainer;
|
||||
import poodinis.factory : InstanceFactory, InstanceEventHandler, InstanceCreationException, InstanceFactoryParameters, CreatesSingleton;
|
||||
import poodinis.factory : InstanceFactory, InstanceEventHandler,
|
||||
InstanceCreationException, InstanceFactoryParameters, CreatesSingleton;
|
||||
|
||||
class Registration {
|
||||
class Registration
|
||||
{
|
||||
private TypeInfo _registeredType = null;
|
||||
private TypeInfo_Class _instanceType = null;
|
||||
private Registration linkedRegistration;
|
||||
|
@ -24,56 +26,70 @@ class Registration {
|
|||
private InstanceFactory _instanceFactory;
|
||||
private void delegate() _preDestructor;
|
||||
|
||||
public @property registeredType() {
|
||||
public @property registeredType()
|
||||
{
|
||||
return _registeredType;
|
||||
}
|
||||
|
||||
public @property instanceType() {
|
||||
public @property instanceType()
|
||||
{
|
||||
return _instanceType;
|
||||
}
|
||||
|
||||
public @property originatingContainer() {
|
||||
public @property originatingContainer()
|
||||
{
|
||||
return _originatingContainer;
|
||||
}
|
||||
|
||||
public @property instanceFactory() {
|
||||
public @property instanceFactory()
|
||||
{
|
||||
return _instanceFactory;
|
||||
}
|
||||
|
||||
public @property preDestructor() {
|
||||
public @property preDestructor()
|
||||
{
|
||||
return _preDestructor;
|
||||
}
|
||||
|
||||
protected @property preDestructor(void delegate() preDestructor) {
|
||||
protected @property preDestructor(void delegate() preDestructor)
|
||||
{
|
||||
_preDestructor = preDestructor;
|
||||
}
|
||||
|
||||
this(TypeInfo registeredType, TypeInfo_Class instanceType, InstanceFactory instanceFactory, shared(DependencyContainer) originatingContainer) {
|
||||
this(TypeInfo registeredType, TypeInfo_Class instanceType,
|
||||
InstanceFactory instanceFactory, shared(DependencyContainer) originatingContainer)
|
||||
{
|
||||
this._registeredType = registeredType;
|
||||
this._instanceType = instanceType;
|
||||
this._originatingContainer = originatingContainer;
|
||||
this._instanceFactory = instanceFactory;
|
||||
}
|
||||
|
||||
public Object getInstance(InstantiationContext context = new InstantiationContext()) {
|
||||
if (linkedRegistration !is null) {
|
||||
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());
|
||||
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) {
|
||||
public Registration linkTo(Registration registration)
|
||||
{
|
||||
this.linkedRegistration = registration;
|
||||
return this;
|
||||
}
|
||||
|
||||
Registration onConstructed(InstanceEventHandler handler) {
|
||||
if(instanceFactory !is null)
|
||||
Registration onConstructed(InstanceEventHandler handler)
|
||||
{
|
||||
if (instanceFactory !is null)
|
||||
instanceFactory.onConstructed(handler);
|
||||
return this;
|
||||
}
|
||||
|
@ -84,32 +100,40 @@ class Registration {
|
|||
*
|
||||
* Effectively makes the given registration a singleton.
|
||||
*/
|
||||
public Registration singleInstance(Registration registration) {
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(registration.instanceType, CreatesSingleton.yes);
|
||||
public Registration singleInstance(Registration registration)
|
||||
{
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(
|
||||
registration.instanceType, CreatesSingleton.yes);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scopes registrations to return a new instance every time the given registration is resolved.
|
||||
*/
|
||||
public Registration newInstance(Registration registration) {
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(registration.instanceType, CreatesSingleton.no);
|
||||
public Registration newInstance(Registration registration)
|
||||
{
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(
|
||||
registration.instanceType, CreatesSingleton.no);
|
||||
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.factoryParameters = InstanceFactoryParameters(registration.instanceType, CreatesSingleton.yes, instance);
|
||||
public Registration existingInstance(Registration registration, Object instance)
|
||||
{
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(
|
||||
registration.instanceType, CreatesSingleton.yes, instance);
|
||||
return registration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scopes registrations to create new instances using the given initializer delegate.
|
||||
*/
|
||||
public Registration initializedBy(T : Object)(Registration registration, T delegate() initializer) {
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(registration.instanceType, CreatesSingleton.no, null, {
|
||||
public Registration initializedBy(T : Object)(Registration registration, T delegate() initializer)
|
||||
{
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(
|
||||
registration.instanceType, CreatesSingleton.no, null, {
|
||||
return cast(Object) initializer();
|
||||
});
|
||||
|
||||
|
@ -119,18 +143,23 @@ public Registration initializedBy(T : Object)(Registration registration, T deleg
|
|||
/**
|
||||
* Scopes registrations to create a new instance using the given initializer delegate. On subsequent resolves the same instance is returned.
|
||||
*/
|
||||
public Registration initializedOnceBy(T : Object)(Registration registration, T delegate() initializer) {
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(registration.instanceType, CreatesSingleton.yes, null, {
|
||||
public Registration initializedOnceBy(T : Object)(Registration registration, T delegate() initializer)
|
||||
{
|
||||
registration.instanceFactory.factoryParameters = InstanceFactoryParameters(
|
||||
registration.instanceType, CreatesSingleton.yes, null, {
|
||||
return cast(Object) initializer();
|
||||
});
|
||||
|
||||
return registration;
|
||||
}
|
||||
|
||||
public string toConcreteTypeListString(Registration[] registrations) {
|
||||
public string toConcreteTypeListString(Registration[] registrations)
|
||||
{
|
||||
auto concreteTypeListString = "";
|
||||
foreach (registration ; registrations) {
|
||||
if (concreteTypeListString.length > 0) {
|
||||
foreach (registration; registrations)
|
||||
{
|
||||
if (concreteTypeListString.length > 0)
|
||||
{
|
||||
concreteTypeListString ~= ", ";
|
||||
}
|
||||
concreteTypeListString ~= registration.instanceType.toString();
|
||||
|
@ -138,4 +167,6 @@ public string toConcreteTypeListString(Registration[] registrations) {
|
|||
return concreteTypeListString;
|
||||
}
|
||||
|
||||
class InstantiationContext {}
|
||||
class InstantiationContext
|
||||
{
|
||||
}
|
||||
|
|
|
@ -18,19 +18,23 @@ import std.string : format;
|
|||
/**
|
||||
* Thrown when something goes wrong during value injection.
|
||||
*/
|
||||
class ValueInjectionException : Exception {
|
||||
class ValueInjectionException : Exception
|
||||
{
|
||||
mixin basicExceptionCtors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Thrown by injectors when the value with the given key cannot be found.
|
||||
*/
|
||||
class ValueNotAvailableException : Exception {
|
||||
this(string key) {
|
||||
class ValueNotAvailableException : Exception
|
||||
{
|
||||
this(string key)
|
||||
{
|
||||
super(format("Value for key %s is not available", key));
|
||||
}
|
||||
|
||||
this(string key, Throwable cause) {
|
||||
this(string key, Throwable cause)
|
||||
{
|
||||
super(format("Value for key %s is not available", key), cause);
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +56,8 @@ class ValueNotAvailableException : Exception {
|
|||
* }
|
||||
* ---
|
||||
*/
|
||||
struct Value {
|
||||
struct Value
|
||||
{
|
||||
/**
|
||||
* The textual key used to find the value by injectors.
|
||||
*
|
||||
|
@ -78,7 +83,8 @@ struct Value {
|
|||
* }
|
||||
* ---
|
||||
*/
|
||||
struct MandatoryValue {
|
||||
struct MandatoryValue
|
||||
{
|
||||
/**
|
||||
* The textual key used to find the value by injectors.
|
||||
*
|
||||
|
@ -109,7 +115,8 @@ struct MandatoryValue {
|
|||
* container.register!(ValueInjector!int, MyIntInjector);
|
||||
* ---
|
||||
*/
|
||||
interface ValueInjector(Type) {
|
||||
interface ValueInjector(Type)
|
||||
{
|
||||
/**
|
||||
* Get a value from the injector by key.
|
||||
*
|
||||
|
@ -120,4 +127,3 @@ interface ValueInjector(Type) {
|
|||
*/
|
||||
Type get(string key);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,10 +10,12 @@ import poodinis.test.testClasses;
|
|||
|
||||
import std.exception;
|
||||
|
||||
version(unittest) {
|
||||
version (unittest)
|
||||
{
|
||||
|
||||
// Test autowiring concrete type to existing instance
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ComponentA;
|
||||
auto componentB = new ComponentB();
|
||||
|
@ -22,7 +24,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring interface type to existing instance
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(InterfaceA, ComponentC);
|
||||
auto componentD = new ComponentD();
|
||||
|
@ -31,16 +34,19 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring private members
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(InterfaceA, ComponentC);
|
||||
auto componentD = new ComponentD();
|
||||
container.autowire(componentD);
|
||||
assert(componentD.privateComponentC is componentD.componentC, "Autowire private dependency failed");
|
||||
assert(componentD.privateComponentC is componentD.componentC,
|
||||
"Autowire private dependency failed");
|
||||
}
|
||||
|
||||
// Test autowiring will only happen once
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(InterfaceA, ComponentC).newInstance();
|
||||
auto componentD = new ComponentD();
|
||||
|
@ -48,37 +54,45 @@ version(unittest) {
|
|||
auto expectedComponent = componentD.componentC;
|
||||
container.autowire(componentD);
|
||||
auto actualComponent = componentD.componentC;
|
||||
assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance");
|
||||
assert(expectedComponent is actualComponent,
|
||||
"Autowiring the second time wired a different instance");
|
||||
}
|
||||
|
||||
// Test autowiring unregistered type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto componentD = new ComponentD();
|
||||
assertThrown!(ResolveException)(container.autowire(componentD), "Autowiring unregistered type should throw ResolveException");
|
||||
assertThrown!(ResolveException)(container.autowire(componentD),
|
||||
"Autowiring unregistered type should throw ResolveException");
|
||||
}
|
||||
|
||||
// Test autowiring member with non-autowire attribute does not autowire
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto componentE = new ComponentE();
|
||||
container.autowire(componentE);
|
||||
assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire");
|
||||
assert(componentE.componentC is null,
|
||||
"Autowiring should not occur for members with attributes other than @Autowire");
|
||||
}
|
||||
|
||||
// Test autowire class with alias declaration
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ComponentA;
|
||||
auto componentDeclarationCocktail = new ComponentDeclarationCocktail();
|
||||
|
||||
container.autowire(componentDeclarationCocktail);
|
||||
|
||||
assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed");
|
||||
assert(componentDeclarationCocktail.componentA !is null,
|
||||
"Autowiring class with non-assignable declarations failed");
|
||||
}
|
||||
|
||||
// Test autowire class with qualifier
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(InterfaceA, ComponentC);
|
||||
container.register!(InterfaceA, ComponentX);
|
||||
|
@ -91,7 +105,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowire class with multiple qualifiers
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(InterfaceA, ComponentC);
|
||||
container.register!(InterfaceA, ComponentX);
|
||||
|
@ -101,23 +116,29 @@ version(unittest) {
|
|||
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");
|
||||
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 {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ComponentA;
|
||||
|
||||
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), new InstanceFactory(), container).singleInstance();
|
||||
auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext());
|
||||
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB),
|
||||
new InstanceFactory(), 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 {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(InterfaceA, ComponentC);
|
||||
container.register!(InterfaceA, ComponentX);
|
||||
|
@ -129,7 +150,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring new instance of singleinstance registration with newInstance UDA
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ComponentA;
|
||||
|
||||
|
@ -138,11 +160,13 @@ version(unittest) {
|
|||
|
||||
container.autowire(charlie);
|
||||
|
||||
assert(charlie.componentA !is regularComponentA, "Autowiring class with AssignNewInstance did not yield a different instance");
|
||||
assert(charlie.componentA !is regularComponentA,
|
||||
"Autowiring class with AssignNewInstance did not yield a different instance");
|
||||
}
|
||||
|
||||
// Test autowiring members from base class
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ComponentA;
|
||||
container.register!ComponentB;
|
||||
|
@ -155,7 +179,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring optional dependencies
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto instance = new OuttaTime();
|
||||
|
||||
|
@ -167,7 +192,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring class using value injection
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
container.register!(ValueInjector!int, TestInjector);
|
||||
|
@ -181,7 +207,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring classes with recursive template parameters
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!CircularTemplateComponentA;
|
||||
container.register!CircularTemplateComponentB;
|
||||
|
|
|
@ -12,17 +12,21 @@ import poodinis.test.foreignDependencies;
|
|||
import std.exception;
|
||||
import core.thread;
|
||||
|
||||
version(unittest) {
|
||||
version (unittest)
|
||||
{
|
||||
|
||||
// Test register concrete type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto registration = container.register!TestClass;
|
||||
assert(registration.registeredType == typeid(TestClass), "Type of registered type not the same");
|
||||
assert(registration.registeredType == typeid(TestClass),
|
||||
"Type of registered type not the same");
|
||||
}
|
||||
|
||||
// Test resolve registered type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestClass;
|
||||
TestClass actualInstance = container.resolve!TestClass;
|
||||
|
@ -31,110 +35,125 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test register interface
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(TestInterface, TestClass);
|
||||
TestInterface actualInstance = container.resolve!TestInterface;
|
||||
assert(actualInstance !is null, "Resolved type is null");
|
||||
assert(cast(TestInterface) actualInstance, "Resolved class is not the same type as expected");
|
||||
assert(cast(TestInterface) actualInstance,
|
||||
"Resolved class is not the same type as expected");
|
||||
}
|
||||
|
||||
// Test resolve non-registered type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
assertThrown!ResolveException(container.resolve!TestClass, "Resolving non-registered type does not fail");
|
||||
assertThrown!ResolveException(container.resolve!TestClass,
|
||||
"Resolving non-registered type does not fail");
|
||||
}
|
||||
|
||||
// Test clear registrations
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestClass;
|
||||
container.clearAllRegistrations();
|
||||
assertThrown!ResolveException(container.resolve!TestClass, "Resolving cleared type does not fail");
|
||||
assertThrown!ResolveException(container.resolve!TestClass,
|
||||
"Resolving cleared type does not fail");
|
||||
}
|
||||
|
||||
// Test resolve single instance for type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestClass.singleInstance();
|
||||
auto instance1 = container.resolve!TestClass;
|
||||
auto instance2 = container.resolve!TestClass;
|
||||
assert(instance1 is instance2, "Resolved instance from single instance scope is not the each time it is resolved");
|
||||
assert(instance1 is instance2,
|
||||
"Resolved instance from single instance scope is not the each time it is resolved");
|
||||
}
|
||||
|
||||
// Test resolve new instance for type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestClass.newInstance();
|
||||
auto instance1 = container.resolve!TestClass;
|
||||
auto instance2 = container.resolve!TestClass;
|
||||
assert(instance1 !is instance2, "Resolved instance from new instance scope is the same each time it is resolved");
|
||||
assert(instance1 !is instance2,
|
||||
"Resolved instance from new instance scope is the same each time it is resolved");
|
||||
}
|
||||
|
||||
// Test resolve existing instance for type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto expectedInstance = new TestClass();
|
||||
container.register!TestClass.existingInstance(expectedInstance);
|
||||
auto actualInstance = container.resolve!TestClass;
|
||||
assert(expectedInstance is actualInstance, "Resolved instance from existing instance scope is not the same as the registered instance");
|
||||
assert(expectedInstance is actualInstance,
|
||||
"Resolved instance from existing instance scope is not the same as the registered instance");
|
||||
}
|
||||
|
||||
// Test creating instance via custom initializer on resolve
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto expectedInstance = new TestClass();
|
||||
container.register!TestClass.initializedBy({
|
||||
return expectedInstance;
|
||||
});
|
||||
container.register!TestClass.initializedBy({ return expectedInstance; });
|
||||
auto actualInstance = container.resolve!TestClass;
|
||||
assert(expectedInstance is actualInstance, "Resolved instance does not come from the custom initializer");
|
||||
assert(expectedInstance is actualInstance,
|
||||
"Resolved instance does not come from the custom initializer");
|
||||
}
|
||||
|
||||
// Test creating instance via initializedBy creates new instance every time
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestClass.initializedBy({
|
||||
return new TestClass();
|
||||
});
|
||||
container.register!TestClass.initializedBy({ return new TestClass(); });
|
||||
auto firstInstance = container.resolve!TestClass;
|
||||
auto secondInstance = container.resolve!TestClass;
|
||||
assert(firstInstance !is secondInstance, "Resolved instance are not different instances");
|
||||
}
|
||||
|
||||
// Test creating instance via initializedOnceBy creates a singleton instance
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestClass.initializedOnceBy({
|
||||
return new TestClass();
|
||||
});
|
||||
container.register!TestClass.initializedOnceBy({ return new TestClass(); });
|
||||
auto firstInstance = container.resolve!TestClass;
|
||||
auto secondInstance = container.resolve!TestClass;
|
||||
assert(firstInstance is secondInstance, "Resolved instance are different instances");
|
||||
}
|
||||
|
||||
// Test autowire resolved instances
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!AutowiredClass;
|
||||
container.register!ComponentClass;
|
||||
auto componentInstance = container.resolve!ComponentClass;
|
||||
auto autowiredInstance = container.resolve!AutowiredClass;
|
||||
assert(componentInstance.autowiredClass is autowiredInstance, "Member is not autowired upon resolving");
|
||||
assert(componentInstance.autowiredClass is autowiredInstance,
|
||||
"Member is not autowired upon resolving");
|
||||
}
|
||||
|
||||
// Test circular autowiring
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ComponentMouse;
|
||||
container.register!ComponentCat;
|
||||
auto mouse = container.resolve!ComponentMouse;
|
||||
auto cat = container.resolve!ComponentCat;
|
||||
assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat, "Circular dependencies should be autowirable");
|
||||
assert(mouse.cat is cat && cat.mouse is mouse && mouse !is cat,
|
||||
"Circular dependencies should be autowirable");
|
||||
}
|
||||
|
||||
// Test remove registration
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestClass;
|
||||
container.removeRegistration!TestClass;
|
||||
|
@ -142,7 +161,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring does not autowire member where instance is non-null
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto existingA = new AutowiredClass();
|
||||
auto existingB = new ComponentClass();
|
||||
|
@ -153,11 +173,13 @@ version(unittest) {
|
|||
auto resolvedA = container.resolve!AutowiredClass;
|
||||
auto resolvedB = container.resolve!ComponentClass;
|
||||
|
||||
assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA, "Autowiring shouldn't rewire member when it is already wired to an instance");
|
||||
assert(resolvedB.autowiredClass is existingA && resolvedA !is existingA,
|
||||
"Autowiring shouldn't rewire member when it is already wired to an instance");
|
||||
}
|
||||
|
||||
// Test autowiring circular dependency by third-degree
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Eenie;
|
||||
container.register!Meenie;
|
||||
|
@ -165,11 +187,13 @@ version(unittest) {
|
|||
|
||||
auto eenie = container.resolve!Eenie;
|
||||
|
||||
assert(eenie.meenie.moe.eenie is eenie, "Autowiring third-degree circular dependency failed");
|
||||
assert(eenie.meenie.moe.eenie is eenie,
|
||||
"Autowiring third-degree circular dependency failed");
|
||||
}
|
||||
|
||||
// Test autowiring deep circular dependencies
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Ittie;
|
||||
container.register!Bittie;
|
||||
|
@ -181,7 +205,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring deep circular dependencies with newInstance scope does not autowire new instance second time
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Ittie.newInstance();
|
||||
container.register!Bittie.newInstance();
|
||||
|
@ -189,29 +214,37 @@ version(unittest) {
|
|||
|
||||
auto ittie = container.resolve!Ittie;
|
||||
|
||||
assert(ittie.bittie.banana.bittie.banana is null, "Autowiring deep dependencies with newInstance scope autowired a reoccuring type.");
|
||||
assert(ittie.bittie.banana.bittie.banana is null,
|
||||
"Autowiring deep dependencies with newInstance scope autowired a reoccuring type.");
|
||||
}
|
||||
|
||||
// Test autowiring type registered by interface
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Bunena;
|
||||
container.register!Bittie;
|
||||
container.register!(SuperInterface, SuperImplementation);
|
||||
|
||||
SuperImplementation superInstance = cast(SuperImplementation) container.resolve!SuperInterface;
|
||||
SuperImplementation superInstance = cast(SuperImplementation) container
|
||||
.resolve!SuperInterface;
|
||||
|
||||
assert(!(superInstance.banana is null), "Instance which was resolved by interface type was not autowired.");
|
||||
assert(!(superInstance.banana is null),
|
||||
"Instance which was resolved by interface type was not autowired.");
|
||||
}
|
||||
|
||||
// Test reusing a container after clearing all registrations
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Banana;
|
||||
container.clearAllRegistrations();
|
||||
try {
|
||||
try
|
||||
{
|
||||
container.resolve!Banana;
|
||||
} catch (ResolveException e) {
|
||||
}
|
||||
catch (ResolveException e)
|
||||
{
|
||||
container.register!Banana;
|
||||
return;
|
||||
}
|
||||
|
@ -219,14 +252,16 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test register multiple concrete classess to same interface type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(Color, Blue);
|
||||
container.register!(Color, Red);
|
||||
}
|
||||
|
||||
// Test removing all registrations for type with multiple registrations.
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(Color, Blue);
|
||||
container.register!(Color, Red);
|
||||
|
@ -234,42 +269,51 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test registering same registration again
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto firstRegistration = container.register!(Color, Blue);
|
||||
auto secondRegistration = container.register!(Color, Blue);
|
||||
|
||||
assert(firstRegistration is secondRegistration, "First registration is not the same as the second of equal types");
|
||||
assert(firstRegistration is secondRegistration,
|
||||
"First registration is not the same as the second of equal types");
|
||||
}
|
||||
|
||||
// Test resolve registration with multiple qualifiers
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(Color, Blue);
|
||||
container.register!(Color, Red);
|
||||
try {
|
||||
try
|
||||
{
|
||||
container.resolve!Color;
|
||||
} catch (ResolveException e) {
|
||||
}
|
||||
catch (ResolveException e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
assert(false);
|
||||
}
|
||||
|
||||
// Test resolve registration with multiple qualifiers using a qualifier
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(Color, Blue);
|
||||
container.register!(Color, Red);
|
||||
auto blueInstance = container.resolve!(Color, Blue);
|
||||
auto redInstance = container.resolve!(Color, Red);
|
||||
|
||||
assert(blueInstance !is redInstance, "Resolving type with multiple, different registrations yielded the same instance");
|
||||
assert(blueInstance !is redInstance,
|
||||
"Resolving type with multiple, different registrations yielded the same instance");
|
||||
assert(blueInstance !is null, "Resolved blue instance to null");
|
||||
assert(redInstance !is null, "Resolved red instance to null");
|
||||
}
|
||||
|
||||
// Test autowire of unqualified member typed by interface.
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Spiders;
|
||||
container.register!(TestInterface, TestClass);
|
||||
|
@ -280,38 +324,45 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Register existing registration
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
auto firstRegistration = container.register!TestClass;
|
||||
auto secondRegistration = container.register!TestClass;
|
||||
|
||||
assert(firstRegistration is secondRegistration, "Registering the same registration twice registers the dependencies twice.");
|
||||
assert(firstRegistration is secondRegistration,
|
||||
"Registering the same registration twice registers the dependencies twice.");
|
||||
}
|
||||
|
||||
// Register existing registration by supertype
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
auto firstRegistration = container.register!(TestInterface, TestClass);
|
||||
auto secondRegistration = container.register!(TestInterface, TestClass);
|
||||
|
||||
assert(firstRegistration is secondRegistration, "Registering the same registration by super type twice registers the dependencies twice.");
|
||||
assert(firstRegistration is secondRegistration,
|
||||
"Registering the same registration by super type twice registers the dependencies twice.");
|
||||
}
|
||||
|
||||
// Resolve dependency depending on itself
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Recursive;
|
||||
|
||||
auto instance = container.resolve!Recursive;
|
||||
|
||||
assert(instance.recursive is instance, "Resolving dependency that depends on itself fails.");
|
||||
assert(instance.recursive.recursive is instance, "Resolving dependency that depends on itself fails.");
|
||||
assert(instance.recursive.recursive is instance,
|
||||
"Resolving dependency that depends on itself fails.");
|
||||
}
|
||||
|
||||
// Test autowire stack pop-back
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Moolah;
|
||||
container.register!Wants.newInstance();
|
||||
|
@ -324,12 +375,11 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test resolving registration registered in different thread
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
auto thread = new Thread(delegate() {
|
||||
container.register!TestClass;
|
||||
});
|
||||
auto thread = new Thread(delegate() { container.register!TestClass; });
|
||||
thread.start();
|
||||
thread.join();
|
||||
|
||||
|
@ -337,7 +387,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test resolving instance previously resolved in different thread
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
shared(TestClass) actualTestClass;
|
||||
|
||||
|
@ -351,27 +402,32 @@ version(unittest) {
|
|||
|
||||
shared(TestClass) expectedTestClass = cast(shared(TestClass)) container.resolve!TestClass;
|
||||
|
||||
assert(expectedTestClass is actualTestClass, "Instance resolved in main thread is not the one resolved in thread");
|
||||
assert(expectedTestClass is actualTestClass,
|
||||
"Instance resolved in main thread is not the one resolved in thread");
|
||||
}
|
||||
|
||||
// Test registering type with option doNotAddConcreteTypeRegistration
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(TestInterface, TestClass)(RegistrationOption.doNotAddConcreteTypeRegistration);
|
||||
container.register!(TestInterface,
|
||||
TestClass)(RegistrationOption.doNotAddConcreteTypeRegistration);
|
||||
|
||||
auto firstInstance = container.resolve!TestInterface;
|
||||
assertThrown!ResolveException(container.resolve!TestClass);
|
||||
}
|
||||
|
||||
// Test registering conrete type with registration option doNotAddConcreteTypeRegistration does nothing
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestClass(RegistrationOption.doNotAddConcreteTypeRegistration);
|
||||
container.resolve!TestClass;
|
||||
}
|
||||
|
||||
// Test registering type will register by contrete type by default
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(TestInterface, TestClass);
|
||||
|
||||
|
@ -382,7 +438,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test resolving all registrations to an interface
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(Color, Blue);
|
||||
container.register!(Color, Red);
|
||||
|
@ -393,7 +450,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring instances resolved in array
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!UnrelatedClass;
|
||||
container.register!(TestInterface, TestClassDeux);
|
||||
|
@ -405,38 +463,45 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test set persistent registration options
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.setPersistentRegistrationOptions(RegistrationOption.doNotAddConcreteTypeRegistration);
|
||||
container.setPersistentRegistrationOptions(
|
||||
RegistrationOption.doNotAddConcreteTypeRegistration);
|
||||
container.register!(TestInterface, TestClass);
|
||||
assertThrown!ResolveException(container.resolve!TestClass);
|
||||
}
|
||||
|
||||
// Test unset persistent registration options
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.setPersistentRegistrationOptions(RegistrationOption.doNotAddConcreteTypeRegistration);
|
||||
container.setPersistentRegistrationOptions(
|
||||
RegistrationOption.doNotAddConcreteTypeRegistration);
|
||||
container.unsetPersistentRegistrationOptions();
|
||||
container.register!(TestInterface, TestClass);
|
||||
container.resolve!TestClass;
|
||||
}
|
||||
|
||||
// Test registration when resolving
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.resolve!(TestInterface, TestClass)(ResolveOption.registerBeforeResolving);
|
||||
container.resolve!TestClass;
|
||||
}
|
||||
|
||||
// Test set persistent resolve options
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.setPersistentResolveOptions(ResolveOption.registerBeforeResolving);
|
||||
container.resolve!TestClass;
|
||||
}
|
||||
|
||||
// Test unset persistent resolve options
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.setPersistentResolveOptions(ResolveOption.registerBeforeResolving);
|
||||
container.unsetPersistentResolveOptions();
|
||||
|
@ -444,27 +509,32 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test ResolveOption registerBeforeResolving fails for interfaces
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
assertThrown!ResolveException(container.resolve!TestInterface(ResolveOption.registerBeforeResolving));
|
||||
assertThrown!ResolveException(
|
||||
container.resolve!TestInterface(ResolveOption.registerBeforeResolving));
|
||||
}
|
||||
|
||||
// Test ResolveOption noResolveException does not throw
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto instance = container.resolve!TestInterface(ResolveOption.noResolveException);
|
||||
assert(instance is null);
|
||||
}
|
||||
|
||||
// ResolveOption noResolveException does not throw for resolveAll
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto instances = container.resolveAll!TestInterface(ResolveOption.noResolveException);
|
||||
assert(instances.length == 0);
|
||||
}
|
||||
|
||||
// Test autowired, constructor injected class
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Red;
|
||||
container.register!Moolah;
|
||||
|
@ -478,7 +548,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting constructor with super-type parameter
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Wallpaper;
|
||||
container.register!(Color, Blue);
|
||||
|
@ -489,7 +560,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test prevention of circular dependencies during constructor injection
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Pot;
|
||||
container.register!Kettle;
|
||||
|
@ -498,7 +570,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test prevention of transitive circular dependencies during constructor injection
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Rock;
|
||||
container.register!Paper;
|
||||
|
@ -508,7 +581,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injection of foreign dependency in constructor
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Ola;
|
||||
container.register!Hello;
|
||||
|
@ -516,7 +590,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test PostConstruct method is called after resolving a dependency
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!PostConstructionDependency;
|
||||
|
||||
|
@ -525,7 +600,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test PostConstruct of base type is called
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ChildOfPostConstruction;
|
||||
|
||||
|
@ -534,7 +610,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test PostConstruct of class implementing interface is not called
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ButThereWontBe;
|
||||
|
||||
|
@ -543,7 +620,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test postconstruction happens after autowiring and value injection
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(ValueInjector!int, PostConstructingIntInjector);
|
||||
container.register!PostConstructionDependency;
|
||||
|
@ -552,7 +630,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test PreDestroy is called when removing a registration
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!PreDestroyerOfFates;
|
||||
auto instance = container.resolve!PreDestroyerOfFates;
|
||||
|
@ -561,7 +640,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test PreDestroy is called when removing all registrations
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!PreDestroyerOfFates;
|
||||
auto instance = container.resolve!PreDestroyerOfFates;
|
||||
|
@ -570,7 +650,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test PreDestroy is called when the container is destroyed
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!PreDestroyerOfFates;
|
||||
auto instance = container.resolve!PreDestroyerOfFates;
|
||||
|
|
|
@ -10,10 +10,12 @@ import poodinis.test.testClasses;
|
|||
|
||||
import std.exception;
|
||||
|
||||
version(unittest) {
|
||||
version (unittest)
|
||||
{
|
||||
|
||||
//Test register component registrations from context
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto context = new TestContext();
|
||||
context.registerContextComponents(container);
|
||||
|
@ -23,7 +25,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
//Test non-annotated methods are not registered
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto context = new TestContext();
|
||||
context.registerContextComponents(container);
|
||||
|
@ -31,7 +34,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
//Test register component by base type
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto context = new TestContext();
|
||||
context.registerContextComponents(container);
|
||||
|
@ -40,7 +44,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
//Test register components with multiple candidates
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto context = new TestContext();
|
||||
context.registerContextComponents(container);
|
||||
|
@ -53,7 +58,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
//Test register component as prototype
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto context = new TestContext();
|
||||
context.registerContextComponents(container);
|
||||
|
@ -66,7 +72,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test setting up simple dependencies through application context
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.registerContext!SimpleContext;
|
||||
auto instance = container.resolve!CakeChart;
|
||||
|
@ -75,7 +82,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test resolving dependency from registered application context
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.registerContext!SimpleContext;
|
||||
auto instance = container.resolve!Apple;
|
||||
|
@ -84,7 +92,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring application context
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!Apple;
|
||||
container.registerContext!AutowiredTestContext;
|
||||
|
@ -95,7 +104,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test autowiring application context with dependencies registered in same context
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.registerContext!ComplexAutowiredTestContext;
|
||||
auto instance = container.resolve!ClassWrapperWrapper;
|
||||
|
@ -108,7 +118,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test resolving registered context
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.registerContext!TestContext;
|
||||
container.resolve!ApplicationContext;
|
||||
|
|
|
@ -10,12 +10,15 @@ import poodinis.test.testClasses;
|
|||
|
||||
import std.exception;
|
||||
|
||||
version(unittest) {
|
||||
version (unittest)
|
||||
{
|
||||
|
||||
// Test instance factory with singletons
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto factory = new InstanceFactory();
|
||||
factory.factoryParameters = InstanceFactoryParameters(typeid(TestImplementation), CreatesSingleton.yes);
|
||||
factory.factoryParameters = InstanceFactoryParameters(typeid(TestImplementation),
|
||||
CreatesSingleton.yes);
|
||||
auto instanceOne = factory.getInstance();
|
||||
auto instanceTwo = factory.getInstance();
|
||||
|
||||
|
@ -24,9 +27,11 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test instance factory with new instances
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto factory = new InstanceFactory();
|
||||
factory.factoryParameters = InstanceFactoryParameters(typeid(TestImplementation), CreatesSingleton.no);
|
||||
factory.factoryParameters = InstanceFactoryParameters(typeid(TestImplementation),
|
||||
CreatesSingleton.no);
|
||||
auto instanceOne = factory.getInstance();
|
||||
auto instanceTwo = factory.getInstance();
|
||||
|
||||
|
@ -35,45 +40,57 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test instance factory with existing instances
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto existingInstance = new TestImplementation();
|
||||
auto factory = new InstanceFactory();
|
||||
factory.factoryParameters = InstanceFactoryParameters(typeid(TestImplementation), CreatesSingleton.yes, existingInstance);
|
||||
factory.factoryParameters = InstanceFactoryParameters(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");
|
||||
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 {
|
||||
unittest
|
||||
{
|
||||
auto existingInstance = new TestImplementation();
|
||||
auto factory = new InstanceFactory();
|
||||
factory.factoryParameters = InstanceFactoryParameters(typeid(TestImplementation), CreatesSingleton.no, existingInstance);
|
||||
factory.factoryParameters = InstanceFactoryParameters(typeid(TestImplementation),
|
||||
CreatesSingleton.no, existingInstance);
|
||||
auto instance = factory.getInstance();
|
||||
|
||||
assert(instance is existingInstance, "Created factory instance is not the existing instance");
|
||||
assert(instance is existingInstance,
|
||||
"Created factory instance is not the existing instance");
|
||||
}
|
||||
|
||||
// Test creating instance using custom factory method
|
||||
unittest {
|
||||
Object factoryMethod() {
|
||||
unittest
|
||||
{
|
||||
Object factoryMethod()
|
||||
{
|
||||
auto instance = new TestImplementation();
|
||||
instance.someContent = "Ducks!";
|
||||
return instance;
|
||||
}
|
||||
|
||||
auto factory = new InstanceFactory();
|
||||
factory.factoryParameters = InstanceFactoryParameters(null, CreatesSingleton.yes, null, &factoryMethod);
|
||||
factory.factoryParameters = InstanceFactoryParameters(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 !is null,
|
||||
"No instance was created by factory or could not be cast to expected type");
|
||||
assert(instance.someContent == "Ducks!");
|
||||
}
|
||||
|
||||
// Test injecting constructor of class
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!TestImplementation;
|
||||
|
||||
|
@ -85,12 +102,14 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting constructor of class with multiple constructors injects the first candidate
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!SomeOtherClassThen;
|
||||
container.register!TestImplementation;
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithMultipleConstructors(container);
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithMultipleConstructors(
|
||||
container);
|
||||
auto instance = cast(ClassWithMultipleConstructors) factory.getInstance();
|
||||
|
||||
assert(instance !is null);
|
||||
|
@ -99,12 +118,14 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting constructor of class with multiple constructor parameters
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!SomeOtherClassThen;
|
||||
container.register!TestImplementation;
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithConstructorWithMultipleParameters(container);
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithConstructorWithMultipleParameters(
|
||||
container);
|
||||
auto instance = cast(ClassWithConstructorWithMultipleParameters) factory.getInstance();
|
||||
|
||||
assert(instance !is null);
|
||||
|
@ -113,11 +134,13 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting constructor of class with primitive constructor parameters
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!SomeOtherClassThen;
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithPrimitiveConstructor(container);
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithPrimitiveConstructor(
|
||||
container);
|
||||
auto instance = cast(ClassWithPrimitiveConstructor) factory.getInstance();
|
||||
|
||||
assert(instance !is null);
|
||||
|
@ -125,7 +148,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting constructor of class with struct constructor parameters
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!SomeOtherClassThen;
|
||||
|
||||
|
@ -137,7 +161,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting constructor of class with empty constructor will skip injection
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithEmptyConstructor(container);
|
||||
|
@ -148,10 +173,12 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting constructor of class with no candidates fails
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithNonInjectableConstructor(container);
|
||||
auto factory = new ConstructorInjectingInstanceFactory!ClassWithNonInjectableConstructor(
|
||||
container);
|
||||
|
||||
assertThrown!InstanceCreationException(factory.getInstance());
|
||||
}
|
||||
|
|
|
@ -7,6 +7,9 @@
|
|||
|
||||
module poodinis.test.foreignDependencies;
|
||||
|
||||
version(unittest) {
|
||||
class Ola {}
|
||||
version (unittest)
|
||||
{
|
||||
class Ola
|
||||
{
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,48 +10,65 @@ import poodinis.test.testClasses;
|
|||
|
||||
import std.exception;
|
||||
|
||||
version(unittest) {
|
||||
version (unittest)
|
||||
{
|
||||
|
||||
// Test getting instance without scope defined throws exception
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
Registration registration = new Registration(typeid(TestType), null, null, null);
|
||||
assertThrown!(InstanceCreationException)(registration.getInstance(), null);
|
||||
}
|
||||
|
||||
// Test set single instance scope using scope setter
|
||||
unittest {
|
||||
Registration registration = new Registration(null, typeid(TestType), new InstanceFactory(), null);
|
||||
unittest
|
||||
{
|
||||
Registration registration = new Registration(null, typeid(TestType),
|
||||
new InstanceFactory(), null);
|
||||
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");
|
||||
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), new InstanceFactory(), null);
|
||||
unittest
|
||||
{
|
||||
Registration registration = new Registration(null, typeid(TestType),
|
||||
new InstanceFactory(), null);
|
||||
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");
|
||||
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 {
|
||||
unittest
|
||||
{
|
||||
Registration registration = new Registration(null, null, new InstanceFactory(), 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");
|
||||
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), new InstanceFactory(), null).singleInstance();
|
||||
Registration secondRegistration = new Registration(typeid(TestImplementation), typeid(TestImplementation), new InstanceFactory(), null).singleInstance().linkTo(firstRegistration);
|
||||
unittest
|
||||
{
|
||||
Registration firstRegistration = new Registration(typeid(TestInterface),
|
||||
typeid(TestImplementation), new InstanceFactory(), null).singleInstance();
|
||||
Registration secondRegistration = new Registration(typeid(TestImplementation),
|
||||
typeid(TestImplementation), new InstanceFactory(), null).singleInstance()
|
||||
.linkTo(firstRegistration);
|
||||
|
||||
auto firstInstance = firstRegistration.getInstance();
|
||||
auto secondInstance = secondRegistration.getInstance();
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,4 +5,6 @@
|
|||
* The full terms of the license can be found in the LICENSE file.
|
||||
*/
|
||||
|
||||
void main() {}
|
||||
void main()
|
||||
{
|
||||
}
|
||||
|
|
|
@ -10,26 +10,32 @@ import poodinis.test.testClasses;
|
|||
|
||||
import std.exception;
|
||||
|
||||
version(unittest) {
|
||||
version (unittest)
|
||||
{
|
||||
|
||||
struct LocalStruct {
|
||||
struct LocalStruct
|
||||
{
|
||||
bool wasInjected = false;
|
||||
}
|
||||
|
||||
class LocalStructInjector : ValueInjector!LocalStruct {
|
||||
public override LocalStruct get(string key) {
|
||||
class LocalStructInjector : ValueInjector!LocalStruct
|
||||
{
|
||||
public override LocalStruct get(string key)
|
||||
{
|
||||
auto data = LocalStruct(true);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
class LocalClassWithStruct {
|
||||
class LocalClassWithStruct
|
||||
{
|
||||
@Value("")
|
||||
public LocalStruct localStruct;
|
||||
}
|
||||
|
||||
// Test injection of values
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!MyConfig;
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
|
@ -43,7 +49,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injection of values throws exception when injector is not there
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!MyConfig;
|
||||
assertThrown!ResolveException(container.resolve!MyConfig);
|
||||
|
@ -52,7 +59,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injection of values with defaults
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ConfigWithDefaults;
|
||||
container.register!(ValueInjector!int, DefaultIntInjector);
|
||||
|
@ -62,7 +70,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test mandatory injection of values which are available
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ConfigWithMandatory;
|
||||
container.register!(ValueInjector!int, MandatoryAvailableIntInjector);
|
||||
|
@ -72,7 +81,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test mandatory injection of values which are not available
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ConfigWithMandatory;
|
||||
container.register!(ValueInjector!int, MandatoryUnavailableIntInjector);
|
||||
|
@ -82,7 +92,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting dependencies within value injectors
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
auto dependency = new Dependency();
|
||||
container.register!Dependency.existingInstance(dependency);
|
||||
|
@ -93,7 +104,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test injecting circular dependencies within value injectors
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(ValueInjector!int, CircularIntInjector);
|
||||
auto injector = cast(CircularIntInjector) container.resolve!(ValueInjector!int);
|
||||
|
@ -103,7 +115,8 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test value injection within value injectors
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(ValueInjector!int, ValueInjectedIntInjector);
|
||||
auto injector = cast(ValueInjectedIntInjector) container.resolve!(ValueInjector!int);
|
||||
|
@ -112,18 +125,21 @@ version(unittest) {
|
|||
}
|
||||
|
||||
// Test value injection within dependencies of value injectors
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!ConfigWithDefaults;
|
||||
|
||||
container.register!(ValueInjector!int, DependencyValueInjectedIntInjector);
|
||||
auto injector = cast(DependencyValueInjectedIntInjector) container.resolve!(ValueInjector!int);
|
||||
auto injector = cast(DependencyValueInjectedIntInjector) container.resolve!(
|
||||
ValueInjector!int);
|
||||
|
||||
assert(injector.config.noms == 8899);
|
||||
}
|
||||
|
||||
// Test resolving locally defined struct injector (github issue #20)
|
||||
unittest {
|
||||
unittest
|
||||
{
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!(ValueInjector!LocalStruct, LocalStructInjector);
|
||||
container.register!LocalClassWithStruct;
|
||||
|
|
Loading…
Reference in a new issue