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

exdi

v1.5.0

Published

Dependency injection container

Downloads

22

Readme

exdi Build Status

Installation

npm install exdi

After that, include exdi in Your file:

var exdi = require('exdi');

Usage

Exdi is a dependency injection container. But whole library is practically a repository of containers. There are two ways to create container:

Named container:

var container = exdi.get('myContainer');

That will create new container and register it under "myContainer" name. So each time you ask for myContainer, same instance will be returned.

Or you can just spawn anonymous container using:

var container = exdi.create();

This will create unregisted container so there is no way to get it again from inside Exdi.

Values and constructors

Basic feature is to set and create container values. Using our "container" variable, we an do this like that:

container.set('name', 'Will'); // this will set "name" variable with "Will" value.
container.set('surname', 'Smith'); // this will set "surname"

container.get('surname'); // this will return "Smith"

Now, it would be not much of dependency injection container if we would not be able to build things using dependencies.

container.set('FullName', function (name, surname) {
    return name + ' ' + surname;
});

Constrution above will register a constructor. Constructors are executed each time you want to retreive them using .get() method. So this code:

container.get('FullName');

will return "Will Smith". There are few things to remember:

  • First capital letter is reserverd for constructors. It's very important. If key have capital letter as first sign, you can only set a function as value. This way that function will be executed each time you want to retreive it.
  • If you would use small letter and call it "fullName", code above would return registered function instead of result of that function.
  • Look how "name" and "surname" is handled. Exdi will extract parameters names and match them with container values. So there is no need for You to provide them. Just remember to have them in container before using constructor.
  • You can also pass other constructors as parameter. Like this:
container.set('age', '27');
container.set('AgeAndFullName', function (age, FullName) {
    return FullName + ', age ' + age;
})

container.get('AgeAndFullName'); // Will Smith, age 27

REMEMBER

If you register function like this:

container.set('FullName', function (name, surname) {
    return name + ' ' + surname;
});

container.get('FullName') will return result of a function since first capital letter means Constructor. This follow convention that most JS progremmers use. So you will get "Will Smith". If you register function like this:

container.set('fullName', function (name, surname) {
    return name + ' ' + surname;
});

container.get('FullName') will return whole function instead of executing it.

Executing any function

Sometimes you will want just execute one of Your functions without registering it in container. You can do it like this:

function add(x, y) {
    return x + y;
}

container.set('x', 1);
container.set('y', 2);

container.execute(add); // this will return 3

And sometimes, you will want to overwrite some container values with Yours without changing them in container:

function add(x, y) {
    return x + y;
}

container.set('x', 1);
container.set('y', 2);

container.execute(add, {
    y: 5
}); // this will return 6. x is intact, y is replaced by 5 but only for execution of a function. Container value is intact.

Also there is a problem of methods. Sometimes you have methods, functions that are part of bigger object and you want to execute them BUT you don't want to loose object context. In other words, you don't want "this" pointing to something else than that object. It's also possible like this:

var myObj = {
    this.add = function add(x, y) {
        return x + y + this.z;
    },
    z: 4
}

container.set('x', 1);
container.set('y', 2);

container.execute(add, {}, myObj); // this will return 7

As you see, just provide object as third parameter and it will be used as context of execution. If context object is not provided, "this" will point to container object. So inside your function you can for example call

function add(x, y) {
    this.get('name'); // Will
    return x + y;
}

Also, sometimes you want to execute function that is already registered. You can do that using string as first parameter for execute. Exdi will try to find value inside container using that name. Just remember, it must be a function.

function add(x, y) {
    this.get('name'); // Will
    return x + y;
}

container.set('x', 1);
container.set('y', 2);
container.set('add', add);

container.execute('add');

Creating objects

There is also alternative to .execute() that will execute given function with operator "new".

var constructContainer = exdi.create();
    constructContainer.set('px', 1);
    constructContainer.set('py', 2);

var c1 = constructContainer.construct(function (px, py) {
    this.x = px * 2;
    this.y = py * 2;
}, { py: 5 });

    c1.x // 2
    c1.y // 10

Queue nad Parallel

Sometimes, you need to take control over code execution. It's hard to do since most of the time You will be dealing with async functions. For that, Exdi have tools like Queue and Parallel. Both can be created using methods with the same names:

var queue = container.createQueue();

OR

var parallel = container.createParallel();

Main difference between queue and paraller is simple. Queue will execute one function at that time in chain. So second function in queue will be executed ONLY if first one is finished. Library will report when each step is done and will also report using events that all steps are finished.

Parallel works in simmilar way but all functions will be called at the same time. It's like a "promise" pattern.

To add function to queue or parallel,, use .add() method:

function soSomethingCool(x, y, exdiDone) {
    this.get('name'); // Will
    // here some async code
    return x + y;
    exdiDone();
}

queue.add(soSomethingCool);
queue.add(soSomethingCool); // again
// or
parallel.add(soSomethingCool);
parallel.add(soSomethingCool); // again if you want

to run either of them, you can use:

queue.execute(); // will start queue
parallel.execute(); //will start parallel

Functions passed to queue and parallel are quite different. Notice custom aprameter "exdiDone". It's a function that You should call when it's finished. Remember we are dealing with async functions. So when you do what You have to do, call:

exdiDone();

or next function will not be called in Queue and Parallel will never finish. You can register events for both libraries:

queue.on('timeout', function () { /* too long */ }); // called after X seconds

Timeout is fired IF code execution takes too long. You can change default value (0) using queue.setTimeoutLimit(5);

Same way, you can register events for parallel. There are two events available:

  • step
  • done

To cancel execution of both tools, use:

queue.clearQueue(); // clear queue, function currently in execution will finish but next one will not be fired
parallel.clearTasks(); // function can finish but step and done event wont called

Minified code

Sometimes, programmers use tools that minify JavaScript code making it unreadable but short and small. This will change variable names in Your code. So for example this:

container.set('fullName', function (name, surname) {
    return name + ' ' + surname;
});

will look like this after minification:

container.set('fullName', function (a, b) {
    return a + ' ' + b;
});

As You probably noticed, this will not work anymore. There is no 'a' and 'b' in our container. There is a way to fix that.

Just pass array first list of keys and function as last argument. This way, container will match function arguments based on order and values of array keys instead of function arguments. Just like that:

container.set('fullName', ['name', 'surname', function (a, b) {
    return a + ' ' + b;
}]);

IMPORTANT Last argument CAN be a string. Just remember that it will be used to extract function from container by given name. Like this:

container.set('concatStrings', function (a, b) {
   return a + ' ' + b;
};
container.set('fullName', ['name', 'surname', 'concatStrings']);

Now Your cod work again.

REMEMBER

  • always use small first letter when registering a value
  • when you set value with first capital letter, you need to pass function and when You try to retrive that key, function will be executed and result of that function will be returned
  • if you register function with small name, code of that function will be returned
  • queue will run one function at a time but parallel will start them all at once
  • you can pass array with keys and function as last argument to avoid problems with minification
  • you can register containers in exdi using .get('name') or create anonymous ones using .create()