codependent
v3.0.8
Published
Dependency injection container for ES6 and beyond
Downloads
30
Maintainers
Readme
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');
// Constructed with an arbitrary container name - used for error messages
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'); // => 'world'
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'); // => 'https://path-to-my.api/photos'
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 // => true
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'); // => 'hello world 1'
container.get('counter'); // => 'hello world 2'
container.get('counter'); // => 'hello world 3'
// ...
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');
// let containerB access all
// values stored in containerA
containerB.extend(containerA);
containerA.constant('greeting', 'Hello world!');
containerB.get('greeting'); // => 'Hello world'
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! :)