manthanhd / injector

Dependency injection framework for JavaScript

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

injector

Dependency injection for JavaScript.

Build Status

Introduction

Working across multiple stacks, I've found dependency injection a blessing in strongly typed languages like Java. This module provides just that. A simple and clean dependency framework for people to use with JavaScript. There are basic two things to injection. The where and the how. Where specifies what function to do the injection in and how specifies how to inject something in. As of now, the library supports two kinds of injections. Injection using a static variable and injection using a factory function. Using a static variable, the framework will inject the value of required variable straightaway. However, when using factory functions, injection will resolve the factory function and its dependencies first, execute the factory function and then inject the return value from that factory function. The framework takes care of this out of the box so you don't have to worry.

Quick start

Using the native mode is the quickest way to get started.

// Require function definitions
var Injector = require("injector").Injector;
var Injectable = require("injector").Injectable;
var InjectorScope = require("injector").InjectorScope;

// create your injector
var myInjector = new Injector();

// bind an injectable variable
myInjector.bind(Injectable.newVariable("name", "Manthan"));

// or bind a factory function
var getDate = function() {
    return new Date();
};
myInjector.bind(Injectable.newFactory("date", getDate));

// enable native injection
Injector.enableNativeInjection(myInjector);

// define your function that you want to use injection on
var printName = function(name) {
    console.log("Hello %s!", name);
};

// execute without any parameters
printName.ix();

Full usage guide

Install and setup

Setup is quick and simple. To install the module in your project, do:

npm install

Once installed, three function definitions will have to be imported.

var Injector = require("injector").Injector;
var Injectable = require("injector").Injectable;
var InjectorScope = require("injector").InjectorScope;

Variables and Scopes

In order to use the injector, you'll have to provide it with some injectables. An injectable is an object that tells the framework how something can be injected. To bind a variable, you'll need to create a injectable first. Injectable defines what it is that you are asking the framework to inject.

var myInjectable = Injectable.newVariable("name", "Manthan");

Once you have an injectable, create the injector instance.

var myInjector = new Injector();

And then bind the injectable to the injector.

myInjector.bind(myInjectable);

You can also bind injectables to a InjectorScope instance which you can then pass into an injector as it's initial scope.

var myScope = new InjectorScope();
myScope.add(myInjectable);
myInjector = new Injector(myScope);

At any given time, an injector has at most two scopes. One is the scope that it has when it is first created. This is the root scope of the injector. The second scope is optional and can be passed in as part of the inject or injectAndExecute functions on the injector itself. This second scope overrides values from the first scope. This means that if secondScope has {name: "Dave"} and the root scope has {name: "Manthan"}, upon injection name will be injected as Dave.

Using injection

Injector will need a function to inject its injectables in. This can be done in two ways. One is to use the normal inject where it will inject the variables and return a prepared function. A prepared function is a function that already has everything injected in and is ready to be executed.

var printName = function(name) {
    console.log("Hello %s!", name);
};

var injectedPrintName = myInjector.inject(printName);
injectedPrintName();

Here injectedPrintName() is a prepared function.

Another way is to let injector execute the function for you. This can be done like so:

myInjector.injectAndExecute(printName);

You can also pass in a child scope while calling inject or injectAndExecute function(s). This for those special cases where something that you're trying to inject isn't constant across all things and you want to override something just this once. Values in root scope are never overwritten. They are simply less prefered when a child scope is provided.

var childScope = new InjectorScope();
childScope.add(new Injectable("name", "Dave"));
myInjector.injectAndExecute(printName, childScope);

Injecting Factories

Sometimes you may want to inject a value that is generated by a calling a function. This can be achieved by creating a factory injectable, binding it to root or current scope and then executing a function that needs the value.

var getDate = function() {
    return new Date();
};
myInjector.bind(Injectable.newFactory("date", getDate));

var printDate = function(date) {
    console.log("Date is %s", date);
};

myInjector.injectAndExecute(printDate);

