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
==================
**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
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.
* 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 RegistrationOptions alias.
**Version 5.0.0**
* 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 RegistrationOptions enum name to RegistrationOption
* DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead.
**Version 4.0.0**
* REMOVE deprecated module "dependency.d"
**Version 3.0.0**
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.
**Version 2.2.0**
* 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.
* ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it.
**Version 2.1.0**
* ADD option for registering a class by concrete type when registering that class by supertype.
**Version 2.0.0**
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.
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.
**Version 1.0.0**
This version introduces changes which are incompatible with previous versions
* REMOVE deprecated autowire constructor
* REMOVE deprecated container alias
* ADD documentation for public API
* REMOVE @Autowired UDA. Use @Autowire instead.
* ADD quickstart from readme to compilable example project.
* ADD example project for the use of qualifiers
**Version 0.3.1**
* FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly.
**Version 0.3.0**
* ADD alternative workaround to readme for autowire limitation
* CHANGE returning of resolved instances by returning them by qualifier type instead
* ADD debug specifier to reduce verbosity of debug output
**Version 0.2.0**
* 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.
**Version 0.1.4**
* Make Poodinis compatible with D 2.066.0 and DUB 0.9.22
* 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
configurations for release and debug builds. You have to specify a build type in DUB.
**Version 0.1.3**
* ADD global autowire function for convenience
* 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.
**Version 0.1.2**
* ADD workaround for failing to autowire types registered by supertype or interface
**Version 0.1.1**
* FIX: Also auto-wire members from base classes
**Version 0.1.0**
* Initial open-source release
* ADD support for registering and resolving
* ADD registration scopes
* ADD autowiring
Poodinis Changelog
==================
**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
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.
* 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 RegistrationOptions alias.
**Version 5.0.0**
* 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 RegistrationOptions enum name to RegistrationOption
* DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead.
**Version 4.0.0**
* REMOVE deprecated module "dependency.d"
**Version 3.0.0**
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.
**Version 2.2.0**
* 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.
* ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it.
**Version 2.1.0**
* ADD option for registering a class by concrete type when registering that class by supertype.
**Version 2.0.0**
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.
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.
**Version 1.0.0**
This version introduces changes which are incompatible with previous versions
* REMOVE deprecated autowire constructor
* REMOVE deprecated container alias
* ADD documentation for public API
* REMOVE @Autowired UDA. Use @Autowire instead.
* ADD quickstart from readme to compilable example project.
* ADD example project for the use of qualifiers
**Version 0.3.1**
* FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly.
**Version 0.3.0**
* ADD alternative workaround to readme for autowire limitation
* CHANGE returning of resolved instances by returning them by qualifier type instead
* ADD debug specifier to reduce verbosity of debug output
**Version 0.2.0**
* 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.
**Version 0.1.4**
* Make Poodinis compatible with D 2.066.0 and DUB 0.9.22
* 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
configurations for release and debug builds. You have to specify a build type in DUB.
**Version 0.1.3**
* ADD global autowire function for convenience
* 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.
**Version 0.1.2**
* ADD workaround for failing to autowire types registered by supertype or interface
**Version 0.1.1**
* FIX: Also auto-wire members from base classes
**Version 0.1.0**
* Initial open-source release
* ADD support for registering and resolving
* ADD registration scopes
* ADD autowiring

138
README.md
View file

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

View file

