mirror of
https://github.com/mbierlee/poodinis.git
synced 2024-11-15 04:04:01 +01:00
Add value injector
This commit is contained in:
parent
c75b025a68
commit
d3ed3e91b9
|
@ -1,6 +1,7 @@
|
|||
Poodinis Changelog
|
||||
==================
|
||||
**Version NEXT**
|
||||
* ADD value injection. Members with UDA @Value will be attempted to be injected with a value-type. See tutorial and examples for more info.
|
||||
* FIX nullpointer exception in instance factory when debugging with poodinisVerbose
|
||||
|
||||
**Version 7.0.1**
|
||||
|
|
|
@ -16,6 +16,7 @@ Features
|
|||
--------
|
||||
* Member injection: Injection of dependencies in class members of any visibility (public, private, etc.)
|
||||
* Constructor injection: Automatic injection of dependencies in class constructors on creation.
|
||||
* Value injection: Value-types such as primitives or structs can be injected using custom value injectors.
|
||||
* Type qualifiers: Inject concrete types into members defined only by abstract types.
|
||||
* Application contexts: Control the creation of dependencies manually through factory methods.
|
||||
* Multi-threadable: Dependency containers return the same dependencies across all threads.
|
||||
|
|
|
@ -20,6 +20,7 @@ module poodinis.autowire;
|
|||
import poodinis.container;
|
||||
import poodinis.registration;
|
||||
import poodinis.factory;
|
||||
import poodinis.valueinjection;
|
||||
|
||||
import std.exception;
|
||||
import std.stdio;
|
||||
|
@ -106,7 +107,7 @@ public void autowire(Type)(shared(DependencyContainer) container, Type instance)
|
|||
printDebugAutowiredInstance(typeid(Type), &instance);
|
||||
}
|
||||
|
||||
// note: recurse into base class if there are more between Type and Object in the hirarchy
|
||||
// Recurse into base class if there are more between Type and Object in the hierarchy
|
||||
static if(BaseClassesTuple!Type.length > 1)
|
||||
{
|
||||
autowire!(BaseClassesTuple!Type[0])(container, instance);
|
||||
|
@ -131,6 +132,9 @@ private void autowireMember(string member, size_t memberIndex, Type)(shared(Depe
|
|||
injectInstance!(member, memberIndex, typeof(attribute.qualifier))(container, instance);
|
||||
} else static if (__traits(isSame, attribute, Autowire)) {
|
||||
injectInstance!(member, memberIndex, UseMemberType)(container, instance);
|
||||
} else static if (is(typeof(attribute) == Value)) {
|
||||
enum key = attribute.key;
|
||||
injectValue!(member, memberIndex, key)(container, instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -200,6 +204,19 @@ private QualifierType createOrResolveInstance(MemberType, QualifierType, bool cr
|
|||
}
|
||||
}
|
||||
|
||||
private void injectValue(string member, size_t memberIndex, string key, Type)(shared(DependencyContainer) container, Type instance) {
|
||||
alias MemberType = typeof(Type.tupleof[memberIndex]);
|
||||
auto injector = container.resolve!(ValueInjector!MemberType);
|
||||
instance.tupleof[memberIndex] = injector.get(key);
|
||||
debug(poodinisVerbose) {
|
||||
printDebugValueInjection(typeid(Type), &instance, member, typeid(MemberType), key);
|
||||
}
|
||||
}
|
||||
|
||||
private void printDebugValueInjection(TypeInfo instanceType, void* instanceAddress, string member, TypeInfo valueType, string key) {
|
||||
writeln(format("DEBUG: Injected value with key '%s' of type %s into [%s@%s].%s", key, valueType, instanceType, instanceAddress, member));
|
||||
}
|
||||
|
||||
/**
|
||||
* Autowire the given instance using the globally available dependency container.
|
||||
*
|
||||
|
|
|
@ -16,3 +16,4 @@ public import poodinis.container;
|
|||
public import poodinis.registration;
|
||||
public import poodinis.context;
|
||||
public import poodinis.factory;
|
||||
public import poodinis.valueinjection;
|
||||
|
|
64
source/poodinis/valueinjection.d
Normal file
64
source/poodinis/valueinjection.d
Normal file
|
@ -0,0 +1,64 @@
|
|||
/**
|
||||
* This module contains facilities to support value injection. Actual injection is done by the
|
||||
* autowiring mechanism.
|
||||
*
|
||||
* 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.valueinjection;
|
||||
|
||||
/**
|
||||
* UDA used for marking class members which should be value-injected.
|
||||
*
|
||||
* A key must be supplied, which can be in any format depending on how
|
||||
* a value injector reads it.
|
||||
*
|
||||
* Examples:
|
||||
* ---
|
||||
* class MyClass {
|
||||
* @Value("general.importantNumber")
|
||||
* private int number;
|
||||
* }
|
||||
* ---
|
||||
*/
|
||||
struct Value {
|
||||
string key;
|
||||
};
|
||||
|
||||
/**
|
||||
* Interface which should be implemented by value injectors.
|
||||
*
|
||||
* Each value injector injects one specific type. The type can be any primitive
|
||||
* type or that of a struct. While class types are also supported, value injectors
|
||||
* are not intended for them.
|
||||
*
|
||||
* Note that value injectors are also autowired before being used. Value injectors should
|
||||
* not contain dependencies on classes which require value injection. Neither should a
|
||||
* value injector have members which are to be value-injected.
|
||||
*
|
||||
* Value injection is not supported for constructor injection.
|
||||
*
|
||||
* Examples:
|
||||
* ---
|
||||
* class MyIntInjector : ValueInjector!int {
|
||||
* public override int get(string key) { ... }
|
||||
* }
|
||||
*
|
||||
* // In order to make the container use your injector, register it by interface:
|
||||
* container.register!(ValueInjector!int, MyIntInjector);
|
||||
* ---
|
||||
*/
|
||||
interface ValueInjector(Type) {
|
||||
/**
|
||||
* Get a value from the injector by key.
|
||||
*
|
||||
* The key can have any format. Generally you are encouraged
|
||||
* to accept a dot separated path, for example: server.http.port
|
||||
*/
|
||||
Type get(string key);
|
||||
}
|
||||
|
|
@ -87,6 +87,14 @@ version(unittest) {
|
|||
public ComponentC[] componentCs;
|
||||
}
|
||||
|
||||
class ValuedClass {
|
||||
@Value("values.int")
|
||||
public int intValue;
|
||||
|
||||
@Autowire
|
||||
public ComponentA unrelated;
|
||||
}
|
||||
|
||||
// Test autowiring concrete type to existing instance
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
@ -229,7 +237,7 @@ version(unittest) {
|
|||
assert(instance.componentA !is null);
|
||||
}
|
||||
|
||||
// Test autowiring optional depenencies
|
||||
// Test autowiring optional dependencies
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
auto instance = new OuttaTime();
|
||||
|
@ -240,4 +248,24 @@ version(unittest) {
|
|||
assert(instance.componentA is null);
|
||||
assert(instance.componentCs is null);
|
||||
}
|
||||
|
||||
// Test autowiring class using value injection
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
class TestInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
assert(key == "values.int");
|
||||
return 8;
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, TestInjector);
|
||||
container.register!ComponentA;
|
||||
auto instance = new ValuedClass();
|
||||
|
||||
container.autowire(instance);
|
||||
|
||||
assert(instance.intValue == 8);
|
||||
assert(instance.unrelated !is null);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -224,6 +224,21 @@ version(unittest) {
|
|||
this(Ola ola) {}
|
||||
}
|
||||
|
||||
struct Thing {
|
||||
int x;
|
||||
}
|
||||
|
||||
class MyConfig {
|
||||
@Value("conf.stuffs")
|
||||
int stuffs;
|
||||
|
||||
@Value("conf.name")
|
||||
string name;
|
||||
|
||||
@Value("conf.thing")
|
||||
Thing thing;
|
||||
}
|
||||
|
||||
// Test register concrete type
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
|
@ -747,4 +762,40 @@ version(unittest) {
|
|||
container.register!Hello;
|
||||
container.resolve!Hello;
|
||||
}
|
||||
|
||||
// Test injection of values
|
||||
unittest {
|
||||
auto container = new shared DependencyContainer();
|
||||
container.register!MyConfig;
|
||||
|
||||
class IntInjector : ValueInjector!int {
|
||||
public override int get(string key) {
|
||||
assert(key == "conf.stuffs");
|
||||
return 364;
|
||||
}
|
||||
}
|
||||
|
||||
class StringInjector : ValueInjector!string {
|
||||
public override string get(string key) {
|
||||
assert(key == "conf.name");
|
||||
return "Le Chef";
|
||||
}
|
||||
}
|
||||
|
||||
class ThingInjector : ValueInjector!Thing {
|
||||
public override Thing get(string key) {
|
||||
assert(key == "conf.thing");
|
||||
return Thing(8899);
|
||||
}
|
||||
}
|
||||
|
||||
container.register!(ValueInjector!int, IntInjector);
|
||||
container.register!(ValueInjector!string, StringInjector);
|
||||
container.register!(ValueInjector!Thing, ThingInjector);
|
||||
|
||||
auto instance = container.resolve!MyConfig;
|
||||
assert(instance.stuffs == 364);
|
||||
assert(instance.name == "Le Chef");
|
||||
assert(instance.thing.x == 8899);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue