Fix line delimiters

This commit is contained in:
Mike Bierlee 2016-01-06 20:28:25 +01:00
parent c35e494dfe
commit 7bd0795b3f
17 changed files with 2440 additions and 2440 deletions

View file

@ -1,80 +1,80 @@
Poodinis Changelog Poodinis Changelog
================== ==================
**Version 6.0.0** **Version 6.0.0**
* CHANGE registration scopes are replaced by a single factory implementation. If you were not doing anything with the internal scope mechanism, you * CHANGE registration scopes are replaced by a single factory implementation. If you were not doing anything with the internal scope mechanism, you
should not be affected by this change. should not be affected by this change.
* ADD application contexts. You can register dependencies within an application context which allow you to fine-tune the creation of dependency instances. * ADD application contexts. You can register dependencies within an application context which allow you to fine-tune the creation of dependency instances.
* CHANGE all public poodinis imports to private. This should not affect you if you use the package import "poodinis" instead of individual modules. * CHANGE all public poodinis imports to private. This should not affect you if you use the package import "poodinis" instead of individual modules.
* REMOVE deprecated ADD_CONCRETE_TYPE_REGISTRATION registration option. * REMOVE deprecated ADD_CONCRETE_TYPE_REGISTRATION registration option.
* REMOVE deprecated RegistrationOptions alias. * REMOVE deprecated RegistrationOptions alias.
**Version 5.0.0** **Version 5.0.0**
* DEPRECATE ADD_CONCRETE_TYPE_REGISTRATION registration option. It basically does nothing anymore. See next point. * DEPRECATE ADD_CONCRETE_TYPE_REGISTRATION registration option. It basically does nothing anymore. See next point.
* CHANGE adding registrations by super type always registers them by concrete type as well now. (Previously done with ADD_CONCRETE_TYPE_REGISTRATION). See DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION for the reverse behaviour. * CHANGE adding registrations by super type always registers them by concrete type as well now. (Previously done with ADD_CONCRETE_TYPE_REGISTRATION). See DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION for the reverse behaviour.
* CHANGE RegistrationOptions enum name to RegistrationOption * CHANGE RegistrationOptions enum name to RegistrationOption
* DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead. * DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead.
**Version 4.0.0** **Version 4.0.0**
* REMOVE deprecated module "dependency.d" * REMOVE deprecated module "dependency.d"
**Version 3.0.0** **Version 3.0.0**
This version is only compatible with DMD 2.068.0 or higher! This version is only compatible with DMD 2.068.0 or higher!
* ADD UDA which always resolved a new instance to an autowired member, regardless of registration scope. * ADD UDA which always resolved a new instance to an autowired member, regardless of registration scope.
**Version 2.2.0** **Version 2.2.0**
* ADD canonical package module "package.d". Use "import poodinis;" to import the project. * ADD canonical package module "package.d". Use "import poodinis;" to import the project.
* DEPRECATE module "dependency.d". Please use the canonical package module. See previous point. * DEPRECATE module "dependency.d". Please use the canonical package module. See previous point.
* ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it. * ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it.
**Version 2.1.0** **Version 2.1.0**
* ADD option for registering a class by concrete type when registering that class by supertype. * ADD option for registering a class by concrete type when registering that class by supertype.
**Version 2.0.0** **Version 2.0.0**
This version introduces changes which might be incompatible with your current codebase This version introduces changes which might be incompatible with your current codebase
* CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible. * CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible.
The implication is that all dependency container instances must be shared now. The implication is that all dependency container instances must be shared now.
You don't have to change anything if you were only using the singleton dependency container. You don't have to change anything if you were only using the singleton dependency container.
**Version 1.0.0** **Version 1.0.0**
This version introduces changes which are incompatible with previous versions This version introduces changes which are incompatible with previous versions
* REMOVE deprecated autowire constructor * REMOVE deprecated autowire constructor
* REMOVE deprecated container alias * REMOVE deprecated container alias
* ADD documentation for public API * ADD documentation for public API
* REMOVE @Autowired UDA. Use @Autowire instead. * REMOVE @Autowired UDA. Use @Autowire instead.
* ADD quickstart from readme to compilable example project. * ADD quickstart from readme to compilable example project.
* ADD example project for the use of qualifiers * ADD example project for the use of qualifiers
**Version 0.3.1** **Version 0.3.1**
* FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly. * FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly.
**Version 0.3.0** **Version 0.3.0**
* ADD alternative workaround to readme for autowire limitation * ADD alternative workaround to readme for autowire limitation
* CHANGE returning of resolved instances by returning them by qualifier type instead * CHANGE returning of resolved instances by returning them by qualifier type instead
* ADD debug specifier to reduce verbosity of debug output * ADD debug specifier to reduce verbosity of debug output
**Version 0.2.0** **Version 0.2.0**
* ADD ability to register type with multiple concrete types. They can be correctly resolved using qualifiers. * ADD ability to register type with multiple concrete types. They can be correctly resolved using qualifiers.
* DEPRECATE template for autowiring in constructor. This workaround is buggy. Use qualifiers instead. * DEPRECATE template for autowiring in constructor. This workaround is buggy. Use qualifiers instead.
**Version 0.1.4** **Version 0.1.4**
* Make Poodinis compatible with D 2.066.0 and DUB 0.9.22 * Make Poodinis compatible with D 2.066.0 and DUB 0.9.22
* FIX incorrect clearing of registrations * FIX incorrect clearing of registrations
This release should be backwards compatible with the previous versions of D and DUB, but please note that there are no more separate This release should be backwards compatible with the previous versions of D and DUB, but please note that there are no more separate
configurations for release and debug builds. You have to specify a build type in DUB. configurations for release and debug builds. You have to specify a build type in DUB.
**Version 0.1.3** **Version 0.1.3**
* ADD global autowire function for convenience * ADD global autowire function for convenience
* CHANGE workaround to be more simple * CHANGE workaround to be more simple
* FIX autowiring classes which contain non-symbolic declarations such as aliases. As a result, only variables are attempted to be autowired. * FIX autowiring classes which contain non-symbolic declarations such as aliases. As a result, only variables are attempted to be autowired.
**Version 0.1.2** **Version 0.1.2**
* ADD workaround for failing to autowire types registered by supertype or interface * ADD workaround for failing to autowire types registered by supertype or interface
**Version 0.1.1** **Version 0.1.1**
* FIX: Also auto-wire members from base classes * FIX: Also auto-wire members from base classes
**Version 0.1.0** **Version 0.1.0**
* Initial open-source release * Initial open-source release
* ADD support for registering and resolving * ADD support for registering and resolving
* ADD registration scopes * ADD registration scopes
* ADD autowiring * ADD autowiring