@ -1,199 +1,199 @@
Poodinis Tutorial
=================
This tutorial will give you an overview of all functionality offered by Poodinis and how to use them.
The container
-------------
To register a class, a new dependency container must be instantiated:
```d
// Register a private container
auto dependencies = new DependencyContainer();
// Or use the singleton container
dependencies = DependencyContainer.getInstance();
```
###Registering dependencies
To make dependencies available, they have to be registered:
```d
// Register concrete class
dependencies.register!ExampleClass;
// Register by super type
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.
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:
```d
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:
```d
auto exampleClassInstance = dependencies.resolve!ExampleInterface;
auto exampleClassInstance2 = dependencies.resolve!ExampleClass;
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:
```d
dependencies.register!(ExampleInterface, ExampleClass)(RegistrationOptions.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION);
auto exampleClassInstance = dependencies.resolve!ExampleInterface;
auto exampleClassInstance2 = dependencies.resolve!ExampleClass; // A ResolveException is thrown
```
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:
* Resolve a dependency using a single instance (default):
```d
dependencies.register!(ExampleClass).singleInstance();
```
* Resolve a dependency with a new instance each time it is resolved:
```d
dependencies.register!(ExampleClass).newInstance();
```
* Resolve a dependency using a pre-existing instance
```d
auto preExistingInstance = new ExampleClass();
dependencies.register!(ExampleClass).existingInstance(preExistingInstance);
```
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:
```d
class ExampleClassA {}
class ExampleClassB {
@Autowire
public ExampleClassA dependency;
}
dependencies.register!ExampleClassA;
auto exampleInstance = new ExampleClassB();
dependencies.autowire(exampleInstance);
assert(exampleInstance.dependency !is null);
```
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:
```d
dependencies.register!ExampleClassA;
dependencies.register!ExampleClassB;
auto instance = dependencies.resolve!ExampleClassB;
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.
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.
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:
```d
// Color is an interface, Blue and Red are classes implementing that interface
dependencies.register!(Color, Blue);
dependencies.register!(Color, Red);
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:
```d
class BluePaint {
@Autowire!Blue
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.
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:
```d
// Color is an interface, Blue and Red are classes implementing that interface
class ColorMixer {
@Autowire
public Color[] colors;
}
dependencies.register!(Color, Blue);
dependencies.register!(Color, Red);
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.
Application Contexts
--------------------
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.
###Defining and using application contexts
An application context is defined as follows:
```d
class Context : ApplicationContext {
public override void registerDependencies(shared(DependencyContainer) container) {
container.register!SomeClass;
container.register!(SomeInterface, SomeOtherClass).newInstance();
}
@Component
public SomeLibraryClass libraryClass() {
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.
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.
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.
Application contexts have to be registered with a dependency container. They are registered as follows:
```d
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.
You can register as many types of application contexts as you like.
###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.
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
class Context : ApplicationContext {
@Autowire
public SomeClass someClass;
@Autowire
public SomeOtherClass someOtherClass;
public override void registerDependencies(shared(DependencyContainer) container) {
container.register!SomeClass;
}
@Component
public SomeLibraryClass libraryClass() {
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
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.
###Controlling component registration
You can further influence how components are registered and created with additional UDAs:
```d
class Context : ApplicationContext {
@Component
@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
public SomeClass someClass() {
return new SomeClass();
}
}
Poodinis Tutorial
=================
This tutorial will give you an overview of all functionality offered by Poodinis and how to use them.
The container
-------------
To register a class, a new dependency container must be instantiated:
```d
// Register a private container
auto dependencies = new DependencyContainer();
// Or use the singleton container
dependencies = DependencyContainer.getInstance();
```
###Registering dependencies
To make dependencies available, they have to be registered:
```d
// Register concrete class
dependencies.register!ExampleClass;
// Register by super type
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.
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:
```d
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:
```d
auto exampleClassInstance = dependencies.resolve!ExampleInterface;
auto exampleClassInstance2 = dependencies.resolve!ExampleClass;
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:
```d
dependencies.register!(ExampleInterface, ExampleClass)(RegistrationOptions.DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION);
auto exampleClassInstance = dependencies.resolve!ExampleInterface;
auto exampleClassInstance2 = dependencies.resolve!ExampleClass; // A ResolveException is thrown
```
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:
* Resolve a dependency using a single instance (default):
```d
dependencies.register!(ExampleClass).singleInstance();
```
* Resolve a dependency with a new instance each time it is resolved:
```d
dependencies.register!(ExampleClass).newInstance();
```
* Resolve a dependency using a pre-existing instance
```d
auto preExistingInstance = new ExampleClass();
dependencies.register!(ExampleClass).existingInstance(preExistingInstance);
```
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:
```d
class ExampleClassA {}
class ExampleClassB {
@Autowire
public ExampleClassA dependency;
}
dependencies.register!ExampleClassA;
auto exampleInstance = new ExampleClassB();
dependencies.autowire(exampleInstance);
assert(exampleInstance.dependency !is null);
```
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:
```d
dependencies.register!ExampleClassA;
dependencies.register!ExampleClassB;
auto instance = dependencies.resolve!ExampleClassB;
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.
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.
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:
```d
// Color is an interface, Blue and Red are classes implementing that interface
dependencies.register!(Color, Blue);
dependencies.register!(Color, Red);
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:
```d
class BluePaint {
@Autowire!Blue
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.
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:
```d
// Color is an interface, Blue and Red are classes implementing that interface
class ColorMixer {
@Autowire
public Color[] colors;
}
dependencies.register!(Color, Blue);
dependencies.register!(Color, Red);
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.
Application Contexts
--------------------
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.
###Defining and using application contexts
An application context is defined as follows:
```d
class Context : ApplicationContext {
public override void registerDependencies(shared(DependencyContainer) container) {
container.register!SomeClass;
container.register!(SomeInterface, SomeOtherClass).newInstance();
}
@Component
public SomeLibraryClass libraryClass() {
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.
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.
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.
Application contexts have to be registered with a dependency container. They are registered as follows:
```d
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.
You can register as many types of application contexts as you like.
###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.
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
class Context : ApplicationContext {
@Autowire
public SomeClass someClass;
@Autowire
public SomeOtherClass someOtherClass;
public override void registerDependencies(shared(DependencyContainer) container) {
container.register!SomeClass;
}
@Component
public SomeLibraryClass libraryClass() {
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
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.
###Controlling component registration
You can further influence how components are registered and created with additional UDAs:
```d
class Context : ApplicationContext {
@Component
@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
public SomeClass someClass() {
return new SomeClass();
}
}
```

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

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