Autoformat a few files

This commit is contained in:
Mike Bierlee 2023-03-07 02:43:03 +03:00
parent 1f018f1991
commit 2dcb4cdbd0
6 changed files with 350 additions and 306 deletions

View file

@ -1,150 +1,179 @@
Poodinis Changelog # Poodinis Changelog
==================
**Version 8.1.4** (??????) **Version 8.1.4** (??????)
* FIX some dlang deprecation warnings (#43). Some remain due to further issues in dlang/phobos.
- FIX some dlang deprecation warnings (#43). Some remain due to further issues in dlang/phobos.
**Version 8.1.3** (27-10-2022) **Version 8.1.3** (27-10-2022)
* FIX unnecessary re-registration of types when registerBeforeResolving is specified.
* FIX registerBeforeResolving not working for classes that have no default constructor - FIX unnecessary re-registration of types when registerBeforeResolving is specified.
- FIX registerBeforeResolving not working for classes that have no default constructor
**Version 8.1.2** (18-02-2022) **Version 8.1.2** (18-02-2022)
* FIX compilation error on importing template types that are not actually types.
- FIX compilation error on importing template types that are not actually types.
**Version 8.1.1** (24-08-2021) **Version 8.1.1** (24-08-2021)
* FIX issues where chained registration scopes get rid of initializedBy's and initializedOnceBy's factory method, reverting it to a default instance factory.
- FIX issues where chained registration scopes get rid of initializedBy's and initializedOnceBy's factory method, reverting it to a default instance factory.
**Version 8.1.0** (06-06-2021) **Version 8.1.0** (06-06-2021)
* ADD ability to provide custom instance creator when registering a dependency (PR #28)
* ADD post-instance-construction callback (PR #28) - ADD ability to provide custom instance creator when registering a dependency (PR #28)
* FIX inheritance type template in custom instance creator (PR #29) - ADD post-instance-construction callback (PR #28)
* CHANGE injection initializers to be defined as a registration scope instead of via Container.register(). See initializedBy(). - FIX inheritance type template in custom instance creator (PR #29)
* ADD initializedOnceBy() to create singleton instances via injection initializer. - CHANGE injection initializers to be defined as a registration scope instead of via Container.register(). See initializedBy().
* FIX multiple template arguments not allowed on constructor argument injection (PR #37) - ADD initializedOnceBy() to create singleton instances via injection initializer.
- FIX multiple template arguments not allowed on constructor argument injection (PR #37)
**Version 8.0.3** (11-06-2018) **Version 8.0.3** (11-06-2018)
* FIX struct types being injected into constructors (Fixes issue #25)
- FIX struct types being injected into constructors (Fixes issue #25)
**Version 8.0.2** (15-04-2018) **Version 8.0.2** (15-04-2018)
* FIX resolving types which use template types with circular type arguments (Thanks to aruthane for fixing this.)
- FIX resolving types which use template types with circular type arguments (Thanks to aruthane for fixing this.)
**Version 8.0.1** (13-08-2017) **Version 8.0.1** (13-08-2017)
* FIX value injectors failing to resolve in certain situations when they inject structs (Fixes issue #20)
- FIX value injectors failing to resolve in certain situations when they inject structs (Fixes issue #20)
**Version 8.0.0** (26-12-2016) **Version 8.0.0** (26-12-2016)
* ADD value injection. Members with UDA @Value will be attempted to be injected with a value-type. See tutorial and examples for more info.
* ADD @PostConstruct UDA for marking methods which should be called after a dependency is resolved and autowired. - ADD value injection. Members with UDA @Value will be attempted to be injected with a value-type. See tutorial and examples for more info.
* ADD @PreDestroy UDA for marking methods which should be called when the container loses a dependency's registration. It is called when - ADD @PostConstruct UDA for marking methods which should be called after a dependency is resolved and autowired.
- ADD @PreDestroy UDA for marking methods which should be called when the container loses a dependency's registration. It is called when
removeRegistration or clearAllRegistrations is called. It is also called when the container is destroyed. removeRegistration or clearAllRegistrations is called. It is also called when the container is destroyed.
* FIX nullpointer exception in instance factory when debugging with poodinisVerbose. - FIX nullpointer exception in instance factory when debugging with poodinisVerbose.
* REMOVE previously deprecated getInstance(). - REMOVE previously deprecated getInstance().
**Version 7.0.1** (05-09-2016) **Version 7.0.1** (05-09-2016)
* FIX codegeneration of constructor injection factories for constructors with dependencies from foreign modules,
- FIX codegeneration of constructor injection factories for constructors with dependencies from foreign modules,
such as modules from other libraries (Issue #12). such as modules from other libraries (Issue #12).
**Version 7.0.0** (03-09-2016) **Version 7.0.0** (03-09-2016)
This version introduces changes which might be incompatible with your current codebase This version introduces changes which might be incompatible with your current codebase
* ADD constructor injection. Injection is done automatically on resolve. See tutorial and examples for more details.
* REMOVE deprecated registration options. They are still available in properly cased forms. - ADD constructor injection. Injection is done automatically on resolve. See tutorial and examples for more details.
* REMOVE deprecated register() and resolve() methods which accept variadics and arrays for options. - REMOVE deprecated registration options. They are still available in properly cased forms.
- REMOVE deprecated register() and resolve() methods which accept variadics and arrays for options.
Since registration and resolve options have become bitfields, you should specify them with logical ANDs. Since registration and resolve options have become bitfields, you should specify them with logical ANDs.
* DEPRECATE DependencyContainer.getInstance(). To properly make use of inversion of control, one should only - DEPRECATE DependencyContainer.getInstance(). To properly make use of inversion of control, one should only
create the dependency container once during set-up and then completly rely on injection. See examples for create the dependency container once during set-up and then completly rely on injection. See examples for
proper usage. You can still create your own singleton factory (method) if this is crucial to your design. proper usage. You can still create your own singleton factory (method) if this is crucial to your design.
**Version 6.3.0** (27-06-2016) **Version 6.3.0** (27-06-2016)
* CHANGE registration and resolve options to be supplied using bit flags instead. (Thanks to tmccombs)
* DEPRECATE all other forms of supplying registration and resolve options (by array or variadics) - CHANGE registration and resolve options to be supplied using bit flags instead. (Thanks to tmccombs)
- DEPRECATE all other forms of supplying registration and resolve options (by array or variadics)
**Version 6.2.0** (10-04-2016) **Version 6.2.0** (10-04-2016)
* ADD ability to mark autowire dependencies as optional. When you use UDA @OptionalDependency, a type which fails to autowire will remain null
- ADD ability to mark autowire dependencies as optional. When you use UDA @OptionalDependency, a type which fails to autowire will remain null
(or an empty array). No ResolveException is thrown. (or an empty array). No ResolveException is thrown.
**Version 6.1.0** (10-02-2016) **Version 6.1.0** (10-02-2016)
* ADD setting persistent registration and resolve options
* DEPRECATE DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION, use doNotAddConcreteTypeRegistration instead - ADD setting persistent registration and resolve options
* DEPRECATE supplying register()'s registration options as variadic arguments. Use register(SuperType, ConcreteType)(RegistrationOption[]) instead. - DEPRECATE DO_NOT_ADD_CONCRETE_TYPE_REGISTRATION, use doNotAddConcreteTypeRegistration instead
* ADD resolve options to container resolve() - DEPRECATE supplying register()'s registration options as variadic arguments. Use register(SuperType, ConcreteType)(RegistrationOption[]) instead.
* ADD ability to register a type while resolving it. Use resolve option registerBeforeResolving - ADD resolve options to container resolve()
* ADD ability to autowire private fields (Thanks to Extrawurst) - ADD ability to register a type while resolving it. Use resolve option registerBeforeResolving
* FIX registration of application contexts with non-public members - ADD ability to autowire private fields (Thanks to Extrawurst)
- FIX registration of application contexts with non-public members
**Version 6.0.0** (29-12-2015) **Version 6.0.0** (29-12-2015)
* CHANGE registration scopes are replaced by a single factory implementation. If you were not doing anything with the internal scope mechanism, you
- CHANGE registration scopes are replaced by a single factory implementation. If you were not doing anything with the internal scope mechanism, you
should not be affected by this change. should not be affected by this change.
* ADD application contexts. You can register dependencies within an application context which allow you to fine-tune the creation of dependency instances. - ADD application contexts. You can register dependencies within an application context which allow you to fine-tune the creation of dependency instances.
* CHANGE all public poodinis imports to private. This should not affect you if you use the package import "poodinis" instead of individual modules. - CHANGE all public poodinis imports to private. This should not affect you if you use the package import "poodinis" instead of individual modules.
* REMOVE deprecated ADD_CONCRETE_TYPE_REGISTRATION registration option. - REMOVE deprecated ADD_CONCRETE_TYPE_REGISTRATION registration option.
* REMOVE deprecated RegistrationOptions alias. - REMOVE deprecated RegistrationOptions alias.
**Version 5.0.0** (26-09-2015) **Version 5.0.0** (26-09-2015)
* 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. - DEPRECATE ADD_CONCRETE_TYPE_REGISTRATION registration option. It basically does nothing anymore. See next point.
* CHANGE RegistrationOptions enum name to RegistrationOption - 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.
* DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead. - CHANGE RegistrationOptions enum name to RegistrationOption
- DEPRECATE Usage of RegistrationOptions, please use RegistrationOption instead.
**Version 4.0.0** (16-08-2015) **Version 4.0.0** (16-08-2015)
* REMOVE deprecated module "dependency.d"
- REMOVE deprecated module "dependency.d"
**Version 3.0.0** (16-08-2015) **Version 3.0.0** (16-08-2015)
This version is only compatible with DMD 2.068.0 or higher! This version is only compatible with DMD 2.068.0 or higher!
* ADD UDA which always resolved a new instance to an autowired member, regardless of registration scope.
- ADD UDA which always resolved a new instance to an autowired member, regardless of registration scope.
**Version 2.2.0** (07-06-2015) **Version 2.2.0** (07-06-2015)
* 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 canonical package module "package.d". Use "import poodinis;" to import the project.
* ADD autowiring of dynamic arrays. All registered instances of the element type of the array will be assigned to it. - 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** (14-05-2015) **Version 2.1.0** (14-05-2015)
* ADD option for registering a class by concrete type when registering that class by supertype.
- ADD option for registering a class by concrete type when registering that class by supertype.
**Version 2.0.0** (04-04-2015) **Version 2.0.0** (04-04-2015)
This version introduces changes which might be incompatible with your current codebase This version introduces changes which might be incompatible with your current codebase
* CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible.
- CHANGE dependency container to be synchronized. Sharing a dependency container between threads is now possible.
The implication is that all dependency container instances must be shared now. The implication is that all dependency container instances must be shared now.
You don't have to change anything if you were only using the singleton dependency container. You don't have to change anything if you were only using the singleton dependency container.
**Version 1.0.0** (21-05-2015) **Version 1.0.0** (21-05-2015)
This version introduces changes which are incompatible with previous versions This version introduces changes which are incompatible with previous versions
* REMOVE deprecated autowire constructor
* REMOVE deprecated container alias - REMOVE deprecated autowire constructor
* ADD documentation for public API - REMOVE deprecated container alias
* REMOVE @Autowired UDA. Use @Autowire instead. - ADD documentation for public API
* ADD quickstart from readme to compilable example project. - REMOVE @Autowired UDA. Use @Autowire instead.
* ADD example project for the use of qualifiers - ADD quickstart from readme to compilable example project.
- ADD example project for the use of qualifiers
**Version 0.3.1** (25-01-2015) **Version 0.3.1** (25-01-2015)
* FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly.
- FIX issue where autowiring members which are declared by interface or supertype would get autowired incorrectly.
**Version 0.3.0** (24-01-2015) **Version 0.3.0** (24-01-2015)
* ADD alternative workaround to readme for autowire limitation
* CHANGE returning of resolved instances by returning them by qualifier type instead - ADD alternative workaround to readme for autowire limitation
* ADD debug specifier to reduce verbosity of debug output - 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** (14-12-2014) **Version 0.2.0** (14-12-2014)
* 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. - 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** (05-10-2014) **Version 0.1.4** (05-10-2014)
* Make Poodinis compatible with D 2.066.0 and DUB 0.9.22
* FIX incorrect clearing of registrations - 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 This release should be backwards compatible with the previous versions of D and DUB, but please note that there are no more separate
configurations for release and debug builds. You have to specify a build type in DUB. configurations for release and debug builds. You have to specify a build type in DUB.
**Version 0.1.3** (13-07-2014) **Version 0.1.3** (13-07-2014)
* ADD global autowire function for convenience
* CHANGE workaround to be more simple - ADD global autowire function for convenience
* FIX autowiring classes which contain non-symbolic declarations such as aliases. As a result, only variables are attempted to be autowired. - 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** (23-06-2014) **Version 0.1.2** (23-06-2014)
* ADD workaround for failing to autowire types registered by supertype or interface
- ADD workaround for failing to autowire types registered by supertype or interface
**Version 0.1.1** (14-06-2014) **Version 0.1.1** (14-06-2014)
* FIX: Also auto-wire members from base classes
- FIX: Also auto-wire members from base classes
**Version 0.1.0** (04-06-2014) **Version 0.1.0** (04-06-2014)
* Initial open-source release
* ADD support for registering and resolving - Initial open-source release
* ADD registration scopes - ADD support for registering and resolving
* ADD autowiring - ADD registration scopes
- ADD autowiring

View file

@ -1,5 +1,5 @@
Poodinis Dependency Injection Framework # Poodinis Dependency Injection Framework
=======================================
Version 8.1.3 Version 8.1.3
Copyright 2014-2023 Mike Bierlee Copyright 2014-2023 Mike Bierlee
Licensed under the terms of the MIT license - See [LICENSE.txt](LICENSE.txt) Licensed under the terms of the MIT license - See [LICENSE.txt](LICENSE.txt)
@ -12,26 +12,29 @@ Requires at least a D 2.068.2 compatible compiler
Uses the Phobos standard library Uses the Phobos standard library
Can be built with DUB 1.1.1 or higher Can be built with DUB 1.1.1 or higher
Features ## Features
--------
* Member injection: Injection of dependencies in class members of any visibility (public, private, etc.) - 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. - 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. - 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. - Type qualifiers: Inject concrete types into members defined only by abstract types.
* Application contexts: Control the creation of dependencies manually through factory methods. - Application contexts: Control the creation of dependencies manually through factory methods.
* Multi-threadable: Dependency containers return the same dependencies across all threads. - Multi-threadable: Dependency containers return the same dependencies across all threads.
* Minimal set-up: Creation and injection of conventional classes requires almost no manual dependency configuration. - Minimal set-up: Creation and injection of conventional classes requires almost no manual dependency configuration.
* Well-tested: Developed test-driven, a great number of scenarios are tested as part of the test suite. - Well-tested: Developed test-driven, a great number of scenarios are tested as part of the test suite.
See the [TUTORIAL.md](TUTORIAL.md) and [examples](example) for a complete walkthrough of all features. See the [TUTORIAL.md](TUTORIAL.md) and [examples](example) for a complete walkthrough of all features.
Getting started ## Getting started
---------------
### DUB Dependency ### DUB Dependency
See the Poodinis [DUB project page] for instructions on how to include Poodinis into your project. See the Poodinis [DUB project page] for instructions on how to include Poodinis into your project.
### Quickstart ### Quickstart
The following example shows the typical usage of Poodinis: The following example shows the typical usage of Poodinis:
```d ```d
import poodinis; import poodinis;
@ -61,49 +64,51 @@ void main() {
auto writer = dependencies.resolve!DataWriter; auto writer = dependencies.resolve!DataWriter;
} }
``` ```
Dependency set-up can further be reduced by enabling "Register on resolve". For more details and examples, see the [examples](example) directory. Dependency set-up can further be reduced by enabling "Register on resolve". For more details and examples, see the [examples](example) directory.
Documentation ## Documentation
-------------
You can find the public API documentation [here](https://poodinis.dpldocs.info/v8.1.3/index.html). You can find the public API documentation [here](https://poodinis.dpldocs.info/v8.1.3/index.html).
Alternatively you can generate documentation from the source code using DUB: Alternatively you can generate documentation from the source code using DUB:
``` ```
dub build --build=ddox dub build --build=ddox
``` ```
The documentation can then be found in docs/ The documentation can then be found in docs/
History ## History
-------
For a full overview of changes, see [CHANGES.md](CHANGES.md) For a full overview of changes, see [CHANGES.md](CHANGES.md)
Value Injectors ## Value Injectors
---------------
Poodinis doesn't come with implementations of value injectors. Value injectors are available in separate projects: Poodinis doesn't come with implementations of value injectors. Value injectors are available in separate projects:
* [Mirage Config value injector](https://github.com/mbierlee/mirage-injector)
* [Proper-d value injector](https://github.com/mbierlee/poodinis-proper-d-injector) - [Mirage Config value injector](https://github.com/mbierlee/mirage-injector)
- [Proper-d value injector](https://github.com/mbierlee/poodinis-proper-d-injector)
Have you made any or do you know of any? Please add them to this section via a pull request or open an issue. Have you made any or do you know of any? Please add them to this section via a pull request or open an issue.
Projects Using Poodinis ## Projects Using Poodinis
-----------------------
* [Hunt Framework](https://github.com/huntlabs/hunt-framework): A Web framework for D Programming Language. Full-stack high-performance.
* [Eloquent](https://github.com/SingingBush/eloquent): A lightweight web application written in D
* [ioc](https://github.com/FilipMalczak/ioc): Slow approach to Inversion of Control in D2 language
Future Work - [Hunt Framework](https://github.com/huntlabs/hunt-framework): A Web framework for D Programming Language. Full-stack high-performance.
----------- - [Eloquent](https://github.com/SingingBush/eloquent): A lightweight web application written in D
* Component scan (auto-registration) - [ioc](https://github.com/FilipMalczak/ioc): Slow approach to Inversion of Control in D2 language
* Phobos collections autowiring
* Named qualifiers ## Future Work
- Component scan (auto-registration)
- Phobos collections autowiring
- Named qualifiers
## Contributing
Contributing
------------
Any and all pull requests are welcome! If you (only) want discuss changes before making them, feel free to open an Issue on github. Any and all pull requests are welcome! If you (only) want discuss changes before making them, feel free to open an Issue on github.
Please develop your changes on (a branch based on) the develop branch. Continuous integration is preferred so feature branches are not neccessary. Please develop your changes on (a branch based on) the develop branch. Continuous integration is preferred so feature branches are not neccessary.
[Spring Framework]: http://projects.spring.io/spring-framework/ [spring framework]: http://projects.spring.io/spring-framework/
[Hypodermic]: https://github.com/ybainier/hypodermic/ [hypodermic]: https://github.com/ybainier/hypodermic/
[DUB]: http://code.dlang.org/ [dub project page]: http://code.dlang.org/packages/poodinis
[DUB project page]: http://code.dlang.org/packages/poodinis
[Github issue tracker]: https://github.com/mbierlee/poodinis/issues

View file

@ -1,36 +1,45 @@
Poodinis Tutorial # Poodinis Tutorial
=================
This tutorial will give you an overview of all functionality offered by Poodinis and how to use them. This tutorial will give you an overview of all functionality offered by Poodinis and how to use them.
The Container ## The Container
-------------
To register a class, a new dependency container must be instantiated: To register a class, a new dependency container must be instantiated:
```d ```d
// Create a shared container // Create a shared container
auto dependencies = new shared DependencyContainer(); auto dependencies = new shared DependencyContainer();
``` ```
A shared dependency container is thread-safe and resolves the same dependencies across all threads. A shared dependency container is thread-safe and resolves the same dependencies across all threads.
### Registering Dependencies ### Registering Dependencies
To make dependencies available, they have to be registered: To make dependencies available, they have to be registered:
```d ```d
// Register concrete class // Register concrete class
dependencies.register!ExampleClass; dependencies.register!ExampleClass;
// Register by super type // Register by super type
dependencies.register!(ExampleInterface, ExampleClass); dependencies.register!(ExampleInterface, ExampleClass);
``` ```
In the above example, dependencies on the concrete class and interface will resolve an instance of class `ExampleClass`. A dependency registered by super type will automatically be registered by concrete type. In the above example, dependencies on the concrete class and interface will resolve an instance of class `ExampleClass`. A dependency registered by super type will automatically be registered by concrete type.
If you want to prevent registrations from being both registered by interface and concrete type, use the `doNotAddConcreteTypeRegistration` option when registering: If you want to prevent registrations from being both registered by interface and concrete type, use the `doNotAddConcreteTypeRegistration` option when registering:
```d ```d
dependencies.register!(ExampleInterface, ExampleClass)([RegistrationOption.doNotAddConcreteTypeRegistration]); dependencies.register!(ExampleInterface, ExampleClass)([RegistrationOption.doNotAddConcreteTypeRegistration]);
``` ```
Resolving Dependencies ## Resolving Dependencies
----------------------
To manually resolve a dependency, all you have to do is resolve the dependency's type using the container in which it is registered: To manually resolve a dependency, all you have to do is resolve the dependency's type using the container in which it is registered:
```d ```d
auto exampleClassInstance = dependencies.resolve!ExampleClass; auto exampleClassInstance = dependencies.resolve!ExampleClass;
``` ```
If the class is registered by interface and not by concrete type, you can still resolve the class by concrete type (unless `doNotAddConcreteTypeRegistration` is applied): If the class is registered by interface and not by concrete type, you can still resolve the class by concrete type (unless `doNotAddConcreteTypeRegistration` is applied):
```d ```d
@ -40,43 +49,52 @@ assert(exampleClassInstance is exampleClassInstance2);
``` ```
It is also possible to register a type while resolving it. Doing so means you don't need to explicitly register it beforehand. To do this, use the resolve option `registerBeforeResolving`: It is also possible to register a type while resolving it. Doing so means you don't need to explicitly register it beforehand. To do this, use the resolve option `registerBeforeResolving`:
```d ```d
dependencies.resolve!ExampleClass([ResolveOption.registerBeforeResolving]); dependencies.resolve!ExampleClass([ResolveOption.registerBeforeResolving]);
``` ```
Naturally this can only be done when you are resolving a concrete type or an interface type by qualifier. Naturally this can only be done when you are resolving a concrete type or an interface type by qualifier.
Dependency Creation Behaviour ## Dependency Creation Behaviour
-----------------
You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available: You can control how a dependency is resolved by specifying a creation scope during registration. The scope determines which instance is returned, be it the same each time or a new one. The following scopes are available:
* Resolve a dependency using a single instance (default): - Resolve a dependency using a single instance (default):
```d ```d
dependencies.register!ExampleClass.singleInstance(); dependencies.register!ExampleClass.singleInstance();
``` ```
* Resolve a dependency with a new instance each time it is resolved: - Resolve a dependency with a new instance each time it is resolved:
```d ```d
dependencies.register!ExampleClass.newInstance(); dependencies.register!ExampleClass.newInstance();
``` ```
* Resolve a dependency using a pre-existing instance: - Resolve a dependency using a pre-existing instance:
```d ```d
auto preExistingInstance = new ExampleClass(); auto preExistingInstance = new ExampleClass();
dependencies.register!ExampleClass.existingInstance(preExistingInstance); dependencies.register!ExampleClass.existingInstance(preExistingInstance);
``` ```
* Resolve a dependency using a custom initializer delegate: - Resolve a dependency using a custom initializer delegate:
```d ```d
dependencies.register!ExampleClass.initializedBy({ dependencies.register!ExampleClass.initializedBy({
return new ExampleClass(); return new ExampleClass();
}); });
``` ```
Automatic Injection ## Automatic Injection
----------
The real value of any dependency injection framework comes from its ability to automatically inject dependencies. Poodinis supports automatic injection either through autowiring members annotated with the `@Autowire` UDA or through constructor injection. The real value of any dependency injection framework comes from its ability to automatically inject dependencies. Poodinis supports automatic injection either through autowiring members annotated with the `@Autowire` UDA or through constructor injection.
### UDA-based Autowiring ### UDA-based Autowiring
UDA-based autowiring can be achieved by annotating members of a class with the `@Autowire` UDA: UDA-based autowiring can be achieved by annotating members of a class with the `@Autowire` UDA:
```d ```d
class ExampleClassA {} class ExampleClassA {}
@ -90,20 +108,24 @@ auto exampleInstance = new ExampleClassB();
dependencies.autowire(exampleInstance); dependencies.autowire(exampleInstance);
assert(exampleInstance.dependency !is null); assert(exampleInstance.dependency !is null);
``` ```
It is possible to autowire public as well as protected and private members. It is possible to autowire public as well as protected and private members.
Dependencies are automatically autowired when a class is resolved. So when you resolve `ExampleClassB`, its member `dependency` is automatically autowired: Dependencies are automatically autowired when a class is resolved. So when you resolve `ExampleClassB`, its member `dependency` is automatically autowired:
```d ```d
dependencies.register!ExampleClassA; dependencies.register!ExampleClassA;
dependencies.register!ExampleClassB; dependencies.register!ExampleClassB;
auto instance = dependencies.resolve!ExampleClassB; auto instance = dependencies.resolve!ExampleClassB;
assert(instance.dependency !is null); assert(instance.dependency !is null);
``` ```
If an interface is to be autowired, you must register a concrete class by interface. A class registered only by concrete type can only be injected into members of that type, not its supertypes. If an interface is to be autowired, you must register a concrete class by interface. A class registered only by concrete type can only be injected into members of that type, not its supertypes.
Using the UDA `OptionalDependency` you can mark an autowired member as being optional. When a member is optional, no ResolveException will be thrown when Using the UDA `OptionalDependency` you can mark an autowired member as being optional. When a member is optional, no ResolveException will be thrown when
the type of the member is not registered and `ResolveOption.registerBeforeResolving` is not set on the container. The member will remain null or an empty array in the type of the member is not registered and `ResolveOption.registerBeforeResolving` is not set on the container. The member will remain null or an empty array in
case of array dependencies. case of array dependencies.
```d ```d
class ExampleClass { class ExampleClass {
@Autowire @Autowire
@ -113,7 +135,9 @@ class ExampleClass {
``` ```
### Constructor Injection ### Constructor Injection
Poodinis also supports automatic injection of dependencies through constructors: Poodinis also supports automatic injection of dependencies through constructors:
```d ```d
class ExampleClassA {} class ExampleClassA {}
@ -131,25 +155,31 @@ dependencies.register!ExampleClassB;
auto instance = dependencies.resolve!ExampleClassB; auto instance = dependencies.resolve!ExampleClassB;
``` ```
`ExampleClassA` is automatically resolved and passed to `ExampleClassB`'s constructor. `ExampleClassA` is automatically resolved and passed to `ExampleClassB`'s constructor.
Classes with multiple constructors can be injected. The following rules apply to constructor injection: Classes with multiple constructors can be injected. The following rules apply to constructor injection:
* Injection is attempted at the order of declaration. However, this is compiler dependant and may not always be the case.
* Injection is attempted for the first constructor which has non-builtin types only in its parameter list. - Injection is attempted at the order of declaration. However, this is compiler dependant and may not always be the case.
* When a constructor with an empty parameter list is found, no other constructors are attempted (and nothing is injected). This can be used to explicitly prevent constructor injection. - Injection is attempted for the first constructor which has non-builtin types only in its parameter list.
* When no injectable constructor is found an InstanceCreationException will be thrown on resolve. - When a constructor with an empty parameter list is found, no other constructors are attempted (and nothing is injected). This can be used to explicitly prevent constructor injection.
- When no injectable constructor is found an InstanceCreationException will be thrown on resolve.
If the constructors of a class are not suitable for injection, you could manually configure its creation using Application Contexts (see chapter further down). If the constructors of a class are not suitable for injection, you could manually configure its creation using Application Contexts (see chapter further down).
Constructor injection has the advantage of not having to import Poodinis throughout your application. Constructor injection has the advantage of not having to import Poodinis throughout your application.
### Value Injection ### Value Injection
Besides injecting class instances, Poodinis can also inject values: Besides injecting class instances, Poodinis can also inject values:
```d ```d
class ExampleClass { class ExampleClass {
@Value("a.key.for.this.value") @Value("a.key.for.this.value")
private int someNumber = 9; // Assignment is kept when the injector cannot find the value associated with key 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:
```d ```d
class MyIntInjector : ValueInjector!int { class MyIntInjector : ValueInjector!int {
int get(string key) { int get(string key) {
@ -159,6 +189,7 @@ class MyIntInjector : ValueInjector!int {
dependencies.register!(ValueInjector!int, MyIntInjector); dependencies.register!(ValueInjector!int, MyIntInjector);
``` ```
Each injector injects a value of a specific type. You are required to register value injectors by interface; when injecting values, the autowiring process will try to resolve an injector of the member type, such as `ValueInjector!int`. Each injector injects a value of a specific type. You are required to register value injectors by interface; when injecting values, the autowiring process will try to resolve an injector of the member type, such as `ValueInjector!int`.
You can only register one value injector per type, a resolve exception will be thrown otherwise (unless you suppress them via resolve options). Naturally a resolve exception will also be thrown when there is no injector for a certain value type. You can only register one value injector per type, a resolve exception will be thrown otherwise (unless you suppress them via resolve options). Naturally a resolve exception will also be thrown when there is no injector for a certain value type.
@ -169,31 +200,36 @@ Value injectors will also be autowired before being used. Value injectors will e
Poodinis doesn't come with any value injector implementations. In the [README.md](README.md) you will find a list of projects which use different libraries as value sources. Poodinis doesn't come with any value injector implementations. In the [README.md](README.md) you will find a list of projects which use different libraries as value sources.
Circular Dependencies ## Circular Dependencies
---------------------
Poodinis can autowire circular dependencies when they are registered with `singleInstance` or `existingInstance` registration scopes. Circular dependencies in registrations with `newInstance` scopes will not be autowired, as this would cause an endless loop. Circular dependencies are only supported when autowiring members through the `@Autowire` UDA; circular dependencies in constructors are not supported and will result in an `InstanceCreationException`. 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. Circular dependencies are only supported when autowiring members through the `@Autowire` UDA; circular dependencies in constructors are not supported and will result in an `InstanceCreationException`.
Registering and Resolving Using Qualifiers ## Registering and Resolving Using Qualifiers
------------------------------------------
You can register multiple concrete types to a super type. When doing so, you will need to specify a qualifier when resolving that type: You can register multiple concrete types to a super type. When doing so, you will need to specify a qualifier when resolving that type:
```d ```d
// Color is an interface, Blue and Red are classes implementing that interface // Color is an interface, Blue and Red are classes implementing that interface
dependencies.register!(Color, Blue); dependencies.register!(Color, Blue);
dependencies.register!(Color, Red); dependencies.register!(Color, Red);
auto blueInstance = dependencies.resolve!(Color, Blue); auto blueInstance = dependencies.resolve!(Color, Blue);
``` ```
If you want to autowire a type registered to multiple concrete types, specify a qualified type as template argument: If you want to autowire a type registered to multiple concrete types, specify a qualified type as template argument:
```d ```d
class BluePaint { class BluePaint {
@Autowire!Blue @Autowire!Blue
private Color color; private Color color;
} }
``` ```
If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a `ResolveException` is thrown stating that there are multiple candidates for the type to be resolved. If you registered multiple concrete types to the same supertype and you do not resolve using a qualifier, a `ResolveException` is thrown stating that there are multiple candidates for the type to be resolved.
Autowiring All Registered Instances to an Array ## Autowiring All Registered Instances to an Array
-----------------------------------------------
If you have registered multiple concrete types to a super type, you can autowire them all to an array, in which case you can easily operate on them all: If you have registered multiple concrete types to a super type, you can autowire them all to an array, in which case you can easily operate on them all:
```d ```d
// Color is an interface, Blue and Red are classes implementing that interface // Color is an interface, Blue and Red are classes implementing that interface
@ -206,14 +242,17 @@ dependencies.register!(Color, Blue);
dependencies.register!(Color, Red); dependencies.register!(Color, Red);
auto mixer = dependencies.resolve!ColorMixer; auto mixer = dependencies.resolve!ColorMixer;
``` ```
Member `mixer.colors` will now contain instances of `Blue` and `Red`. The order in which instances are resolved is not guaranteed to be that of the order in which they were registered. Member `mixer.colors` will now contain instances of `Blue` and `Red`. The order in which instances are resolved is not guaranteed to be that of the order in which they were registered.
Application Contexts ## Application Contexts
--------------------
You can fine-tune dependency configuration using application contexts. Application contexts allow you to centralize all dependency configuration as well as define how instances of certain classes should be constructed using factory methods. 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 ### Defining and Using Application Contexts
An application context is defined as follows: An application context is defined as follows:
```d ```d
class Context : ApplicationContext { class Context : ApplicationContext {
public override void registerDependencies(shared(DependencyContainer) container) { public override void registerDependencies(shared(DependencyContainer) container) {
@ -227,6 +266,7 @@ class Context : ApplicationContext {
} }
} }
``` ```
In the override `registerDependencies()` you can register all dependencies which do not need complex set-up, just like you would do when directly using the dependency container. In the override `registerDependencies()` you can register all dependencies which do not need complex set-up, just like you would do when directly using the dependency container.
This override is optional. You can still register simple dependencies outside of the context (or in another context). This override is optional. You can still register simple dependencies outside of the context (or in another context).
@ -237,15 +277,19 @@ They are annotated with the `@Component` UDA to let the container know that thes
Factory methods are useful when you have to deal with dependencies which require constructor arguments or elaborate set-up after instantiation. Factory methods are useful when you have to deal with dependencies which require constructor arguments or elaborate set-up after instantiation.
Application contexts have to be registered with a dependency container. They are registered as follows: Application contexts have to be registered with a dependency container. They are registered as follows:
```d ```d
container.registerContext!Context; container.registerContext!Context;
``` ```
All registered dependencies can now be resolved by the same dependency container. Registering a context will also register it as a dependency, meaning you can autowire the application context in other classes. All registered dependencies can now be resolved by the same dependency container. Registering a context will also register it as a dependency, meaning you can autowire the application context in other classes.
You can register as many types of application contexts as you like. You can register as many types of application contexts as you like.
### Autowiring Application Contexts ### Autowiring Application Contexts
Application contexts can make use of autowired dependencies like any other dependency. When registering an application context, all its components are registered first after which the application context is autowired. Application contexts can make use of autowired dependencies like any other dependency. When registering an application context, all its components are registered first after which the application context is autowired.
This means that after the registration of an application context some dependencies will already be resolved and instantiated. The following example illustrates how autowired members can be used in a context: This means that after the registration of an application context some dependencies will already be resolved and instantiated. The following example illustrates how autowired members can be used in a context:
```d ```d
class Context : ApplicationContext { class Context : ApplicationContext {
@ -265,13 +309,16 @@ class Context : ApplicationContext {
} }
} }
``` ```
As you can see, autowired dependencies can be used within factory methods. When `SomeLibraryClass` is resolved, it will be created with a resolved instance of `SomeClass` and `SomeOtherClass`. As shown, autowired dependencies can be registered within the same As you can see, autowired dependencies can be used within factory methods. When `SomeLibraryClass` is resolved, it will be created with a resolved instance of `SomeClass` and `SomeOtherClass`. As shown, autowired dependencies can be registered within the same
application context, but don't neccesarily have to be. You can even autowire dependencies which are created within a factory method within the same application context. application context, but don't neccesarily have to be. You can even autowire dependencies which are created within a factory method within the same application context.
Application contexts are directly autowired after they have been registered. This means that all autowired dependencies which are not registered in the application context itself need to be registered before registering the application context. Application contexts are directly autowired after they have been registered. This means that all autowired dependencies which are not registered in the application context itself need to be registered before registering the application context.
### Controlling Component Registration ### Controlling Component Registration
You can further influence how components are registered and created with additional UDAs: You can further influence how components are registered and created with additional UDAs:
```d ```d
class Context : ApplicationContext { class Context : ApplicationContext {
@Component @Component
@ -283,23 +330,28 @@ class Context : ApplicationContext {
} }
``` ```
Persistent Registration and Resolve Options ## Persistent Registration and Resolve Options
-------------------------------------------
If you want registration options to be persistent (applicable for every call to `register()`), you can use the container method `setPersistentRegistrationOptions()`: If you want registration options to be persistent (applicable for every call to `register()`), you can use the container method `setPersistentRegistrationOptions()`:
```d ```d
dependencies.setPersistentRegistrationOptions(RegistrationOption.doNotAddConcreteTypeRegistration); // Sets the options dependencies.setPersistentRegistrationOptions(RegistrationOption.doNotAddConcreteTypeRegistration); // Sets the options
dependencies.unsetPersistentRegistrationOptions(); // Clears the persistentent options dependencies.unsetPersistentRegistrationOptions(); // Clears the persistentent options
``` ```
Likewise, the same is possible for resolve options: Likewise, the same is possible for resolve options:
```d ```d
dependencies.setPersistentResolveOptions(ResolveOption.registerBeforeResolving); // Sets the options dependencies.setPersistentResolveOptions(ResolveOption.registerBeforeResolving); // Sets the options
dependencies.unsetPersistentResolveOptions(); // Clears the persistentent options dependencies.unsetPersistentResolveOptions(); // Clears the persistentent options
``` ```
Please note that setting options will unset previously set options; the options specified will be the only ones in effect. Please note that setting options will unset previously set options; the options specified will be the only ones in effect.
Post-Constructors and Pre-Destructors ## Post-Constructors and Pre-Destructors
-------------------------------------
Using the `@PostConstruct` and `@PreDestroy` UDAs you can let the container call public methods in your class right after the container constructed it or when it loses its registration for your class. The pre-constructor is called right after the container has created a new instance of your class and has autowired its members. The post-construtor is called when `removeRegistration` or `clearAllRegistrations` is called, or when the container's destructor is called. A post-constructor or pre-destructor must have the signature `void(void)`. Using the `@PostConstruct` and `@PreDestroy` UDAs you can let the container call public methods in your class right after the container constructed it or when it loses its registration for your class. The pre-constructor is called right after the container has created a new instance of your class and has autowired its members. The post-construtor is called when `removeRegistration` or `clearAllRegistrations` is called, or when the container's destructor is called. A post-constructor or pre-destructor must have the signature `void(void)`.
```d ```d
class MyFineClass { class MyFineClass {
@PostConstruct @PostConstruct
@ -313,4 +365,5 @@ class MyFineClass {
} }
} }
``` ```
You can have multiple post-constructors and pre-destructors, they will all be called; however, the order in which they are called is undetermined. You can have multiple post-constructors and pre-destructors, they will all be called; however, the order in which they are called is undetermined.

View file

@ -5,9 +5,7 @@
"copyright": "Copyright 2014-2023 Mike Bierlee", "copyright": "Copyright 2014-2023 Mike Bierlee",
"license": "MIT", "license": "MIT",
"-ddoxTool": "scod", "-ddoxTool": "scod",
"-ddoxFilterArgs": [ "-ddoxFilterArgs": [],
],
"configurations": [ "configurations": [
{ {
"name": "library", "name": "library",
@ -16,129 +14,85 @@
{ {
"name": "unittest", "name": "unittest",
"targetType": "executable", "targetType": "executable",
"sourcePaths": [ "sourcePaths": ["test"],
"test"
],
"mainSourceFile": "test/poodinis/testmain.d" "mainSourceFile": "test/poodinis/testmain.d"
}, },
{ {
"name": "unittestVerbose", "name": "unittestVerbose",
"targetType": "executable", "targetType": "executable",
"debugVersions": ["poodinisVerbose"], "debugVersions": ["poodinisVerbose"],
"sourcePaths": [ "sourcePaths": ["test"],
"test"
],
"mainSourceFile": "test/poodinis/testmain.d" "mainSourceFile": "test/poodinis/testmain.d"
}, },
{ {
"name": "quickstartExample", "name": "quickstartExample",
"targetType": "executable", "targetType": "executable",
"targetName": "quickstartExample", "targetName": "quickstartExample",
"sourcePaths": [ "sourcePaths": ["example/quickstart"],
"example/quickstart" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "qualifiersExample", "name": "qualifiersExample",
"targetType": "executable", "targetType": "executable",
"targetName": "qualifiersExample", "targetName": "qualifiersExample",
"sourcePaths": [ "sourcePaths": ["example/qualifiers"],
"example/qualifiers" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "arrayCompletionExample", "name": "arrayCompletionExample",
"targetType": "executable", "targetType": "executable",
"targetName": "arrayCompletionExample", "targetName": "arrayCompletionExample",
"sourcePaths": [ "sourcePaths": ["example/arraycompletion"],
"example/arraycompletion" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "annotationsExample", "name": "annotationsExample",
"targetType": "executable", "targetType": "executable",
"targetName": "annotationsExample", "targetName": "annotationsExample",
"sourcePaths": [ "sourcePaths": ["example/annotations"],
"example/annotations" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "applicationContextExample", "name": "applicationContextExample",
"targetType": "executable", "targetType": "executable",
"targetName": "applicationContextExample", "targetName": "applicationContextExample",
"sourcePaths": [ "sourcePaths": ["example/applicationcontext"],
"example/applicationcontext" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "registerOnResolveExample", "name": "registerOnResolveExample",
"targetType": "executable", "targetType": "executable",
"targetName": "registerOnResolveExample", "targetName": "registerOnResolveExample",
"sourcePaths": [ "sourcePaths": ["example/registeronresolve"],
"example/registeronresolve" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "constructorInjectionExample", "name": "constructorInjectionExample",
"targetType": "executable", "targetType": "executable",
"targetName": "constructorInjectionExample", "targetName": "constructorInjectionExample",
"sourcePaths": [ "sourcePaths": ["example/constructorinjection"],
"example/constructorinjection" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "valueInjectionExample", "name": "valueInjectionExample",
"targetType": "executable", "targetType": "executable",
"targetName": "valueInjectionExample", "targetName": "valueInjectionExample",
"sourcePaths": [ "sourcePaths": ["example/valueinjection"],
"example/valueinjection" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "postConPreDestExample", "name": "postConPreDestExample",
"targetType": "executable", "targetType": "executable",
"targetName": "postConPreDestExample", "targetName": "postConPreDestExample",
"sourcePaths": [ "sourcePaths": ["example/postconpredest"],
"example/postconpredest" "importPaths": ["source"]
],
"importPaths": [
"source"
]
}, },
{ {
"name": "injectionInitializerExample", "name": "injectionInitializerExample",
"targetType": "executable", "targetType": "executable",
"targetName": "injectionInitializerExample", "targetName": "injectionInitializerExample",
"sourcePaths": [ "sourcePaths": ["example/injectioninitializer"],
"example/injectioninitializer" "importPaths": ["source"]
],
"importPaths": [
"source"
]
} }
] ]
} }

View file

@ -1,10 +1,13 @@
To run any of these examples, specify the relevant DUB configuration: To run any of these examples, specify the relevant DUB configuration:
``` ```
dub run --build=release --config=<name>Example dub run --build=release --config=<name>Example
``` ```
Where \<name\> corresponds to the name of the folder where the code example is in (camel-cased). Where \<name\> corresponds to the name of the folder where the code example is in (camel-cased).
e.g: e.g:
``` ```
dub run --build=release --config=constructorInjectionExample dub run --build=release --config=constructorInjectionExample
``` ```