138
README.md
View file

@ -1,69 +1,69 @@
Poodinis Dependency Injection Framework Poodinis Dependency Injection Framework
======================================= =======================================
Version 6.0.0 Version 6.0.0
Copyright 2014-2016 Mike Bierlee Copyright 2014-2016 Mike Bierlee
Licensed under the terms of the MIT license - See [LICENSE.txt](LICENSE.txt) Licensed under the terms of the MIT license - See [LICENSE.txt](LICENSE.txt)
Master: [![Build Status](https://api.travis-ci.org/mbierlee/poodinis.png?branch=master)](https://travis-ci.org/mbierlee/poodinis) - Dev: [![Build Status](https://api.travis-ci.org/mbierlee/poodinis.png?branch=develop)](https://travis-ci.org/mbierlee/poodinis) Master: [![Build Status](https://api.travis-ci.org/mbierlee/poodinis.png?branch=master)](https://travis-ci.org/mbierlee/poodinis) - Dev: [![Build Status](https://api.travis-ci.org/mbierlee/poodinis.png?branch=develop)](https://travis-ci.org/mbierlee/poodinis)
Poodinis is a dependency injection framework for the D programming language. It is inspired by the [Spring Framework] and [Hypodermic] IoC container for C++. Poodinis supports registering and resolving classes either by concrete type or interface. Automatic injection of dependencies is supported through the use of UDAs (Referred to as autowiring). Poodinis is a dependency injection framework for the D programming language. It is inspired by the [Spring Framework] and [Hypodermic] IoC container for C++. Poodinis supports registering and resolving classes either by concrete type or interface. Automatic injection of dependencies is supported through the use of UDAs (Referred to as autowiring).
Developed for D 2.069.2 Developed for D 2.069.2
Uses the Phobos standard library Uses the Phobos standard library
Can be built with DUB 0.9.24 Can be built with DUB 0.9.24
History History
------- -------
For a full overview of changes, see [CHANGES.md](CHANGES.md) For a full overview of changes, see [CHANGES.md](CHANGES.md)
Getting started Getting started
--------------- ---------------
###DUB Dependency ###DUB Dependency
See the Poodinis [DUB project page] for instructions on how to include Poodinis into your project. See the Poodinis [DUB project page] for instructions on how to include Poodinis into your project.
###Quickstart ###Quickstart
The following example shows the typical usage of Poodinis: The following example shows the typical usage of Poodinis:
```d ```d
import poodinis; import poodinis;
interface Database{}; interface Database{};
class RelationalDatabase : Database {} class RelationalDatabase : Database {}
class DataWriter { class DataWriter {
@Autowire @Autowire
public Database database; // Automatically injected when class is resolved public Database database; // Automatically injected when class is resolved
} }
void main() { void main() {
auto dependencies = DependencyContainer.getInstance(); auto dependencies = DependencyContainer.getInstance();
dependencies.register!DataWriter; dependencies.register!DataWriter;
dependencies.register!(Database, RelationalDatabase); dependencies.register!(Database, RelationalDatabase);
auto writer = dependencies.resolve!DataWriter; auto writer = dependencies.resolve!DataWriter;
} }
``` ```
For more examples, see the [examples](example) directory. For more examples, see the [examples](example) directory.
###Tutorial ###Tutorial
For an extended tutorial walking you through all functionality offered by Poodinis, see [TUTORIAL.md](TUTORIAL.md) For an extended tutorial walking you through all functionality offered by Poodinis, see [TUTORIAL.md](TUTORIAL.md)
Documentation Documentation
------------- -------------
You can generate Public API documentation from the source code using DUB: You can generate Public API documentation from the source code using DUB:
``` ```
dub build --build=ddox dub build --build=ddox
``` ```
The documentation can then be found in docs/ The documentation can then be found in docs/
Future Work Future Work
----------- -----------
* Component scan (auto-registration) * Component scan (auto-registration)
* Phobos collections autowiring * Phobos collections autowiring
* Constructor injection * Constructor injection
* Named qualifiers * Named qualifiers
[Spring Framework]: http://projects.spring.io/spring-framework/ [Spring Framework]: http://projects.spring.io/spring-framework/
[Hypodermic]: https://github.com/ybainier/hypodermic/ [Hypodermic]: https://github.com/ybainier/hypodermic/
[DUB]: http://code.dlang.org/ [DUB]: http://code.dlang.org/
[DUB project page]: http://code.dlang.org/packages/poodinis [DUB project page]: http://code.dlang.org/packages/poodinis
[Github issue tracker]: https://github.com/mbierlee/poodinis/issues [Github issue tracker]: https://github.com/mbierlee/poodinis/issues

View file

@ -1,199 +1,199 @@
Poodinis Tutorial Poodinis Tutorial
================= =================
This tutorial will give you an overview of all functionality offered by Poodinis and how to use them. This tutorial will give you an overview of all functionality offered by Poodinis and how to use them.
The container The container
------------- -------------
To register a class, a new dependency container must be instantiated: To register a class, a new dependency container must be instantiated:
```d ```d
// Register a private container // Register a private container
auto dependencies = new DependencyContainer(); auto dependencies = new DependencyContainer();
// Or use the singleton container // Or use the singleton container
dependencies = DependencyContainer.getInstance(); dependencies = DependencyContainer.getInstance();
``` ```
###Registering dependencies ###Registering dependencies
To make dependencies available, they have to be registered: To make dependencies available, they have to be registered:
```d ```d
// Register concrete class // Register concrete class
dependencies.register!ExampleClass; dependencies.register!ExampleClass;
// Register by super type // Register by super type
dependencies.register!(ExampleInterface, ExampleClass); dependencies.register!(ExampleInterface, ExampleClass);
``` ```
In the above example, dependencies on the concrete class and interface will resolve an instance of class ExampleClass. A dependency registered by super type will automatically be registered by concrete type. In the above example, dependencies on the concrete class and interface will resolve an instance of class ExampleClass. A dependency registered by super type will automatically be registered by concrete type.
Resolving dependencies Resolving dependencies
---------------------- ----------------------
To manually resolve a dependency, all you have to do is resolve the dependency's type using the container in which it is registered: To manually resolve a dependency, all you have to do is resolve the dependency's type using the container in which it is registered:
```d ```d
auto exampleClassInstance = dependencies.resolve!ExampleClass; auto exampleClassInstance = dependencies.resolve!ExampleClass;
``` ```
If the class is registered by interface and not by concrete type, you can still resolve the class by concrete type: If the class is registered by interface and not by concrete type, you can still resolve the class by concrete type:
```d ```d
auto exampleClassInstance = dependencies.resolve!ExampleInterface; auto exampleClassInstance = dependencies.resolve!ExampleInterface;
auto exampleClassInstance2 = dependencies.resolve!ExampleClass; auto exampleClassInstance2 = dependencies.resolve!ExampleClass;
assert(exampleClassInstance is exampleClassInstance2); assert(exampleClassInstance is exampleClassInstance2);
``` ```
If you want to prevent registrations from being both registered by interface and concrete type, use the DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION option when registering: If you want to prevent registrations from being both registered by interface and concrete type, use the DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION option when registering:
```d ```d
dependencies.register!(ExampleInterface, ExampleClass)(RegistrationOptions.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION); dependencies.register!(ExampleInterface, ExampleClass)(RegistrationOptions.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION);
auto exampleClassInstance = dependencies.resolve!ExampleInterface; auto exampleClassInstance = dependencies.resolve!ExampleInterface;
auto exampleClassInstance2 = dependencies.resolve!ExampleClass; // A ResolveException is thrown auto exampleClassInstance2 = dependencies.resolve!ExampleClass; // A ResolveException is thrown
``` ```
Dependency creation behaviour Dependency creation behaviour
----------------- -----------------
You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available: You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available:
* Resolve a dependency using a single instance (default): * Resolve a dependency using a single instance (default):
```d ```d
dependencies.register!(ExampleClass).singleInstance(); dependencies.register!(ExampleClass).singleInstance();
``` ```
* Resolve a dependency with a new instance each time it is resolved: * Resolve a dependency with a new instance each time it is resolved:
```d ```d
dependencies.register!(ExampleClass).newInstance(); dependencies.register!(ExampleClass).newInstance();
``` ```
* Resolve a dependency using a pre-existing instance * Resolve a dependency using a pre-existing instance
```d ```d
auto preExistingInstance = new ExampleClass(); auto preExistingInstance = new ExampleClass();
dependencies.register!(ExampleClass).existingInstance(preExistingInstance); dependencies.register!(ExampleClass).existingInstance(preExistingInstance);
``` ```
Autowiring Autowiring
---------- ----------
The real value of any dependency injection framework comes from its ability to autowire dependencies. Poodinis supports autowiring by simply applying the **@Autowire** UDA to a member of a class: The real value of any dependency injection framework comes from its ability to autowire dependencies. Poodinis supports autowiring by simply applying the **@Autowire** UDA to a member of a class:
```d ```d
class ExampleClassA {} class ExampleClassA {}
class ExampleClassB { class ExampleClassB {
@Autowire @Autowire
public ExampleClassA dependency; public ExampleClassA dependency;
} }
dependencies.register!ExampleClassA; dependencies.register!ExampleClassA;
auto exampleInstance = new ExampleClassB(); auto exampleInstance = new ExampleClassB();
dependencies.autowire(exampleInstance); dependencies.autowire(exampleInstance);
assert(exampleInstance.dependency !is null); assert(exampleInstance.dependency !is null);
``` ```
At the moment, it is only possible to autowire public members or properties. At the moment, it is only possible to autowire public members or properties.
Dependencies are automatically autowired when a class is resolved. So when you register ExampleClassB, its member, *dependency*, is automatically autowired: Dependencies are automatically autowired when a class is resolved. So when you register ExampleClassB, its member, *dependency*, is automatically autowired:
```d ```d
dependencies.register!ExampleClassA; dependencies.register!ExampleClassA;
dependencies.register!ExampleClassB; dependencies.register!ExampleClassB;
auto instance = dependencies.resolve!ExampleClassB; auto instance = dependencies.resolve!ExampleClassB;
assert(instance.dependency !is null); assert(instance.dependency !is null);
``` ```
If an interface is to be autowired, you must register a concrete class by interface. Any class registered by concrete type can only be injected when a dependency on a concrete type is autowired. If an interface is to be autowired, you must register a concrete class by interface. Any class registered by concrete type can only be injected when a dependency on a concrete type is autowired.
Circular dependencies Circular dependencies
--------------------- ---------------------
Poodinis can autowire circular dependencies when they are registered with singleInstance or existingInstance registration scopes. Circular dependencies in registrations with newInstance scopes will not be autowired, as this would cause an endless loop. Poodinis can autowire circular dependencies when they are registered with singleInstance or existingInstance registration scopes. Circular dependencies in registrations with newInstance scopes will not be autowired, as this would cause an endless loop.
Registering and resolving using qualifiers Registering and resolving using qualifiers
------------------------------------------ ------------------------------------------
You can register multiple concrete types to a super type. When doing so, you will need to specify a qualifier when resolving that type: You can register multiple concrete types to a super type. When doing so, you will need to specify a qualifier when resolving that type:
```d ```d
// Color is an interface, Blue and Red are classes implementing that interface // Color is an interface, Blue and Red are classes implementing that interface
dependencies.register!(Color, Blue); dependencies.register!(Color, Blue);
dependencies.register!(Color, Red); dependencies.register!(Color, Red);
auto blueInstance = dependencies.resolve!(Color, Blue); auto blueInstance = dependencies.resolve!(Color, Blue);
``` ```
If you want to autowire a type registered to multiple concrete types, specify a qualified type as template argument: If you want to autowire a type registered to multiple concrete types, specify a qualified type as template argument:
```d ```d
class BluePaint { class BluePaint {
@Autowire!Blue @Autowire!Blue
public Color color; public Color color;
} }
``` ```
If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a ResolveException is thrown stating that there are multiple candidates for the type to be resolved. If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a ResolveException is thrown stating that there are multiple candidates for the type to be resolved.
Autowiring all registered instances to an array Autowiring all registered instances to an array
----------------------------------------------- -----------------------------------------------
If you have registered multiple concrete types to a super type, you can autowire them all to an array, in which case you can easily operate on them all: If you have registered multiple concrete types to a super type, you can autowire them all to an array, in which case you can easily operate on them all:
```d ```d
// Color is an interface, Blue and Red are classes implementing that interface // Color is an interface, Blue and Red are classes implementing that interface
class ColorMixer { class ColorMixer {
@Autowire @Autowire
public Color[] colors; public Color[] colors;
} }
dependencies.register!(Color, Blue); dependencies.register!(Color, Blue);
dependencies.register!(Color, Red); dependencies.register!(Color, Red);
auto mixer = dependencies.resolve!ColorMixer; auto mixer = dependencies.resolve!ColorMixer;
``` ```
Member mixer.colors will now contain instances of Blue and Red. The order of the instances is not guarenteed to be that of the order in which they were registered. Member mixer.colors will now contain instances of Blue and Red. The order of the instances is not guarenteed to be that of the order in which they were registered.
Application Contexts Application Contexts
-------------------- --------------------
You can fine-tune dependency configuration using application contexts. Application contexts allow you to centralize all dependency configuration as well as define You can fine-tune dependency configuration using application contexts. Application contexts allow you to centralize all dependency configuration as well as define
how instances of certain classes should be constructed using factory methods. how instances of certain classes should be constructed using factory methods.
###Defining and using application contexts ###Defining and using application contexts
An application context is defined as follows: An application context is defined as follows:
```d ```d
class Context : ApplicationContext { class Context : ApplicationContext {
public override void registerDependencies(shared(DependencyContainer) container) { public override void registerDependencies(shared(DependencyContainer) container) {
container.register!SomeClass; container.register!SomeClass;
container.register!(SomeInterface, SomeOtherClass).newInstance(); container.register!(SomeInterface, SomeOtherClass).newInstance();
} }
@Component @Component
public SomeLibraryClass libraryClass() { public SomeLibraryClass libraryClass() {
return new SomeLibraryClass("This class needs constructor parameters so I have to register it through an application context"); return new SomeLibraryClass("This class needs constructor parameters so I have to register it through an application context");
} }
} }
``` ```
In the override *registerDependencies()* you can register all dependencies which do not need complex set-up, just like you would do when directly using the dependency container. In the override *registerDependencies()* you can register all dependencies which do not need complex set-up, just like you would do when directly using the dependency container.
This override is optional. You can still register simple dependencies outside of the context (or in another context). This override is optional. You can still register simple dependencies outside of the context (or in another context).
Complex dependencies are registered through member methods of the context. These member methods serve as factory methods which will be called when a dependency is resolved. Complex dependencies are registered through member methods of the context. These member methods serve as factory methods which will be called when a dependency is resolved.
They are annotated with the *@Component* UDA to let the container know that these methods should be registered as dependencies. The type of the registration is the same as the return type of the method. They are annotated with the *@Component* UDA to let the container know that these methods should be registered as dependencies. The type of the registration is the same as the return type of the method.
Factory methods are useful when you have to deal with dependencies which require constructor arguments or elaborate set-up after instantiation. Factory methods are useful when you have to deal with dependencies which require constructor arguments or elaborate set-up after instantiation.
Application contexts have to be registered with a dependency container. They are registered as follows: Application contexts have to be registered with a dependency container. They are registered as follows:
```d ```d
container.registerContext!Context; container.registerContext!Context;
``` ```
All registered dependencies can now be resolved by the same dependency container. Registering a context will also register it as a dependency, meaning you can autowire the application context in other classes. All registered dependencies can now be resolved by the same dependency container. Registering a context will also register it as a dependency, meaning you can autowire the application context in other classes.
You can register as many types of application contexts as you like. You can register as many types of application contexts as you like.
###Autowiring application contexts ###Autowiring application contexts
Application contexts can make use of autowired dependencies like any other dependency. When registering an application context, all its components are registered first after which the application context is autowired. Application contexts can make use of autowired dependencies like any other dependency. When registering an application context, all its components are registered first after which the application context is autowired.
This means that after the registration of an application context some dependencies will already be resolved and instantiated. The following example illustrates how autowired members can be used in a context: This means that after the registration of an application context some dependencies will already be resolved and instantiated. The following example illustrates how autowired members can be used in a context:
```d ```d
class Context : ApplicationContext { class Context : ApplicationContext {
@Autowire @Autowire
public SomeClass someClass; public SomeClass someClass;
@Autowire @Autowire
public SomeOtherClass someOtherClass; public SomeOtherClass someOtherClass;
public override void registerDependencies(shared(DependencyContainer) container) { public override void registerDependencies(shared(DependencyContainer) container) {
container.register!SomeClass; container.register!SomeClass;
} }
@Component @Component
public SomeLibraryClass libraryClass() { public SomeLibraryClass libraryClass() {
return new SomeLibraryClass(someClass, someOtherClass); return new SomeLibraryClass(someClass, someOtherClass);
} }
} }
``` ```
As you can see, autowired dependencies can be used within factory methods. When *SomeLibraryClass* is resolved, it will be created with a resolved instance of *SomeClass* and *SomeOtherClass*. As shown, autowired dependencies can be registered within the same As you can see, autowired dependencies can be used within factory methods. When *SomeLibraryClass* is resolved, it will be created with a resolved instance of *SomeClass* and *SomeOtherClass*. As shown, autowired dependencies can be registered within the same
application context, but don't neccesarily have to be. You can even autowire dependencies which are created within a factory method within the same application context. application context, but don't neccesarily have to be. You can even autowire dependencies which are created within a factory method within the same application context.
Application contexts are directly autowired after they have been registered. This means that all autowired dependencies which are not registered in the application context itself need to be registered before registering the application context. Application contexts are directly autowired after they have been registered. This means that all autowired dependencies which are not registered in the application context itself need to be registered before registering the application context.
###Controlling component registration ###Controlling component registration
You can further influence how components are registered and created with additional UDAs: You can further influence how components are registered and created with additional UDAs:
```d ```d
class Context : ApplicationContext { class Context : ApplicationContext {
@Component @Component
@Prototype // Will create a new instance every time the dependency is resolved. @Prototype // Will create a new instance every time the dependency is resolved.
@RegisterByType!SomeInterface // Registers the dependency by the specified super type instead of the return type @RegisterByType!SomeInterface // Registers the dependency by the specified super type instead of the return type
public SomeClass someClass() { public SomeClass someClass() {
return new SomeClass(); return new SomeClass();
} }
} }
``` ```

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,206 +1,206 @@
/** /**
* Contains functionality for autowiring dependencies using a dependency container. * Contains functionality for autowiring dependencies using a dependency container.
* *
* This module is used in a dependency container for autowiring dependencies when resolving them. * This module is used in a dependency container for autowiring dependencies when resolving them.
* You typically only need this module if you want inject dependencies into a class instance not * You typically only need this module if you want inject dependencies into a class instance not
* managed by a dependency container. * managed by a dependency container.
* *
* Part of the Poodinis Dependency Injection framework. * Part of the Poodinis Dependency Injection framework.
* *
* Authors: * Authors:
* Mike Bierlee, m.bierlee@lostmoment.com * Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2016 Mike Bierlee * Copyright: 2014-2016 Mike Bierlee
* License: * License:
* This software is licensed under the terms of the MIT license. * This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file. * The full terms of the license can be found in the LICENSE file.
*/ */
module poodinis.autowire; module poodinis.autowire;
import poodinis.container; import poodinis.container;
import poodinis.registration; import poodinis.registration;
import std.exception; import std.exception;
import std.stdio; import std.stdio;
import std.string; import std.string;
import std.traits; import std.traits;
import std.range; import std.range;
struct UseMemberType {}; struct UseMemberType {};
/** /**
* UDA for annotating class members as candidates for autowiring. * UDA for annotating class members as candidates for autowiring.
* *
* Optionally a template parameter can be supplied to specify the type of a qualified class. The qualified type * Optionally a template parameter can be supplied to specify the type of a qualified class. The qualified type
* of a concrete class is used to autowire members declared by supertype. If no qualifier is supplied, the type * of a concrete class is used to autowire members declared by supertype. If no qualifier is supplied, the type
* of the member is used as qualifier. * of the member is used as qualifier.
* *
* Examples: * Examples:
* Annotate member of class to be autowired: * Annotate member of class to be autowired:
* --- * ---
* class Car { * class Car {
* @Autowire * @Autowire
* public Engine engine; * public Engine engine;
* } * }
* --- * ---
* *
* Annotate member of class with qualifier: * Annotate member of class with qualifier:
* --- * ---
* class FuelEngine : Engine { ... } * class FuelEngine : Engine { ... }
* class ElectricEngine : Engine { ... } * class ElectricEngine : Engine { ... }
* *
* class HybridCar { * class HybridCar {
* @Autowire!FuelEngine * @Autowire!FuelEngine
* public Engine fuelEngine; * public Engine fuelEngine;
* *
* @Autowire!ElectricEngine * @Autowire!ElectricEngine
* public Engine electricEngine; * public Engine electricEngine;
* } * }
* --- * ---
* The members of an instance of "HybridCar" will now be autowired properly, because the autowire mechanism will * The members of an instance of "HybridCar" will now be autowired properly, because the autowire mechanism will
* autowire member "fuelEngine" as if it's of type "FuelEngine". This means that the members of instance "fuelEngine" * autowire member "fuelEngine" as if it's of type "FuelEngine". This means that the members of instance "fuelEngine"
* will also be autowired because the autowire mechanism knows that member "fuelEngine" is an instance of "FuelEngine" * will also be autowired because the autowire mechanism knows that member "fuelEngine" is an instance of "FuelEngine"
*/ */
struct Autowire(QualifierType = UseMemberType) { struct Autowire(QualifierType = UseMemberType) {
QualifierType qualifier; QualifierType qualifier;
}; };
/** /**
* UDA for annotating class members to be autowired with a new instance regardless of their registration scope. * UDA for annotating class members to be autowired with a new instance regardless of their registration scope.
* *
* Examples: * Examples:
*--- *---
* class Car { * class Car {
* @Autowire * @Autowire
* @AssignNewInstance * @AssignNewInstance
* public Antenna antenna; * public Antenna antenna;
* } * }
*--- *---
* antenna will always be assigned a new instance of class Antenna. * antenna will always be assigned a new instance of class Antenna.
*/ */
struct AssignNewInstance {} struct AssignNewInstance {}
private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) { private void printDebugAutowiredInstance(TypeInfo instanceType, void* instanceAddress) {
writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress)); writeln(format("DEBUG: Autowiring members of [%s@%s]", instanceType, instanceAddress));
} }
/** /**
* Autowires members of a given instance using dependencies registered in the given container. * Autowires members of a given instance using dependencies registered in the given container.
* *
* All public members of the given instance, which are annotated using the "Autowire" UDA, are autowired. * All public members of the given instance, which are annotated using the "Autowire" UDA, are autowired.
* All members are resolved using the given container. Qualifiers are used to determine the type of class to * All members are resolved using the given container. Qualifiers are used to determine the type of class to
* resolve for any member of instance. * resolve for any member of instance.
* *
* Note that private members will not be autowired because the autowiring mechanism is not able to by-pass * Note that private members will not be autowired because the autowiring mechanism is not able to by-pass
* member visibility protection. * member visibility protection.
* *
* See_Also: Autowire * See_Also: Autowire
*/ */
public void autowire(Type)(shared(DependencyContainer) container, Type instance) { public void autowire(Type)(shared(DependencyContainer) container, Type instance) {
debug(poodinisVerbose) { debug(poodinisVerbose) {
printDebugAutowiredInstance(typeid(Type), &instance); printDebugAutowiredInstance(typeid(Type), &instance);
} }
foreach (member ; __traits(allMembers, Type)) { foreach (member ; __traits(allMembers, Type)) {
autowireMember!member(container, instance); autowireMember!member(container, instance);
} }
} }
private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType, void* candidateInstanceAddress, TypeInfo instanceType, void* instanceAddress, string member) { private void printDebugAutowiringCandidate(TypeInfo candidateInstanceType, void* candidateInstanceAddress, TypeInfo instanceType, void* instanceAddress, string member) {
writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType, candidateInstanceAddress, instanceType, instanceAddress, member)); writeln(format("DEBUG: Autowired instance [%s@%s] to [%s@%s].%s", candidateInstanceType, candidateInstanceAddress, instanceType, instanceAddress, member));
} }
private void printDebugAutowiringArray(TypeInfo superTypeInfo, TypeInfo instanceType, void* instanceAddress, string member) { private void printDebugAutowiringArray(TypeInfo superTypeInfo, TypeInfo instanceType, void* instanceAddress, string member) {
writeln(format("DEBUG: Autowired all registered instances of super type %s to [%s@%s].%s", superTypeInfo, instanceType, instanceAddress, member)); writeln(format("DEBUG: Autowired all registered instances of super type %s to [%s@%s].%s", superTypeInfo, instanceType, instanceAddress, member));
} }
private void autowireMember(string member, Type)(shared(DependencyContainer) container, Type instance) { private void autowireMember(string member, Type)(shared(DependencyContainer) container, Type instance) {
static if(__traits(compiles, __traits(getMember, instance, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, instance, member)))) { static if(__traits(compiles, __traits(getMember, instance, member)) && __traits(compiles, __traits(getAttributes, __traits(getMember, instance, member)))) {
foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, instance, member))) { foreach(autowireAttribute; __traits(getAttributes, __traits(getMember, instance, member))) {
static if (__traits(isSame, autowireAttribute, Autowire) || is(autowireAttribute == Autowire!T, T)) { static if (__traits(isSame, autowireAttribute, Autowire) || is(autowireAttribute == Autowire!T, T)) {
if (__traits(getMember, instance, member) is null) { if (__traits(getMember, instance, member) is null) {
alias MemberType = typeof(__traits(getMember, instance, member)); alias MemberType = typeof(__traits(getMember, instance, member));
enum assignNewInstance = hasUDA!(__traits(getMember, instance, member), AssignNewInstance); enum assignNewInstance = hasUDA!(__traits(getMember, instance, member), AssignNewInstance);
static if (isDynamicArray!MemberType) { static if (isDynamicArray!MemberType) {
alias MemberElementType = ElementType!MemberType; alias MemberElementType = ElementType!MemberType;
auto instances = container.resolveAll!MemberElementType; auto instances = container.resolveAll!MemberElementType;
__traits(getMember, instance, member) = instances; __traits(getMember, instance, member) = instances;
debug(poodinisVerbose) { debug(poodinisVerbose) {
printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member); printDebugAutowiringArray(typeid(MemberElementType), typeid(Type), &instance, member);
} }
} else { } else {
debug(poodinisVerbose) { debug(poodinisVerbose) {
TypeInfo qualifiedInstanceType = typeid(MemberType); TypeInfo qualifiedInstanceType = typeid(MemberType);
} }
MemberType qualifiedInstance; MemberType qualifiedInstance;
static if (is(autowireAttribute == Autowire!T, T) && !is(autowireAttribute.qualifier == UseMemberType)) { static if (is(autowireAttribute == Autowire!T, T) && !is(autowireAttribute.qualifier == UseMemberType)) {
alias QualifierType = typeof(autowireAttribute.qualifier); alias QualifierType = typeof(autowireAttribute.qualifier);
qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType, assignNewInstance)(container); qualifiedInstance = createOrResolveInstance!(MemberType, QualifierType, assignNewInstance)(container);
debug(poodinisVerbose) { debug(poodinisVerbose) {
qualifiedInstanceType = typeid(QualifierType); qualifiedInstanceType = typeid(QualifierType);
} }
} else { } else {
qualifiedInstance = createOrResolveInstance!(MemberType, MemberType, assignNewInstance)(container); qualifiedInstance = createOrResolveInstance!(MemberType, MemberType, assignNewInstance)(container);
} }
__traits(getMember, instance, member) = qualifiedInstance; __traits(getMember, instance, member) = qualifiedInstance;
debug(poodinisVerbose) { debug(poodinisVerbose) {
printDebugAutowiringCandidate(qualifiedInstanceType, &qualifiedInstance, typeid(Type), &instance, member); printDebugAutowiringCandidate(qualifiedInstanceType, &qualifiedInstance, typeid(Type), &instance, member);
} }
} }
} }
break; break;
} }
} }
} }
} }
private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) { private QualifierType createOrResolveInstance(MemberType, QualifierType, bool createNew)(shared(DependencyContainer) container) {
static if (createNew) { static if (createNew) {
auto instanceFactory = new InstanceFactory(typeid(MemberType), CreatesSingleton.no, null); auto instanceFactory = new InstanceFactory(typeid(MemberType), CreatesSingleton.no, null);
return cast(MemberType) instanceFactory.getInstance(); return cast(MemberType) instanceFactory.getInstance();
} else { } else {
return container.resolve!(MemberType, QualifierType); return container.resolve!(MemberType, QualifierType);
} }
} }
/** /**
* Autowire the given instance using the globally available dependency container. * Autowire the given instance using the globally available dependency container.
* *
* See_Also: DependencyContainer * See_Also: DependencyContainer
*/ */
public void globalAutowire(Type)(Type instance) { public void globalAutowire(Type)(Type instance) {
DependencyContainer.getInstance().autowire(instance); DependencyContainer.getInstance().autowire(instance);
} }
class AutowiredRegistration(RegistrationType : Object) : Registration { class AutowiredRegistration(RegistrationType : Object) : Registration {
private shared(DependencyContainer) container; private shared(DependencyContainer) container;
public this(TypeInfo registeredType, shared(DependencyContainer) container) { public this(TypeInfo registeredType, shared(DependencyContainer) container) {
enforce(!(container is null), "Argument 'container' is null. Autowired registrations need to autowire using a container."); enforce(!(container is null), "Argument 'container' is null. Autowired registrations need to autowire using a container.");
this.container = container; this.container = container;
super(registeredType, typeid(RegistrationType)); super(registeredType, typeid(RegistrationType));
} }
public override Object getInstance(InstantiationContext context = new AutowireInstantiationContext()) { public override Object getInstance(InstantiationContext context = new AutowireInstantiationContext()) {
RegistrationType instance = cast(RegistrationType) super.getInstance(context); RegistrationType instance = cast(RegistrationType) super.getInstance(context);
AutowireInstantiationContext autowireContext = cast(AutowireInstantiationContext) context; AutowireInstantiationContext autowireContext = cast(AutowireInstantiationContext) context;
enforce(!(autowireContext is null), "Given instantiation context type could not be cast to an AutowireInstantiationContext. If you relied on using the default assigned context: make sure you're calling getInstance() on an instance of type AutowiredRegistration!"); enforce(!(autowireContext is null), "Given instantiation context type could not be cast to an AutowireInstantiationContext. If you relied on using the default assigned context: make sure you're calling getInstance() on an instance of type AutowiredRegistration!");
if (autowireContext.autowireInstance) { if (autowireContext.autowireInstance) {
container.autowire(instance); container.autowire(instance);
} }
return instance; return instance;
} }
} }
class AutowireInstantiationContext : InstantiationContext { class AutowireInstantiationContext : InstantiationContext {
public bool autowireInstance = true; public bool autowireInstance = true;
} }

