npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

get-in-di

v1.2.4

Published

JS Dependency Injection

Downloads

35

Readme

Get In DI

A DI container for node (>= 10).

There are three concepts: definitions, references, and containers.

Definitions wrap values and provides methods for initialising classes, and creating factories, and singletons, References allow definitions to be shared, and a container holds it together.

Install

$ npm i get-in-di

Or

$ yarn add get-in-di

A contrived example

// Create a container
const container = new Container();

// We want to store some config in our container.
container.add('db_user', process.env.DB_USER);
container.add('db_pass', process.env.DB_PASS);

// set up a db connection
container.add('connection', new Definition(require('db-lib')))
    // construct our connection
    .construct([
        new ReferenceObject({
            url: 'url_to_db_1',
            // use References to configure the constructor args
            user: new Reference('db_user'),
            password: new Reference('db_pass'),
        })
    ])
    // our db lib doesn't use promises so lets sort that out.
    .decorate((connection) => {
        const { promisify } = require('util');
        connection.query = promisify(connection.query);
    });

// set up an app with the connection
container.add('app', new Def(App))
    .construct([new Reference('connection')]);

// Get the app... this will create our connection before
// injecting it into our app.
container.get('app');

Recipes

add and get a value

container.add('foo', new Definition('bar'));
container.get('foo') // => 'bar'

Call a function

container.add('cache', new Definition(redis.create))
    .call([{
        host: '127.0.0.1',
        port: '6379'
    }]);

.call tells the definition to call the value using an optional array param as the argument list for the definition call.

Constructing a class

Here's the class were going to use:

class Counter {
    constructor(count = 0) {
        this.count = count;
        this.incrementAmount = 1;
    }
    increment() {
        this.count += this.incrementAmount;
    }
}

It's easy to add a definition for the class...

container.add('counter', new Definition(Counter));
container.get('counter') // => [Function: Counter]

...but if we call get now the container will just return our class. We need tell the definition to create a new instance.

container.add returns the value that was added. So here we can user the Definition method construct to make the definition resolve its value to an instance of Counter:

container.add('counter', new Definition(Counter))
    .construct();

container.get('counter') // => Counter { count: 0, incrementAmount: 1 }

This is great! But what if we want to pass some values to the constructor? We can do this by passing a array of arguments to definition.construct. This can be a value, or a Reference. (In fact the argument array can be replaced by a Reference).

container.add('counter', new Definition(Counter))
    .construct([new Reference('initialCount')]);

container.add('initialCount', 100);

container.get('counter') // => Counter { count: 100, incrementAmount: 1 }

Note: We can define initialCount after counter because the counter isn't initialised until container.get('counter') is called.

We can also call methods and set properties on a Definition value:

container.add('counter', new Definition(Counter))
    .construct([new Reference('initialCount')])
    .callMethod('increment')
    .setProp('incrementAmount', 10)
    .callMethod('increment')

container.add('initialCount', 100);

container.get('counter') // => Counter { count: 111, incrementAmount: 10 }

construct, callMethod, setProp, and decorate (which we haven't covered here) are called in order and can be called on any definition value; so dont call construct on a number definition!

Singleton vs Factory

We can set isShared to control how the behaviour of definitions.

In this example Math.random will be called once the first time shared_random is accessed. The the random value is then stored for each subsequent get:

container.add('shared_random', new Definition(Math.random))
    .call(); // we need this to tell the Definition to run Math.random instead of return it

container.get('shared_random') // => 0.6240852289721388;
container.get('shared_random') // => 0.6240852289721388;

By setting isShared to false we're telling the definition to call Math.random each time random_factory is accessed.

container.add('random_factory', new Definition(Math.random, { isShared: false }))
    .call();

container.get('random_factory'); // => 0.5616454027240336
container.get('random_factory'); // => 0.48624780071259344

Using Objects

Calling functions with argument lists is all well and good, but a lot of the time functions and classes require and object for one or more parameters. To inject values into these we need to use a ReferenceObject.

A ReferenceObject takes an object where some of it's value are instance of Reference. When it is resolved it returns a new object with the references resolved and the remaining properties shallowly copied across.

// add some values to our container
container.add('user', 'root');
container.add('password', 'lolcatz');
container.add('logFile', new Definition(LogFile))
    .construct('./log.log');


container.add('db', new Definition(DB))
    .construct([
        new ReferenceObject({
            user: new Reference('user'),
            password: new Reference('password'),
            // References in arrays will also be resolved
            logFiles: [
                new Reference('logFile'),
            ]
        })
    ]);

container.get('db') // => DB { user: 'root', password: 'lolcatz', logFiles: [LogFile] }

You can also use Definition.resolveProps to resolve all properties any value. This is more useful if you're not dealing with an object for some reason...

container.add('string', 'hello');
container.add('number', 123);

container.add('thing', new Definition({
    foo: new Reference('string'),
    bar: new Reference('number'),
}))
    .resolveProps();

container.get('thing') // => { foo: "hello", bar: 123 }

Definition.resolveProps can be chained like any other definition action.

Shorthand class names

We export short hand class names for less typing:

| Class | Shorthand | |-------|-----------| | Definition | Def | | Reference | Ref | | ReferenceObject | RefObj |