codependent

3.0.8 • Public • Published

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! :)

Package Sidebar

Install

npm i codependent

Weekly Downloads

3

Version

3.0.8

License

ISC

Last publish

Collaborators

  • adrianhelvik