View file

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

View file

@ -1,68 +1,68 @@
/** /**
* Contains the implementation of application context setup. * Contains the implementation of application context setup.
* *
* Part of the Poodinis Dependency Injection framework. * Part of the Poodinis Dependency Injection framework.
* *
* Authors: * Authors:
* Mike Bierlee, m.bierlee@lostmoment.com * Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2016 Mike Bierlee * Copyright: 2014-2016 Mike Bierlee
* License: * License:
* This software is licensed under the terms of the MIT license. * This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file. * The full terms of the license can be found in the LICENSE file.
*/ */
module poodinis.context; module poodinis.context;
import poodinis.container; import poodinis.container;
import poodinis.registration; import poodinis.registration;
import std.traits; import std.traits;
class ApplicationContext { class ApplicationContext {
public void registerDependencies(shared(DependencyContainer) container) {} public void registerDependencies(shared(DependencyContainer) container) {}
} }
/** /**
* A component annotation is used for specifying which factory methods produce components in * A component annotation is used for specifying which factory methods produce components in
* an application context. * an application context.
*/ */
struct Component {} struct Component {}
/** /**
* This annotation allows you to specify by which super type the component should be registered. This * This annotation allows you to specify by which super type the component should be registered. This
* enables you to use type-qualified alternatives for dependencies. * enables you to use type-qualified alternatives for dependencies.
*/ */
struct RegisterByType(Type) { struct RegisterByType(Type) {
Type type; Type type;
} }
/** /**
* Components with the prototype registration will be scoped as dependencies which will created * Components with the prototype registration will be scoped as dependencies which will created
* new instances every time they are resolved. The factory method will be called repeatedly. * new instances every time they are resolved. The factory method will be called repeatedly.
*/ */
struct Prototype {} struct Prototype {}
public void registerContextComponents(ApplicationContextType : ApplicationContext)(ApplicationContextType context, shared(DependencyContainer) container) { public void registerContextComponents(ApplicationContextType : ApplicationContext)(ApplicationContextType context, shared(DependencyContainer) container) {
foreach (member ; __traits(allMembers, ApplicationContextType)) { foreach (member ; __traits(allMembers, ApplicationContextType)) {
static if (hasUDA!(__traits(getMember, context, member), Component)) { static if (hasUDA!(__traits(getMember, context, member), Component)) {
auto factoryMethod = &__traits(getMember, context, member); auto factoryMethod = &__traits(getMember, context, member);
Registration registration = null; Registration registration = null;
auto createsSingleton = CreatesSingleton.yes; auto createsSingleton = CreatesSingleton.yes;
foreach(attribute; __traits(getAttributes, __traits(getMember, context, member))) { foreach(attribute; __traits(getAttributes, __traits(getMember, context, member))) {
static if (is(attribute == RegisterByType!T, T)) { static if (is(attribute == RegisterByType!T, T)) {
registration = container.register!(typeof(attribute.type), ReturnType!factoryMethod); registration = container.register!(typeof(attribute.type), ReturnType!factoryMethod);
} else static if (__traits(isSame, attribute, Prototype)) { } else static if (__traits(isSame, attribute, Prototype)) {
createsSingleton = CreatesSingleton.no; createsSingleton = CreatesSingleton.no;
} }
} }
if (registration is null) { if (registration is null) {
registration = container.register!(ReturnType!factoryMethod); registration = container.register!(ReturnType!factoryMethod);
} }
registration.instanceFactory = new InstanceFactory(registration.instanceType, createsSingleton, null, factoryMethod); registration.instanceFactory = new InstanceFactory(registration.instanceType, createsSingleton, null, factoryMethod);
} }
} }
} }

