di-cast v0.1.0
DiCast
A JavaScript dependency injector for Node.js and the Browser.
Set up.
DiCast can be used in AMD, Browser and CommonJS environments.
AMD
require(['path/to/di-cast'], function(DiCast) {
var injector = new DiCast();
});
// or
define('Module', ['path/to/di-cast'], function(DiCast) {
var injector = new DiCast();
});
Browser
<script src="path/to/di-cast"></script>
<script>
var injector = new DiCast();
</script>
CommonJS
var Injector = require('path/to/injector.js).DiCast;
var injector = new DiCast();
Basic mappings.
Mappings are created by calling the Injector's map
method.
A mapping is defines the properties the injector uses to describe an injection. The injector stores each mapping against a key.
At the very least a mapping must have a target
property which is the value to be injected when the mapping is requested.
// Create a basic mapping
injector.map('greeting').toValue({
target: 'Hello World'
});
Mappings can be retrieved using the get
method.
// Retrieve the mapping
var greeting = injector.get('greeting');
console.log(greeting);
// 'Hello World'
Mappings can also be retrieved by defining them as dependencies of other mappings.
injector.map('MyType').toType({
target: function(greeting) {
this.sayHello = function() {
return greeting;
};
},
using: ['greeting']
});
There are three different kinds of mapping available.
toValue mappings.
Returns the target
value when requested. The target
can be any type value.
The injector will return the value unchanged unless the target
is an Object with public properties that are marked as injectable using the '{I}'
token. In this case, the injector will attempt to resolve the property as a dependency using the property name as the mapping key.
injector.map('MyValue').toValue({
target: {
greeting: '{I}',
greet: function() {
console.log(this.greeting);
}
},
api: [{name: 'greet'}]
});
Config options.
target The value to be returned. It can be anything. Required.
api An Array describing the value interface.
toType mappings.
Returns a new instance from the `target' constructor function when requested.
injector.map('MyType').toType({
target: function MyType(MyDependency) {
this.MyDependency = MyDependency;
},
api: [{name: 'getProperty'}],
isSingleton: true,
using: ['MyDependency']
});
Config options.
target The factory constructor function. Required.
api An array describing the factory instance interface.
isSingleton An optional Boolean flag which, when set, will always return the same instance.
using An Array of mapping keys to inject into the factory as constructor arguments when it is instantiated. Dependencies will be resolved and injected lazily.
toFactory mappings.
Similar to toType
mappings but it returns the result of invoking the target
factory function when requested.
If a toFactory
target function does not return a value an InjectonError
will be thrown.
injector.map('MyFactory').toFactory({
target: function MyFactory(MyDependency) {
return {
myDependency: MyDependency
};
},
api: [{name: 'myDependency'}],
isSingleton: true,
using: ['MyDependency']
});
Config options.
target The factory function. Required.
api An Array describing the factory instance interface.
isSingleton An optional Boolean flag which, when set, will always return the same result.
using An Array of mapping keys to inject into the factory.
Singleton mappings.
toFactory
and toType
mappings can be defined as singletons in the config options to ensure that the same value is always returned.
injector.map('singleton').toType({
target: function() {},
isSingleton: true
});
injector.get('singleton') === injector.get('singleton');
// true
Type checking mappings.
To ensure the correct interface is set on a mapping an optional api
property can be specified in the config options.
The api
property expects an Array of definition objects. Each definition must have a name
property which is the name of a property required on the target
. Each definition can also have an optional arity
property which sets the number of arguments a required function should have.
var IMyInterface = [
{name: 'myMethod', arity: 1},
{name: 'myProperty'}
];
// A valid mapping
injector.map('typed').toType({
target: function() {
this.myMethod = function(myArg) {};
this.myProperty = 'myProperty';
},
api: IMyInterface
});
injector.get('typed');
// An invalid mapping
injector.map('notTyped').toType({
target: function() {},
api: IMyInterface
});
injector.get('notTyped');
// Throws an InjectionError
Nested injection.
DiCast instances can take an optional parent injector constructor argument. If an injector with a parent cannot resolve a dependency in it's local scope it will look in the parental scope before throwing an error.
Dependencies with the same name as a dependency in the parental scope will overwrite in the local scope but will not affect the parent scope.
Injection errors.
DiCast has a custom error type with the following properties:
stack The stack trace for the error.
message Basic error information.
info A context specific description of the error.
Grunt tasks.
> grunt doc
Generate API YUIDocs in bin/docs
.
> grunt test
Run Jasmine tests and generate a coverage report in bin/coverage
.
> grunt plato
Generate a code complexity report in bin/complexity
.
> grunt dist
Generate a minified file in dist
10 years ago