From eba3e47182cffa6d1603db48637ea9c744bce8e1 Mon Sep 17 00:00:00 2001 From: Victor Woeltjen Date: Mon, 3 Nov 2014 10:14:13 -0800 Subject: [PATCH] [Framework] Implement ExtensionResolver Initial implementation of class responsible for resolving extensions (that is, loading modules associated with defined extensions.) Also, add methods to Bundle and Extension classes to support logging performed during the extension resolution phase. WTD-518. --- platform/framework/src/Bundle.js | 20 ++++++ platform/framework/src/Extension.js | 19 +++++ platform/framework/src/ExtensionResolver.js | 78 ++++++++++++++++++++- 3 files changed, 115 insertions(+), 2 deletions(-) diff --git a/platform/framework/src/Bundle.js b/platform/framework/src/Bundle.js index e2ab0a719e..92c6027a92 100644 --- a/platform/framework/src/Bundle.js +++ b/platform/framework/src/Bundle.js @@ -33,6 +33,7 @@ define( function Bundle(path, bundleDefinition) { // Start with defaults var definition = Object.create(Constants.DEFAULT_BUNDLE), + logName = path, self; // Utility function for resolving paths in this bundle @@ -45,6 +46,15 @@ define( definition[k] = bundleDefinition[k]; }); + // Build up the log-friendly name for this bundle + if (definition.key || definition.name) { + logName += "("; + logName += definition.key || ""; + logName += (definition.key && definition.name) ? " " : ""; + logName += definition.name || ""; + logName += ")"; + } + return (self = { /** * @@ -88,6 +98,16 @@ define( return resolvePath(subpath); }, + /** + * Get a log-friendly name for this bundle; this will + * include both the key (machine-readable name for this + * bundle) and the name (human-readable name for this + * bundle.) + * @returns {string} log-friendly name for this bundle + */ + getLogName: function () { + return logName; + }, getExtensions: function (category) { var extensions = definition.extensions[category] || []; diff --git a/platform/framework/src/Extension.js b/platform/framework/src/Extension.js index c71904e800..9d6df56bef 100644 --- a/platform/framework/src/Extension.js +++ b/platform/framework/src/Extension.js @@ -31,7 +31,16 @@ define( * @constructor */ function Extension(bundle, category, definition) { + var logName = category; + // Build up the log-friendly name for this bundle + if (definition.key || definition.name) { + logName += "("; + logName += definition.key || ""; + logName += (definition.key && definition.name) ? " " : ""; + logName += definition.name || ""; + } + logName += " from " + bundle.getLogName(); return { /** @@ -68,6 +77,16 @@ define( bundle.getSourcePath(definition.implementation) : undefined; }, + /** + * Get a log-friendly name for this extension; this will + * include both the key (machine-readable name for this + * extension) and the name (human-readable name for this + * extension.) + * @returns {string} log-friendly name for this extension + */ + getLogName: function () { + return logName; + }, /** * @memberof Extension# * @returns {ExtensionDefinition} diff --git a/platform/framework/src/ExtensionResolver.js b/platform/framework/src/ExtensionResolver.js index 57f8dc0d2f..8572175ca0 100644 --- a/platform/framework/src/ExtensionResolver.js +++ b/platform/framework/src/ExtensionResolver.js @@ -9,12 +9,86 @@ define( "use strict"; /** + * An ExtensionResolver is responsible for loading any implementation + * modules associated with specific extensions. * + * @param {ImplementationLoader} loader used to load implementations + * @param {*} $log Angular's logging service * @constructor */ - function ExtensionResolver(require) { - return { + function ExtensionResolver(loader, $log) { + function loadImplementation(extension) { + var implPath = extension.getImplementationPath(), + implPromise = loader.load(implPath), + definition = extension.getDefinition(); + // Attach values from the object definition to the + // loaded implementation. + function attachDefinition(impl) { + var result = Object.create(impl); + + Object.keys(definition).forEach(function (k) { + result[k] = definition[k]; + }); + + // Log that this load was successful + $log.info("Loaded " + extension.getLogName()); + + return result; + } + + // Log any errors in loading the implementation, and + // return the plain extension definition instead. + function handleError(err) { + // Build up a log message from parts + var message = [ + "Could not load implementation for extension ", + extension.getLogName(), + " due to ", + err.message + ].join(""); + + // Log that the extension was not loaded + $log.warn(message); + + return extension.getDefinition(); + } + + // Log that loading has begun + $log.info([ + "Loading implementation ", + implPath, + " for extension ", + extension.getLogName() + ].join("")); + + return implPromise.then(attachDefinition, handleError); + } + + return { + /** + * Resolve the provided extension; this will give a promise + * for the extension's implementation, if one has been + * specified, or for the plain definition of the extension + * otherwise. The plain definition will also be given + * if the implementation fails to load for some reason. + * + * All key-value pairs from the extension definition + * will additionally be attached to any loaded implementation. + * + * @param {Extension} extension + */ + resolve: function (extension) { + // Log that loading has begun + $log.info([ + "Resolving extension ", + extension.getLogName() + ].join("")); + + return extension.hasImplementation() ? + loadImplementation(extension) : + Promise.resolve(extension.getDefinition()); + } }; }