0.1.2 • Published 8 years ago

@dliv/minject v0.1.2

Weekly downloads
1
License
MIT
Repository
github
Last release
8 years ago

Minject

Minject is a Javascript Dependency Injection library built with an emphasis on constructor injection and ES6 classes. The 'M' is for 'map' and/or 'minimal'. Map because constructor arguments are injected as a single object for easy destructuring. Minimal because it has (a lot) fewer features than other libraries (Angular's module system, BottleJS, etc).

Minject is also tiny: 2.5KB before gzip.

Getting Started

The Minject module can be loaded as:

  • A CommonJS / Node module available as the npm package @dliv/minject
  • A script tag (creates the global variable Minject when dist/index.js is included on the page)

API and Examples

The most comprehensive documentation is the set of spec files in the src folder and the ESDocs associated with each public method.

Minject (constructor)

The constructor does not accept any options. That should probably change.

ES6 module

Install with npm i @dliv/minject. Then:

import Minject from '@dliv/minject';

const services = new Minject();

Minject is a scoped npm module; ostensibly because of the left-pad issue (but really because some Haxe thing already has the name).

script tag

Include dist/index.js on your page. Then:

var services = new window.Minject();

chaining

Methods for adding values / services are chainable.

services
    .value('debugMode', false)
    .eagerService('Water', Water)
    .lazyService('ColdBrew', ColdBrew, { water: 'Water', coffee: 'Coffee', brewTime: 'BrewTime' })
    .service('Coffee', Coffee)
    .service('BrewTime', BrewTime);

const coldBrew = services.get('ColdBrew'); // `services.get(name)` can't return `services` for obvious reasons

Minject#value(serviceName, serviceValue)

services.value('name', value);

Registers a value that is already constructed as an injectible dependency for services when requested by @param name. The @param value will not be created with new, invoked, or mutated - it is passed as is when asked for by a service.

Minject#service(serviceName, serviceConstructor, dependencies)

Registers a service name and constructor pair, with optional dependencies for the constructor. The created service is a cached singleton (it can be retrieved multiple times but only one instance will be created).

class Water {
    constructor () {
        // ...
    }
}


class Coffee {
    constructor ({ water, coffee, time }) {
        // ...
    }

    brew () {
        const { water, coffee, time } = this._services;
        // ...
    }
}


class AngularCoffee {
    constructor ({ water, coffee, time }) {
        // ...
    }
}

AngularCoffee.$inject = { water: 'ColdWater', coffee: 'CoarseGroundCoffee', time: 'OverNight '};
// or `AngularCoffee.$inject = ['water', 'coffee', 'time'];`


class ImplicitCoffee {
    constructor ({ water, coffee, time }) {
        // ...
    }
}

ImplicitCoffee._inject =  { water: 'ColdWater', coffee: 'CoarseGroundCoffee', time: 'OverNight '};
// or `ImplicitCoffee._inject = ['water', 'coffee', 'time'];`

services
    .service('water', Water)
    .service('ColdBrew', Coffee, { water: 'ColdWater', coffee: 'CoarseGroundCoffee', time: 'OverNight '})
    .service('BasicCoffee', Coffee, ['water', 'coffee', 'time'])
    .service('AngularCoffee', AngularCoffee)
    .service('ImplicitCoffee', ImplicitCoffee);

The dependencies param can be an array of registered service names or an object mapping constructor param name to registered dependency name; when using the array syntax, the constructor param name will be inferred (as equal to) the dependency's name. If the dependency param is omitted, the constructor will also be checked for a (static) property named $inject or _inject.

As a side effect, after the constructor is invoked, a _services property is added to the constructed service; this convenience means the constructor does not have to save every service it is passed.

Dependencies are looked up when the service is created; lazy services can specify dependencies that have not been registered yet, while eager services can only use already registered services as dependencies. It is unspecified whether Minject#service is constructed lazily or eagerly. The current default is eager (to surface errors sooner, at the expense of potentially creating services that are never injected).

WARNING: may register a lazy service in a future version.

Minject#eagerService(serviceName, serviceConstructor, dependencies)

Registers a dependency name and value; immediately creates the value. See Minject#service.

class Eager {
    constructor () {
        console.log('Eager service created.');
    }
}

services.eagerService('Eager', Eager);

// immediately logs 'Eager service created.'
// but service may never be injected or retrieved with `Minject#get`

Minject#lazyService(serviceName, serviceConstructor, dependencies)

Registers a dependency name and value; delays creating the value until it is needed. See Minject#service.

class LazyPi {
    constructor () {
        console.log('LazyPi created.');
        this.value = this._calculatePi();
    }
}

class CircleAreaCalculator {
    constructor ({ pi }) {
        console.log('CircleAreaCalculator created.');
        // ...
    }
}

services.lazyService('pi', LazyPi);

// <nothing logged>

services.eagerService('Calculator', CircleAreaCalculator, ['pi']);

// logs 'LazyPi created.'
// then 'CircleAreaCalculator created.'

Minject#get(serviceName)

Returns the specified service; will create (with dependencies) and cache the service if needed. If you can organize your app as a single (eager) service, you don't need this method. Overuse of this method will lead to the Service Locator pattern and is discouraged.

WARNING: may be renamed, removed, or limited in a future version.

// detect-env.js
services.value('$window', typeof window === 'undefined' ? mockWindow : window);


// controller1.js
const $window = services.get('$window');

Status

Very beta. Besides the limited functionality, lots of error checking is missing (e.g. checking for circular dependencies).