3.0.25 • Published 9 years ago

simple-ioc v3.0.25

Weekly downloads
15
License
BSD
Repository
github
Last release
9 years ago

simple-ioc

Simple-ioc is a module for simple inversion of control for node.js. Main features are:

  • Easy dependency injection without special syntax - in most cases modules can be used without simple-ioc.
  • Easy exchangeability of components - settings can determine which components that should be used in different environments.
  • Automatic asynchronous resolving - components are only resolved when all dependencies are resolved

Installation

Simple-ioc is installed from npm.

npm install simple-ioc

Basic usage and simple example

The following is a simple example of how to use simple-ioc

Example of usage

// ./lib/store.js
module.exports = function( databaseAdapter, callback ) {
	databaseAdapter.connect( function( err, connection ) {
		if( err ) {
        	console.log( err );
        	process.exit( 1 ); // Application cannot start!
        }
		else {
        	var pub = {};
			pub.getData = function( callback ) {
				connection.query( callback );
			};
            callback( pub );
		}
	} );
};
// ./lib/module1.js
module.exports = function( pub, store ) {
	pub.printData = function() {
    	store.getData( function( err, data ) {
    	} );
    };
};
// ./index.js
module.exports = require( 'simple-ioc' )
	.getContainer()
    .registerResolved( 'databaseAdapter', require( 'some-database-adapter' )
    .autoRegisterPath( './lib' )
    .inject( function( module1 ) {
    	module1.printdata();
    } );

Additionally every container has the following pre-registered components, which makes them virtually reserved as well:

The use of pub is optional. It might be handy but it complicates using the module without the ioc, so use it only when you feel comfortable with simple-ioc.

Example:

module.exports = function() {
	var pub = {};
    pub.func = function() {};
	return pub;
};

is equivalent to:

module.exports = function( pub ) {
	pub.func = function() {};
};

If using pub the module is more complex to resolve without the ioc. It can still be done by doing something like this:

var resolved = {};
myModule( resolved );

Example:

var container = require( 'simple-ioc' )
	.getContainer()
    .registerInjectable( {
    	transientModule: function( parentName ) {
        	return function() {
            	console.log( parentName );
            };
        },
        singletonModule1: function( transientModule ) {
        	return transientModule;
        },
        singletonModule2: function( transientModule ) {
        	return transientModule;
        }
    } )
    .inject( function( singletonModule1, singletonModule2 ) {
    	singletonModule1(); // Will output "singletonModule1"
    	singletonModule2(); // Will output "singletonModule2"
    } );

Example:

var container = require( 'simple-ioc' )
	.getContainer()
    .registerInjectable( {
    	asyncModule: function( callback ) {
        	var pub = {};
			someAsyncSetup( function( err, something ) {
            	pub.func = function() {
                	something();
                };
                callback( pub );
            } );
		}
    } )
    .inject( function( asyncModule ) {
    	asyncModule.fun(); // asyncModule is ready to be used
    } );

Note that this this is not implemented yet, but might be a good idea to use.

Example:

var container = require( 'simple-ioc' )
	.getContainer()
    .registerInjectable( {
    	module1: function( setup, callback ) {
        	var pub = {},
            	something;
            pub.func = function() {
                something();
            };
            setup( function() {
				someAsyncSetup( function( err, _something ) {
                	something = _something;
    	            callback( pub );
        	    } );
			} );
		}
    } );

Documentation

ioc

container

log

errRerouter

The main function of the ioc is to create containers, but it also has a built-in log and settings.


Arguments

None.

Returns

A new container.

Remarks

The ioc, the container itself and errRerouter is registered automaticaly to the new container.

Example

var container = require( 'simple-ioc' ).getContainer()

Arguments

Variable amount of objects with settings, the settings will be merged with the existing settings.

Returns

The ioc.

Remarks

The ioc has 5 built-in output-writers, these are

  • consoleJson - More or less console.log( JSON.stringify( logObject ) )
  • bunyanJson - Same idea of consoleJson, but you can use bunyan command to read pretty printed errors as an human.
  • consoleReadable - Logs in a readable format, with some coloring of level
  • devNull - writes nothing
  • memoryJson - writes to memory, which is searchable afterwards. This should never be used in production, only in tests.

The default settings are:

{
	log: {
		level: 0,
		includeEnvironmentVariables: { env: 'NODE_ENV' },
		output: 'devNull'
	}
}

Example

require( 'simple-ioc' ).setSettings(
	{
		log: {
			level: 3
		}
    },
	{
		log: {
			output: 'consoleReadable'
		}
	}
);
/*
will result in the following settings
{
	log: {
		level: 3,
		includeEnvironmentVariables: { env: 'NODE_ENV' },
		output: 'consoleReadable'
	}
}
*/

Bunyan Example

node-bunyan is a medium-complex logging solution, integration in simple-ioc is really minimal, and completely optional. To use the bunyan writer you have to add bunyan to your project dependencies.

require( 'simple-ioc' ).setSettings(
    {
        log: {
            output: 'bunyanJson'
        }
    }
);

Then you can pipe the output of your program to bunyan command for pretty-print.


Arguments

None.

Returns

The registered settings

Remarks

Normally this function is not used, instead settings are injected, but might be useful for debugging.

Example

require( 'simple-ioc' ).setSettings( {
	key: 'value'
} ).getSettings();
/* Will return
{
	log: {
		level: 0,
		includeEnvironmentVariables: { env: 'NODE_ENV' },
		output: 'devNull'
	},
    key: 'value'
}
*/

  • output( logObject ) - called on every log that is on a level that should be logged.
  • getEntries( component ) - can be implemented, but should not be used in production.
  • reset() - to reset the log entries.

Arguments

  • resolvedWriter an object that implements at least output( logObject )

Returns

The ioc.

Remarks

None.

Example

require( 'simple-ioc' ).useLogWriter( {
	output: function( logObject ) {
		console.log( logObject.level );
	}
} );
// Will only ouptut the level of the log (numeric) to the console.

container

Containers are the central part of the ioc, it stores and resolves components.

Note: All functions on the container returns the container itself.

Arguments

  • name - the identifying name of the component
  • instance - the resolved instance

OR

  • name - An object with key/value pairs reprecenting names/instances

Returns

The container

Remarks

Packages that have a simple name (without special characters, such as "-") and are possible to require within the current scope, does not need to be registered. For example, a module can have a dependency to "http" without it being registered. If a dependency exists to a component that is not registered, the container will try to require the name of the dependency and register it as a singleton if successful.

Example

var container = require( 'simple-ioc' ).getContainer()
	.registerResolved( async: require( 'async' ) ) // Registers async
	.registerResolved( { // Registers express and request
		express: require( 'express' ),
		request: require( 'request' )
	} )
    .inject( function( express, http, request, async ) {
    	// Will succeed since the container will register http automatically.
    } );

Arguments

  • name - the identifying name of the component
  • fn - the injectable function

OR

  • name - An object with key/value pairs reprecenting names/instances

Returns

The container.

Remarks

Injectable functions has some reserved parameternames used by the ioc, these cannot be registered or used as normal dependencies. See Reserved dependencies for more information.

Example

var container = require( 'simple-ioc' ).getContainer()
	.registerInjectable( 'myComponent', function( pub, callback ) {
		pub.func1 = function( params ) {
			return whatEver;
		};
		doSomething( function() {
			callback();
		} )
	} ) // Registers a singleton component that has an async setup and a function func1 as myComponent
	.registerInjectable( {
		anotherComponent: function( setup, pub, callback ) {
			setup( function() {
				someSetup( function() {
					pub.xxx = function() {
						return amazingStuff;
					};
					callback();
				} );
			} );
			// Registers a singleton component that has a setup that also is async. The result is a
			// component registered as anotherComponent with a function xxx.
		},
		yetAnother: function( parentName ) {
			return {
				func3: function() {
					return parentName;
				}
			};
			// Registeres a transient component as yetAnother with a function func3. Everytime yetAnother
			// is injected the registered function will be called, creating a new enclosed scope.
		}
	} );

Arguments

  • name name of the component to mock
  • properties the properties to mock with functions, setting the default value of the mocked function

or

  • name an object with key/value pairs reprecenting names/properties

Returns

The container

Remarks

Mocking of modules by using "mock" might not be totaly straight forward and cannot be used in every mocking situation. See the example how a possible way to use it and how to achieve the same result without using mock. The values of mocked functions can be changed by setting the properties later.

Mock checks when a function is invoked, if last parameter is a function it will treat is as an async function.

Example

require( 'simple-ioc' )
	.getContainer()
    .mock( {
    	module1: {
        	sync: 'syncVal',
            async: 'asyncVal'
        }
    } )
    .registerResolved( { // module2 is mocked without using mock
    	module2: {
        	sync: function() {
            	return 'syncVal';
            },
            async: function( param1, callback ) {
            	callback( undefined, 'asyncVal' ); 
            }
        }
    } )
    .inject( function( assert, module1, module2 ) {
		// Sync
		assert.equal( module1.sync(), 'syncVal' );
    	assert.equal( module2.sync(), 'syncVal' );
        // Async
    	module1.async( 'test', function( err, value ) {
        	assert.ok( !err );
			assert.equal( value, 'asyncVal' );
		} );
    	module2.async( 'test', function( err, value ) {
        	assert.ok( !err );
			assert.equal( value, 'asyncVal' );
		} );
        // Changing sync
        module1.sync = 'newSyncVal'; // Changing what sync will return when invoked
        module2.sync = function() { // ... same without mock
        	return 'newSyncVal';
        };
    	assert.equal( module1.sync(), 'newSyncVal' );
    	assert.equal( module2.sync(), 'newSyncVal' );
        // Changing async
        module1.async = 'newAsyncVal'; // Changing what async will callback when invoked
        module2.async = function( param1, callback ) { // ... same without mock
        	callback( undefined, 'newAsyncVal' ); 
		};
    	module1.async( 'test', function( err, value ) {
        	assert.ok( !err );
			assert.equal( value, 'newAsyncVal' );
		} );
    	module2.async( 'test', function( err, value ) {
        	assert.ok( !err );
			assert.equal( value, 'newAsyncVal' );
		} );
        // Change so async callbacks an error
		module1.async.err = 'myError'; // Change async so it callbacks with an error
        module2.async = function( param1, callback ) { // ... same without mock
        	callback( 'myError' ); 
		};
    	module1.async( 'test', function( err, value ) {
        	assert.equal( err, 'myError' );
		} );
    	module2.async( 'test', function( err, value ) {
        	assert.equal( err, 'myError' );
		} );
    } );

Arguments

  • settingsKey the key in settings that specifies which components should be wrapped

Returns

The container.

Remarks

Wrappers can implement functions async and or sync, see example of usage. Wrapping is global and affects all containers in the ioc.

Example

require( 'simple-ioc' )
    .setSettings( {
        wrapping: {
            request: 'requestWrapper',
            module1: 'syncWrapper'
        }
    } )
    .registerResolved( {
        request: require( 'request' )
    } )
    .registerInjectable( {
        module1: function( pub ) {
            pub.func = function( param1, param2 ) {
                return param1 + param2;
            };
        },
        requestWrapper: function( assert, pub ) {
            pub.async = function( context, arguments, callback ) {
                assert.equal( context.async, true );
                var wrappedComponent = context.parentName; // e.g. request
                var wrappedFunction = context.wrappedFunction; // e.g. get
                var timeOfExecution = context.ts;
                var executionTime = context.executionTime;
                var result = context.result;
                var argumentsToFunction = arguments; // e.g. [ 'www.google.com' ]
                // Do something with this information, e.g. logging
                callback( function( err, result ) { // Will be invoked when on async callback
                    // Do some more logging... e.g. time = Date.now() - timeOfExecution;
                } );
            }
        },
        syncWrapper: function( assert, pub ) {
            pub.sync = function( context, arguments, result ) {
                assert.equal( context.sync, true );
                var wrappedComponent = context.parentName; // e.g. module1
                var wrappedFunction = context.wrappedFunction; // e.g. func
                var timeOfExecution = context.ts;
                var argumentsToFunction = arguments; // e.g. [ 1, 2 ]
                var resultOfInvokation = result; // e.g. 3
                // Do something with this information, e.g. logging
            };
        }
    } )
    .registerGlobalWrappersFromSettings( 'wrapping' )
    .inject( function( request, module1 ) {
        request.get( 'www.google.com', function( err, res, body ) {
            // wrapper has been called before request and when responce is received.
        } );
        var result = module1.func( 1, 2 ); // wrapper has been called
    } );

Arguments

  • relativePath relative path or absolut path that th container will recursively look in.
  • omitFileIocComments (optinal) default false, if true, the container will not look for ioc specific comments.
  • omitFileLengthLogging (optional) default false, if true, no warnings for long files.

Returns

The container.

Remarks

Files that contains the followin comments will be handeled different by autoRegisterPath:

  • /* ioc:ignore */ - file will be ignored.
  • /* ioc:noresolve */ - file will be registered as resolved

If omitFileLengthLogging is not set, the ioc will info log if files exceed 100 lines and warning log if files exceed 200 lines.

Normally the ioc uses the name of the file as name of the component, but if the function is not anonymous, the name of the function is used. In case the name of the component is hyphenated (some-component.js), it will be camelCased when injected (someComponent).

Example

// ./lib/module1.js
module.exports = function( pub ) {
	pub.name = 'mod1';
};
// ./lib/module2.js
module.exports = function( pub, module1 ) {
	pub.name = [ module1.name, 'mod2' ].join( '.' );
};
// ./lib/module3ButWithAnotherName.js
module.exports = function module3( pub, module2 ) {
	pub.name = [ module2.name, 'mod3' ].join( '.' );
};
// ./index.js
module.exports = require( 'simple-ioc' )
	.getContainer()
    .autoRegisterPath( './lib' )
    .inject( function( module3 ) {
    	console.log( module3.name ); // Will print out "mod1.mod2.mod3"
    } );

Arguments

  • name name of the component to resolve
  • callback( err, instance ) function to be called with the result of the resolve.

Returns

The container.

Remarks

Resolve can safely be used anytime, since it callbacks an error if the component is unresolvable.

Example

var assert = require( 'assert' ); 
module.exports = require( 'simple-ioc' )
	.getContainer()
    .registerInjectable( {
    	test: function( pub ) {}
    } )
    .resolve( 'test', function( err, instance ) {
		assert.ok( !err );
        assert.ok( !!pub );
	} )
    .resolve( 'notRegistered', function( err, instance ) {
		assert.ok( err );
        assert.ok( !pub );
	} )

Arguments

  • name the name settings should be registerd as, defaults to "settings"

Returns

The container.

Remarks

None.

Example

var assert = require( 'assert' ); 
module.exports = require( 'simple-ioc' )
	.setSettings( {
    	key: 'value'
    } )
	.getContainer()
    .registerIocSettings()
    .inject( function( settings ) {
		assert.equal( settings.key, 'value' );
	} );

Arguments

  • name the name settings should be registerd as, defaults to "log"

Returns

The container.

Remarks

None.

Example

var assert = require( 'assert' ); 
module.exports = require( 'simple-ioc' )
	.getContainer()
    .registerIocLog( 'log' )
    .inject( function( log ) {
		assert.ok( !!log.info );
	} );

Arguments

  • fn function to inject after all registered injectable singleton components are resolved.

Returns

The container.

Remarks

ResolveAllAndInject will log information about components that does not have any components that are depending on them.

Example

var assert = require( 'assert' ); 
module.exports = require( 'simple-ioc' )
	.getContainer()
    .registerInjectable( {
    	module1: function( pub ) {
        	console.log( 'module1' );
            pub.value = 'val1';
        },
        module2: function( module1, callback ) {
        	console.log( 'module2' );
            callback( {
            	value: [ module1.value, 'val2' ].join( '.' );
            } );
        }
    } )
    .resolveAllAndInject( function() {
    	console.log( 'injected' );
    } );
    // Will have the following output:
    // module1
    // module2
    // injected

Arguments

  • fn function to inject.
  • callback optional, called after the functions is injected.

Returns

The container

Remarks

None.

Example

var assert = require( 'assert' ); 
module.exports = require( 'simple-ioc' )
	.getContainer()
    .registerInjectable( {
    	module1: function( pub ) {
            pub.value = 'val1';
        },
        module2: function( pub ) {
        	pub.value = 'val2';
        }
    } )
    .inject( function( assert, module1, module2 ) {
    	assert.equal( module1.value, 'val1' );
    	assert.equal( module2.value, 'val2');
    } );

Arguments

  • fn function to inject

Returns

The container.

Remarks

None.

Example

// ./index.js
var assert = require( 'assert' ); 
module.exports = require( 'simple-ioc' )
	.getContainer()
    .registerInjectable( {
    	module1: function( pub, callback ) {
            setTimeout( function() {
            	pub.value = 'val1';
            	callback();
            }, 500 );
        },
        module2: function( pub ) {
        	pub.value = 'val2';
        }
    } )
    .resolveAllAndInject( function( assert, module1, module2 ) {
		console.log( 'Application started' );
    } );
// ./tests/system/test.js
var container = require( '../../../index.js' )
	.injectAfterResolveAll( function( module1 ) {
    	assert.equal( module1.value, 'val1' );
    } );

Arguments

  • settingKey - the dot notated key in settings (true/false)
  • name - the identifying name of the component
  • fn - the resolved component

Returns

The container.

Remarks

None.

Example

var container = require( 'simple-ioc' ).getContainer()
	.setSettings( {
		use: {
			adapter: true
		}
	} )
	.registerResolvedIfSetting( 'use.adapter', 'componentName', require( 'someResolvedComponent' ) ); // Will register required component as resolved

Arguments

  • settingKey - the dot notated key in settings (true/false)
  • name - the identifying name of the component
  • fn - the injectable function

Returns

The container.

Remarks

None.

Example

var container = require( 'simple-ioc' ).getContainer()
	.setSettings( {
		use: {
			adapter: true
		}
	} )
	.registerInjectableIfSetting( 'use.adapter', 'componentName', require( 'someInjectableComponent' ) ); // Will register required component as injectable

Arguments

  • settingKey - the dot notated key in settings that referes to the path

Returns

The container.

Remarks

None.

Example

var container = require( 'simple-ioc' ).getContainer()
	.setSettings( {
		use: {
			adapter: '/myPath/adaper1'
		}
	} )
	.autoRegisterPathInSetting( 'use.adapter' ); // Will auto register all files in the path '/myPath/adaper1'

Arguments

  • name - the identifying name of the component to remove from the container

Returns

The container.

Remarks

Only injectable components that has not yet been resolved can be removed.

Example

// ./index.js
var assert = require( 'assert' ); 
module.exports = require( 'simple-ioc' )
    .getContainer()
    .registerInjectable( {
        module1: function( pub, callback ) {
            setTimeout( function() {
                pub.value = 'val1';
                callback();
            }, 500 );
        },
        module2: function( pub, module1 ) {
            pub.value = module1.value;
        }
    } )
    .resolveAllAndInject( function() {
        console.log( 'Application started' );
    } );
// ./tests/system/test.js
var container = require( '../../../index.js' )
    .removeRegistered( 'module1' )
    .registerResolved( {
        module1: { value: 'newVal1' }
    } )
    .injectAfterResolveAll( function( assert, module2 ) {
        assert.equal( module2.value, 'newVal1' );
    } );

Arguments

  • name - the identifying name of the component to export

Returns

An injectable functions with that callbacks the component

Remarks

None.

Example

// ./index.js
var assert = require( 'assert' ); 
module.exports = require( 'simple-ioc' )
    .getContainer()
    .registerInjectable( {
        moduleFromOtherApplication: require( 'otherApplication' ).export( 'moduleFromOtherApplication' )
    } )
    .resolveAllAndInject( function( moduleFromOtherApplication ) {
        console.log( 'Application started' );
    } );

{
	level: level,
	message: message,
	data: data,
	component: parentName,
	...environment variables specified in the settings
}

In the log-settings you can specify envronment values that you would like to include in the log-objects

includeEnvironmentVariables: { enviro: 'ENV_NAME' }
// Would include ENV_NAME as "enviro"

Arguments

  • message log message
  • data optional dataobject that will be in the output

Returns

Undefined.

Remarks

After the log is written system.exit() is automatically called.

Example

log.fatal( 'Fatal error occured, not recoverable', err ); // Application will exit

Arguments

  • message log message
  • data optional dataobject that will be in the output

Returns

Undefined.

Remarks

None.

Example

log.error( 'Error occured, request probably fails', err );

Arguments

  • message log message
  • data optional dataobject that will be in the output

Returns

Undefined.

Remarks

None.

Example

log.warning( 'Unexpected behaviour, recoverable, request will probably not fail', err );

Arguments

  • message log message
  • data optional dataobject that will be in the output

Returns

Undefined.

Remarks

None.

Example

log.info( 'Setup was successful' );

Arguments

  • message log message
  • data optional dataobject that will be in the output

Returns

Undefined.

Remarks

None.

Example

log.debug( 'Incomming request', req );

Arguments

  • message log message
  • data optional dataobject that will be in the output

Returns

Undefined.

Remarks

None.

Example

log.trace( 'Session resolved', session );

Arguments

  • componentName optional, name of component to get logs from

Returns

Undefined.

Remarks

None.

Example

log.info( 'Incomming request' );
assert.equal( log.getEntries()[ 0 ].message, 'Incomming request' );

Arguments

None.

Returns

Undefined.

Remarks

None.

Example

log.info( 'Incomming request' );
log.reset();
assert.equal( log.getEntries()[ 0 ].length, 0 );

Arguments

  • callback the callback to send error to
  • successFn function to call if first argument evaluates as false.

Returns

The rerouter

remarks

None.

Example

module.exports = function( pub, errRerouter, someErrorThrowingAsyncComponent ) {
    pub.get = function( callback ) {
        someErrorThrowingAsyncComponent.get( errRerouter( callback, function( data ) {
            callback( undefined, data.someData ); // Just implement "happy-flow"
        } ) );
    };
};

Is equivalent to

module.exports = function( pub, someErrorThrowingAsyncComponent ) {
    pub.get = function( callback ) {
        someErrorThrowingAsyncComponent.get( function( err, data ) {
            if( err )
                callback( err ); // Check for error needed
            else
                callback( undefined, data.someData );
        } );
    };
};
3.0.25

9 years ago

3.0.24

9 years ago

3.0.23

9 years ago

3.0.22

9 years ago

3.0.21

9 years ago

3.0.20

9 years ago

3.0.19

9 years ago

3.0.18

9 years ago

3.0.17

9 years ago

3.0.16

9 years ago

3.0.15

9 years ago

3.0.14

9 years ago

3.0.13

9 years ago

3.0.12

9 years ago

3.0.11

9 years ago

3.0.10

9 years ago

3.0.9

9 years ago

3.0.8

9 years ago

3.0.7

9 years ago

3.0.6

9 years ago

3.0.5

9 years ago

3.0.3

9 years ago

3.0.2

9 years ago

3.0.1

9 years ago

3.0.0

9 years ago

2.5.5

10 years ago

2.5.4

10 years ago

2.5.3

10 years ago

2.5.2

10 years ago

2.5.1

10 years ago

2.5.0

10 years ago

2.4.3

10 years ago

2.4.2

10 years ago

2.4.1

10 years ago

2.4.0

10 years ago

2.3.8

10 years ago

2.3.7

10 years ago

2.3.5

10 years ago

2.3.4

10 years ago

2.3.3

11 years ago

2.3.2

11 years ago

2.3.1

11 years ago

2.3.0

11 years ago

2.2.3

11 years ago

2.2.2

11 years ago

2.2.1

11 years ago

2.2.0

11 years ago

2.1.1

11 years ago

2.1.0

11 years ago

2.0.8

11 years ago

2.0.7

11 years ago

2.0.6

11 years ago

2.0.5

11 years ago

2.0.4

11 years ago

2.0.3

11 years ago

2.0.2

11 years ago

2.0.1

11 years ago

2.0.0

11 years ago

1.2.8

11 years ago

1.2.7

11 years ago

1.2.6

11 years ago

1.2.5

11 years ago

1.2.4

11 years ago

1.2.3

11 years ago

1.2.2

11 years ago

1.2.1

11 years ago

1.2.0

11 years ago

1.1.16

11 years ago

1.1.15

11 years ago

1.1.13

11 years ago

1.1.12

11 years ago

1.1.11

11 years ago

1.1.10

11 years ago

1.1.9

11 years ago

1.1.8

11 years ago

1.1.7

11 years ago

1.1.6

11 years ago

1.1.5

11 years ago

1.1.4

11 years ago

1.1.3

11 years ago

1.1.2

11 years ago

1.1.1

11 years ago

1.1.0

11 years ago

1.0.5

11 years ago

1.0.4

11 years ago

1.0.3

11 years ago

1.0.2

11 years ago

1.0.1

11 years ago

1.0.0

11 years ago