In this example, getDate is a factory function which we've bound to the rootScope of myInjector. Note that the injectable that we've created is bound to simple date. Further on, this date variable is required by the printDate function. Upon injection, the injector sees that the printDate requires date variable. It also sees that this variable can be obtained by calling the getDate function. Thus, it executes getDate function, retrieves its value and then injects its value in the printDate function.

More often than not, the factory function may require other variables as parameters in order for it to generate its value. As long as these parameters are in the rootScope or current scope, the injection framework will suffice all the dependencies to nth degree. Here's a example.

var getCurrentDate = function() {
    return new Date();
};

var getProfile = function(date) {
    return {
        name: "Manthan",
        generatedOn: date
    };
};

var printProfile = function(profile){
    console.log("Profile for: %s. Generated on %s", profile.name, profile.generatedOn);
};

var injector = new Injector();
injector.bind(Injectable.newFactory("date", getCurrentDate));
injector.bind(Injectable.newFactory("profile", getProfile));
injector.injectAndExecute(printProfile);

We've got two factory functions in the above example. One is the getCurrentDate function that returns the current date and does not require anything in its function parameters. The second factory function is the getProfile function which requires date in its parameters and returns a profile object. Using the injector we've bound the two functions getCurrentDate as date and getProfile as profile variables. Then using the injectAndExecute function, we're executing the printProfile function. Injector will resolve the dependency on profile that this function has by calling the getProfile function but before it can do that it needs to resolve the dependency that getProfile has on date by calling the getCurrentDate function.

API Docs

Injector(initialScope)

Defines a new Injector instance with its root scope being initialScope if provided.

bind(injectable)

Allows binding of an injectable to the rootScope. Throws TypeError when parameter injectable is not of type Injectable. #####Parameter: injectable Required. Must be of type Injectable.

inject(fn, scope)

Returns a prepared function that has it's parameters injected in and is ready to be executed without any parameters.

Parameter: fn

Required. Must be a function.

Parameter: scope

Optional. If provided, must be of type InjectorScope. Throws TypeError otherwise.

injectAndExecute(fn, scope)

Injects all required dependencies and executes provided function. Prefers child scope if provided as scope.

Parameter: fn

Required. Must be a function.

Parameter: scope

Optional. If provided, must be of type InjectorScope. Throws TypeError otherwise.

Injector.enableNativeInjection(injector)

Enables native injection for all functions at once for the specified injector. Adds two methods i() and ix() to all native functions. i(scope) Returns prepared functions with everything injected in and ready to go. Accepts an optional child scope as scope. scope must be of type InjectorScope. ix(scope) Injects required parameters and executes the function. Accepts an optional child scope as scope. scope must be of type InjectorScope.

Parameter: injector

Required. Must be an injector.

InjectorScope()

Defines a new InjectorScope instance.

get(name)

Returns injectable bound to the scope by name name.

Parameter: name

Required. Must be a string.

add(injectable)

Allows binding of an injectable to the scope instance. Throws TypeError when parameter injectable is not of type Injectable. #####Parameter: injectable Required. Must be of type Injectable.

Injectable()

Defines a new injectable instance.

getName()

Returns the name of the injectable. This is used to bind the injectable to a scope.

getValue()

Returns the value of the injectable.

isFactory()

Returns whether or not the injectable is a factory.

isVariable()

Returns whether or not the injectable is a variable.

Injectable.newVariable(name, val)

Returns a new injectable that is a static variable. Use this to create an injectable of type static variable.

Parameter: name

Required. Name of the injectable. This is the name that is used to bind the injectable to a scope (if ever).

Parameter: val

Required. Value of the injectable.

Injectable.newFactory(name, val)

Returns a new injectable that is a factory. Use this to create an injectable of type factory.

Parameter: name

Required. Name of the injectable. This is the name that is used to bind the injectable to a scope (if ever).

Parameter: val

Required. Value of the injectable. Must be a function. Throws TypeError if it isn't.

About

Dependency injection framework for JavaScript

License:MIT License


Languages

Language:JavaScript 100.0%