exdi
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');
container.set('surname', 'Smith');
container.get('surname');
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');
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);
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
});
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);
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');
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
c1.y
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');
return x + y;
exdiDone();
}
queue.add(soSomethingCool);
queue.add(soSomethingCool);
parallel.add(soSomethingCool);
parallel.add(soSomethingCool);
to run either of them, you can use:
queue.execute();
parallel.execute();
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 () { });
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:
To cancel execution of both tools, use:
queue.clearQueue();
parallel.clearTasks();
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()