Piquouze
piquouze
is a featureful dependency injection module for
nodejs.
Features
- Containers hierarchy
- Registration of any first class value (including function and classes)
- Injection of function (arrow and regular)
- Injection of class constructors and methods
- Injection of generators
- Detection of contained factories names
- Detection of dependencies from parameter names
- Fallback to parameters default values
- Clean error on dependencies cycle
- Extensible dependencies caching policies
See the changelog and the API reference
Caveats
Because of the nature of the Javascript legacy, it is very hard to properly
determine whether a regular function (one using the function
keyword) is
actually a prototype constructor or a simple function.
It is generally harmless to call a simple function with the new
keyword.
However, doing so is considered a prototype instantiation and as such, the
actual value returned from the new
keyword is the return value of the
function if and only if it is a an Object (of any kind). Primitive values
(undefined
, null
, booleans, numbers, strings, symbols) will be substituted
by the value of the receiver (the this
value).
piquouze
will mimic the effects of the new
operator on calls to injected
regular function if the return value is an instance of Object
or if the bound
receiver has own properties after the call. Otherwise it will return the
function return value.
Furthermore, bound function cannot be register as factories or injected (but they can be registered as value). This is because there is no clean way to retrieve the bounded function parameters and thus it cannot be injected properly.
Requirements
piquouze
work with NodeJS 4.x and above. Install it the usual way:
npm install piquouze
How To
First start by creating a dependencies container:
const piquouze = ; const container = ;
Then register some first class values and/or factories (when resolving injection, factories will be injected too):
container;container;
Then define a functor to inject:
{ thisvalue = myConst; ; }
Then inject it, and create an instance:
let functor = container;let instance = ; // -> hello, worldconsole; // -> trueconsole; // -> 42
Dependencies are automatically inferred from the name of the parameters, but they can also be forced:
myTargetClass$inject = "anotherConst" "anotherLog";
Regular and arrow functions can be injected as well, and all of them can take any number of extra parameters:
{ thisvalue = myConst; thisextra = args; ; } functor = container;instance = ; // -> hello, worldconsole; // -> ["extraneous"]
Of course with node version prior to 6.x, the rest operator is not supported,
so you have to either use arguments
or name all the extra parameters and
force the injection list of dependencies.
Child containers can be created easily:
const childContainer = container;
Dependencies can be redefined, however changes are not reflected to already injected functors. Dependencies can exist anywhere in the container hierarchy and will be resolved by name from children to parent containers.
Factories can be registered too:
container;
Dependencies on factories will be injected as factories results (or instance if the factory was a class):
functor = container;console; // -> {data: 42}
Factories can be register with an optional caching policy indicating how often the factory is to be called to get new values:
container;
piquouze
has the following policies:
caching.Always
: the value is cached forevercaching.PerContainer
: the value is cached once per containercaching.PerInjection
: the value is cached once per injection (this is the default)caching.Never
: the value is never cached
Factories
Factories are functors that are in turn injectable with dependencies.
Factories can be registered with the own name (or the name can be forced):
container;container;
Factory dependencies can have default values, either as function default values (for node > 6.x, in which case they cannot depend on external identifiers) or as decorations on the functor itself (in which case they can in turn be injected, recursively):
container; const functor = a + b;functor$defaults = a: 1 dependencyvalue;container;
Functors injected at the container level can have default values for their dependencies as well.
Merging
Containers can be merged into a independent container:
container = Container;
All values and factories registered in the merged containers are available in the result container. However changes on these containers after the merge will have no effect on the result container.
Iterables
You can iterate over the values registered either on a single container:
forentry of container
or on a container and all of its parentship hierarchy:
forentry of container
Entries are arrays with three values:
- the first element is the name under which the value was registered
- the second element is the value registered
- the third element is the type of registration performed (either
"factory"
or"value"
)