View file

@ -1,17 +1,17 @@
/** /**
* Package module for the Poodinis Dependency Injection framework. * Package module for the Poodinis Dependency Injection framework.
* *
* Authors: * Authors:
* Mike Bierlee, m.bierlee@lostmoment.com * Mike Bierlee, m.bierlee@lostmoment.com
* Copyright: 2014-2016 Mike Bierlee * Copyright: 2014-2016 Mike Bierlee
* License: * License:
* This software is licensed under the terms of the MIT license. * This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file. * The full terms of the license can be found in the LICENSE file.
*/ */
module poodinis; module poodinis;
public import poodinis.autowire; public import poodinis.autowire;
public import poodinis.container; public import poodinis.container;
public import poodinis.registration; public import poodinis.registration;
public import poodinis.context; public import poodinis.context;

View file

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

View file

@ -1,191 +1,191 @@
/** /**
* Poodinis Dependency Injection Framework * Poodinis Dependency Injection Framework
* Copyright 2014-2016 Mike Bierlee * Copyright 2014-2016 Mike Bierlee
* This software is licensed under the terms of the MIT license. * This software is licensed under the terms of the MIT license.
* The full terms of the license can be found in the LICENSE file. * The full terms of the license can be found in the LICENSE file.
*/ */
import poodinis; import poodinis;
import std.exception; import std.exception;
version(unittest) { version(unittest) {
class ComponentA {} class ComponentA {}
class ComponentB { class ComponentB {
public @Autowire ComponentA componentA; public @Autowire ComponentA componentA;
} }
interface InterfaceA {} interface InterfaceA {}
class ComponentC : InterfaceA {} class ComponentC : InterfaceA {}
class ComponentD { class ComponentD {
public @Autowire InterfaceA componentC = null; public @Autowire InterfaceA componentC = null;
} }
class DummyAttribute{}; class DummyAttribute{};
class ComponentE { class ComponentE {
@DummyAttribute @DummyAttribute
public ComponentC componentC; public ComponentC componentC;
} }
class ComponentDeclarationCocktail { class ComponentDeclarationCocktail {
alias noomer = int; alias noomer = int;
@Autowire @Autowire
public ComponentA componentA; public ComponentA componentA;
public void doesNothing() { public void doesNothing() {
} }
~this(){ ~this(){
} }
} }
class ComponentX : InterfaceA {} class ComponentX : InterfaceA {}
class MonkeyShine { class MonkeyShine {
@Autowire!ComponentX @Autowire!ComponentX
public InterfaceA component; public InterfaceA component;
} }
class BootstrapBootstrap { class BootstrapBootstrap {
@Autowire!ComponentX @Autowire!ComponentX
public InterfaceA componentX; public InterfaceA componentX;
@Autowire!ComponentC @Autowire!ComponentC
public InterfaceA componentC; public InterfaceA componentC;
} }
class LordOfTheComponents { class LordOfTheComponents {
@Autowire @Autowire
public InterfaceA[] components; public InterfaceA[] components;
} }
class ComponentCharlie { class ComponentCharlie {
@Autowire @Autowire
@AssignNewInstance @AssignNewInstance
public ComponentA componentA; public ComponentA componentA;
} }
// Test autowiring concrete type to existing instance // Test autowiring concrete type to existing instance
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA; container.register!ComponentA;
auto componentB = new ComponentB(); auto componentB = new ComponentB();
container.autowire!(ComponentB)(componentB); container.autowire!(ComponentB)(componentB);
assert(componentB !is null, "Autowirable dependency failed to autowire"); assert(componentB !is null, "Autowirable dependency failed to autowire");
} }
// Test autowiring interface type to existing instance // Test autowiring interface type to existing instance
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC); container.register!(InterfaceA, ComponentC);
auto componentD = new ComponentD(); auto componentD = new ComponentD();
container.autowire!(ComponentD)(componentD); container.autowire!(ComponentD)(componentD);
assert(componentD.componentC !is null, "Autowirable dependency failed to autowire"); assert(componentD.componentC !is null, "Autowirable dependency failed to autowire");
} }
// Test autowiring will only happen once // Test autowiring will only happen once
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC).newInstance(); container.register!(InterfaceA, ComponentC).newInstance();
auto componentD = new ComponentD(); auto componentD = new ComponentD();
container.autowire!(ComponentD)(componentD); container.autowire!(ComponentD)(componentD);
auto expectedComponent = componentD.componentC; auto expectedComponent = componentD.componentC;
container.autowire!(ComponentD)(componentD); container.autowire!(ComponentD)(componentD);
auto actualComponent = componentD.componentC; auto actualComponent = componentD.componentC;
assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance"); assert(expectedComponent is actualComponent, "Autowiring the second time wired a different instance");
} }
// Test autowiring unregistered type // Test autowiring unregistered type
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto componentD = new ComponentD(); auto componentD = new ComponentD();
assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException"); assertThrown!(ResolveException)(container.autowire!(ComponentD)(componentD), "Autowiring unregistered type should throw ResolveException");
} }
// Test autowiring member with non-autowire attribute does not autowire // Test autowiring member with non-autowire attribute does not autowire
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
auto componentE = new ComponentE(); auto componentE = new ComponentE();
container.autowire!ComponentE(componentE); container.autowire!ComponentE(componentE);
assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire"); assert(componentE.componentC is null, "Autowiring should not occur for members with attributes other than @Autowire");
} }
// Test autowire class with alias declaration // Test autowire class with alias declaration
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA; container.register!ComponentA;
auto componentDeclarationCocktail = new ComponentDeclarationCocktail(); auto componentDeclarationCocktail = new ComponentDeclarationCocktail();
container.autowire(componentDeclarationCocktail); container.autowire(componentDeclarationCocktail);
assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed"); assert(componentDeclarationCocktail.componentA !is null, "Autowiring class with non-assignable declarations failed");
} }
// Test autowire class with qualifier // Test autowire class with qualifier
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC); container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX); container.register!(InterfaceA, ComponentX);
auto componentX = container.resolve!(InterfaceA, ComponentX); auto componentX = container.resolve!(InterfaceA, ComponentX);
auto monkeyShine = new MonkeyShine(); auto monkeyShine = new MonkeyShine();
container.autowire(monkeyShine); container.autowire(monkeyShine);
assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed"); assert(monkeyShine.component is componentX, "Autowiring class with qualifier failed");
} }
// Test autowire class with multiple qualifiers // Test autowire class with multiple qualifiers
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC); container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX); container.register!(InterfaceA, ComponentX);
auto componentC = container.resolve!(InterfaceA, ComponentC); auto componentC = container.resolve!(InterfaceA, ComponentC);
auto componentX = container.resolve!(InterfaceA, ComponentX); auto componentX = container.resolve!(InterfaceA, ComponentX);
auto bootstrapBootstrap = new BootstrapBootstrap(); auto bootstrapBootstrap = new BootstrapBootstrap();
container.autowire(bootstrapBootstrap); container.autowire(bootstrapBootstrap);
assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed"); assert(bootstrapBootstrap.componentX is componentX, "Autowiring class with multiple qualifiers failed");
assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed"); assert(bootstrapBootstrap.componentC is componentC, "Autowiring class with multiple qualifiers failed");
} }
// Test getting instance from autowired registration will autowire instance // Test getting instance from autowired registration will autowire instance
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA; container.register!ComponentA;
auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance(); auto registration = new AutowiredRegistration!ComponentB(typeid(ComponentB), container).singleInstance();
auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext()); auto instance = cast(ComponentB) registration.getInstance(new AutowireInstantiationContext());
assert(instance.componentA !is null); assert(instance.componentA !is null);
} }
// Test autowiring a dynamic array with all qualified types // Test autowiring a dynamic array with all qualified types
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!(InterfaceA, ComponentC); container.register!(InterfaceA, ComponentC);
container.register!(InterfaceA, ComponentX); container.register!(InterfaceA, ComponentX);
auto lord = new LordOfTheComponents(); auto lord = new LordOfTheComponents();
container.autowire(lord); container.autowire(lord);
assert(lord.components.length == 2, "Dynamic array was not autowired"); assert(lord.components.length == 2, "Dynamic array was not autowired");
} }
// Test autowiring new instance of singleinstance registration with newInstance UDA // Test autowiring new instance of singleinstance registration with newInstance UDA
unittest { unittest {
shared(DependencyContainer) container = new DependencyContainer(); shared(DependencyContainer) container = new DependencyContainer();
container.register!ComponentA; container.register!ComponentA;
auto regularComponentA = container.resolve!ComponentA; auto regularComponentA = container.resolve!ComponentA;
auto charlie = new ComponentCharlie(); auto charlie = new ComponentCharlie();
container.autowire(charlie); container.autowire(charlie);
assert(charlie.componentA !is regularComponentA, "Autowiring class with AssignNewInstance did not yield a different instance"); assert(charlie.componentA !is regularComponentA, "Autowiring class with AssignNewInstance did not yield a different instance");
} }
} }

File diff suppressed because it is too large Load diff

View file

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

View file

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