Add optional value injection when values cannot be injected by injectors

This commit is contained in:
Mike Bierlee 2016-12-11 01:15:30 +01:00
parent 9eb3a89695
commit 52dcecef9a
4 changed files with 47 additions and 6 deletions

View file

@ -140,7 +140,7 @@ Besides injecting class instances, Poodinis can also inject values:
``` ```
class ExampleClass { class ExampleClass {
@Value("a.key.for.this.value") @Value("a.key.for.this.value")
private int someNumber; private int someNumber = 9; // Assignment is kept when the injector cannot find the value associated with key
} }
``` ```
The value will automatically be injected during the autowiring process. In order for Poodinis to be able to inject values, ValueInjectors must be available and registered with the dependency container: The value will automatically be injected during the autowiring process. In order for Poodinis to be able to inject values, ValueInjectors must be available and registered with the dependency container:

View file

@ -134,7 +134,7 @@ private void autowireMember(string member, size_t memberIndex, Type)(shared(Depe
injectInstance!(member, memberIndex, UseMemberType)(container, instance); injectInstance!(member, memberIndex, UseMemberType)(container, instance);
} else static if (is(typeof(attribute) == Value)) { } else static if (is(typeof(attribute) == Value)) {
enum key = attribute.key; enum key = attribute.key;
injectValue!(member, memberIndex, key)(container, instance); injectValue!(member, memberIndex, key, false)(container, instance);
} }
} }
} }
@ -204,7 +204,7 @@ private QualifierType createOrResolveInstance(MemberType, QualifierType, bool cr
} }
} }
private void injectValue(string member, size_t memberIndex, string key, Type)(shared(DependencyContainer) container, Type instance) { private void injectValue(string member, size_t memberIndex, string key, bool mandatory, Type)(shared(DependencyContainer) container, Type instance) {
alias MemberType = typeof(Type.tupleof[memberIndex]); alias MemberType = typeof(Type.tupleof[memberIndex]);
try { try {
auto injector = container.resolve!(ValueInjector!MemberType); auto injector = container.resolve!(ValueInjector!MemberType);
@ -214,6 +214,10 @@ private void injectValue(string member, size_t memberIndex, string key, Type)(sh
} }
} catch (ResolveException e) { } catch (ResolveException e) {
throw new ValueInjectionException(format("Could not inject value of type %s into %s.%s: value injector is missing for this type.", typeid(MemberType), typeid(Type), member)); throw new ValueInjectionException(format("Could not inject value of type %s into %s.%s: value injector is missing for this type.", typeid(MemberType), typeid(Type), member));
} catch (ValueNotAvailableException e) {
static if (mandatory) {
throw new ValueInjectionException(format("Could not inject value of type %s into %s.%s", typeid(MemberType), typeid(Type), member), e);
}
} }
} }

View file

@ -12,6 +12,7 @@
module poodinis.valueinjection; module poodinis.valueinjection;
import std.exception; import std.exception;
import std.string;
/** /**
* Thrown when something goes wrong during value injection. * Thrown when something goes wrong during value injection.
@ -20,6 +21,19 @@ class ValueInjectionException : Exception {
mixin basicExceptionCtors; mixin basicExceptionCtors;
} }
/**
* Thrown by injectors when the value with the given key cannot be found.
*/
class ValueNotAvailableException : Exception {
this(string key) {
super(format("Value for key %s is not available", key));
}
this(string key, Throwable cause) {
super(format("Value for key %s is not available", key), cause);
}
}
/** /**
* UDA used for marking class members which should be value-injected. * UDA used for marking class members which should be value-injected.
* *
@ -53,9 +67,8 @@ struct Value {
* type or that of a struct. While class types are also supported, value injectors * type or that of a struct. While class types are also supported, value injectors
* are not intended for them. * are not intended for them.
* *
* Note that value injectors are also autowired before being used. Value injectors should * Note that value injectors are also autowired before being used. Values within dependencies of
* not contain dependencies on classes which require value injection. Neither should a * a value injector are not injected. Neither are values within the value injector itself.
* value injector have members which are to be value-injected.
* *
* Value injection is not supported for constructor injection. * Value injection is not supported for constructor injection.
* *
@ -75,6 +88,8 @@ interface ValueInjector(Type) {
* *
* The key can have any format. Generally you are encouraged * The key can have any format. Generally you are encouraged
* to accept a dot separated path, for example: server.http.port * to accept a dot separated path, for example: server.http.port
*
* Throws: ValueNotAvailableException when the value for the given key is not available for any reason
*/ */
Type get(string key); Type get(string key);
} }

View file

@ -25,6 +25,11 @@ version(unittest) {
Thing thing; Thing thing;
} }
class ConfigWithDefaults {
@Value("conf.missing")
int noms = 9;
}
// Test injection of values // Test injection of values
unittest { unittest {
auto container = new shared DependencyContainer(); auto container = new shared DependencyContainer();
@ -69,4 +74,21 @@ version(unittest) {
assertThrown!ValueInjectionException(autowire(container, new MyConfig())); assertThrown!ValueInjectionException(autowire(container, new MyConfig()));
} }
// Test injection of values with defaults
unittest {
auto container = new shared DependencyContainer();
container.register!ConfigWithDefaults;
class IntInjector : ValueInjector!int {
public override int get(string key) {
throw new ValueNotAvailableException(key);
}
}
container.register!(ValueInjector!int, IntInjector);
auto instance = container.resolve!ConfigWithDefaults;
assert(instance.noms == 9);
}
} }