kuul-ioc v1.0.1
Kuul-ioc Node.js and browser module for asynchronous promise based inversion of control
Motivation
As Node.js developer I really missed some good asynchronous inversion of control module for Node.js, there are some but I was not fully satisfied with them. Also using native Node.js require is not always best idea, definitely not for bigger project. Good IoC is foundation stone for every Javascript application, so here you have one :)
Features
- Resolve your dependencies asynchronously with promises
- Module logic can be wrapped into promise executor, generator function or async function
- Resolve singleton instance of your module
- You can extend ( add or replace ) dependencies when you resolving module
- Simple mocking modules for tests and replacement
- It's perfect to use if you like promises, ES6 generators or ES7 async / await features
- You don't have to worry about
module.exportorexportkeyword anymore,kuul-iocwill handle that for you - Small and very powerful library
Installation
$ npm install kuul-iocExample folder
You can see example usage for sample koa server
Simple usage example
Module definition
file core/simple.js
var ioc = require('kuul-ioc')
ioc.createModule(module)
.dependencyValue('sentence', 'Anything can be there')
.dependencyValue('object', {'greeting' : 'Hello world !'})
.module(function (dep, resolve, reject) {
console.log(dep.sentence)
/* 'Anything can be there' */
console.log(dep.object.greeting)
/* 'Hello world !' */
/* some async work */
setTimeout(function () {
resolve(dep.object.greeting)
}, 1000)
})Module resolve
file index.js
var ioc = require('kuul-ioc')
var appContainer = ioc.get('app').setBasePath(__dirname)
appContainer.module('core/simple').get()
.then(function(simple) {
console.log(simple)
/* 'Hello world !' */
})Advanced usage example
Module definition
file core/advanced.js
var ioc = require('kuul-ioc')
var someContainer = ioc.createContainer().setBasePath(__dirname)
ioc.createModule(module)
.dependency('simple', 'core/simple')
.dependency('simpleRelative', './simple')
.dependency('moduleResolver', someContainer.module('config') )
.dependency('moduleOnTheFly',
ioc.createModule()
.dependencyValue('sentence', 'Anything can be there')
.module(function(dep, resolve, reject) {
resolve(dep.sentence)
})
)
.dependencyValue('sentence', 'Anything can be there')
.dependencyFunction('fcePromiseExecutor', function(resolve, reject) {
resolve('This is like factory')
})
.dependencyFunctionOnce('fceGeneratorOnce', function* () {
yield 'This is like singleton'
})
.module(function (dep, resolve, reject) {
console.log(dep.sentence)
/* 'Anything can be there' */
console.log(dep.object.greeting)
/* 'Hello world !' */
/* some async work */
setTimeout(function () {
resolve(dep.object.greeting)
}, 1000)
})Module resolve
file index.js
var ioc = require('kuul-ioc')
var appContainer = ioc.get('app').setBasePath(__dirname)
appContainer.module('core/advanced').get()
.then(function(simple) {
console.log(simple)
/* 'Hello world !' */
})Table of contents
Module definition - file
Module definition - file with Promise executor
file hello-world.js
var ioc = require('ioc')
ioc
.createModule(module)
.module(function (dep, resolve, reject) {
setTimeout(function() {
resolve('Hello world')
}, 1000)
})Module definition - file with ES6 generator function
file hello-world.js
var ioc = require('ioc')
ioc
.createModule(module)
.module(function* (dep) {
yield new Promise(function (resolve, reject) {
setTimeout(resolve, 1000)
})
return 'Hello world'
})Module definition - file with ES7 async function
file hello-world.js
var ioc = require('ioc')
ioc
.createModule(module)
.module(async function (dep) {
await new Promise(function (resolve, reject) {
setTimeout(resolve, 1000)
})
return 'Hello world'
})Module definition - on the fly
var ioc = require('ioc')
var container = ioc.createContainer().setBasePath('/')
/*
Now you dont' pass module variable into createModule function
ioc.createModule(module)
but you just left it empty
ioc.createModule()
*/
var onTheFlyModule = ioc
.createModule()
.module(function* (dep) {
yield new Promise(function (resolve, reject) {
setTimeout(resolve, 1000)
})
return 'Hello world'
})
container.module('not/real/path').set(onTheFlyModule)Module resolve
ioc.get('containerName').module('someModule').get()
get()if module is singleton it will be resolved only first time, next time you call get() on singleton module it will return cached result, it will be exact same result as first time, see Module resolve - singleton
ioc.get('containerName').module('someModule').resolve()
resolve()Does not care if module is singleton, it will always return a new instance. Basically it always run your module function and fetch the result, see Module resolve - factory
This will be our file structure
file core/config.js
ioc
.createModule(module)
.module(async function (dep) {
return 'config'
})file core/factory.js
ioc
.createModule(module)
.setSingleton(false)
.module(async function (dep) {
return 'factory'
})Module resolve - Singeton
Module is singeton by default, you don't have to use setSingleton(true),
filecore/config.js
var ioc = require('kuul-ioc')
ioc.get('nameForContainer').setBasePath(__dirname)
var moduleResolver = ioc.get('nameForContainer').module('core/config')
moduleResolver.get().then(function(configA) {
moduleResolver.get().then(function(configB) {
moduleResolver.resolve().then(function(configC) {
console.log( configA === configB )
/* returns true */
console.log( configA === configC )
/* returns false
resolve() will ignore module singeton boolean, it will always run module executor function and fetch result
*/
})
})
})Module resolve - Factory
In this example
comodule is used to transform generator function to async control flow, you can read more about it here https://www.npmjs.com/package/co
kuul-iocusecomodule internally to resolve your generator function, so you can use all it's features
comodule internally use alsokoa
var ioc = require('kuul-ioc')
var co = require('co')
ioc.get('nameForContainer').setBasePath(__dirname)
var moduleResolver = ioc.get('nameForContainer').module('core/factory')
co(function* () {
var factoryA = yield moduleResolver.get()
var factoryB = yield moduleResolver.get()
var factoryC = yield moduleResolver.get()
/* this would be same as above, because core/factory.js have
setSingleton(false)
var factoryA = yield moduleResolver.resolve()
var factoryB = yield moduleResolver.resolve()
var factoryC = yield moduleResolver.resolve()
*/
console.log( configA === configB )
/* returns false */
console.log( configA === configC )
/* returns false */
})Module resolve - ES7 async/await
To run this example, you have to use some compiler, ex. babel
file .babelrc
{
"presets": [ "es2015-node5", "stage-3" ]
}var ioc = require('kuul-ioc')
var co = require('co')
ioc.get('nameForContainer').setBasePath(__dirname)
var moduleResolver = ioc.get('nameForContainer').module('core/factory')
(async function() {
var factory = await moduleResolver.get()
})()Api
Class: Ioc
ioc.get(name)
nameStringName of container
return Container instance
Create new or return existing instance of
Container
ioc.createContainer()
return Container instance
Create new instance of
Container
ioc.createModule(module)
moduleNode.jsmodulekeyword, used tomodule.exportsorexportsObjects from native Node.js module system
return Module instance
Create new instance of
Module
Parameter module is optional because you can create Module instance on fly so kuulioc will not internally call require function to get Module instance. So basically you don't provide any parameter when creating Module instance on fly.
Class: Container
container.setBasePath(basePath)
basePathStringBase path of container
return Container instance
Set base path of container
container.module(modulePath)
modulePathStringPath to requested module
return ModuleResolver instance
Create
ModuleResolverinstance for module specified bymodulePath
container.getReplacements()
return replacements Object
Get
Containerreplacements Object
container.setReplacements(mixed)
mixedContainer|Object
return ModuleResolver instance
Set replacements object. Replacements object is used with all
ModuleResolved.set*functions, also used when resolving replaced / mocked modules
Class: Module
module.setSingleton(boolean)
booleanBoolean
return Module instance
It specify if module should be resolved as singleton or new instance every time you resolve it
module.dependency(name, mixed)
nameStringName of dependency when it will be resolved and put in module functionmixedString|ModuleResolver|Module
return Module instance
Add dependency to your module
module.dependencyValue(name, mixed)
nameStringName of dependency when it will be resolved and put in module functionmixedMixedAnything
return Module instance
Add dependency to your module
module.dependencyFunction(name, mixed)
nameStringName of dependency when it will be resolved and put in module functionmixedFunction|GeneratorFunction|AsyncFunction
return Module instance
Add dependency to your module
module.dependencyFunctionOnce(name, mixed)
nameStringName of dependency when it will be resolved and put in module functionmixedFunction|GeneratorFunction|AsyncFunction
return Module instance
Add dependency to your module
module.module(mixed)
mixedFunction|GeneratorFunction|AsyncFunction
return Module instance
Function that handle logic of module or in other words function that return content of module
Class: ModuleResolver
moduleResolver.get()
return Promise instance
Asynchronously resolve module, if module is singleton it will be resolved only first time, next time you call get() on singleton module it will return the same result as first time
moduleResolver.resolve()
return Promise instance
Asynchronously resolve module, it does not care if module is singleton, it will always return a new instance. Basically it always run your module function and fetch the result
moduleResolver.extend(name, mixed)
nameStringName of dependency that will be added or replacedmixedString|ModuleResolver|Module
return ModuleResolver instance
Add or replace dependency to your module
moduleResolver.extendValue(name, mixed)
nameStringName of dependency that will be added or replacedmixedMixedAnything
return ModuleResolver instance
Add or replace dependency to your module
moduleResolver.extendFunction(name, mixed)
nameStringName of dependency that will be added or replacedmixedFunction|GeneratorFunction|AsyncFunction
return ModuleResolver instance
Add or replace dependency to your module
moduleResolver.extendFunctionOnce(name, mixed)
nameStringName of dependency that will be added or replacedmixedFunction|GeneratorFunction|AsyncFunction
return ModuleResolver instance
Add or replace dependency to your module
moduleResolver.set(mixed)
mixedString|ModuleResolver|Module
return ModuleResolver instance
It replace your module
moduleResolver.setValue(mixed)
mixedMixedAnything
return ModuleResolver instance
It replace your module
moduleResolver.setFunction(mixed)
mixedFunction|GeneratorFunction|AsyncFunction
return ModuleResolver instance
It replace your module
moduleResolver.setFunctionOnce(mixed)
mixedFunction|GeneratorFunction|AsyncFunction
return ModuleResolver instance
It replace your module
