UNPKG

simple-ioc

Version:

Simple Inversion of Control for node.js

227 lines (226 loc) 8.42 kB
var anonymousParentResolver = require( './genericHelpers/anonymousParentResolver.js' )(), files = require( './containerHelpers/files.js' )(), injectableComponent = require( './containerHelpers/injectableComponent.js' )(), resolvedComponent = require( './containerHelpers/resolvedComponent.js' )(), IocError = require( './genericHelpers/IocError.js' )(), settingsHelper = require( './genericHelpers/settingsHelper.js' )(), containerCount = 0, errRerouter = require( './genericHelpers/errRerouter.js' )(); module.exports = function( ioc, trueCallback ) { var pub = {}, resolver = require( './containerHelpers/resolver.js' )( trueCallback ), log = require( './log/log.js' )( 'container ' + containerCount++ ), store = require( './containerHelpers/store.js' )(), fnToInjectAfterAllResolved, loaderName = anonymousParentResolver.resolvePath( 3 ).split( '/' ).pop(), shouldResolveAllAndInject = true; pub.registerResolved = function( name, instance ) { if( typeof( name ) === 'string' ) { log.debug( 'registerResolved', name ); if( store.isRegistered( name ) ) throw new IocError( 'Component already registered', name ); else store.setResolved( name, resolvedComponent.get( instance ) ); } else Object.keys( name ).forEach( function( comnonentName ) { pub.registerResolved( comnonentName, name[ comnonentName ] ); } ); return pub; }; var registerInjectable = function( name, fn ) { log.debug( 'registerInjectable', name ); if( store.isRegistered( name ) ) throw new IocError( 'Component already registered', name ); else { var component = injectableComponent.get( fn ); if( component.dependencies.hasUnused ) log.error( name + ' has unused dependencies', component.dependencies.unused.map( function( dependency ) { return dependency.name; } ) ); store.setInjectable( name, component ); } }; var mock = function( name, properties ) { log.debug( 'mock', name ); if( store.isRegistered( name ) ) throw new IocError( 'Component already registered', name ); else { var instance = {}, values = {}, funcs = {}; Object.keys( properties ).forEach( function( property ) { values[ property ] = properties[ property ]; funcs[ property ] = function() { if( typeof( arguments[ arguments.length - 1 ] ) === 'function' ) setImmediate( arguments[ arguments.length - 1 ], funcs[ property ].err, values[ property ] ); else return values[ property ]; }; Object.defineProperty( instance, property, { set: function( value ) { values[ property ] = value; }, get: function() { return funcs[ property ]; } } ); } ); store.setResolved( name, resolvedComponent.get( instance ) ); } }; pub.mock = function( name, properties ) { if( typeof( name ) === 'string' ) mock( name, properties ); else Object.keys( name ).forEach( function( comnonentName ) { pub.mock( comnonentName, name[ comnonentName ] ); } ); return pub; }; pub.registerInjectable = function( name, fn ) { if( typeof( name ) === 'string' ) if( typeof( fn ) === 'function' ) registerInjectable( name, fn ); else throw( new IocError( 'Injectable is not a function', name ) ); else Object.keys( name ).forEach( function( comnonentName ) { pub.registerInjectable( comnonentName, name[ comnonentName ] ); } ); return pub; }; pub.renameRegistered = function( from, to ) { return pub .registerInjectable( to, store.getInjectable( from ).fn ) .removeRegistered( from ); }; pub.registerGlobalWrappersFromSettings = function( settingsKey ) { var wrapSettings = settingsHelper.getSetting( ioc.getSettings(), settingsKey ); if( wrapSettings ) Object.keys( wrapSettings ).forEach( function( componentName ) { resolver.registerGlobalWrapper( componentName, pub.resolve.bind( pub, wrapSettings[ componentName ] ) ); } ); return pub; }; pub.autoRegisterPath = function( relativePath, omitFileIocComments, omitFileLengthLogging ) { log.trace( 'autoRegisterPath', relativePath ); var modulesInPath = files.getModulesInPath( relativePath, omitFileIocComments, omitFileLengthLogging, 2 ); Object.keys( modulesInPath.injectables ).forEach( function( name ) { var exposedName = name.replace(/-([a-z])/g, function (g) { return g[1].toUpperCase(); }); pub.registerInjectable( exposedName, modulesInPath.injectables[ name ] ); } ); Object.keys( modulesInPath.resolved ).forEach( function( name ) { pub.registerResolved( name, modulesInPath.resolved[ name ] ); } ); return pub; }; pub.resolve = function( name, callback ) { resolver.resolve( store, name, anonymousParentResolver.resolveName( 2 ), callback ); return pub; }; pub.registerIocSettings = function( name ) { pub.registerResolved( name || 'settings', ioc.getSettings() ); return pub; }; pub.registerIocLog = function( name ) { pub.registerInjectable( name || 'log', require( './log/log.js' ) ); return pub; }; var currentResolvning = {}, ts; pub.resolveAllAndInject = function( fn ) { if( !ts ) ts = Date.now(); var nextResolvable = store.getAllResolvable().filter( function( name ) { if( currentResolvning[ name ] ) return false; else { currentResolvning[ name ] = true; return true; } } ); if( nextResolvable.length ) setImmediate( function() { if( shouldResolveAllAndInject ) nextResolvable.forEach( function( resolvable ) { var ts = Date.now(); pub.resolve( resolvable, function( err, instance ) { if( trueCallback && err ) log.fatal( 'Component "' + resolvable + '" returned an error', err ); log.debug( 'Resolved ' + resolvable, Date.now() - ts + ' ms' ); delete currentResolvning[ resolvable ]; if( err ) log.fatal( 'resolveAllAndInject failed when resolving component', err ); else pub.resolveAllAndInject( fn ); } ); } ); } ); else if( !Object.keys( currentResolvning ).length ) { var problems = store.getAllResolvingProblems(); if( problems.length ) { log.debug( 'Problems during resolveAllAndInject', problems ); var nonRegisteredProblems = {}; problems.forEach( function( problem ) { var filteredErrors = problem.errors.filter( function( error ) { return error.problemType === 'dependencyNotRegistered'; } ); if( filteredErrors.length ) nonRegisteredProblems[ problem.name ] = filteredErrors[ 0 ].dependencyNames; } ); log.fatal( 'Failed to resolveAllAndInject, non registered components', nonRegisteredProblems ); } pub.inject( fn, function() { var unrefered = store.getUnreferencedComponents(); if( unrefered.length ) log.info( 'All resolved ' + loaderName + ' in ' + ( Date.now() - ts ) + ' ms, unrefered components', unrefered ); else log.info( 'All resolved ' + loaderName + ' in ' + ( Date.now() - ts ) + ' ms' ); if( fnToInjectAfterAllResolved ) { pub.inject( fnToInjectAfterAllResolved, function() {} ); } } ); } return pub; }; pub.injectAfterResolveAll = function( fn ) { fnToInjectAfterAllResolved = fn; }; pub.inject = function( fn, callback ) { resolver.inject( store, fn, anonymousParentResolver.resolveName( 2 ), callback ); return pub; }; pub.registerResolvedIfSetting = function( settingKey, name, instance ) { if( settingsHelper.getSetting( ioc.getSettings(), settingKey ) ) pub.registerResolved( name, instance ); return pub; }; pub.registerInjectableIfSetting = function( settingKey, name, fn ) { if( settingsHelper.getSetting( ioc.getSettings(), settingKey ) ) pub.registerInjectable( name, fn ); return pub; }; pub.autoRegisterPathInSetting = function( settingKey ) { var settingValue = settingsHelper.getSetting( ioc.getSettings(), settingKey ); if( settingValue ) pub.autoRegisterPath( settingValue ); return pub; }; pub.removeRegistered = function( name ) { store.removeRegistered( name ); return pub; }; pub.export = function( name ) { shouldResolveAllAndInject = false; return function( callback ) { pub.resolve( name, function( err, resolved ) { if( err ) log.fatal( 'Could not transfer component', err ); else callback( resolved ); } ); }; }; pub.registerResolved( 'ioc', ioc ) .registerResolved( 'container', pub ) .registerResolved( 'errRerouter', errRerouter ); return pub; };