Codependent - A dependency injector
We all know about angular style dependency injection. It is nice and sweet,
but what if it could be even better? Due to the conditional initialization
of default parameters it can!
Requirements
This package does not work with older versions of Node. You will need
Node/6.0.0 or greater.
How to create an injection container
const Codependent = require('codependent');
const container = new Codependent('my container');
How to register objects in the container
container.constant(‹name› ‹value›)
Stores the value in the container. That simple
container.constant('hello', 'world');
container.get('hello');
container.register(‹name›, ‹handler›)
The handler is a function that is dependecy
injected. This is useful if you need something
from the container when creating the object.
container.constant('apiUrl', 'https://path-to-my.api');
container.register('photosUrl', (apiUrl) => apiUrl + '/photos');
container.get('photosUrl');
container.class(‹name›, ‹class or function›)
Creates a new instance of the class every time
it is injected. In an ES6 class, the constructor
is injected, in an ES5 class, the function itself
is injected. Either way it is newed up.
class MyClass {
constructor(meaningOfLife) {
this.meaningOfLife = meaningOfLife;
}
}
container.costant('meaningOfLife', 42);
container.class('myClass', MyClass);
container.get('myClass').meaningOfLife === 42
container.singleton(‹name›, ‹class or function›)
Same as function except that only one instance
will ever be created (unless the value is redefined
in the container). Singletons are eagerly
instantiated, so you must register it in the
container before its dependencies
container.singleton('myClass', MyClass);
container.provider(‹name›, ‹handler›)
The handler function is called and injected every
time you inject the value. The return value of
the handler is what is injected.
let i = 0;
container.constant('message', 'hello world');
container.provider('counter', message => {
i += 1;
return message + ' ' + i;
});
container.get('counter');
container.get('counter');
container.get('counter');
How to create injectable classes, functions and methods
Default argument
Arguments are lazy loaded, which enables us to inject
dependencies with this sweet syntax. Admittedly it is
a hack, so if you're not comfortable with that, use
angular style injection.
class MyClass {
constructor(x = isInjected) {
}
}
function myFunc(x = isInjected) {
}
let myFunc = (x = isInjected) => {
}
let myObj = {
myMethod(x = isInjected) {
}
}
Angular style
class MyClass {
constructor(isInjected) {
}
}
function myFunc(isInjected) {
}
let myFunc = (isInjected) => {
}
let myFunc = isInjected => {
}
let myObj = {
myMethod(isInjected) {
}
}
How to inject
... into classes
container.instantiate(MyClass);
... into functions
container.callFunction(myFunction);
Extending a Codependent container
const containerA = new Codependent('A');
const containerB = new Codependent('B');
containerB.extend(containerA);
containerA.constant('greeting', 'Hello world!');
containerB.get('greeting');
Recursive injection
How it's awesome - simplicity
When a class or provider is injected, all of
its dependencies will themselves be injected.
Thus it all resolves quite nicely into the
desired object.
How it can be bad - infinite recursion
so if module A requires itself or if another
infinite dependency recursion occurs, it will
cause the call stack size to be exceeded.
How it can be remedied
Register a value or a provider depending on
your needs and manually create the object.
How to dependency inject
In these examples injected
and injectedAsWell
are the keys
that are looked up in the injection container and x is a
variable the injected value is assigned to in the function
or class constructor.
container.callFunction(‹function›)
Dependency inject the function and call it.
container.callFunction(function (x = injected, injectedAsWell) {
});
container.instantiate(‹class or function›)
Dependency inject a class constructor or a function
and create a new instance.
class MyClass {
constructor(x = injected, injectedAsWell) {
}
}
function FnClass(x = injected, injectedAsWell) {}
container.instantiate(MyClass);
container.instantiate(FnClass);
container.get(‹name›)
Returns the injectable value. Works just the same as when
you actually inject the value.
container.get('injected');
TODO
get-args
- Refactor get-args into separate npm-module.
- Replace regex with state machine.
- Exhaust all possible ways a function/method can be made in es7
Container
- If an injected class extends another class and has no constructor, the constructor of the extended class should be injected into.
- This has some isses that needs to be worked out...
Contributer notes
Testing
npm install -g mocha
npm install
npm test
Not happy with something?
Send me a pull request and I will get it sorted out! Tests are mandatory for pull requests.
The get-args function has tests and is working, but it is ugly and probably has suboptimal
performance, so I would be happy about pull requests for that one! :)