diff --git a/HISTORY.md b/HISTORY.md index 940411993..0ac20406e 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,5 +1,14 @@ +version 0.9.3 +============= + +Notes +----- + +* Various improvements to the Resource Store, which reduce start up time. +* Yahoo Arrow dependency was upgraded to yahoo-arrow@0.5.x + version 0.9.2 -================= +============= Notes ----- @@ -14,7 +23,7 @@ Notes * Express devDependency was upgraded to express@3.5.x version 0.9.1 -================= +============= Notes ----- @@ -29,10 +38,10 @@ Notes * Express devDependency was upgraded to express@3.5.x version 0.9.0 -================= +============= Notes ------------- +----- This release introduces a set of new APIs and concepts. @@ -40,7 +49,7 @@ Please refer to some of the examples apps under the [`examples/`](https://github an overview of what has changed. Deprecations, Removals ------------- +---------------------- * Mojito no longer supports `index.js` and `server.js` to start up the server. Applications will instead instantiate Mojito as follows: @@ -99,7 +108,7 @@ Deprecations, Removals Features ------------- +-------- * To register Mojito routes programmatically instead of using `routes.json`: @@ -142,7 +151,7 @@ New Dependencies [`express-annotations`](https://github.com/yahoo/express-annotations#express-annotations) version 0.8.3 -================= +============= Bug Fixes --------- diff --git a/lib/app/addons/rs/dispatch-helper.js b/lib/app/addons/rs/dispatch-helper.js deleted file mode 100644 index 224f4f246..000000000 --- a/lib/app/addons/rs/dispatch-helper.js +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Copyright (c) 2012, Yahoo! Inc. All rights reserved. - * Copyrights licensed under the New BSD License. - * See the accompanying LICENSE file for terms. - */ - -/*jslint anon:true, nomen:true, node:true*/ -/*global YUI*/ - - -/** - * @module ResourceStoreAddon - */ - -/** - * RS addon that computes AC addon dependencies at startup to be attached - * at runtime. - * - * @class RSAddonDispatchHelper - * @extension ResourceStore.server - */ -YUI.add('addon-rs-dispatch-helper', function (Y, NAME) { - - 'use strict'; - - var libpath = require('path'); - - function RSAddonDispatchHelper() { - RSAddonDispatchHelper.superclass.constructor.apply(this, arguments); - } - - - RSAddonDispatchHelper.NS = 'dispatch-helper'; - - - Y.extend(RSAddonDispatchHelper, Y.Plugin.Base, { - - - initializer: function (config) { - this.onHostEvent('resolveMojitDetails', this.onResolveMojitDetails, this); - }, - - - /** - * This is called when the ResourceStore fires this event. - * It precomputes the list of AC addons used by the mojit's controller, - * to be used later during onGetMojitTypeDetails. - * @method onResolveMojitDetails - * @param {object} evt The fired event - * @return {nothing} - */ - onResolveMojitDetails: function (evt) { - var store = this.get('host'), - env = evt.args.env, - mojitType = evt.args.type, - r, - res, - ress = evt.args.ress, - details = evt.mojitDetails, - modules = {}, - required = {}, - sorted, - yuiName, - addonName, - acAddonNames = {}; - - if (!evt.args.ress) { - return; - } - - if ('shared' === mojitType) { - return; - } - - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if (!res.yui || !res.yui.name) { - continue; - } - if ('controller' === res.type) { - modules[res.yui.name] = store.yui._makeYUIModuleConfig(env, res); - } - if ('addon' === res.type && 'ac' === res.subtype) { - modules[res.yui.name] = store.yui._makeYUIModuleConfig(env, res); - // HACK/TODO: we are assuming the name of the filename will be - // the same as the addon namespace. This is a bold assumption - // and we will do the right thing eventually. - acAddonNames[res.yui.name] = libpath.basename(res.name); - } - } - - if (!details.controller) { - // It's not an error if a mojit is missing a controller, since - // some mojits only run on the server side (or only on the - // client side). - return; - } - - required[details.controller] = true; - // the language doesn't matter for this - details.acAddons = []; - sorted = store.yui._precomputeYUIDependencies('en', env, mojitType, modules, required, true); - for (yuiName in sorted.paths) { - if (sorted.paths.hasOwnProperty(yuiName)) { - addonName = acAddonNames[yuiName]; - if (addonName) { - details.acAddons.push(addonName); - } - } - } - } - - }); - - Y.namespace('mojito.addons.rs')['dispatch-helper'] = RSAddonDispatchHelper; - -}, '0.0.1', { requires: [ - 'addon-rs-yui', - 'plugin', - 'oop' -]}); diff --git a/lib/app/autoload/package-walker.server.js b/lib/app/autoload/package-walker.server.js index 746df88e4..76c66a38b 100644 --- a/lib/app/autoload/package-walker.server.js +++ b/lib/app/autoload/package-walker.server.js @@ -103,8 +103,9 @@ BreadthFirstPackageWalker.prototype._walkPackage = function(work, cb) { }; work.pkg = pkg; } - cb(null, work); - this._walkModules(work); + if (cb(null, work) !== false) { + this._walkModules(work); + } }; diff --git a/lib/app/autoload/store.server.js b/lib/app/autoload/store.server.js index 12803262d..b5eb21732 100644 --- a/lib/app/autoload/store.server.js +++ b/lib/app/autoload/store.server.js @@ -6,7 +6,7 @@ /*jslint - anon:true, sloppy:true, regexp: true, continue: true, nomen:true, node:true, stupid:true, plusplus: true + forin: true, anon:true, regexp: true, continue: true, nomen:true, node:true, stupid:true, plusplus: true */ /*global YUI*/ @@ -688,9 +688,14 @@ YUI.add('mojito-resource-store', function(Y, NAME) { views: {} }, template, - specPath; - - for (r = 0; r < ress.length; r += 1) { + specPath, + modules = {}, + controller, + required = {}, + addonName, + acAddonNames = {}; + + for (r in ress) { res = ress[r]; if (res.type === 'config') { @@ -709,6 +714,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (res.type === 'controller') { details.controller = res.yui.name; + controller = this.yui._makeYUIModuleConfig(env, res); continue; } @@ -761,15 +767,34 @@ YUI.add('mojito-resource-store', function(Y, NAME) { continue; } - if (res.type === 'addon') { - // No need to track either of these explicitly, since - // AC addons is handled by calculating dependencies of the - // controller, and view engines are handled by the view - // filenames. - continue; + if ('addon' === res.type && 'ac' === res.subtype) { + modules[res.yui.name] = this.yui._makeYUIModuleConfig(env, res); + // HACK/TODO: we are assuming the name of the filename will be + // the same as the addon namespace. This is a bold assumption + // and we will do the right thing eventually. + acAddonNames[res.yui.name] = this._libs.path.basename(res.name); } } + if (controller) { + details.acAddons = []; + (function getRequiredAddons(requires, seen) { + var r, + module; + for (r = 0; r < requires.length; r++) { + module = requires[r]; + if (!modules[module] || seen[module]) { + continue; + } + seen[module] = true; + getRequiredAddons(modules[module].requires, seen); + if (acAddonNames[module]) { + details.acAddons.push(acAddonNames[module]); + } + } + }(controller.requires, {})); + } + // Since the binders are not part of the server runtime, but are needed // to define the binders map, we need to synthetically build this. if (env !== 'client') { @@ -1155,6 +1180,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if ('mojito' === info.pkg.name) { walkedMojito = true; } + + if (info.depth !== 0 && (!info.pkg.yahoo || !info.pkg.yahoo.mojito)) { + return false; + } + me._preloadPackage(info); }); @@ -1517,10 +1547,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @method resolveResourceVersions */ resolveResourceVersions: function () { - var crtType, - resources, + var mojitType, + ress, r, - crtRes, + res, e, env, envs = ['client', 'server'], @@ -1528,29 +1558,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { posl, posls; - // for each resource of each mojit, create the store data structure - for (crtType in this._mojitRVs) { - if (this._mojitRVs.hasOwnProperty(crtType)) { - resources = this._mojitRVs[crtType]; - - this._mojitDetails[crtType] = {}; - - for (r = 0; r < resources.length; r++) { - crtRes = resources[r]; - - // create the new selector->affinity map if it doesn't exist - if (!this._mojitDetails[crtType][crtRes.selector]) { - this._mojitDetails[crtType][crtRes.selector] = {}; - } - if (!this._mojitDetails[crtType][crtRes.selector][crtRes.affinity]) { - this._mojitDetails[crtType][crtRes.selector][crtRes.affinity] = []; - } - - this._mojitDetails[crtType][crtRes.selector][crtRes.affinity].push(crtRes); - } - } - } - // if we don't want to lazy resolve, resolve for all posls if (!this.lazyResolve) { posls = this.selector.getAllPOSLs(); @@ -1559,22 +1566,20 @@ YUI.add('mojito-resource-store', function(Y, NAME) { for (p = 0; p < posls.length; p++) { - for (crtType in this._mojitRVs) { - if (this._mojitRVs.hasOwnProperty(crtType)) { - - if (crtType === 'shared') { - continue; - } + for (mojitType in this._mojitRVs) { - this.resolveVersion(crtType, envs[e], posls[p]); + if (mojitType === 'shared') { + continue; } + + this.resolveVersion(mojitType, envs[e], posls[p]); } } } } - }, + /** * Find a mojit details in the resource store at runtime * @param {String} type the mojit type @@ -1586,51 +1591,80 @@ YUI.add('mojito-resource-store', function(Y, NAME) { var s, currentSelector, r, - currentResources, + res, + ress, + mojitResources, currentResourcesLen, - t, currentType, - types = [type, 'shared'], - e, + a, affinities = [env, 'common'], poslString = posl.toString(), + sharedResources, resolvedResources = {}, mojitRes, - result; + result, + details = this._mojitDetailsCache[type + poslString + env]; - if (this._mojitDetailsCache[type + poslString + env]) { - return JSON.parse(this._mojitDetailsCache[type + poslString + env]); + if (details) { + return type === 'shared' ? details : JSON.parse(details); } - for (s = 0; s < posl.length; s++) { // ~3 - currentSelector = posl[s]; + ress = this._mojitRVs[type]; - for (t = 0; t < types.length; t++) { // 2 - currentType = types[t]; + this._mojitDetails[type] = {}; - for (e = 0; e < affinities.length; e++) { // 2 - currentResources = (this._mojitDetails[currentType][currentSelector] && - this._mojitDetails[currentType][currentSelector][affinities[e]]) || []; + for (r = 0; r < ress.length; r++) { + res = ress[r]; + + // create the new selector->affinity map if it doesn't exist + if (!this._mojitDetails[type][res.selector]) { + this._mojitDetails[type][res.selector] = {}; + } + if (!this._mojitDetails[type][res.selector][res.affinity]) { + this._mojitDetails[type][res.selector][res.affinity] = []; + } + + this._mojitDetails[type][res.selector][res.affinity].push(res); + } - // remember each resource we find for this posl, type and environment + for (s = 0; s < posl.length; s++) { + currentSelector = posl[s]; + + for (a = 0; a < affinities.length; a++) { + mojitResources = (this._mojitDetails[type][currentSelector] && + this._mojitDetails[type][currentSelector][affinities[a]]) || []; + + // remember each resource we find for this posl, type and environment + // prioritize: we prefer shallower resources + mojitResources.sort(resourceSortByDepthTest); + for (r = 0; r < mojitResources.length; r++) { // prioritize: we prefer shallower resources - currentResources.sort(resourceSortByDepthTest); - for (r = 0; r < currentResources.length; r++) { // ~3 - // prioritize: we prefer shallower resources - resolvedResources[currentResources[r].id] = resolvedResources[currentResources[r].id] || currentResources[r]; - } + resolvedResources[mojitResources[r].id] = resolvedResources[mojitResources[r].id] || mojitResources[r]; } } } + if (type === 'shared') { + this._mojitDetailsCache[type + poslString + env] = resolvedResources; + return this._mojitDetailsCache[type + poslString + env]; + } + + sharedResources = this.resolveVersion('shared', env, posl); + + for (r in sharedResources) { + resolvedResources[r] = resolvedResources[r] || sharedResources[r]; + } + + // TODO: should we resolve mojitDetails if there is no corresponding controller? mojitRes = this.getResourceVersions({type: 'mojit', name: type, selector: '*'})[0]; - result = this.resolveMojitDetails(env, posl, type, Y.Object.values(resolvedResources), mojitRes); + result = this.resolveMojitDetails(env, posl, type, resolvedResources, mojitRes); this._mojitDetailsCache[type + poslString + env] = JSON.stringify(result); - return result; + return result; }, + //==================================================================== // PRIVATE METHODS diff --git a/package.json b/package.json index 009e2a5af..f4828d1ea 100644 --- a/package.json +++ b/package.json @@ -52,7 +52,7 @@ "mojito-cli": "~0.1", "node-static": ">0.6.8", "wrench": "~1.3.9", - "yahoo-arrow": "0.3.3", + "yahoo-arrow": "0.5.x", "portfinder": "0.2.1" }, "optionalDependencies": { diff --git a/tests/func/serveronly/serveronlytest_descriptor.json b/tests/func/serveronly/serveronlytest_descriptor.json index 79864cd45..a5903b049 100644 --- a/tests/func/serveronly/serveronlytest_descriptor.json +++ b/tests/func/serveronly/serveronlytest_descriptor.json @@ -325,8 +325,8 @@ { "controller": "locator", "params": { - "value": "#a_headers", - "click": true + "value": "#a_headers", + "click": true } }, { @@ -347,13 +347,10 @@ "params": { "value": "#p_simpleWS", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#output" - } + }, + "waitForElements": [ + "#output" + ] }, { "test" : "testrestlib-SimpleWSCallClient.js" @@ -373,25 +370,12 @@ "params": { "value": "#p_inspectResp", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#code" - } - }, - { - "controller": "locator", - "params": { - "value": "#msg" - } - }, - { - "controller": "locator", - "params": { - "value": "#output" - } + }, + "waitForElements": [ + "#code", + "#msg", + "#output" + ] }, { "test" : "testrestlib-inspectResponseClient.js" @@ -411,13 +395,10 @@ "params": { "value": "#p_inspectResp", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#headers" - } + }, + "waitForElements": [ + "#headers" + ] }, { "test" : "testrestlib-inspectResponseHeaderClient.js" @@ -437,13 +418,10 @@ "params": { "value": "#p_inspectErr", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#td_inspectErr" - } + }, + "waitForElements": [ + "#td_inspectErr" + ] }, { "test" : "testrestlib-inspectErrorClient.js" @@ -463,13 +441,10 @@ "params": { "value": "#p_getParam", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#output" - } + }, + "waitForElements": [ + "#output" + ] }, { "test" : "testrestlib-GETWithParamsClient.js" @@ -489,13 +464,10 @@ "params": { "value": "#p_getParamNegative", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#output" - } + }, + "waitForElements": [ + "#output" + ] }, { "test" : "testrestlib-GETWithParamsNegativeClient.js" @@ -515,13 +487,10 @@ "params": { "value": "#p_postParam", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#output" - } + }, + "waitForElements": [ + "#output" + ] }, { "test" : "testrestlib-POSTWithParamsClient.js" @@ -541,13 +510,10 @@ "params": { "value": "#p_postParamNegative", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#output" - } + }, + "waitForElements": [ + "#output" + ] }, { "test" : "testrestlib-POSTWithParamsNegativeClient.js" @@ -567,13 +533,10 @@ "params": { "value": "#p_putParam", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#output" - } + }, + "waitForElements": [ + "#output" + ] }, { "test" : "testrestlib-PUTWithParamsClient.js" @@ -593,13 +556,10 @@ "params": { "value": "#p_deleteParam", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#output" - } + }, + "waitForElements": [ + "#my_header" + ] }, { "test" : "testrestlib-DELETEWithParamsClient.js" @@ -619,13 +579,11 @@ "params": { "value": "#p_headers", "click": true - } - }, - { - "controller": "locator", - "params": { - "value": "#my_header" - } + }, + "waitForElements": [ + "#output" + ] + }, { "test" : "testrestlib-HeadersClient.js" @@ -734,7 +692,7 @@ ] } } - } + } }, {