fire-up
Version:
Fire Up! is a dependency injection container designed specifically for node.js with a powerful but sleek API.
182 lines (127 loc) • 5.38 kB
JavaScript
;
var _ = require('lodash');
var path = require('path');
var constants = require('../definitions/constants.js');
var descriptor = require('./descriptor.js');
function newModuleEntry() {
return {
type: constants.MODULE_TYPE_SINGLETON,
implements: [],
inject: [],
status: constants.MODULE_STATUS_UNCHECKED,
cache: {
module: null,
instantiationPromise: null,
singletonInstance: null
}
};
}
function newInterfaceEntry(interfaceName) {
return {
interfaceName: interfaceName,
interfaces: {},
file: null,
conflictingFiles: null
};
}
function newRegistry(fireUp) {
var registry = {
basePathForFileRefs: process.cwd(),
modules: {},
interfaces: {}
};
registry.registerInterface = function (filePathRelativeToCwd, implementedInterface) {
var interfaceChain = descriptor.parseInterfaceName(implementedInterface);
var registryPointer = fireUp._internal.registry.interfaces;
var currentInterface = "";
for ( var p = 0; p < interfaceChain.length; p+=1 ) {
if (p > 0) {
currentInterface += ":";
}
currentInterface += interfaceChain[p];
if (_.isPlainObject(registryPointer[interfaceChain[p]]) === false) {
registryPointer[interfaceChain[p]] = newInterfaceEntry(currentInterface);
}
// Check for conflicts when arriving at last extended interface
if (p === interfaceChain.length-1) {
if (_.isNull(registryPointer[interfaceChain[p]].file) === false ||
_.isNull(registryPointer[interfaceChain[p]].conflictingFiles) === false) {
if (_.isNull(registryPointer[interfaceChain[p]].conflictingFiles)) {
registryPointer[interfaceChain[p]].conflictingFiles = [];
}
if (_.isNull(registryPointer[interfaceChain[p]].file) === false) {
registryPointer[interfaceChain[p]].conflictingFiles.push(registryPointer[interfaceChain[p]].file);
registryPointer[interfaceChain[p]].file = null;
}
registryPointer[interfaceChain[p]].conflictingFiles.push(filePathRelativeToCwd);
var err = new fireUp.errors.InterfaceRegistrationConflictError(currentInterface, registryPointer[interfaceChain[p]].conflictingFiles);
fireUp.log.error(err);
throw err;
}
registryPointer[interfaceChain[p]].file = filePathRelativeToCwd;
fireUp.log.info({}, 'Registered module at \'%s\' as implementation for interface \'%s\'.', filePathRelativeToCwd, currentInterface);
}
registryPointer = registryPointer[interfaceChain[p]].interfaces;
}
};
registry.getInterface = function (interfaceName) {
var segments = descriptor.parseInterfaceName(interfaceName);
var registryPointer = registry.interfaces;
var lastImplementedInterface = "";
for ( var i = 0; i < segments.length; i+=1 ) {
if (_.isUndefined(registryPointer[segments[i]])) {
fireUp.log.warn({}, "No implementation found for the interface '%s'.", interfaceName);
if (lastImplementedInterface !== "") {
fireUp.log.debug({}, "FYI there is an implementation for the more general interface '%s'.", lastImplementedInterface);
}
return;
}
if (i < segments.length-1) {
if (_.isString(registryPointer[segments[i]].file)) {
lastImplementedInterface = registryPointer[segments[i]].interfaceName;
}
registryPointer = registryPointer[segments[i]].interfaces;
}
}
return registryPointer[_.last(segments)];
};
registry.findInterface = function (interfaceName) {
var registryPointer = this.getInterface(interfaceName);
if (_.isUndefined(registryPointer)) {
return;
} else if (_.isString(registryPointer.file)) {
return registryPointer;
} else if (_.isArray(registryPointer.conflictingFiles)) {
fireUp.log.warn({
interfaceName: registryPointer.interfaceName,
conflictingFiles: registryPointer.conflictingFiles
}, "No implementation available for the interface '%s' due to conflicting implementations for the same interface.", interfaceName);
return;
}
// Get a more specific implementation if unambiguous
var interfaces;
while ((interfaces = _.keys(registryPointer.interfaces)).length === 1) {
registryPointer = registryPointer.interfaces[interfaces[0]];
if (_.isString(registryPointer.file)) {
return registryPointer;
} else if (_.isArray(registryPointer.conflictingFiles)) {
fireUp.log.warn({
interfaceName: registryPointer.interfaceName,
conflictingFiles: registryPointer.conflictingFiles
}, "No implementation available for the interface '%s' due to conflicting implementations for the same interface.", interfaceName);
return;
}
}
fireUp.log.warn({}, "No exact or more specific, unambiguous implementation found for the interface '%s'.", interfaceName);
fireUp.log.debug({
interfaces: interfaces
}, 'There are multiple implementations for more specific interfaces. However, the ambiguity prohibits loading a more specific implementation.');
return;
};
return registry;
}
module.exports = {
newModuleEntry: newModuleEntry,
newInterfaceEntry: newInterfaceEntry,
new: newRegistry
};