From fbbb5b4d7e97b1e7b826e9fb92fea55250838c50 Mon Sep 17 00:00:00 2001 From: Paul de Vries Date: Tue, 16 Feb 2016 20:46:02 +0000 Subject: [PATCH 1/6] Simplefy debug message with lodash/underscore --- src/octoprint/static/js/app/main.js | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index 402933a6..bfb5f030 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -199,11 +199,8 @@ $(function() { constructorParameters = []; } - if (_.some(constructorParameters, function(parameter) { return parameter === undefined; })) { - var _extractName = function(entry) { return entry[0]; }; - var _onlyUnresolved = function(entry) { return entry[1] === undefined; }; - var missingParameters = _.map(_.filter(_.zip(viewModelParameters, constructorParameters), _onlyUnresolved), _extractName); - log.debug("Postponing", viewModel[0].name, "due to missing parameters:", missingParameters); + if (constructorParameters.indexOf(undefined) !== -1) { + log.debug("Postponing", viewModel[0].name, "due to missing parameters:", _.keys(_.pick(_.object(viewModelParameters, constructorParameters), _.isUndefined))); return; } From f5fbd773dcbb53bb11b745abc9e247583e2ea779 Mon Sep 17 00:00:00 2001 From: Paul de Vries Date: Wed, 17 Feb 2016 00:19:14 +0000 Subject: [PATCH 2/6] 1. Objectify the OCTOPRINT_VIEWMODEL pool 2. Add optional viewmodel(s) by pushing an extra array or by using object.optional 3. Add format option and return dependencies as object, by default it will return an array --- src/octoprint/static/js/app/main.js | 87 ++++++++++++++++++----------- 1 file changed, 54 insertions(+), 33 deletions(-) diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index bfb5f030..de085f2d 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -182,31 +182,33 @@ $(function() { } // helper to create a view model instance with injected constructor parameters from the view model map - var _createViewModelInstance = function(viewModel, viewModelMap){ - var viewModelClass = viewModel[0]; - var viewModelParameters = viewModel[1]; + var _createViewModelInstance = function(viewModel, viewModelMap) { - if (viewModelParameters != undefined) { - if (!_.isArray(viewModelParameters)) { - viewModelParameters = [viewModelParameters]; + var viewModelParametersMap = function(parameter) { + + // Check if parameter is found within optional array and if all conditions are met return null + if (optionalDependencyPass && viewModel.optional.indexOf(parameter) !== -1 && !viewModelMap[parameter]) { + log.debug("Resolving optional parameter", [parameter], "without viewmodel"); + return null } - // now we'll try to resolve all of the view model's constructor parameters via our view model map - var constructorParameters = _.map(viewModelParameters, function(parameter){ - return viewModelMap[parameter] - }); - } else { - constructorParameters = []; - } + return viewModelMap[parameter] + }; + + // try to resolve all of the view model's constructor parameters via our view model map + var constructorParameters = _.map(viewModel.dependencies, viewModelParametersMap) || []; if (constructorParameters.indexOf(undefined) !== -1) { - log.debug("Postponing", viewModel[0].name, "due to missing parameters:", _.keys(_.pick(_.object(viewModelParameters, constructorParameters), _.isUndefined))); + log.debug("Postponing", viewModel.name, "due to missing parameters:", _.keys(_.pick(_.object(viewModel.dependencies, constructorParameters), _.isUndefined))); return; } + // transform array into object if a plugin wants it as an object + constructorParameters = (viewModel.format === "object") ? _.object(viewModel.dependencies, constructorParameters) : constructorParameters; + // if we came this far then we could resolve all constructor parameters, so let's construct that view model - log.debug("Constructing", viewModel[0].name, "with parameters:", viewModelParameters); - return new viewModelClass(constructorParameters); + log.debug("Constructing", viewModel.name, "with parameters:", viewModel.dependencies); + return new viewModel.construct(constructorParameters); }; // map any additional view model bindings we might need to make @@ -226,8 +228,7 @@ $(function() { }); // helper for translating the name of a view model class into an identifier for the view model map - var _getViewModelId = function(viewModel){ - var name = viewModel[0].name; + var _getViewModelId = function(name){ return name.substr(0, 1).toLowerCase() + name.substr(1); // FooBarViewModel => fooBarViewModel }; @@ -240,20 +241,39 @@ $(function() { var allViewModels = []; var allViewModelData = []; var pass = 1; + var optionalDependencyPass = false; log.info("Starting dependency resolution..."); while (unprocessedViewModels.length > 0) { log.debug("Dependency resolution, pass #" + pass); var startLength = unprocessedViewModels.length; var postponed = []; + if(optionalDependencyPass) { + log.debug("Resolving dependencies with optional flag"); + } + // now try to instantiate every one of our as of yet unprocessed view model descriptors while (unprocessedViewModels.length > 0){ var viewModel = unprocessedViewModels.shift(); - var viewModelId = _getViewModelId(viewModel); + + // Wrap anything not object related into a object + if(!(viewModel.constructor === Object)) { + viewModel = { + construct: viewModel[0], + dependencies: viewModel[1] || [], + elements: viewModel[2] || [], + optional: viewModel[3] || [] + }; + } + + viewModel.name = viewModel.name || _getViewModelId(viewModel.construct.name); + viewModel.dependencies = (_.isArray(viewModel.dependencies)) ? viewModel.dependencies : [viewModel.dependencies]; + viewModel.elements = (_.isArray(viewModel.elements)) ? viewModel.elements : [viewModel.elements]; + viewModel.optional = (_.isArray(viewModel.optional)) ? viewModel.optional : [viewModel.optional]; // make sure that we don't have two view models going by the same name - if (_.has(viewModelMap, viewModelId)) { - log.error("Duplicate name while instantiating " + viewModelId); + if (_.has(viewModelMap, viewModel.name)) { + log.error("Duplicate name while instantiating " + viewModel.name); continue; } @@ -266,18 +286,15 @@ $(function() { } // we could resolve the depdendencies and the view model is not defined yet => add it, it's now fully processed - var viewModelBindTargets = viewModel[2]; - if (!_.isArray(viewModelBindTargets)) { - viewModelBindTargets = [viewModelBindTargets]; - } + var viewModelBindTargets = viewModel.elements; - if (additionalBindings.hasOwnProperty(viewModelId)) { - viewModelBindTargets = viewModelBindTargets.concat(additionalBindings[viewModelId]); + if (additionalBindings.hasOwnProperty(viewModel.name)) { + viewModelBindTargets = viewModelBindTargets.concat(additionalBindings[viewModel.name]); } allViewModelData.push([viewModelInstance, viewModelBindTargets]); allViewModels.push(viewModelInstance); - viewModelMap[viewModelId] = viewModelInstance; + viewModelMap[viewModel.name] = viewModelInstance; } // anything that's now in the postponed list has to be readded to the unprocessedViewModels @@ -287,11 +304,15 @@ $(function() { // couldn't instantiate any more view models over a whole iteration, which in turn mean we can't resolve the // dependencies of remaining ones, so log that as an error and then quit the loop if (unprocessedViewModels.length == startLength) { - log.error("Could not instantiate the following view models due to unresolvable dependencies:"); - _.each(unprocessedViewModels, function(entry) { - log.error(entry[0].name + " (missing: " + _.filter(entry[1], function(id) { return !_.has(viewModelMap, id); }).join(", ") + " )"); - }); - break; + if(!optionalDependencyPass) { + optionalDependencyPass = true; + } else { + log.error("Could not instantiate the following view models due to unresolvable dependencies:"); + _.each(unprocessedViewModels, function(entry) { + log.error(entry.name + " (missing: " + _.filter(entry.dependencies, function(id) { return !_.has(viewModelMap, id); }).join(", ") + " )"); + }); + break; + } } log.debug("Dependency resolution pass #" + pass + " finished, " + unprocessedViewModels.length + " view models left to process"); From ef411298e28b44eebdd59c762bbff0ada6007394 Mon Sep 17 00:00:00 2001 From: Paul de Vries Date: Thu, 18 Feb 2016 21:27:36 +0000 Subject: [PATCH 3/6] Add ability to push only a function in the OCTOPRINT_VIEWMODELS array Add more comments Fine tweak code: - Remove vanilla Object check with jQuery (lodash gives invalid result on arrays with isObject) - Add function check on constructor - Generate function name on anonymous functions --- src/octoprint/static/js/app/main.js | 37 +++++++++++++++++------------ 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index de085f2d..341af3bb 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -184,15 +184,15 @@ $(function() { // helper to create a view model instance with injected constructor parameters from the view model map var _createViewModelInstance = function(viewModel, viewModelMap) { + // mirror the requested dependencies with an array of the viewModels var viewModelParametersMap = function(parameter) { - - // Check if parameter is found within optional array and if all conditions are met return null + // check if parameter is found within optional array and if all conditions are met return null instead of undefined if (optionalDependencyPass && viewModel.optional.indexOf(parameter) !== -1 && !viewModelMap[parameter]) { log.debug("Resolving optional parameter", [parameter], "without viewmodel"); return null } - return viewModelMap[parameter] + return viewModelMap[parameter] || undefined }; // try to resolve all of the view model's constructor parameters via our view model map @@ -248,25 +248,30 @@ $(function() { var startLength = unprocessedViewModels.length; var postponed = []; - if(optionalDependencyPass) { - log.debug("Resolving dependencies with optional flag"); - } - // now try to instantiate every one of our as of yet unprocessed view model descriptors while (unprocessedViewModels.length > 0){ var viewModel = unprocessedViewModels.shift(); - // Wrap anything not object related into a object - if(!(viewModel.constructor === Object)) { + // wrap anything not object related into a object (use jQuery since lodash returns invalid results) + if(!$.isPlainObject(viewModel)) { viewModel = { - construct: viewModel[0], - dependencies: viewModel[1] || [], - elements: viewModel[2] || [], - optional: viewModel[3] || [] + construct: (_.isArray(viewModel)) ? viewModel[0] : viewModel, + dependencies: viewModel[1] || [], + elements: viewModel[2] || [], + optional: viewModel[3] || [] }; } - viewModel.name = viewModel.name || _getViewModelId(viewModel.construct.name); + // make sure we have atleast a function + if (!_.isFunction(viewModel.construct)) { + log.error("No function to instantiate with", viewModel); + continue; + } + + // if name is not set, get name from construct, if it's an anonymous function; generate one + viewModel.name = viewModel.name || _getViewModelId(viewModel.construct.name) || "unnamedViewModel" + _.uniqueId(); + + // make sure all value's are in an array viewModel.dependencies = (_.isArray(viewModel.dependencies)) ? viewModel.dependencies : [viewModel.dependencies]; viewModel.elements = (_.isArray(viewModel.elements)) ? viewModel.elements : [viewModel.elements]; viewModel.optional = (_.isArray(viewModel.optional)) ? viewModel.optional : [viewModel.optional]; @@ -303,8 +308,10 @@ $(function() { // if we still have the same amount of items in our list of unprocessed view models it means that we // couldn't instantiate any more view models over a whole iteration, which in turn mean we can't resolve the // dependencies of remaining ones, so log that as an error and then quit the loop - if (unprocessedViewModels.length == startLength) { + if (unprocessedViewModels.length === startLength) { + // I'm gonna let you finish but we will do another pass with the optional dependencies flag enabled if(!optionalDependencyPass) { + log.debug("Resolving next pass with optional dependencies flag enabled"); optionalDependencyPass = true; } else { log.error("Could not instantiate the following view models due to unresolvable dependencies:"); From f67da6507f5e83723b1393ad492d11191e136aa7 Mon Sep 17 00:00:00 2001 From: Paul de Vries Date: Thu, 18 Feb 2016 21:55:22 +0000 Subject: [PATCH 4/6] Fix typo and move prefix into function call --- src/octoprint/static/js/app/main.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index 341af3bb..aac26033 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -252,7 +252,7 @@ $(function() { while (unprocessedViewModels.length > 0){ var viewModel = unprocessedViewModels.shift(); - // wrap anything not object related into a object (use jQuery since lodash returns invalid results) + // wrap anything not object related into an object (use jQuery since lodash returns invalid results) if(!$.isPlainObject(viewModel)) { viewModel = { construct: (_.isArray(viewModel)) ? viewModel[0] : viewModel, @@ -268,8 +268,8 @@ $(function() { continue; } - // if name is not set, get name from construct, if it's an anonymous function; generate one - viewModel.name = viewModel.name || _getViewModelId(viewModel.construct.name) || "unnamedViewModel" + _.uniqueId(); + // if name is not set, get name from constructor, if it's an anonymous function generate one + viewModel.name = viewModel.name || _getViewModelId(viewModel.construct.name) || _.uniqueId("unnamedViewModel"); // make sure all value's are in an array viewModel.dependencies = (_.isArray(viewModel.dependencies)) ? viewModel.dependencies : [viewModel.dependencies]; From 0827ae10f206910b4fd82a5d987790f2fe784ae5 Mon Sep 17 00:00:00 2001 From: Paul de Vries Date: Thu, 18 Feb 2016 22:41:53 +0000 Subject: [PATCH 5/6] Rename format into returnObject w/ (true/false/undefined) --- src/octoprint/static/js/app/main.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index aac26033..3608188e 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -204,7 +204,7 @@ $(function() { } // transform array into object if a plugin wants it as an object - constructorParameters = (viewModel.format === "object") ? _.object(viewModel.dependencies, constructorParameters) : constructorParameters; + constructorParameters = (viewModel.returnObject) ? _.object(viewModel.dependencies, constructorParameters) : constructorParameters; // if we came this far then we could resolve all constructor parameters, so let's construct that view model log.debug("Constructing", viewModel.name, "with parameters:", viewModel.dependencies); From 5c83aef52d9a3164f7aa7612b691238db1e691b1 Mon Sep 17 00:00:00 2001 From: Paul de Vries Date: Fri, 4 Mar 2016 18:29:05 +0000 Subject: [PATCH 6/6] Use _.isPlainObject over $.isPlainObject --- src/octoprint/static/js/app/main.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/octoprint/static/js/app/main.js b/src/octoprint/static/js/app/main.js index 3608188e..d0d5fef2 100644 --- a/src/octoprint/static/js/app/main.js +++ b/src/octoprint/static/js/app/main.js @@ -252,8 +252,8 @@ $(function() { while (unprocessedViewModels.length > 0){ var viewModel = unprocessedViewModels.shift(); - // wrap anything not object related into an object (use jQuery since lodash returns invalid results) - if(!$.isPlainObject(viewModel)) { + // wrap anything not object related into an object + if(!_.isPlainObject(viewModel)) { viewModel = { construct: (_.isArray(viewModel)) ? viewModel[0] : viewModel, dependencies: viewModel[1] || [],