From 1eeea27086b3778e8d9c088c5c6654516a144d13 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 23 May 2012 15:05:42 -0700 Subject: [PATCH 001/119] moved initial resource store redesign work --- .../part4/paged-yql/application.json | 12 + source/lib/libs/ycb.js | 30 + source/lib/management/commands/x.js | 179 +++ source/lib/store-new.server.js | 1090 +++++++++++++++++ 4 files changed, 1311 insertions(+) create mode 100644 source/lib/management/commands/x.js create mode 100644 source/lib/store-new.server.js diff --git a/examples/getting-started-guide/part4/paged-yql/application.json b/examples/getting-started-guide/part4/paged-yql/application.json index 524272a8c..b58ffbdcc 100644 --- a/examples/getting-started-guide/part4/paged-yql/application.json +++ b/examples/getting-started-guide/part4/paged-yql/application.json @@ -23,5 +23,17 @@ "type": "PagedFlickr" } } + }, + { + "settings": [ "device:iphone" ], + "selector": "iphone" + }, + { + "settings": [ "device:android" ], + "selector": "droid" + }, + { + "settings": [ "device:android", "environment:dev" ], + "selector": "devdroid" } ] diff --git a/source/lib/libs/ycb.js b/source/lib/libs/ycb.js index 10fdf24ae..3bce9ad62 100644 --- a/source/lib/libs/ycb.js +++ b/source/lib/libs/ycb.js @@ -62,6 +62,36 @@ module.exports = { return config; }, + readNoMerge: function(bundle, context, debug){ + var rawConfig, lookupPaths, + path, config = []; + + if(!context){ + context = {}; + } + + rawConfig = this._processRawBundle(bundle); + lookupPaths = this._getLookupPaths(rawConfig.dimensions, context); + + if(debug){ + console.log(JSON.stringify(context,null,4)); + console.log(JSON.stringify(rawConfig,null,4)); + console.log(JSON.stringify(lookupPaths,null,4)); + } + + // Now we simply merge each macting settings section we find into the config + for(path=0; path= 1 && 'actions' === fs.typeDirArray[0]) { + if ('.js' !== fs.ext) { + return false; + } + res = { + source: evt.source, + mojit: evt.mojitType, + type: 'action', + affinity: 'server', + selector: '*' + }; + if (baseParts.length >= 3) { + res.selector = baseParts.pop(); + } + if (baseParts.length >= 2) { + res.affinity = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid action filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + if (!fs.isFile && '.' === fs.typeDir && 'addons' === fs.basename) { + return true; + } + if (!fs.isFile && 'addons' === fs.typeDirArray[0]) { + return true; + } + if (fs.isFile && fs.typeDirArray.length >= 2 && 'addons' === fs.typeDirArray[0]) { + if ('.js' !== fs.ext) { + return false; + } + res = { + source: evt.source, + mojit: evt.mojitType, + type: 'action', + subtype: fs.typeDirArray[1], + affinity: 'server', + selector: '*' + }; + if (baseParts.length >= 3) { + res.selector = baseParts.pop(); + } + if (baseParts.length >= 2) { + res.affinity = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid addon filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(2).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(2).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + if (!fs.isFile && '.' === fs.typeDir && 'archetypes' === fs.basename) { + return true; + } + if (!fs.isFile && fs.typeDirArray.length < 2 && 'archetypes' === fs.typeDirArray[0]) { + return true; + } + if (!fs.isFile && fs.typeDirArray.length == 2 && 'archetypes' === fs.typeDirArray[0]) { + res = { + source: evt.source, + mojit: null, + type: 'archetype', + subtype: fs.typeDirArray[1], + name: fs.basename, + affinity: 'common', + selector: '*' + }; + fs.relativePath = fs.basename; + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + if (!fs.isFile && '.' === fs.typeDir && 'assets' === fs.basename) { + return true; + } + if (!fs.isFile && 'assets' === fs.typeDirArray[0]) { + return true; + } + if (fs.isFile && fs.typeDirArray.length >= 1 && 'assets' === fs.typeDirArray[0]) { + res = { + source: evt.source, + mojit: evt.mojitType, + type: 'asset', + subtype: fs.ext.substr(1), + affinity: 'common', + selector: '*' + }; + if (baseParts.length >= 2) { + res.selector = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid asset filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + if (!fs.isFile && '.' === fs.typeDir && 'binders' === fs.basename) { + return true; + } + if (!fs.isFile && 'binders' === fs.typeDirArray[0]) { + return true; + } + if (fs.isFile && fs.typeDirArray.length >= 1 && 'binders' === fs.typeDirArray[0]) { + if ('.js' !== fs.ext) { + return false; + } + res = { + source: evt.source, + mojit: evt.mojitType, + type: 'binder', + affinity: 'common', + selector: '*' + }; + if (baseParts.length >= 2) { + res.selector = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid binder filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + if (!fs.isFile && '.' === fs.typeDir && 'commands' === fs.basename) { + return true; + } + if (!fs.isFile && 'commands' === fs.typeDirArray[0]) { + return true; + } + if (fs.isFile && fs.typeDirArray.length >= 1 && 'commands' === fs.typeDirArray[0]) { + if ('.js' !== fs.ext) { + return false; + } + res = { + source: evt.source, + mojit: null, + type: 'command', + affinity: 'common', + selector: '*' + }; + if (baseParts.length !== 1) { + Y.log('invalid command filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + if (fs.isFile && '.' === fs.typeDir && 'controller' === baseParts[0]) { + if ('.js' !== fs.ext) { + return false; + } + res = { + source: evt.source, + mojit: evt.mojitType, + type: 'controller', + name: 'controller', + affinity: 'server', + selector: '*' + }; + if (baseParts.length >= 3) { + res.selector = baseParts.pop(); + } + if (baseParts.length >= 2) { + res.affinity = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid controller filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + if (!fs.isFile && '.' === fs.typeDir && 'middleware' === fs.basename) { + return true; + } + if (!fs.isFile && 'middleware' === fs.typeDirArray[0]) { + return true; + } + if (fs.isFile && fs.typeDirArray.length >= 1 && 'middleware' === fs.typeDirArray[0]) { + if ('.js' !== fs.ext) { + return false; + } + res = { + source: evt.source, + mojit: null, + type: 'middleware', + affinity: 'common', + selector: '*' + }; + if (baseParts.length !== 1) { + Y.log('invalid middleware filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + if (!fs.isFile && '.' === fs.typeDir && 'models' === fs.basename) { + return true; + } + if (!fs.isFile && 'models' === fs.typeDirArray[0]) { + return true; + } + if (fs.isFile && fs.typeDirArray.length >= 1 && 'models' === fs.typeDirArray[0]) { + if ('.js' !== fs.ext) { + return false; + } + res = { + source: evt.source, + mojit: evt.mojitType, + type: 'model', + affinity: 'server', + selector: '*' + }; + if (baseParts.length >= 3) { + res.selector = baseParts.pop(); + } + if (baseParts.length >= 2) { + res.affinity = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid model filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + // TODO: spec, in "config" or "instance" plugin? + + if (!fs.isFile && '.' === fs.typeDir && 'views' === fs.basename) { + return true; + } + if (!fs.isFile && 'views' === fs.typeDirArray[0]) { + return true; + } + if (fs.isFile && fs.typeDirArray.length >= 1 && 'views' === fs.typeDirArray[0]) { + res = { + source: evt.source, + mojit: evt.mojitType, + type: 'view', + viewOutputFormat: fs.ext.substr(1), + viewEngine: baseParts.pop(), + affinity: 'common', + selector: '*' + }; + if (baseParts.length >= 2) { + res.selector = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid view filename. skipping ' + fs.fullPath, 'warn', NAME); + return false; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.addResourceVersion(res); + return false; + } + + // TODO: yui-lang in the "yui" plugin + + // TODO: yui-module in the "yui" plugin + +//console.log('--TODO-- preloadFile ' + evt.mojitType + ' ' + libpath.join(fs.typeDir, fs.basename + fs.ext)); + return true; + }, + + + addResourceVersion: function(res) { +// console.log('---------------------------------------------- TODO addResourceVersion -- ' + res.mojit + ' ' + res.id); +// console.log(res); + }, + + + resolveResourceVersions: function() { + console.log('---------------------------------------------- TODO resolveResourceVersions'); + // OLD: _cookdown + }, + + + /** + * Finds resources based on our conventions + * -doesn't- load mojits or their contents. That's done elsewhere. + * + * @method _findResourcesByConvention + * @param dir {string} directory from which to find resources + * @param pkg {object} metadata (name and version) about the package + * @param mojitType {string|null} name of mojit to which the resource belongs + * @return {array} list of resources + * @private + */ + _findResourcesByConvention: function(dir, pkg, mojitType) { + var me = this; + //console.log('-- FIND RESOURCES BY CONVENTION -- ' + pkg.name + '@' + pkg.version + ' -- ' + mojitType); + + this._walkDirRecursive(dir, function(error, subdir, file, isFile) { + var source; + + if ('node_modules' === file) { + return false; + } + if ('libs' === file) { + return false; + } + // TODO: merge in fixes from master + if ('tests' === file) { + return false; + } + // mojits are loaded another way later + // TODO: better test for what is a mojit dir (i.e., check against mojitDirs and mojitsDirs) + if ('.' === subdir && 'mojits' === file) { + return false; + } + + source = { + fs: { + fullPath: libpath.join(dir, subdir, file), + baseDir: dir, + typeDir: subdir, + typeDirArray: subdir.split('/'), + isFile: isFile, + ext: libpath.extname(file) + }, + pkg: pkg + }; + source.fs.basename = libpath.basename(file, source.fs.ext); + + if (me._skipBadPath(source.fs)) { + return false; + } + return me.fire('preloadFile', { source: source, mojitType: mojitType } ); + }); + }, + + + /** + * Indicates whether file should be skipped based on its path + * + * @method _skipBadPath + * @param pathParts {object} the "source.fs" part of the resource + * @return {boolean} true indicates that the file should be skipped + * @private + */ + _skipBadPath: function(fs) { + if (fs.isFile && fs.ext.substr(1).match(isNotAlphaNum)) { + return true; + } + return false; + }, + + + /** + * A wrapper for fs.readdirSync() that guarantees ordering. The order in + * which the file system is walked is significant within the resource + * store, e.g., when looking up a matching context. + * + * @method _sortedReaddirSync + * @param path {string} directory to read + * @return {array} files in the directory + * @private + */ + _sortedReaddirSync: function(path) { + var out = libfs.readdirSync(path); + return out.sort(); + }, + + + /** + * Recursively walks a directory + * + * @method _walkDirRecursive + * @param dir {string} directory to start at + * @param cb {function(error, subdir, name, isFile)} callback called for each file + * @param _subdir {string} INTERNAL argument, please ignore + * @return {nothing} value returned via callback + * @private + */ + _walkDirRecursive: function(dir, cb, _subdir) { + var subdir, + fulldir, + children, + i, + childName, + childPath, + childFullPath, + childStat; + + subdir = _subdir || '.'; + fulldir = libpath.join(dir, subdir); + if (!libpath.existsSync(fulldir)) { + return; + } + + children = this._sortedReaddirSync(fulldir); + for (i = 0; i < children.length; i += 1) { + childName = children[i]; + if ('.' === childName.substring(0, 1)) { + continue; + } + if ('node_modules' === childName) { + continue; + } + childPath = libpath.join(subdir, childName); + childFullPath = libpath.join(dir, childPath); + childStat = libfs.statSync(childFullPath); + if (childStat.isFile()) { + cb(null, subdir, childName, true); + } else if (childStat.isDirectory()) { + if (cb(null, subdir, childName, false)) { + this._walkDirRecursive(dir, cb, childPath); + } + } + } + }, + + + /** + * takes a list of globs and turns it into a list of matching paths + * @param prefix {string} prefix for every path in the list + * @param list {array} list of globs + * @return {array} list of paths matching the globs + * @private + */ + _globList: function(prefix, list) { + var found = [], + i, + glob; + for (i = 0; i < list.length; i += 1) { + glob = list[i]; + if ('/' !== glob.charAt(0)) { + glob = libpath.join(prefix, glob); + } + libglob.globSync(glob, {}, found); + } + return found; + }, + + + // from http://stackoverflow.com/questions/171251/ + // how-can-i-merge-properties-of-two-javascript-objects-dynamically/ + // 383245#383245 + /** + * Recursively merge one object onto another + * + * @method mergeRecursive + * @param dest {object} object to merge into + * @param src {object} object to merge onto "dest" + * @param matchType {boolean} controls whether a non-object in the src is + * allowed to clobber a non-object in the dest (if a different type) + * @return {object} the modified "dest" object is also returned directly + */ + mergeRecursive: function(dest, src, typeMatch) { + var p; + for (p in src) { + if (src.hasOwnProperty(p)) { + // Property in destination object set; update its value. + if (src[p] && src[p].constructor === Object) { + if (!dest[p]) { + dest[p] = {}; + } + dest[p] = this.mergeRecursive(dest[p], src[p]); + } else { + if (dest[p] && typeMatch) { + if (typeof dest[p] === typeof src[p]) { + dest[p] = src[p]; + } + } else { + dest[p] = src[p]; + } + } + } + } + return dest; + }, + + + /** + * @method cloneObj + * @param o {mixed} + * @return {mixed} deep copy of argument + */ + cloneObj: function(o) { + var newO, + i; + + if (typeof o !== 'object') { + return o; + } + if (!o) { + return o; + } + + if ('[object Array]' === Object.prototype.toString.apply(o)) { + newO = []; + for (i = 0; i < o.length; i += 1) { + newO[i] = this.cloneObj(o[i]); + } + return newO; + } + + newO = {}; + for (i in o) { + if (o.hasOwnProperty(i)) { + newO[i] = this.cloneObj(o[i]); + } + } + return newO; + } + + + }); + + Y.namespace('mojito'); + Y.mojito.ResourceStore = ResourceStore; + + +}, '0.0.1', { requires: [ + 'base', + 'oop' +]}); + + +YUI.add('addon-rs-config', function(Y, NAME) { + + var libfs = require('fs'), + libpath = require('path'), + libycb = require('./libs/ycb'); + + function RSAddonConfig() { + RSAddonConfig.superclass.constructor.apply(this, arguments); + } + RSAddonConfig.NS = 'config'; + RSAddonConfig.ATTRS = {}; + + Y.extend(RSAddonConfig, Y.Plugin.Base, { + + initializer: function(config) { + this.rs = config.host; + this.appRoot = config.appRoot; + this.mojitoRoot = config.mojitoRoot; + this.rs.on('preloadFile', this.preloadFile, this); + + this._jsonCache = {}; // fullPath: contents as JSON object + this._ycbCache = {}; // fullPath: context: YCB config object + this._ycbDims = this._readYcbDimensions(); + }, + + + // TODO: needed to break cycle so we don't leak memory? + destructor: function() { + this.rs = null; + }, + + + /** + * Reads and parses a JSON file + * + * @method readConfigJSON + * @param fullPath {string} path to JSON file + * @return {mixed} contents of JSON file + */ + readConfigJSON: function(fullPath) { + var json, + contents; + if (!libpath.existsSync(fullPath)) { + return {}; + } + json = this._jsonCache[fullPath]; + if (!json) { + try { + contents = libfs.readFileSync(fullPath, 'utf-8'); + json = JSON.parse(contents); + } catch (e) { + throw new Error('Error parsing JSON file: ' + fullPath); + } + this._jsonCache[fullPath] = json; + } + return json; + }, + + + /** + * reads a configuration file that is in YCB format + * + * @method readConfigYCB + * @param ctx {object} runtime context + * @param fullPath {string} path to the YCB file + * @return {object} the contextualized configuration + */ + readConfigYCB: function(fullPath, ctx) { + var cacheKey, + json, + ycb; + + ctx = this.rs.mergeRecursive(this.rs.getStaticContext(), ctx); + + //cache key only needs to account for dynamic context + cacheKey = JSON.stringify(ctx); + + if (!this._ycbCache[fullPath]) { + this._ycbCache[fullPath] = {}; + } + + ycb = this._ycbCache[fullPath][cacheKey]; + if (!ycb) { + json = this.readConfigJSON(fullPath); + json = this._ycbDims.concat(json); + + // libycb.read() will distructively modify its first argument + ycb = libycb.read(this.rs.cloneObj(json), ctx); + + this._ycbCache[fullPath][cacheKey] = ycb; + } + return ycb; + }, + + + preloadFile: function(evt) { + var fs = evt.source.fs, + mojit = evt.mojitType, + use = false, + baseParts, + res; + + // we only care about files + if (!fs.isFile) { + return true; + } + // we don't care about files in subdirectories + if ('.' !== fs.typeDir) { + return; + } + // we only care about json files + if ('.json' !== fs.ext) { + return; + } + // always use package.json + if ('package' === fs.basename) { + use = true; + } + // use all configs in the application + if (0 === evt.source.pkg.depth) { + use = true; + } + // use configs from non-shared mojits + if (mojit && 'shared' !== mojit) { + use = true; + } + if (!use) { + return; + } + + baseParts = fs.basename.split('.'); + res = { + source: evt.source, + mojit: mojit, + type: 'config', + affinity: 'common', + selector: '*' + }; + if (baseParts.length !== 1) { + Y.log('invalid config filename. skipping ' + fs.fullPath, 'warn', NAME); + return; + } + fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + this.rs.addResourceVersion(res); + }, + + + /** + * Read the application's dimensions.json file for YCB processing. If not + * available, fall back to the framework's default dimensions.json. + * + * @method _readYcbDimensions + * @return {array} contents of the dimensions.json file + * @private + */ + _readYcbDimensions: function() { + var path = libpath.join(this.appRoot, 'dimensions.json'); + if (!libpath.existsSync(path)) { + path = libpath.join(this.mojitoRoot, 'dimensions.json'); + } + return this.readConfigJSON(path); + } + + + }); + Y.namespace('mojito.addons.rs'); + Y.mojito.addons.rs.config = RSAddonConfig; + +}, '0.0.1', { requires: ['plugin', 'oop']}); + From 86f55145c8396cd90ad21fd903dd443ff0eba3ad Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sat, 26 May 2012 09:16:37 -0700 Subject: [PATCH 002/119] cleanups --- source/lib/store-new.server.js | 121 ++++++++++++++++----------------- 1 file changed, 58 insertions(+), 63 deletions(-) diff --git a/source/lib/store-new.server.js b/source/lib/store-new.server.js index 0861c0fa7..727874f56 100644 --- a/source/lib/store-new.server.js +++ b/source/lib/store-new.server.js @@ -328,20 +328,16 @@ YUI.add('resource-store', function(Y, NAME) { mojitType = packageJson.name; } // FUTURE: check NPM "engine" - // TODO: register mojit's package.json as a static asset (in "static handler" plugin?) + // TODO: register mojit's package.json as a static asset, in "static handler" plugin } // TODO: this._mojitPaths[mojitType] = dir; - // replace with something more generic + // replace with something more generic (some kind of "mojitType attributes" store) definitionJson = this.config.readConfigYCB(libpath.join(dir, 'definition.json'), {}); if (definitionJson.appLevel) { mojitType = 'shared'; } - if ('shared' !== mojitType) { - // TODO: register definition.json as a dynamic URL - } - this._findResourcesByConvention(dir, pkg, mojitType); }, @@ -352,13 +348,13 @@ YUI.add('resource-store', function(Y, NAME) { res; // TODO: test - if (!fs.isFile && '.' === fs.typeDir && 'actions' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'actions' === fs.basename) { return true; } - if (!fs.isFile && 'actions' === fs.typeDirArray[0]) { + if (!fs.isFile && 'actions' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.typeDirArray.length >= 1 && 'actions' === fs.typeDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 1 && 'actions' === fs.subDirArray[0]) { if ('.js' !== fs.ext) { return false; } @@ -379,20 +375,20 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid action filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; } - if (!fs.isFile && '.' === fs.typeDir && 'addons' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'addons' === fs.basename) { return true; } - if (!fs.isFile && 'addons' === fs.typeDirArray[0]) { + if (!fs.isFile && 'addons' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.typeDirArray.length >= 2 && 'addons' === fs.typeDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 2 && 'addons' === fs.subDirArray[0]) { if ('.js' !== fs.ext) { return false; } @@ -400,7 +396,7 @@ YUI.add('resource-store', function(Y, NAME) { source: evt.source, mojit: evt.mojitType, type: 'action', - subtype: fs.typeDirArray[1], + subtype: fs.subDirArray[1], affinity: 'server', selector: '*' }; @@ -414,25 +410,25 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid addon filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(2).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(2).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(2).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(2).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; } - if (!fs.isFile && '.' === fs.typeDir && 'archetypes' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'archetypes' === fs.basename) { return true; } - if (!fs.isFile && fs.typeDirArray.length < 2 && 'archetypes' === fs.typeDirArray[0]) { + if (!fs.isFile && fs.subDirArray.length < 2 && 'archetypes' === fs.subDirArray[0]) { return true; } - if (!fs.isFile && fs.typeDirArray.length == 2 && 'archetypes' === fs.typeDirArray[0]) { + if (!fs.isFile && fs.subDirArray.length == 2 && 'archetypes' === fs.subDirArray[0]) { res = { source: evt.source, mojit: null, type: 'archetype', - subtype: fs.typeDirArray[1], + subtype: fs.subDirArray[1], name: fs.basename, affinity: 'common', selector: '*' @@ -443,13 +439,13 @@ YUI.add('resource-store', function(Y, NAME) { return false; } - if (!fs.isFile && '.' === fs.typeDir && 'assets' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'assets' === fs.basename) { return true; } - if (!fs.isFile && 'assets' === fs.typeDirArray[0]) { + if (!fs.isFile && 'assets' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.typeDirArray.length >= 1 && 'assets' === fs.typeDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 1 && 'assets' === fs.subDirArray[0]) { res = { source: evt.source, mojit: evt.mojitType, @@ -465,20 +461,20 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid asset filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; } - if (!fs.isFile && '.' === fs.typeDir && 'binders' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'binders' === fs.basename) { return true; } - if (!fs.isFile && 'binders' === fs.typeDirArray[0]) { + if (!fs.isFile && 'binders' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.typeDirArray.length >= 1 && 'binders' === fs.typeDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 1 && 'binders' === fs.subDirArray[0]) { if ('.js' !== fs.ext) { return false; } @@ -496,20 +492,20 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid binder filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; } - if (!fs.isFile && '.' === fs.typeDir && 'commands' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'commands' === fs.basename) { return true; } - if (!fs.isFile && 'commands' === fs.typeDirArray[0]) { + if (!fs.isFile && 'commands' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.typeDirArray.length >= 1 && 'commands' === fs.typeDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 1 && 'commands' === fs.subDirArray[0]) { if ('.js' !== fs.ext) { return false; } @@ -524,14 +520,14 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid command filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; } - if (fs.isFile && '.' === fs.typeDir && 'controller' === baseParts[0]) { + if (fs.isFile && '.' === fs.subDir && 'controller' === baseParts[0]) { if ('.js' !== fs.ext) { return false; } @@ -553,19 +549,19 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid controller filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; } - if (!fs.isFile && '.' === fs.typeDir && 'middleware' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'middleware' === fs.basename) { return true; } - if (!fs.isFile && 'middleware' === fs.typeDirArray[0]) { + if (!fs.isFile && 'middleware' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.typeDirArray.length >= 1 && 'middleware' === fs.typeDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 1 && 'middleware' === fs.subDirArray[0]) { if ('.js' !== fs.ext) { return false; } @@ -580,20 +576,20 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid middleware filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; } - if (!fs.isFile && '.' === fs.typeDir && 'models' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'models' === fs.basename) { return true; } - if (!fs.isFile && 'models' === fs.typeDirArray[0]) { + if (!fs.isFile && 'models' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.typeDirArray.length >= 1 && 'models' === fs.typeDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 1 && 'models' === fs.subDirArray[0]) { if ('.js' !== fs.ext) { return false; } @@ -614,22 +610,22 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid model filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; } - // TODO: spec, in "config" or "instance" plugin? + // TODO: spec, in "instance" plugin - if (!fs.isFile && '.' === fs.typeDir && 'views' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && 'views' === fs.basename) { return true; } - if (!fs.isFile && 'views' === fs.typeDirArray[0]) { + if (!fs.isFile && 'views' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.typeDirArray.length >= 1 && 'views' === fs.typeDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 1 && 'views' === fs.subDirArray[0]) { res = { source: evt.source, mojit: evt.mojitType, @@ -646,8 +642,8 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid view filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.addResourceVersion(res); return false; @@ -657,7 +653,7 @@ YUI.add('resource-store', function(Y, NAME) { // TODO: yui-module in the "yui" plugin -//console.log('--TODO-- preloadFile ' + evt.mojitType + ' ' + libpath.join(fs.typeDir, fs.basename + fs.ext)); +//console.log('--TODO-- preloadFile ' + evt.mojitType + ' ' + libpath.join(fs.subDir, fs.basename + fs.ext)); return true; }, @@ -698,8 +694,7 @@ YUI.add('resource-store', function(Y, NAME) { if ('libs' === file) { return false; } - // TODO: merge in fixes from master - if ('tests' === file) { + if ('tests' === file && 'test' !== me._appConfigStatic.env) { return false; } // mojits are loaded another way later @@ -711,9 +706,9 @@ YUI.add('resource-store', function(Y, NAME) { source = { fs: { fullPath: libpath.join(dir, subdir, file), - baseDir: dir, - typeDir: subdir, - typeDirArray: subdir.split('/'), + rootDir: dir, + subDir: subdir, + subDirArray: subdir.split('/'), isFile: isFile, ext: libpath.extname(file) }, @@ -1023,7 +1018,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { return true; } // we don't care about files in subdirectories - if ('.' !== fs.typeDir) { + if ('.' !== fs.subDir) { return; } // we only care about json files @@ -1058,8 +1053,8 @@ YUI.add('addon-rs-config', function(Y, NAME) { Y.log('invalid config filename. skipping ' + fs.fullPath, 'warn', NAME); return; } - fs.relativePath = libpath.join(fs.typeDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.typeDirArray.slice(1).join('/'), baseParts.join('.')); + fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); this.rs.addResourceVersion(res); }, From ace2ac81dd4dac13b815c40af636e019f1c5e8f7 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 27 May 2012 08:38:50 -0700 Subject: [PATCH 003/119] reworked how resources are found by convention --- source/lib/store-new.server.js | 501 ++++++++++++++++----------------- 1 file changed, 240 insertions(+), 261 deletions(-) diff --git a/source/lib/store-new.server.js b/source/lib/store-new.server.js index 727874f56..fdf7cb56d 100644 --- a/source/lib/store-new.server.js +++ b/source/lib/store-new.server.js @@ -22,7 +22,22 @@ YUI.add('resource-store', function(Y, NAME) { isNotAlphaNum = /[^a-zA-Z0-9]/, - mojitoRoot = __dirname; + mojitoRoot = __dirname, + + CONVENTION_SUBDIR_TYPES = { + // subdir: type + 'actions': 'action', + 'binders': 'binder', + 'commands': 'command', + 'middleware': 'middleware', + 'models': 'model', + 'views': 'view' + }, + CONVENTION_SUBDIR_TYPE_IS_JS = { + 'action': true, + 'binder': true, + 'model': true + }; // The Affinity object is to manage the use of the affinity string in @@ -60,9 +75,6 @@ YUI.add('resource-store', function(Y, NAME) { this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: context: YCB config object - this.publish('preloadFile', { prefix:'rs', emitFacade:true }); - this.on('preloadFile', this.preloadFile, this); - Y.Object.each(Y.mojito.addons.rs, function(fn, name) { this.plug(fn, { appRoot:cfg.root, mojitoRoot:mojitoRoot }); }, this); @@ -136,6 +148,7 @@ YUI.add('resource-store', function(Y, NAME) { // user might not have installed mojito as a dependency of their // application. (they -should- have but might not have.) + // FUTURE: instead walk -all- global packages? if (!walkedMojito) { dir = libpath.join(mojitoRoot, '..'); info = { @@ -201,11 +214,10 @@ YUI.add('resource-store', function(Y, NAME) { case 'bundle': dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); this._preloadDirBundle(dir, pkg); - this._preloadDirMojits(libpath.join(dir, 'mojits'), pkg); break; case 'mojit': dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); - this.preloadDirMojit(dir, pkg); + this.preloadDirMojit(dir, 'pkg', pkg); break; default: Y.log('Unknown package type "' + info.pkg.yahoo.mojito.type + '"', 'warn', NAME); @@ -223,21 +235,31 @@ YUI.add('resource-store', function(Y, NAME) { * @return {nothing} work down via other called methods */ preloadApp: function(pkg) { - var list, + var ress, + r, + res, + list, i; - this._preloadDirBundle(this._config.root, pkg); + ress = this._findResourcesByConvention(this._config.root, 'app', pkg, 'shared'); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if ('mojit' !== res.type) { + // ignore app-level mojits found by convention, since they'll be loaded below + this.addResourceVersion(ress[r]); + } + } // load mojitsDirs list = this._globList(this._config.root, this._appConfigStatic.mojitsDirs); for (i = 0; i < list.length; i += 1) { - this._preloadDirMojits(list[i], pkg); + this._preloadDirMojits(list[i], 'app', pkg); } // load mojitDirs list = this._globList(this._config.root, this._appConfigStatic.mojitDirs || []); for (i = 0; i < list.length; i += 1) { - this.preloadDirMojit(list[i], pkg); + this.preloadDirMojit(list[i], 'app', pkg); } }, @@ -252,7 +274,17 @@ YUI.add('resource-store', function(Y, NAME) { * @private */ _preloadDirBundle: function(dir, pkg) { - this._findResourcesByConvention(dir, pkg, 'shared'); + var ress, + r, + res; + // FUTURE: support configuration too + + ress = this._findResourcesByConvention(dir, 'bundle', pkg, 'shared'); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + this.addResourceVersion(res); + } + this._preloadDirMojits(libpath.join(dir, 'mojits'), 'bundle', pkg); }, @@ -261,11 +293,12 @@ YUI.add('resource-store', function(Y, NAME) { * * @method _preloadDirMojits * @param dir {string} directory path + * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" * @param pkg {object} metadata (name and version) about the package * @return {nothing} work down via other called methods * @private */ - _preloadDirMojits: function(dir, pkg) { + _preloadDirMojits: function(dir, dirType, pkg) { var i, realDirs, children, @@ -287,7 +320,7 @@ YUI.add('resource-store', function(Y, NAME) { continue; } childPath = libpath.join(dir, childName); - this.preloadDirMojit(childPath, pkg); + this.preloadDirMojit(childPath, dirType, pkg); } }, @@ -297,21 +330,18 @@ YUI.add('resource-store', function(Y, NAME) { * * @method preloadDirMojit * @param dir {string} directory path + * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" * @param pkg {object} metadata (name and version) about the package * @return {nothing} work down via other called methods * @private */ - preloadDirMojit: function(dir, pkg) { - var i, - realDirs, - resources, - res, - mojitType, + preloadDirMojit: function(dir, dirType, pkg) { + var mojitType, packageJson, definitionJson, - appConfig, - prefix, - url; + ress, + r, + res; if ('/' !== dir.charAt(0)) { dir = libpath.join(this._config.root, dir); @@ -338,85 +368,83 @@ YUI.add('resource-store', function(Y, NAME) { mojitType = 'shared'; } - this._findResourcesByConvention(dir, pkg, mojitType); + res = { + source: { + fs: { + fullPath: dir, + rootDir: dir, + rootType: dirType, + subDir: '.', + subDirArray: ['.'], + basename: libpath.basename(dir), + isFile: false, + ext: null + }, + pkg: pkg + }, + type: 'mojit', + subtype: null, + name: mojitType, + id: 'mojit--' + mojitType, + mojit: null, + affinity: 'common', + selector: '*' + }; + this.addResourceVersion(res); + + ress = this._findResourcesByConvention(dir, 'mojit', pkg, mojitType); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + // just in case, only add those resources that really do belong to us + if (res.mojit === mojitType) { + this.addResourceVersion(res); + } + // FUTURE: else warn? + } }, - preloadFile: function(evt) { - var fs = evt.source.fs, + findResourceByConvention: function(source, mojitType) { + var fs = source.fs, baseParts = fs.basename.split('.'), - res; + type; - // TODO: test - if (!fs.isFile && '.' === fs.subDir && 'actions' === fs.basename) { + if (!fs.isFile && '.' === fs.subDir && CONVENTION_SUBDIR_TYPES[fs.basename]) { return true; } - if (!fs.isFile && 'actions' === fs.subDirArray[0]) { + type = CONVENTION_SUBDIR_TYPES[fs.subDirArray[0]]; + if (!fs.isFile && type) { return true; } - if (fs.isFile && fs.subDirArray.length >= 1 && 'actions' === fs.subDirArray[0]) { - if ('.js' !== fs.ext) { + if (fs.isFile && type && fs.subDirArray.length >= 1) { + if (CONVENTION_SUBDIR_TYPE_IS_JS[type] && '.js' !== fs.ext) { return false; } - res = { - source: evt.source, - mojit: evt.mojitType, - type: 'action', - affinity: 'server', - selector: '*' + return { + type: type, + skipSubdirParts: 1 }; - if (baseParts.length >= 3) { - res.selector = baseParts.pop(); - } - if (baseParts.length >= 2) { - res.affinity = baseParts.pop(); - } - if (baseParts.length !== 1) { - Y.log('invalid action filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; - } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); - res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; } + // special case: addons if (!fs.isFile && '.' === fs.subDir && 'addons' === fs.basename) { return true; } - if (!fs.isFile && 'addons' === fs.subDirArray[0]) { + if (!fs.isFile && fs.subDirArray.length < 2 && 'addons' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.subDirArray.length >= 2 && 'addons' === fs.subDirArray[0]) { + if (fs.isFile && fs.subDirArray.length >= 1 && 'addons' === fs.subDirArray[0]) { if ('.js' !== fs.ext) { return false; } - res = { - source: evt.source, - mojit: evt.mojitType, - type: 'action', + return { + type: 'addon', subtype: fs.subDirArray[1], - affinity: 'server', - selector: '*' + skipSubdirParts: 2 }; - if (baseParts.length >= 3) { - res.selector = baseParts.pop(); - } - if (baseParts.length >= 2) { - res.affinity = baseParts.pop(); - } - if (baseParts.length !== 1) { - Y.log('invalid addon filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; - } - fs.relativePath = libpath.join(fs.subDirArray.slice(2).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.subDirArray.slice(2).join('/'), baseParts.join('.')); - res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; } + // special case: archetypes if (!fs.isFile && '.' === fs.subDir && 'archetypes' === fs.basename) { return true; } @@ -424,118 +452,86 @@ YUI.add('resource-store', function(Y, NAME) { return true; } if (!fs.isFile && fs.subDirArray.length == 2 && 'archetypes' === fs.subDirArray[0]) { - res = { - source: evt.source, - mojit: null, + return { type: 'archetype', subtype: fs.subDirArray[1], - name: fs.basename, - affinity: 'common', - selector: '*' + skipSubdirParts: 2 }; - fs.relativePath = fs.basename; - res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; } + // special case: assets if (!fs.isFile && '.' === fs.subDir && 'assets' === fs.basename) { return true; } if (!fs.isFile && 'assets' === fs.subDirArray[0]) { return true; } - if (fs.isFile && fs.subDirArray.length >= 1 && 'assets' === fs.subDirArray[0]) { - res = { - source: evt.source, - mojit: evt.mojitType, + if (fs.isFile && 'assets' === fs.subDirArray[0] && fs.subDirArray.length >= 1) { + return { type: 'asset', subtype: fs.ext.substr(1), - affinity: 'common', - selector: '*' + skipSubdirParts: 1 }; - if (baseParts.length >= 2) { - res.selector = baseParts.pop(); - } - if (baseParts.length !== 1) { - Y.log('invalid asset filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; - } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); - res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; } - if (!fs.isFile && '.' === fs.subDir && 'binders' === fs.basename) { - return true; - } - if (!fs.isFile && 'binders' === fs.subDirArray[0]) { - return true; - } - if (fs.isFile && fs.subDirArray.length >= 1 && 'binders' === fs.subDirArray[0]) { + // special case: controller + if (fs.isFile && '.' === fs.subDir && 'controller' === baseParts[0]) { if ('.js' !== fs.ext) { return false; } - res = { - source: evt.source, - mojit: evt.mojitType, - type: 'binder', - affinity: 'common', - selector: '*' + return { + type: 'controller' }; - if (baseParts.length >= 2) { - res.selector = baseParts.pop(); - } - if (baseParts.length !== 1) { - Y.log('invalid binder filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; - } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); - res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; } - if (!fs.isFile && '.' === fs.subDir && 'commands' === fs.basename) { - return true; - } - if (!fs.isFile && 'commands' === fs.subDirArray[0]) { - return true; + // special case: mojit + if (!fs.isFile && '.' === fs.subDir && 'mojits' === fs.basename) { + // don't bother finding mojits here, since they're loaded explicitly in + // the app and bundle in different ways + return false; } - if (fs.isFile && fs.subDirArray.length >= 1 && 'commands' === fs.subDirArray[0]) { - if ('.js' !== fs.ext) { + + // unknown path + return true; + }, + + + parseResource: function(source, type, subtype, mojitType) { + var fs = source.fs, + baseParts = fs.basename.split('.'), + res; + + // app-level resources + if ('archetype' === type || 'command' === type || 'middleware' === type) { + if ('mojit' === fs.rootType) { + Y.log(type + ' cannot be defined in a mojit. skipping ' + fs.fullPath, 'warn', NAME); return false; } res = { - source: evt.source, + source: source, + type: type, + subtype: subtype, + name: fs.basename, mojit: null, - type: 'command', - affinity: 'common', + affinity: 'server', selector: '*' }; - if (baseParts.length !== 1) { - Y.log('invalid command filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; - } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; + return res; } - if (fs.isFile && '.' === fs.subDir && 'controller' === baseParts[0]) { - if ('.js' !== fs.ext) { - return false; - } + // mojit parts with format {name}.{affinity}.{selector} + if ( + 'action' === type || + 'addon' === type || + 'controller' === type || + 'model' === type + ) { res = { - source: evt.source, - mojit: evt.mojitType, - type: 'controller', - name: 'controller', + source: source, + type: type, + subtype: subtype, + mojit: mojitType, affinity: 'server', selector: '*' }; @@ -546,90 +542,43 @@ YUI.add('resource-store', function(Y, NAME) { res.affinity = baseParts.pop(); } if (baseParts.length !== 1) { - Y.log('invalid controller filename. skipping ' + fs.fullPath, 'warn', NAME); + Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; - } - - if (!fs.isFile && '.' === fs.subDir && 'middleware' === fs.basename) { - return true; - } - if (!fs.isFile && 'middleware' === fs.subDirArray[0]) { - return true; - } - if (fs.isFile && fs.subDirArray.length >= 1 && 'middleware' === fs.subDirArray[0]) { - if ('.js' !== fs.ext) { - return false; - } - res = { - source: evt.source, - mojit: null, - type: 'middleware', - affinity: 'common', - selector: '*' - }; - if (baseParts.length !== 1) { - Y.log('invalid middleware filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; - } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; + return res; } - if (!fs.isFile && '.' === fs.subDir && 'models' === fs.basename) { - return true; - } - if (!fs.isFile && 'models' === fs.subDirArray[0]) { - return true; - } - if (fs.isFile && fs.subDirArray.length >= 1 && 'models' === fs.subDirArray[0]) { - if ('.js' !== fs.ext) { - return false; - } + // mojit parts with format {name}.{selector} + if ('asset' === type || 'binder' === type) { res = { - source: evt.source, - mojit: evt.mojitType, - type: 'model', - affinity: 'server', + source: source, + type: type, + subtype: subtype, + mojit: mojitType, + affinity: 'common', selector: '*' }; - if (baseParts.length >= 3) { - res.selector = baseParts.pop(); - } if (baseParts.length >= 2) { - res.affinity = baseParts.pop(); + res.selector = baseParts.pop(); } if (baseParts.length !== 1) { - Y.log('invalid model filename. skipping ' + fs.fullPath, 'warn', NAME); + Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; + return res; } - // TODO: spec, in "instance" plugin - - if (!fs.isFile && '.' === fs.subDir && 'views' === fs.basename) { - return true; - } - if (!fs.isFile && 'views' === fs.subDirArray[0]) { - return true; - } - if (fs.isFile && fs.subDirArray.length >= 1 && 'views' === fs.subDirArray[0]) { + // special case: view + if ('view' === type) { res = { - source: evt.source, - mojit: evt.mojitType, - type: 'view', + source: source, + type: type, + subtype: subtype, + mojit: mojitType, viewOutputFormat: fs.ext.substr(1), viewEngine: baseParts.pop(), affinity: 'common', @@ -642,25 +591,23 @@ YUI.add('resource-store', function(Y, NAME) { Y.log('invalid view filename. skipping ' + fs.fullPath, 'warn', NAME); return false; } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); - this.addResourceVersion(res); - return false; + return res; } - // TODO: yui-lang in the "yui" plugin - - // TODO: yui-module in the "yui" plugin - -//console.log('--TODO-- preloadFile ' + evt.mojitType + ' ' + libpath.join(fs.subDir, fs.basename + fs.ext)); - return true; + // just ignore unknown types + return null; }, addResourceVersion: function(res) { -// console.log('---------------------------------------------- TODO addResourceVersion -- ' + res.mojit + ' ' + res.id); -// console.log(res); + console.log('---------------------------------------------- TODO addResourceVersion -- ' + + res.source.fs.rootType + ' ' + res.mojit + + ' ' + res.id + + ' ' + res.affinity + ':' + res.selector + ); + //console.log(res); }, @@ -676,17 +623,19 @@ YUI.add('resource-store', function(Y, NAME) { * * @method _findResourcesByConvention * @param dir {string} directory from which to find resources + * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" * @param pkg {object} metadata (name and version) about the package * @param mojitType {string|null} name of mojit to which the resource belongs * @return {array} list of resources * @private */ - _findResourcesByConvention: function(dir, pkg, mojitType) { - var me = this; + _findResourcesByConvention: function(dir, dirType, pkg, mojitType) { + var me = this, + ress = []; //console.log('-- FIND RESOURCES BY CONVENTION -- ' + pkg.name + '@' + pkg.version + ' -- ' + mojitType); this._walkDirRecursive(dir, function(error, subdir, file, isFile) { - var source; + var source, ret, res; if ('node_modules' === file) { return false; @@ -697,16 +646,12 @@ YUI.add('resource-store', function(Y, NAME) { if ('tests' === file && 'test' !== me._appConfigStatic.env) { return false; } - // mojits are loaded another way later - // TODO: better test for what is a mojit dir (i.e., check against mojitDirs and mojitsDirs) - if ('.' === subdir && 'mojits' === file) { - return false; - } source = { fs: { fullPath: libpath.join(dir, subdir, file), rootDir: dir, + rootType: dirType, subDir: subdir, subDirArray: subdir.split('/'), isFile: isFile, @@ -719,8 +664,25 @@ YUI.add('resource-store', function(Y, NAME) { if (me._skipBadPath(source.fs)) { return false; } - return me.fire('preloadFile', { source: source, mojitType: mojitType } ); + + // TODO: have fRBC() return mojitType and pass to parseResource() + ret = me.findResourceByConvention(source, mojitType); + if ('object' === typeof ret) { + if (ret.skipSubdirParts) { + source.fs.subDirArray = source.fs.subDirArray.slice(ret.skipSubdirParts); + source.fs.subDir = source.fs.subDirArray.join('/') || '.'; + } + res = me.parseResource(source, ret.type, ret.subtype, mojitType); + if ('object' === typeof res) { + ress.push(res); + } + // don't recurse into resources that are directories + return false; + } + return ret; }); + + return ress; }, @@ -907,10 +869,12 @@ YUI.add('resource-store', function(Y, NAME) { }, '0.0.1', { requires: [ 'base', - 'oop' + 'oop', + 'addon-rs-config' ]}); + YUI.add('addon-rs-config', function(Y, NAME) { var libfs = require('fs'), @@ -929,7 +893,8 @@ YUI.add('addon-rs-config', function(Y, NAME) { this.rs = config.host; this.appRoot = config.appRoot; this.mojitoRoot = config.mojitoRoot; - this.rs.on('preloadFile', this.preloadFile, this); + this.afterHostMethod('findResourceByConvention', this.findResourceByConvention, this); + this.beforeHostMethod('parseResource', this.parseResource, this); this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: context: YCB config object @@ -1006,16 +971,13 @@ YUI.add('addon-rs-config', function(Y, NAME) { }, - preloadFile: function(evt) { - var fs = evt.source.fs, - mojit = evt.mojitType, - use = false, - baseParts, - res; + findResourceByConvention: function(source, mojitType) { + var fs = source.fs, + use = false; // we only care about files if (!fs.isFile) { - return true; + return; } // we don't care about files in subdirectories if ('.' !== fs.subDir) { @@ -1025,38 +987,53 @@ YUI.add('addon-rs-config', function(Y, NAME) { if ('.json' !== fs.ext) { return; } - // always use package.json - if ('package' === fs.basename) { + // use package.json for the app and the mojit + if ('package' === fs.basename && 'bundle' !== fs.rootType) { use = true; } // use all configs in the application - if (0 === evt.source.pkg.depth) { + if ('app' === fs.rootType) { use = true; } - // use configs from non-shared mojits - if (mojit && 'shared' !== mojit) { + // use configs from non-shared mojit resources + if (mojitType && 'shared' !== mojitType) { use = true; } if (!use) { return; } - baseParts = fs.basename.split('.'); + return new Y.Do.AlterReturn(null, { + type: 'config' + }); + }, + + + parseResource: function(source, type, subtype, mojitType) { + var baseParts, + res; + + if ('config' !== type) { + return; + } + + baseParts = source.fs.basename.split('.'); res = { - source: evt.source, - mojit: mojit, + source: source, type: 'config', affinity: 'common', selector: '*' }; + if ('app' !== source.fs.rootType) { + res.mojit = mojitType; + } if (baseParts.length !== 1) { - Y.log('invalid config filename. skipping ' + fs.fullPath, 'warn', NAME); - return; + Y.log('invalid config filename. skipping ' + source.fs.fullPath, 'warn', NAME); + return false; } - fs.relativePath = libpath.join(fs.subDirArray.slice(1).join('/'), fs.basename + fs.ext); - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); + res.name = libpath.join(source.fs.subDir, baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); - this.rs.addResourceVersion(res); + return new Y.Do.Halt(null, res); }, @@ -1083,3 +1060,5 @@ YUI.add('addon-rs-config', function(Y, NAME) { }, '0.0.1', { requires: ['plugin', 'oop']}); + + From 78d3224e3a46d476bc2da35eebe62a34cf8f0111 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 27 May 2012 11:46:46 -0700 Subject: [PATCH 004/119] refactored to add a more efficient OO interface --- source/lib/libs/ycb.js | 538 ++++++++++++++++++++++------------------- 1 file changed, 294 insertions(+), 244 deletions(-) diff --git a/source/lib/libs/ycb.js b/source/lib/libs/ycb.js index 3bce9ad62..2fbd0ca4c 100644 --- a/source/lib/libs/ycb.js +++ b/source/lib/libs/ycb.js @@ -5,176 +5,194 @@ */ -module.exports = { - - version: '2.0.0', +var VERSION = '2.0.0', + DEFAULT = '*', + SEPARATOR = '/', + SUBMATCH = /\$\$[a-zA-Z0-9.-_]*\$\$/; + + +//--------------------------------------------------------------- +// OBJECT ORIENTED INTERFACE + +function Ycb(bundle) { + this.dimensions = {}; + this.settings = {}; + this.schema = {}; + this._processRawBundle(bundle); +} +Ycb.prototype = { - DEFAULT: '*', - SEPARATOR: '/', - SUBMATCH: /\$\$[a-zA-Z0-9.-_]*\$\$/, - /* - * Processes an Object representing a YCB 2.0 Bundle as defined in the spec. - * - * + /** * @method read - * @param {object} bundle - * @param {object} context - * @param {boolean} validate - * @param {boolean} debug + * @param context {object} + * @param options {object} * @return {object} */ - read: function(bundle, context, validate, debug){ - - var rawConfig, lookupPaths, - path, config = {}; + read: function(context, options) { + var lookupPaths, + path, + config = {}; - if(!context){ + if (!context) { context = {}; } + lookupPaths = this._getLookupPaths(context); - rawConfig = this._processRawBundle(bundle); - lookupPaths = this._getLookupPaths(rawConfig.dimensions, context); - - if(debug){ + if (options.debug) { console.log(JSON.stringify(context,null,4)); - console.log(JSON.stringify(rawConfig,null,4)); + console.log(JSON.stringify(this.dimensions,null,4)); + console.log(JSON.stringify(this.settings,null,4)); + console.log(JSON.stringify(this.schema,null,4)); console.log(JSON.stringify(lookupPaths,null,4)); } // Now we simply merge each macting settings section we find into the config - for(path=0; path combination[location].total){ - + if (combination[location].current > combination[location].total) { combination[location].current = 0; - return tumble(combination, location-1); } return true; } - do{ + do { path = []; - - for(pos=0; pos Date: Sun, 27 May 2012 15:38:04 -0700 Subject: [PATCH 005/119] uses new OO interface to YCB --- source/lib/store-new.server.js | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/source/lib/store-new.server.js b/source/lib/store-new.server.js index fdf7cb56d..219a446eb 100644 --- a/source/lib/store-new.server.js +++ b/source/lib/store-new.server.js @@ -360,8 +360,6 @@ YUI.add('resource-store', function(Y, NAME) { // FUTURE: check NPM "engine" // TODO: register mojit's package.json as a static asset, in "static handler" plugin } - // TODO: this._mojitPaths[mojitType] = dir; - // replace with something more generic (some kind of "mojitType attributes" store) definitionJson = this.config.readConfigYCB(libpath.join(dir, 'definition.json'), {}); if (definitionJson.appLevel) { @@ -665,7 +663,6 @@ YUI.add('resource-store', function(Y, NAME) { return false; } - // TODO: have fRBC() return mojitType and pass to parseResource() ret = me.findResourceByConvention(source, mojitType); if ('object' === typeof ret) { if (ret.skipSubdirParts) { @@ -897,7 +894,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { this.beforeHostMethod('parseResource', this.parseResource, this); this._jsonCache = {}; // fullPath: contents as JSON object - this._ycbCache = {}; // fullPath: context: YCB config object + this._ycbCache = {}; // fullPath: YCB config object this._ycbDims = this._readYcbDimensions(); }, @@ -950,24 +947,14 @@ YUI.add('addon-rs-config', function(Y, NAME) { ctx = this.rs.mergeRecursive(this.rs.getStaticContext(), ctx); - //cache key only needs to account for dynamic context - cacheKey = JSON.stringify(ctx); - - if (!this._ycbCache[fullPath]) { - this._ycbCache[fullPath] = {}; - } - - ycb = this._ycbCache[fullPath][cacheKey]; + ycb = this._ycbCache[fullPath]; if (!ycb) { json = this.readConfigJSON(fullPath); json = this._ycbDims.concat(json); - - // libycb.read() will distructively modify its first argument - ycb = libycb.read(this.rs.cloneObj(json), ctx); - - this._ycbCache[fullPath][cacheKey] = ycb; + ycb = new libycb.Ycb(json); + this._ycbCache[fullPath] = ycb; } - return ycb; + return ycb.read(ctx, {}); }, From a7684e1cd4e2989846987d9b61b8891c7acae38d Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 27 May 2012 19:24:03 -0700 Subject: [PATCH 006/119] monster (but hopefully clean) resource resolution --- source/lib/store-new.server.js | 399 ++++++++++++++++++++++++++++++--- 1 file changed, 362 insertions(+), 37 deletions(-) diff --git a/source/lib/store-new.server.js b/source/lib/store-new.server.js index 219a446eb..3c604b505 100644 --- a/source/lib/store-new.server.js +++ b/source/lib/store-new.server.js @@ -15,10 +15,7 @@ YUI.add('resource-store', function(Y, NAME) { var libfs = require('fs'), libglob = require('./glob'), libpath = require('path'), - libqs = require('querystring'), - libvm = require('vm'), libwalker = require('./package-walker.server'), - libycb = require('./libs/ycb'), isNotAlphaNum = /[^a-zA-Z0-9]/, @@ -40,6 +37,7 @@ YUI.add('resource-store', function(Y, NAME) { }; + // The Affinity object is to manage the use of the affinity string in // filenames. Some files have affinities that have multiple parts // (e.g. "server-tests"). @@ -61,6 +59,7 @@ YUI.add('resource-store', function(Y, NAME) { + // TODO DOCS function ResourceStore(config) { ResourceStore.superclass.constructor.apply(this, arguments); } @@ -75,6 +74,11 @@ YUI.add('resource-store', function(Y, NAME) { this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: context: YCB config object + this._appRVs = {}; // res.type: array of resource versions + this._mojitRVs = {}; // mojitType: array of resource versions + this._appResources = {}; // env: posl: res.type: array of resources + this._mojitResources = {}; // env: posl: mojitType: array of resources + Y.Object.each(Y.mojito.addons.rs, function(fn, name) { this.plug(fn, { appRoot:cfg.root, mojitoRoot:mojitoRoot }); }, this); @@ -82,16 +86,19 @@ YUI.add('resource-store', function(Y, NAME) { destructor: function() {}, + // TODO DOCS getStaticContext: function() { return this.cloneObj(this._config.context); }, + // TODO DOCS getStaticAppConfig: function() { return this.cloneObj(this._appConfigStatic); }, + // TODO DOCS getAppConfig: function(ctx) { var appConfig, ycb; @@ -114,6 +121,13 @@ YUI.add('resource-store', function(Y, NAME) { }, + /** + * Preloads everything in the app, and as well pertinent parts of + * the framework. + * + * @method preload + * @return {nothing} + */ preload: function() { this._fwConfig = this.config.readConfigJSON(libpath.join(mojitoRoot, 'config.json')); this._appConfigStatic = this.getAppConfig({}); @@ -125,9 +139,9 @@ YUI.add('resource-store', function(Y, NAME) { /** * preload metadata about all resources in the application (and Mojito framework) * + * @private * @method _preloadMeta * @return {nothing} work down via other called methods - * @private */ _preloadMeta: function() { var me = this, @@ -143,7 +157,7 @@ YUI.add('resource-store', function(Y, NAME) { if ('mojito' === info.pkg.name) { walkedMojito = true; } - me.preloadPackage(info); + me._preloadPackage(info); }); // user might not have installed mojito as a dependency of their @@ -173,7 +187,7 @@ YUI.add('resource-store', function(Y, NAME) { }; } - this.preloadPackage(info); + this._preloadPackage(info); } }, @@ -182,11 +196,12 @@ YUI.add('resource-store', function(Y, NAME) { * preloads metadata about resources in a package * (but not subpackages in its node_modules/) * - * @method preloadPackage + * @private + * @method _preloadPackage * @param info {object} metadata about the package * @return {nothing} work down via other called methods */ - preloadPackage: function(info) { + _preloadPackage: function(info) { var dir, pkg; // FUTURE: use info.inherit to scope mojit dependencies @@ -204,7 +219,7 @@ YUI.add('resource-store', function(Y, NAME) { }; if (0 === info.depth) { // the actual application is handled specially - this.preloadApp(pkg); + this._preloadApp(pkg); return; } if (!info.pkg.yahoo || !info.pkg.yahoo.mojito) { @@ -217,7 +232,7 @@ YUI.add('resource-store', function(Y, NAME) { break; case 'mojit': dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); - this.preloadDirMojit(dir, 'pkg', pkg); + this._preloadDirMojit(dir, 'pkg', pkg); break; default: Y.log('Unknown package type "' + info.pkg.yahoo.mojito.type + '"', 'warn', NAME); @@ -230,11 +245,12 @@ YUI.add('resource-store', function(Y, NAME) { * preloads metadata about resources in the application directory * (but not node_modules/) * - * @method preloadApp + * @private + * @method _preloadApp * @param pkg {object} metadata (name and version) about the app's package * @return {nothing} work down via other called methods */ - preloadApp: function(pkg) { + _preloadApp: function(pkg) { var ress, r, res, @@ -259,7 +275,7 @@ YUI.add('resource-store', function(Y, NAME) { // load mojitDirs list = this._globList(this._config.root, this._appConfigStatic.mojitDirs || []); for (i = 0; i < list.length; i += 1) { - this.preloadDirMojit(list[i], 'app', pkg); + this._preloadDirMojit(list[i], 'app', pkg); } }, @@ -291,12 +307,12 @@ YUI.add('resource-store', function(Y, NAME) { /** * preloads a directory containing many mojits * + * @private * @method _preloadDirMojits * @param dir {string} directory path * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" * @param pkg {object} metadata (name and version) about the package * @return {nothing} work down via other called methods - * @private */ _preloadDirMojits: function(dir, dirType, pkg) { var i, @@ -320,7 +336,7 @@ YUI.add('resource-store', function(Y, NAME) { continue; } childPath = libpath.join(dir, childName); - this.preloadDirMojit(childPath, dirType, pkg); + this._preloadDirMojit(childPath, dirType, pkg); } }, @@ -328,14 +344,14 @@ YUI.add('resource-store', function(Y, NAME) { /** * preloads a directory that represents a single mojit * - * @method preloadDirMojit + * @private + * @method _preloadDirMojit * @param dir {string} directory path * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" * @param pkg {object} metadata (name and version) about the package * @return {nothing} work down via other called methods - * @private */ - preloadDirMojit: function(dir, dirType, pkg) { + _preloadDirMojit: function(dir, dirType, pkg) { var mojitType, packageJson, definitionJson, @@ -402,6 +418,7 @@ YUI.add('resource-store', function(Y, NAME) { }, + // TODO DOCS findResourceByConvention: function(source, mojitType) { var fs = source.fs, baseParts = fs.basename.split('.'), @@ -494,6 +511,7 @@ YUI.add('resource-store', function(Y, NAME) { }, + // TODO DOCS parseResource: function(source, type, subtype, mojitType) { var fs = source.fs, baseParts = fs.basename.split('.'), @@ -599,19 +617,262 @@ YUI.add('resource-store', function(Y, NAME) { }, + // TODO DOCS addResourceVersion: function(res) { - console.log('---------------------------------------------- TODO addResourceVersion -- ' - + res.source.fs.rootType + ' ' + res.mojit - + ' ' + res.id - + ' ' + res.affinity + ':' + res.selector - ); - //console.log(res); + res.affinity = new Affinity(res.affinity); + if (res.mojit) { + if (!this._mojitRVs[res.mojit]) { + this._mojitRVs[res.mojit] = []; + } + this._mojitRVs[res.mojit].push(res); + } else { + if (!this._appRVs[res.type]) { + this._appRVs[res.type] = []; + } + this._appRVs[res.type].push(res); + } }, + /** + * For each possible runtime configuration (based on context), pre-calculates + * which versions of the resources will be used. + * The priority (highest to lowest): + * source, + * selector, + * affinity (env or "common"). + * + * @method resolveResourceVersions + * @return {nothing} + */ resolveResourceVersions: function() { - console.log('---------------------------------------------- TODO resolveResourceVersions'); - // OLD: _cookdown + var c, ctx, ctxs, + poslKey, posl, posls = {}, + e, env, envs = [ 'client', 'server' ], + affinities, selectors, sourceBase, + type, ress, + p; + + ctxs = this._listAllContexts(); + for (var c = 0; c < ctxs.length; c++) { + ctx = ctxs[c]; + posl = this.selector.getListFromContext(ctx); + posls[JSON.stringify(posl)] = posl; + } + + for (e = 0; e < envs.length; e += 1) { + env = envs[e]; + + affinities = {}; // affinity: priority modifier + affinities[env] = 1; + affinities.common = 0; + + for (poslKey in posls) { + if (posls.hasOwnProperty(poslKey)) { + posl = posls[poslKey]; + selectors = {}; // selector: priority modifier + for (p = 0; p < posl.length; p += 1) { + selectors[posl[p]] = (posl.length - p - 1) * 2; + } + sourceBase = posl.length * 2; + //console.log('-- source base ' + sourceBase); + //console.log(selectors); + //console.log(affinities); + + if (!this._appResources[env]) { + this._appResources[env] = {} + } + if (!this._appResources[env][poslKey]) { + this._appResources[env][poslKey] = {} + } + for (type in this._appRVs) { + if (this._appRVs.hasOwnProperty(type)) { + this._appResources[env][poslKey][type] = + this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs[type] ]); + } + } + + if (!this._mojitResources[env]) { + this._mojitResources[env] = {} + } + if (!this._mojitResources[env][poslKey]) { + this._mojitResources[env][poslKey] = {} + } + for (type in this._mojitRVs) { + if ('shared' === type) { + continue; + } + if (this._mojitRVs.hasOwnProperty(type)) { + this._mojitResources[env][poslKey][type] = + this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); + // TODO: fire event that mojit has been resolved + } + } + } + } + } + }, + + + /** + * Resolves versions for a list of resources. + * The priority is based on passed-in configuration. See + * resolveResourceVersions() for details. + * + * @private + * @method _resolveVersions + * @param affinities {object} lookup hash for priority adjustment for each affinity + * @param selectors {object} lookup hash for priority adjustment for each selector + * @param sourceBase {int} multiplier for order in priority list + * @param srcs {array of arrays} resource versions to resolve + * @return {array} list of resolved resources + */ + _resolveVersions: function(affinities, selectors, sourceBase, srcs) { + var s, src, + r, res, + priority, + versions = {}, // id: priority: resource + out = [], + resid, + highest; + + for (s = 0; s < srcs.length; s += 1) { + src = srcs[s]; + for (r = 0; r < src.length; r += 1) { + res = src[r]; + if (!selectors.hasOwnProperty(res.selector)) { + continue; + } + if (!affinities.hasOwnProperty(res.affinity)) { + continue; + } + // TODO: conditionally skip optional affinities + priority = (s * sourceBase) + + selectors[res.selector] + affinities[res.affinity]; + //console.log('--DEBUG-- pri=' + priority + ' --' + // + ' src' + s + '=' + (s * sourceBase) + // + ' ' + res.selector + '=' + selectors[res.selector] + // + ' ' + res.affinity + '=' + affinities[res.affinity] + // + ' -- ' + res.id); + if (!versions[res.id]) { + versions[res.id] = {}; + } + versions[res.id][priority] = res; + } + } + for (resid in versions) { + if (versions.hasOwnProperty(resid)) { + highest = Math.max.apply(Math, Object.keys(versions[resid])) + //console.log('--DEBUG-- highest=' + highest + ' -- ' + resid); + out.push(versions[resid][highest]); + } + } + return out; + }, + + + /** + * Generates a list of all possible context (which is a lot!). + * @private + * @method _listAllContext + * @return {array of objects} all possible contexts + */ + _listAllContexts: function() { + var dims = this.config.getDimensions(), + nctxs, c, ctxs = [], + dn, dname, dnames, + dv, dval, dvals, + e, each, mod, + // only because we might want to change it at some point + // (not including it helps reduce the number of contexts) + SKIP_RUNTIME = true; + + dims = dims[0].dimensions; + dims = this._flattenDims(dims); + dnames = Object.keys(dims); + + nctxs = 1; + for (dn = 0; dn < dnames.length; dn++) { + dname = dnames[dn]; + if (SKIP_RUNTIME && dname === 'runtime') { + continue; + } + dvals = dims[dname]; + if (dname !== 'runtime') { + // we never have indeterminant runtime + dvals.push('*'); + } + nctxs *= dvals.length; + } + + for (c = 0; c < nctxs; c++) { + ctxs[c] = {}; + } + mod = 1; + for (dn = 0; dn < dnames.length; dn++) { + dname = dnames[dn]; + if (SKIP_RUNTIME && dname === 'runtime') { + continue; + } + dvals = dims[dname]; + mod *= dvals.length; + each = nctxs / mod; + + e = each; + dv = 0; + for (c = 0; c < nctxs; --e, c++) { + if (0 === e) { + e = each; + dv++; + dv = dv % dvals.length; + } + dval = dvals[dv]; + if ('*' !== dval) { + ctxs[c][dname] = dval; + } + } + } + return ctxs; + }, + + + /** + * Flattens dimensions so that the structure of the dimension values doesn't matter. + * @private + * @method _flattenDims + * @param dims {object} dimensions structure + * @return {object} + */ + _flattenDims: function(dims) { + var d, dim, + name, out = {}; + for (d = 0; d < dims.length; d++) { + dim = dims[d]; + name = Object.keys(dim)[0]; + out[name] = this._listKeys(dim[name]); + } + return out; + }, + + + /** + * Recursively finds all keys for the object (plus child objects). + * @private + * @method _listKeys + * @param obj {object} + * @return {array} list of all keys in the object (no matter how deep) + */ + _listKeys: function(obj) { + var k, keys = []; + for (k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + if ('object' === typeof obj[k]) { + keys = keys.concat(this._listKeys(obj[k])); + } + } + } + return keys; }, @@ -619,13 +880,13 @@ YUI.add('resource-store', function(Y, NAME) { * Finds resources based on our conventions * -doesn't- load mojits or their contents. That's done elsewhere. * + * @private * @method _findResourcesByConvention * @param dir {string} directory from which to find resources * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" * @param pkg {object} metadata (name and version) about the package * @param mojitType {string|null} name of mojit to which the resource belongs * @return {array} list of resources - * @private */ _findResourcesByConvention: function(dir, dirType, pkg, mojitType) { var me = this, @@ -686,10 +947,10 @@ YUI.add('resource-store', function(Y, NAME) { /** * Indicates whether file should be skipped based on its path * + * @private * @method _skipBadPath * @param pathParts {object} the "source.fs" part of the resource * @return {boolean} true indicates that the file should be skipped - * @private */ _skipBadPath: function(fs) { if (fs.isFile && fs.ext.substr(1).match(isNotAlphaNum)) { @@ -704,10 +965,10 @@ YUI.add('resource-store', function(Y, NAME) { * which the file system is walked is significant within the resource * store, e.g., when looking up a matching context. * + * @private * @method _sortedReaddirSync * @param path {string} directory to read * @return {array} files in the directory - * @private */ _sortedReaddirSync: function(path) { var out = libfs.readdirSync(path); @@ -718,12 +979,12 @@ YUI.add('resource-store', function(Y, NAME) { /** * Recursively walks a directory * + * @private * @method _walkDirRecursive * @param dir {string} directory to start at * @param cb {function(error, subdir, name, isFile)} callback called for each file * @param _subdir {string} INTERNAL argument, please ignore * @return {nothing} value returned via callback - * @private */ _walkDirRecursive: function(dir, cb, _subdir) { var subdir, @@ -766,10 +1027,10 @@ YUI.add('resource-store', function(Y, NAME) { /** * takes a list of globs and turns it into a list of matching paths + * @private * @param prefix {string} prefix for every path in the list * @param list {array} list of globs * @return {array} list of paths matching the globs - * @private */ _globList: function(prefix, list) { var found = [], @@ -786,11 +1047,11 @@ YUI.add('resource-store', function(Y, NAME) { }, - // from http://stackoverflow.com/questions/171251/ - // how-can-i-merge-properties-of-two-javascript-objects-dynamically/ - // 383245#383245 /** - * Recursively merge one object onto another + * Recursively merge one object onto another. + * From http://stackoverflow.com/questions/171251/ + * how-can-i-merge-properties-of-two-javascript-objects-dynamically/ + * 383245#383245. * * @method mergeRecursive * @param dest {object} object to merge into @@ -867,7 +1128,8 @@ YUI.add('resource-store', function(Y, NAME) { }, '0.0.1', { requires: [ 'base', 'oop', - 'addon-rs-config' + 'addon-rs-config', + 'addon-rs-selector' ]}); @@ -899,12 +1161,17 @@ YUI.add('addon-rs-config', function(Y, NAME) { }, - // TODO: needed to break cycle so we don't leak memory? destructor: function() { + // TODO: needed to break cycle so we don't leak memory? this.rs = null; }, + getDimensions: function() { + return this.rs.cloneObj(this._ycbDims); + }, + + /** * Reads and parses a JSON file * @@ -912,6 +1179,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { * @param fullPath {string} path to JSON file * @return {mixed} contents of JSON file */ + // TODO: async interface readConfigJSON: function(fullPath) { var json, contents; @@ -940,6 +1208,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { * @param fullPath {string} path to the YCB file * @return {object} the contextualized configuration */ + // TODO: async interface readConfigYCB: function(fullPath, ctx) { var cacheKey, json, @@ -1049,3 +1318,59 @@ YUI.add('addon-rs-config', function(Y, NAME) { +YUI.add('addon-rs-selector', function(Y, NAME) { + + var libpath = require('path'), + libycb = require('./libs/ycb'); + + function RSAddonSelector() { + RSAddonSelector.superclass.constructor.apply(this, arguments); + } + RSAddonSelector.NS = 'selector'; + RSAddonSelector.DEPS = ['config']; + RSAddonSelector.ATTRS = {}; + + Y.extend(RSAddonSelector, Y.Plugin.Base, { + + initializer: function(config) { + var dims, + json; + this.rs = config.host; + this.appRoot = config.appRoot; + this.mojitoRoot = config.mojitoRoot; + + dims = this.rs.config.getDimensions(); + json = this.rs.config.readConfigJSON(libpath.join(this.appRoot, 'application.json')); + json = dims.concat(json); + this._appConfigYCB = new libycb.Ycb(json); + }, + + + destructor: function() { + // TODO: needed to break cycle so we don't leak memory? + this.rs = null; + }, + + + getListFromContext: function(ctx) { + var sels = ['*']; + var p, part, parts; + parts = this._appConfigYCB.readNoMerge(ctx, {}); + for (p = 0; p < parts.length; p++) { + part = parts[p]; + if (part.selector) { + sels.unshift(part.selector); + } + } + return sels; + } + + + }); + Y.namespace('mojito.addons.rs'); + Y.mojito.addons.rs.selector = RSAddonSelector; + +}, '0.0.1', { requires: ['plugin', 'oop']}); + + + From 04178f93b5630c7263e38e521e46762fa00f859b Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 28 May 2012 09:25:39 -0700 Subject: [PATCH 007/119] api docs update --- source/lib/store-new.server.js | 165 ++++++++++++++++++++++++++++----- 1 file changed, 144 insertions(+), 21 deletions(-) diff --git a/source/lib/store-new.server.js b/source/lib/store-new.server.js index 3c604b505..f0317874f 100644 --- a/source/lib/store-new.server.js +++ b/source/lib/store-new.server.js @@ -9,7 +9,57 @@ anon:true, sloppy:true, regexp: true, continue: true, nomen:true, node:true */ - +/** + * The ResourceStore manages information about the "resources" in a Mojito + * application. These resources are things that have representation on the + * filesystem. + * + * You generally don't need to worry about this class (and its addons) unless + * you are extending Mojito. + * + * Each resource can have many different versions. This is not talking about + * revisions, which is how the resource changes over time. It is instead + * talking about how there can be a version of the resource just for iphones, + * one just for android, a fallback, etc. + * + * The metadata kept about each resource is normalized to the follow keys: + *
+ *
source (object)
+ *
where the source came from. (not shipped to the client.) + *
+ *
fs (object)
+ *
filesystem details
+ *
pkg (object)
+ *
packaging details
+ *
+ *
+ *
mojit (string)
+ *
which mojit this applies to, if any. ("shared" means the resource is available to all mojits.)
+ *
type (string)
+ *
resource type
+ *
subtype (string)
+ *
not all types of subtypes
+ *
name (string)
+ *
common to all versions of the resource
+ *
id (string)
+ *
unique ID. common to all versions of the resource. (typically {type}-{subtype}-{name}.)
+ *
yui (object)
+ *
for resources that are YUI modules
+ *
+ * + * The following are only used in the metadata for each resource version + * (The metadata for resolved resources won't have these, since they're intrinsically + * part of the resolved resource.) + *
+ *
affinity (string)
+ *
runtime affinity. either server, client, or common
+ *
selector (string)
+ *
version selector
+ * + * + * @module ResourceStore + * @main + */ YUI.add('resource-store', function(Y, NAME) { var libfs = require('fs'), @@ -59,7 +109,15 @@ YUI.add('resource-store', function(Y, NAME) { - // TODO DOCS + /** + * @class ResourceStore.server + * @constructor + * @requires addon-rs-config, addon-rs-selector + * @param config {object} + * @param root {string} directory to manage (usually the application directory) + * @param context {object} static context + * @param appConfig {object} overrides for `application.json` + */ function ResourceStore(config) { ResourceStore.superclass.constructor.apply(this, arguments); } @@ -86,19 +144,32 @@ YUI.add('resource-store', function(Y, NAME) { destructor: function() {}, - // TODO DOCS + /** + * Returns the static (non-runtime-sensitive) context + * @method getStaticContext + * @return {object} the context + */ getStaticContext: function() { return this.cloneObj(this._config.context); }, - // TODO DOCS + /** + * Returns the static (non-runtime-sensitive) version of the application.json. + * @method getStaticAppConfig + * @return {object} the configuration from applications.json + */ getStaticAppConfig: function() { return this.cloneObj(this._appConfigStatic); }, - // TODO DOCS + /** + * Returns a contextualized application configuration. + * @method getAppConfig + * @param ctx {object} the context + * @return {object} the application configuration contextualized by the "ctx" argument. + */ getAppConfig: function(ctx) { var appConfig, ycb; @@ -396,11 +467,11 @@ YUI.add('resource-store', function(Y, NAME) { }, pkg: pkg }, + mojit: null, type: 'mojit', subtype: null, name: mojitType, id: 'mojit--' + mojitType, - mojit: null, affinity: 'common', selector: '*' }; @@ -418,7 +489,30 @@ YUI.add('resource-store', function(Y, NAME) { }, - // TODO DOCS + /** + * Called by the ResourceStore to decide if a file should be considered + * a resource. You most often don't want to call this directly, but + * instead to hook into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.afterHostMethod('findResourceByConvention', this._myFindResourceByConvention, this); + * ``` + * + * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. + * This method figures out the type (and subtype) of a file, and `parseResource()` turns + * the file into an actual resource. + * + * @method findResourceByConvention + * @param source {object} the same as the `source` part of a resource + * @param mojitType {string} the name of the mojit + * @return {boolean|object} If the source is a directory, a boolean can be returned. + * True indicates that the directory contents should be scanned, while false + * indicates that the directory should be skipped. + * If the source does represent a resource, then an object with the following + * fields should be returned; + * @param type {string} type of the resource + * @param subtype {string} optional subtype of the resource + * @param skipSubdirParts {integer} number of path parts of `source.fs.subDir` to skip + */ findResourceByConvention: function(source, mojitType) { var fs = source.fs, baseParts = fs.basename.split('.'), @@ -511,7 +605,25 @@ YUI.add('resource-store', function(Y, NAME) { }, - // TODO DOCS + /** + * Called by the ResourceStore to turn a file into a resource. + * You most often don't want to call this directly, but instead to hook + * into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.beforeHostMethod('parseResource', this._myParseResource, this); + * ``` + * + * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. + * `findResourceByConvention()` figures out the type (and subtype) of a file, and + * this method turns the file into an actual resource. + * + * @method parseResource + * @param source {object} the same as the `source` part of a resource + * @param type {string} the resource type of the file + * @param subtype {string} the optional resource subtype of the file + * @param mojitType {string} the name of the mojit + * @return {object|undefined} the resource version + */ parseResource: function(source, type, subtype, mojitType) { var fs = source.fs, baseParts = fs.basename.split('.'), @@ -521,14 +633,14 @@ YUI.add('resource-store', function(Y, NAME) { if ('archetype' === type || 'command' === type || 'middleware' === type) { if ('mojit' === fs.rootType) { Y.log(type + ' cannot be defined in a mojit. skipping ' + fs.fullPath, 'warn', NAME); - return false; + return; } res = { source: source, + mojit: null, type: type, subtype: subtype, name: fs.basename, - mojit: null, affinity: 'server', selector: '*' }; @@ -545,9 +657,9 @@ YUI.add('resource-store', function(Y, NAME) { ) { res = { source: source, + mojit: mojitType, type: type, subtype: subtype, - mojit: mojitType, affinity: 'server', selector: '*' }; @@ -559,7 +671,7 @@ YUI.add('resource-store', function(Y, NAME) { } if (baseParts.length !== 1) { Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; + return; } res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); @@ -570,9 +682,9 @@ YUI.add('resource-store', function(Y, NAME) { if ('asset' === type || 'binder' === type) { res = { source: source, + mojit: mojitType, type: type, subtype: subtype, - mojit: mojitType, affinity: 'common', selector: '*' }; @@ -581,20 +693,20 @@ YUI.add('resource-store', function(Y, NAME) { } if (baseParts.length !== 1) { Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; + return; } res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); - return res; - } + return res; + } // special case: view if ('view' === type) { res = { source: source, + mojit: mojitType, type: type, subtype: subtype, - mojit: mojitType, viewOutputFormat: fs.ext.substr(1), viewEngine: baseParts.pop(), affinity: 'common', @@ -605,7 +717,7 @@ YUI.add('resource-store', function(Y, NAME) { } if (baseParts.length !== 1) { Y.log('invalid view filename. skipping ' + fs.fullPath, 'warn', NAME); - return false; + return; } res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); @@ -613,11 +725,22 @@ YUI.add('resource-store', function(Y, NAME) { } // just ignore unknown types - return null; + return; }, - // TODO DOCS + /** + * Called by the ResourceStore to register a resource version. + * You most often don't want to call this directly, but instead to hook + * into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.beforeHostMethod('parseResource', this._myParseResource, this); + * ``` + * + * @method addResourceVersion + * @param res {object} the resource version + * @return {nothing} + */ addResourceVersion: function(res) { res.affinity = new Affinity(res.affinity); if (res.mojit) { @@ -1285,7 +1408,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { } if (baseParts.length !== 1) { Y.log('invalid config filename. skipping ' + source.fs.fullPath, 'warn', NAME); - return false; + return; } res.name = libpath.join(source.fs.subDir, baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); From 6648748a34761a8374aaa951f167f9bb52c2fe2e Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 28 May 2012 10:49:24 -0700 Subject: [PATCH 008/119] moved archetypes/ and management/commands/ into app/ --- .../archetypes/app/default/application.json | 0 .../archetypes/app/default/assets/favicon.ico | Bin .../{ => app}/archetypes/app/default/index.js | 0 .../app/default/mojits/.placeholder | 0 .../archetypes/app/default/package.json.mu | 0 .../archetypes/app/default/routes.json | 0 .../archetypes/app/default/server.js | 0 .../archetypes/app/full/application.json | 0 .../archetypes/app/full/assets/favicon.ico | Bin .../{ => app}/archetypes/app/full/index.js | 0 .../archetypes/app/full/mojits/.placeholder | 0 .../archetypes/app/full/package.json.mu | 0 .../{ => app}/archetypes/app/full/routes.json | 0 .../{ => app}/archetypes/app/full/server.js | 0 .../archetypes/app/simple/application.json | 0 .../archetypes/app/simple/mojits/.placeholder | 0 .../archetypes/app/simple/package.json.mu | 0 .../{ => app}/archetypes/app/simple/server.js | 0 .../archetypes/mojit/default/assets/index.css | 0 .../mojit/default/binders/index.js.mu | 0 .../mojit/default/controller.server.js.mu | 0 .../archetypes/mojit/default/definition.json | 0 .../mojit/default/models/foo.server.js.mu | 0 .../tests/binders/index.common-tests.js.mu | 0 .../tests/controller.server-tests.js.mu | 0 .../tests/models/foo.server-tests.js.mu | 0 .../mojit/default/views/index.mu.html | 0 .../archetypes/mojit/full/assets/.placeholder | 0 .../archetypes/mojit/full/assets/index.css | 0 .../archetypes/mojit/full/binders/index.js.mu | 0 .../mojit/full/controller.server.js.mu | 0 .../archetypes/mojit/full/defaults.json | 0 .../archetypes/mojit/full/definition.json | 0 .../mojit/full/models/foo.server.js.mu | 0 .../tests/binders/index.common-tests.js.mu | 0 .../full/tests/controller.server-tests.js.mu | 0 .../tests/models/model.server-tests.js.mu | 0 .../archetypes/mojit/full/views/index.mu.html | 0 .../mojit/simple/controller.server.js.mu | 0 .../lib/{management => app}/commands/build.js | 0 .../{management => app}/commands/compile.js | 0 .../{management => app}/commands/create.js | 0 .../lib/{management => app}/commands/docs.js | 0 source/lib/{management => app}/commands/gv.js | 0 .../lib/{management => app}/commands/help.js | 0 .../lib/{management => app}/commands/info.js | 0 .../{management => app}/commands/jslint.js | 0 .../lib/{management => app}/commands/start.js | 0 .../lib/{management => app}/commands/test.js | 0 .../{management => app}/commands/version.js | 0 source/lib/management/commands/x.js | 179 ------------------ 51 files changed, 179 deletions(-) rename source/lib/{ => app}/archetypes/app/default/application.json (100%) rename source/lib/{ => app}/archetypes/app/default/assets/favicon.ico (100%) rename source/lib/{ => app}/archetypes/app/default/index.js (100%) rename source/lib/{ => app}/archetypes/app/default/mojits/.placeholder (100%) rename source/lib/{ => app}/archetypes/app/default/package.json.mu (100%) rename source/lib/{ => app}/archetypes/app/default/routes.json (100%) rename source/lib/{ => app}/archetypes/app/default/server.js (100%) rename source/lib/{ => app}/archetypes/app/full/application.json (100%) rename source/lib/{ => app}/archetypes/app/full/assets/favicon.ico (100%) rename source/lib/{ => app}/archetypes/app/full/index.js (100%) rename source/lib/{ => app}/archetypes/app/full/mojits/.placeholder (100%) rename source/lib/{ => app}/archetypes/app/full/package.json.mu (100%) rename source/lib/{ => app}/archetypes/app/full/routes.json (100%) rename source/lib/{ => app}/archetypes/app/full/server.js (100%) rename source/lib/{ => app}/archetypes/app/simple/application.json (100%) rename source/lib/{ => app}/archetypes/app/simple/mojits/.placeholder (100%) rename source/lib/{ => app}/archetypes/app/simple/package.json.mu (100%) rename source/lib/{ => app}/archetypes/app/simple/server.js (100%) rename source/lib/{ => app}/archetypes/mojit/default/assets/index.css (100%) rename source/lib/{ => app}/archetypes/mojit/default/binders/index.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/default/controller.server.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/default/definition.json (100%) rename source/lib/{ => app}/archetypes/mojit/default/models/foo.server.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/default/tests/binders/index.common-tests.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/default/tests/controller.server-tests.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/default/tests/models/foo.server-tests.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/default/views/index.mu.html (100%) rename source/lib/{ => app}/archetypes/mojit/full/assets/.placeholder (100%) rename source/lib/{ => app}/archetypes/mojit/full/assets/index.css (100%) rename source/lib/{ => app}/archetypes/mojit/full/binders/index.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/full/controller.server.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/full/defaults.json (100%) rename source/lib/{ => app}/archetypes/mojit/full/definition.json (100%) rename source/lib/{ => app}/archetypes/mojit/full/models/foo.server.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/full/tests/binders/index.common-tests.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/full/tests/controller.server-tests.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/full/tests/models/model.server-tests.js.mu (100%) rename source/lib/{ => app}/archetypes/mojit/full/views/index.mu.html (100%) rename source/lib/{ => app}/archetypes/mojit/simple/controller.server.js.mu (100%) rename source/lib/{management => app}/commands/build.js (100%) rename source/lib/{management => app}/commands/compile.js (100%) rename source/lib/{management => app}/commands/create.js (100%) rename source/lib/{management => app}/commands/docs.js (100%) rename source/lib/{management => app}/commands/gv.js (100%) rename source/lib/{management => app}/commands/help.js (100%) rename source/lib/{management => app}/commands/info.js (100%) rename source/lib/{management => app}/commands/jslint.js (100%) rename source/lib/{management => app}/commands/start.js (100%) rename source/lib/{management => app}/commands/test.js (100%) rename source/lib/{management => app}/commands/version.js (100%) delete mode 100644 source/lib/management/commands/x.js diff --git a/source/lib/archetypes/app/default/application.json b/source/lib/app/archetypes/app/default/application.json similarity index 100% rename from source/lib/archetypes/app/default/application.json rename to source/lib/app/archetypes/app/default/application.json diff --git a/source/lib/archetypes/app/default/assets/favicon.ico b/source/lib/app/archetypes/app/default/assets/favicon.ico similarity index 100% rename from source/lib/archetypes/app/default/assets/favicon.ico rename to source/lib/app/archetypes/app/default/assets/favicon.ico diff --git a/source/lib/archetypes/app/default/index.js b/source/lib/app/archetypes/app/default/index.js similarity index 100% rename from source/lib/archetypes/app/default/index.js rename to source/lib/app/archetypes/app/default/index.js diff --git a/source/lib/archetypes/app/default/mojits/.placeholder b/source/lib/app/archetypes/app/default/mojits/.placeholder similarity index 100% rename from source/lib/archetypes/app/default/mojits/.placeholder rename to source/lib/app/archetypes/app/default/mojits/.placeholder diff --git a/source/lib/archetypes/app/default/package.json.mu b/source/lib/app/archetypes/app/default/package.json.mu similarity index 100% rename from source/lib/archetypes/app/default/package.json.mu rename to source/lib/app/archetypes/app/default/package.json.mu diff --git a/source/lib/archetypes/app/default/routes.json b/source/lib/app/archetypes/app/default/routes.json similarity index 100% rename from source/lib/archetypes/app/default/routes.json rename to source/lib/app/archetypes/app/default/routes.json diff --git a/source/lib/archetypes/app/default/server.js b/source/lib/app/archetypes/app/default/server.js similarity index 100% rename from source/lib/archetypes/app/default/server.js rename to source/lib/app/archetypes/app/default/server.js diff --git a/source/lib/archetypes/app/full/application.json b/source/lib/app/archetypes/app/full/application.json similarity index 100% rename from source/lib/archetypes/app/full/application.json rename to source/lib/app/archetypes/app/full/application.json diff --git a/source/lib/archetypes/app/full/assets/favicon.ico b/source/lib/app/archetypes/app/full/assets/favicon.ico similarity index 100% rename from source/lib/archetypes/app/full/assets/favicon.ico rename to source/lib/app/archetypes/app/full/assets/favicon.ico diff --git a/source/lib/archetypes/app/full/index.js b/source/lib/app/archetypes/app/full/index.js similarity index 100% rename from source/lib/archetypes/app/full/index.js rename to source/lib/app/archetypes/app/full/index.js diff --git a/source/lib/archetypes/app/full/mojits/.placeholder b/source/lib/app/archetypes/app/full/mojits/.placeholder similarity index 100% rename from source/lib/archetypes/app/full/mojits/.placeholder rename to source/lib/app/archetypes/app/full/mojits/.placeholder diff --git a/source/lib/archetypes/app/full/package.json.mu b/source/lib/app/archetypes/app/full/package.json.mu similarity index 100% rename from source/lib/archetypes/app/full/package.json.mu rename to source/lib/app/archetypes/app/full/package.json.mu diff --git a/source/lib/archetypes/app/full/routes.json b/source/lib/app/archetypes/app/full/routes.json similarity index 100% rename from source/lib/archetypes/app/full/routes.json rename to source/lib/app/archetypes/app/full/routes.json diff --git a/source/lib/archetypes/app/full/server.js b/source/lib/app/archetypes/app/full/server.js similarity index 100% rename from source/lib/archetypes/app/full/server.js rename to source/lib/app/archetypes/app/full/server.js diff --git a/source/lib/archetypes/app/simple/application.json b/source/lib/app/archetypes/app/simple/application.json similarity index 100% rename from source/lib/archetypes/app/simple/application.json rename to source/lib/app/archetypes/app/simple/application.json diff --git a/source/lib/archetypes/app/simple/mojits/.placeholder b/source/lib/app/archetypes/app/simple/mojits/.placeholder similarity index 100% rename from source/lib/archetypes/app/simple/mojits/.placeholder rename to source/lib/app/archetypes/app/simple/mojits/.placeholder diff --git a/source/lib/archetypes/app/simple/package.json.mu b/source/lib/app/archetypes/app/simple/package.json.mu similarity index 100% rename from source/lib/archetypes/app/simple/package.json.mu rename to source/lib/app/archetypes/app/simple/package.json.mu diff --git a/source/lib/archetypes/app/simple/server.js b/source/lib/app/archetypes/app/simple/server.js similarity index 100% rename from source/lib/archetypes/app/simple/server.js rename to source/lib/app/archetypes/app/simple/server.js diff --git a/source/lib/archetypes/mojit/default/assets/index.css b/source/lib/app/archetypes/mojit/default/assets/index.css similarity index 100% rename from source/lib/archetypes/mojit/default/assets/index.css rename to source/lib/app/archetypes/mojit/default/assets/index.css diff --git a/source/lib/archetypes/mojit/default/binders/index.js.mu b/source/lib/app/archetypes/mojit/default/binders/index.js.mu similarity index 100% rename from source/lib/archetypes/mojit/default/binders/index.js.mu rename to source/lib/app/archetypes/mojit/default/binders/index.js.mu diff --git a/source/lib/archetypes/mojit/default/controller.server.js.mu b/source/lib/app/archetypes/mojit/default/controller.server.js.mu similarity index 100% rename from source/lib/archetypes/mojit/default/controller.server.js.mu rename to source/lib/app/archetypes/mojit/default/controller.server.js.mu diff --git a/source/lib/archetypes/mojit/default/definition.json b/source/lib/app/archetypes/mojit/default/definition.json similarity index 100% rename from source/lib/archetypes/mojit/default/definition.json rename to source/lib/app/archetypes/mojit/default/definition.json diff --git a/source/lib/archetypes/mojit/default/models/foo.server.js.mu b/source/lib/app/archetypes/mojit/default/models/foo.server.js.mu similarity index 100% rename from source/lib/archetypes/mojit/default/models/foo.server.js.mu rename to source/lib/app/archetypes/mojit/default/models/foo.server.js.mu diff --git a/source/lib/archetypes/mojit/default/tests/binders/index.common-tests.js.mu b/source/lib/app/archetypes/mojit/default/tests/binders/index.common-tests.js.mu similarity index 100% rename from source/lib/archetypes/mojit/default/tests/binders/index.common-tests.js.mu rename to source/lib/app/archetypes/mojit/default/tests/binders/index.common-tests.js.mu diff --git a/source/lib/archetypes/mojit/default/tests/controller.server-tests.js.mu b/source/lib/app/archetypes/mojit/default/tests/controller.server-tests.js.mu similarity index 100% rename from source/lib/archetypes/mojit/default/tests/controller.server-tests.js.mu rename to source/lib/app/archetypes/mojit/default/tests/controller.server-tests.js.mu diff --git a/source/lib/archetypes/mojit/default/tests/models/foo.server-tests.js.mu b/source/lib/app/archetypes/mojit/default/tests/models/foo.server-tests.js.mu similarity index 100% rename from source/lib/archetypes/mojit/default/tests/models/foo.server-tests.js.mu rename to source/lib/app/archetypes/mojit/default/tests/models/foo.server-tests.js.mu diff --git a/source/lib/archetypes/mojit/default/views/index.mu.html b/source/lib/app/archetypes/mojit/default/views/index.mu.html similarity index 100% rename from source/lib/archetypes/mojit/default/views/index.mu.html rename to source/lib/app/archetypes/mojit/default/views/index.mu.html diff --git a/source/lib/archetypes/mojit/full/assets/.placeholder b/source/lib/app/archetypes/mojit/full/assets/.placeholder similarity index 100% rename from source/lib/archetypes/mojit/full/assets/.placeholder rename to source/lib/app/archetypes/mojit/full/assets/.placeholder diff --git a/source/lib/archetypes/mojit/full/assets/index.css b/source/lib/app/archetypes/mojit/full/assets/index.css similarity index 100% rename from source/lib/archetypes/mojit/full/assets/index.css rename to source/lib/app/archetypes/mojit/full/assets/index.css diff --git a/source/lib/archetypes/mojit/full/binders/index.js.mu b/source/lib/app/archetypes/mojit/full/binders/index.js.mu similarity index 100% rename from source/lib/archetypes/mojit/full/binders/index.js.mu rename to source/lib/app/archetypes/mojit/full/binders/index.js.mu diff --git a/source/lib/archetypes/mojit/full/controller.server.js.mu b/source/lib/app/archetypes/mojit/full/controller.server.js.mu similarity index 100% rename from source/lib/archetypes/mojit/full/controller.server.js.mu rename to source/lib/app/archetypes/mojit/full/controller.server.js.mu diff --git a/source/lib/archetypes/mojit/full/defaults.json b/source/lib/app/archetypes/mojit/full/defaults.json similarity index 100% rename from source/lib/archetypes/mojit/full/defaults.json rename to source/lib/app/archetypes/mojit/full/defaults.json diff --git a/source/lib/archetypes/mojit/full/definition.json b/source/lib/app/archetypes/mojit/full/definition.json similarity index 100% rename from source/lib/archetypes/mojit/full/definition.json rename to source/lib/app/archetypes/mojit/full/definition.json diff --git a/source/lib/archetypes/mojit/full/models/foo.server.js.mu b/source/lib/app/archetypes/mojit/full/models/foo.server.js.mu similarity index 100% rename from source/lib/archetypes/mojit/full/models/foo.server.js.mu rename to source/lib/app/archetypes/mojit/full/models/foo.server.js.mu diff --git a/source/lib/archetypes/mojit/full/tests/binders/index.common-tests.js.mu b/source/lib/app/archetypes/mojit/full/tests/binders/index.common-tests.js.mu similarity index 100% rename from source/lib/archetypes/mojit/full/tests/binders/index.common-tests.js.mu rename to source/lib/app/archetypes/mojit/full/tests/binders/index.common-tests.js.mu diff --git a/source/lib/archetypes/mojit/full/tests/controller.server-tests.js.mu b/source/lib/app/archetypes/mojit/full/tests/controller.server-tests.js.mu similarity index 100% rename from source/lib/archetypes/mojit/full/tests/controller.server-tests.js.mu rename to source/lib/app/archetypes/mojit/full/tests/controller.server-tests.js.mu diff --git a/source/lib/archetypes/mojit/full/tests/models/model.server-tests.js.mu b/source/lib/app/archetypes/mojit/full/tests/models/model.server-tests.js.mu similarity index 100% rename from source/lib/archetypes/mojit/full/tests/models/model.server-tests.js.mu rename to source/lib/app/archetypes/mojit/full/tests/models/model.server-tests.js.mu diff --git a/source/lib/archetypes/mojit/full/views/index.mu.html b/source/lib/app/archetypes/mojit/full/views/index.mu.html similarity index 100% rename from source/lib/archetypes/mojit/full/views/index.mu.html rename to source/lib/app/archetypes/mojit/full/views/index.mu.html diff --git a/source/lib/archetypes/mojit/simple/controller.server.js.mu b/source/lib/app/archetypes/mojit/simple/controller.server.js.mu similarity index 100% rename from source/lib/archetypes/mojit/simple/controller.server.js.mu rename to source/lib/app/archetypes/mojit/simple/controller.server.js.mu diff --git a/source/lib/management/commands/build.js b/source/lib/app/commands/build.js similarity index 100% rename from source/lib/management/commands/build.js rename to source/lib/app/commands/build.js diff --git a/source/lib/management/commands/compile.js b/source/lib/app/commands/compile.js similarity index 100% rename from source/lib/management/commands/compile.js rename to source/lib/app/commands/compile.js diff --git a/source/lib/management/commands/create.js b/source/lib/app/commands/create.js similarity index 100% rename from source/lib/management/commands/create.js rename to source/lib/app/commands/create.js diff --git a/source/lib/management/commands/docs.js b/source/lib/app/commands/docs.js similarity index 100% rename from source/lib/management/commands/docs.js rename to source/lib/app/commands/docs.js diff --git a/source/lib/management/commands/gv.js b/source/lib/app/commands/gv.js similarity index 100% rename from source/lib/management/commands/gv.js rename to source/lib/app/commands/gv.js diff --git a/source/lib/management/commands/help.js b/source/lib/app/commands/help.js similarity index 100% rename from source/lib/management/commands/help.js rename to source/lib/app/commands/help.js diff --git a/source/lib/management/commands/info.js b/source/lib/app/commands/info.js similarity index 100% rename from source/lib/management/commands/info.js rename to source/lib/app/commands/info.js diff --git a/source/lib/management/commands/jslint.js b/source/lib/app/commands/jslint.js similarity index 100% rename from source/lib/management/commands/jslint.js rename to source/lib/app/commands/jslint.js diff --git a/source/lib/management/commands/start.js b/source/lib/app/commands/start.js similarity index 100% rename from source/lib/management/commands/start.js rename to source/lib/app/commands/start.js diff --git a/source/lib/management/commands/test.js b/source/lib/app/commands/test.js similarity index 100% rename from source/lib/management/commands/test.js rename to source/lib/app/commands/test.js diff --git a/source/lib/management/commands/version.js b/source/lib/app/commands/version.js similarity index 100% rename from source/lib/management/commands/version.js rename to source/lib/app/commands/version.js diff --git a/source/lib/management/commands/x.js b/source/lib/management/commands/x.js deleted file mode 100644 index 1c1f8d2d5..000000000 --- a/source/lib/management/commands/x.js +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved. - * Copyrights licensed under the New BSD License. - * See the accompanying LICENSE file for terms. - */ - - -/*jslint anon:true, sloppy:true*/ - - -var - libpath = require('path'), - libfs = require('fs'), - libqs = require('querystring'), - libutils = require('../utils'), - libycb = require('../../libs/ycb'), - ResourceStore = require('../../store.server.js'), - - SKIP_RUNTIME = false; - - - -// deep copies an object -function _cloneObj(o) { - var newO, - i; - - if (typeof o !== 'object') { - return o; - } - if (!o) { - return o; - } - - if ('[object Array]' === Object.prototype.toString.apply(o)) { - newO = []; - for (i = 0; i < o.length; i += 1) { - newO[i] = _cloneObj(o[i]); - } - return newO; - } - - newO = {}; - for (i in o) { - if (o.hasOwnProperty(i)) { - newO[i] = _cloneObj(o[i]); - } - } - return newO; -} - - -function list_keys(obj) { - var k, keys = []; - for (k in obj) { - if (obj.hasOwnProperty(k)) { - keys.push(k); - if ('object' === typeof obj[k]) { - keys = keys.concat(list_keys(obj[k])); - } - } - } - return keys; -} - - -function flatten_dims(dims) { - var d, dim; - var names, out = {}; - for (d = 0; d < dims.length; d++) { - dim = dims[d]; - name = Object.keys(dim)[0]; - out[name] = list_keys(dim[name]); - } - return out; -} - - -// there is no doubt a better way of doing this -function list_all_ctxs(dims) { - var nctxs, c, ctxs = [], - dn, dname, dnames, - dv, dval, dvals, - e, each, mod; - - dims = flatten_dims(dims); - dnames = Object.keys(dims); - - nctxs = 1; - for (dn = 0; dn < dnames.length; dn++) { - dname = dnames[dn]; - if (SKIP_RUNTIME && dname === 'runtime') { - continue; - } - dvals = dims[dname]; - if (dname !== 'runtime') { - // we never have indeterminant runtime - dvals.push('*'); - } - nctxs *= dvals.length; - } - - for (c = 0; c < nctxs; c++) { - ctxs[c] = {}; - } - mod = 1; - for (dn = 0; dn < dnames.length; dn++) { - dname = dnames[dn]; - if (SKIP_RUNTIME && dname === 'runtime') { - continue; - } - dvals = dims[dname]; - mod *= dvals.length; - each = nctxs / mod; - - e = each; - dv = 0; - for (c = 0; c < nctxs; --e, c++) { - if (0 === e) { - e = each; - dv++; - dv = dv % dvals.length; - } - dval = dvals[dv]; - if ('*' !== dval) { - ctxs[c][dname] = dval; - } - } - } - return ctxs; -} - - -function list_selectors(ycb, ctx) { - var sels = [ '*' ]; - var p, part, parts; - parts = libycb.readNoMerge(_cloneObj(ycb), ctx); - for (p = 0; p < parts.length; p++) { - part = parts[p]; - if (part.selector) { - sels.unshift(part.selector); - } - } - return sels; -} - - -function run(params, options) { - var store = new ResourceStore(process.cwd()); - var dims = store._readYcbDimensions(); - var ycb = store._readConfigJSON('application.json'); - ycb = dims.concat(ycb); - - dims = dims[0].dimensions; - - var ctx; - var sels; - var posls = {}; - var ctxs = list_all_ctxs(dims); - for (var c = 0; c < ctxs.length; c++) { - ctx = ctxs[c]; - sels = list_selectors(ycb, ctx); - console.log('-- context ' + libycb._getLookupPath(dims, ctx) + ' -- selectors ' + JSON.stringify(sels)); - posls[JSON.stringify(sels)] = sels; - } - for (var poslKey in posls) { - if (posls.hasOwnProperty(poslKey)) { - var posl = posls[poslKey]; - console.log('-- posl ' + posl.join(',')); - } - } -}; - - -exports.usage = 'mojito x // experiments\n'; -exports.options = []; -exports.run = run; - - From c37ff7dd94a2d17b89109bf24fff35db37143c0d Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 28 May 2012 10:50:10 -0700 Subject: [PATCH 009/119] fixed up paths from move --- source/lib/app/commands/build.js | 2 +- source/lib/app/commands/compile.js | 2 +- source/lib/app/commands/create.js | 6 +++--- source/lib/app/commands/docs.js | 6 +++--- source/lib/app/commands/gv.js | 4 ++-- source/lib/app/commands/info.js | 2 +- source/lib/app/commands/jslint.js | 2 +- source/lib/app/commands/start.js | 2 +- source/lib/app/commands/test.js | 9 ++++----- source/lib/app/commands/version.js | 2 +- source/lib/management/cli.js | 5 +++-- source/lib/management/utils.js | 2 +- 12 files changed, 22 insertions(+), 22 deletions(-) diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 44c492b47..6fd63bbb1 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -9,7 +9,7 @@ var libpath = require('path'), - utils = require('../utils'), + utils = require(libpath.join(__dirname, '../../management/utils')), fs = require('fs'), libqs = require('querystring'), ResourceStore = require(libpath.join(__dirname, '../..', diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 573ff31a9..b28f0ba04 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -9,7 +9,7 @@ var path = require('path'), - utils = require('../utils'), + utils = require(path.join(__dirname, '../../management/utils')), fs = require('fs'), // Mojito Resource Store diff --git a/source/lib/app/commands/create.js b/source/lib/app/commands/create.js index 4a9cba71d..1778e9a33 100644 --- a/source/lib/app/commands/create.js +++ b/source/lib/app/commands/create.js @@ -8,10 +8,10 @@ /*jslint anon:true, sloppy:true, regexp:true, nomen:true*/ -var utils = require('../utils'), +var path = require('path'), fs = require('fs'), - path = require('path'), - archetypePath = path.join(__dirname, '/../../archetypes'), + utils = require(path.join(__dirname, '../../management/utils')), + archetypePath = path.join(__dirname, '../archetypes'), // Found at http://www.crockford.com/javascript/survey.html reservedWords = [ 'abstract', diff --git a/source/lib/app/commands/docs.js b/source/lib/app/commands/docs.js index e9b7323b4..4c4ced0f7 100644 --- a/source/lib/app/commands/docs.js +++ b/source/lib/app/commands/docs.js @@ -8,11 +8,11 @@ /*jslint anon:true, sloppy:true, regexp:true, nomen:true*/ -var utils = require('../utils'), - fs = require('fs'), +var fs = require('fs'), path = require('path'), exec = require('child_process').exec, - copyExclude = require('../utils').copyExclude, + utils = require(path.join(__dirname, '../../management/utils')), + copyExclude = utils.copyExclude, usage, dir_mojito = path.join(__dirname, '../../'), dir_yuidoc = path.join(dir_mojito, 'libs/yuidoc'), diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index 72dd95b28..41ffc230e 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -19,11 +19,11 @@ var run, libpath = require('path'), libfs = require('fs'), - libutils = require('../utils'), + libutils = require(libpath.join(__dirname, '../../management/utils')), MODE_ALL = parseInt('777', 8), - ResourceStore = require('../../store.server.js'), + ResourceStore = require(libpath.join(__dirname, '../../store.server.js')), artifactsDir = 'artifacts', resultsDir = 'artifacts/gv'; diff --git a/source/lib/app/commands/info.js b/source/lib/app/commands/info.js index ce5035109..90c6b28c0 100644 --- a/source/lib/app/commands/info.js +++ b/source/lib/app/commands/info.js @@ -10,7 +10,7 @@ var fs = require('fs'), path = require('path'), - utils = require('../utils'); + utils = require(path.join(__dirname, '../../management/utils')); /** diff --git a/source/lib/app/commands/jslint.js b/source/lib/app/commands/jslint.js index b847ae2ba..80a8baae3 100644 --- a/source/lib/app/commands/jslint.js +++ b/source/lib/app/commands/jslint.js @@ -10,7 +10,7 @@ var fs = require('fs'), path = require('path'), - utils = require('../utils'), + utils = require(path.join(__dirname, '../../management/utils')), usage = 'mojito jslint [app | mojit] []'; diff --git a/source/lib/app/commands/start.js b/source/lib/app/commands/start.js index 3840e2481..2b279b92f 100644 --- a/source/lib/app/commands/start.js +++ b/source/lib/app/commands/start.js @@ -9,7 +9,7 @@ var path = require('path'), - utils = require('../utils'), + utils = require(path.join(__dirname, '../../management/utils')), fs = require('fs'); diff --git a/source/lib/app/commands/test.js b/source/lib/app/commands/test.js index 95e591259..d9dc6a7bc 100644 --- a/source/lib/app/commands/test.js +++ b/source/lib/app/commands/test.js @@ -7,15 +7,14 @@ /*jslint anon:true, sloppy:true, regexp:true, nomen:true*/ - var pathlib = require('path'), fs = require('fs'), - utils = require('../utils'), - ymc = require('../yui-module-configurator'), + utils = require(pathlib.join(__dirname, '../../management/utils')), + ymc = require(pathlib.join(__dirname, '../../management/yui-module-configurator')), exec = require('child_process').exec, - copyExclude = require('../utils').copyExclude, - copyFile = require('../utils').copyFile, + copyExclude = utils.copyExclude, + copyFile = utils.copyFile, MODE_ALL = parseInt('777', 8), diff --git a/source/lib/app/commands/version.js b/source/lib/app/commands/version.js index 221349412..f72089bdf 100644 --- a/source/lib/app/commands/version.js +++ b/source/lib/app/commands/version.js @@ -10,7 +10,7 @@ var fs = require('fs'), path = require('path'), - utils = require('../utils'), + utils = require(path.join(__dirname, '../../management/utils')), usage = 'mojito version [app | mojit] []'; diff --git a/source/lib/management/cli.js b/source/lib/management/cli.js index 291005de9..cd950b78e 100644 --- a/source/lib/management/cli.js +++ b/source/lib/management/cli.js @@ -7,7 +7,8 @@ /*jslint anon:true, sloppy:true*/ -var utils = require('./utils'); +var utils = require('./utils'), + libpath = require('path'); /* * A command is expected to export the following: @@ -99,7 +100,7 @@ function main() { command = require('mojito-cli-cmd-' + commandName); } catch (e) { try { - command = require('./commands/' + commandName); + command = require(libpath.join(__dirname, '../app/commands/', commandName)); } catch (e) { utils.error('Invalid command: ' + command, 'mojito [] []'); diff --git a/source/lib/management/utils.js b/source/lib/management/utils.js index d49c732ac..ce68d5b42 100644 --- a/source/lib/management/utils.js +++ b/source/lib/management/utils.js @@ -15,7 +15,7 @@ var fs = require('fs'), http = require('http'), tty = require('tty'), mojito = require('../index.js'), - archetypes_dir = path.join(__dirname, '/../archetypes'), + archetypes_dir = path.join(__dirname, '../app/archetypes'), isatty = tty.isatty(1) && tty.isatty(2); From 0d4f181c3c633a70298dbcf37e99a83fbd0cdcec Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 29 May 2012 10:21:31 -0700 Subject: [PATCH 010/119] real addon location and management --- source/lib/app/addons/rs/config.server.js | 187 + source/lib/app/addons/rs/selector.server.js | 61 + source/lib/store-new.server.js | 1499 ------ source/lib/store.server.js | 5337 ++++--------------- 4 files changed, 1381 insertions(+), 5703 deletions(-) create mode 100644 source/lib/app/addons/rs/config.server.js create mode 100644 source/lib/app/addons/rs/selector.server.js delete mode 100644 source/lib/store-new.server.js diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js new file mode 100644 index 000000000..6ca583e66 --- /dev/null +++ b/source/lib/app/addons/rs/config.server.js @@ -0,0 +1,187 @@ +/* + * Copyright (c) 2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ + +YUI.add('addon-rs-config', function(Y, NAME) { + + var libfs = require('fs'), + libpath = require('path'), + libycb = require(libpath.join(__dirname, '../../../libs/ycb')); + + function RSAddonConfig() { + RSAddonConfig.superclass.constructor.apply(this, arguments); + } + RSAddonConfig.NS = 'config'; + RSAddonConfig.ATTRS = {}; + + Y.extend(RSAddonConfig, Y.Plugin.Base, { + + initializer: function(config) { + this.rs = config.host; + this.appRoot = config.appRoot; + this.mojitoRoot = config.mojitoRoot; + this.afterHostMethod('findResourceByConvention', this.findResourceByConvention, this); + this.beforeHostMethod('parseResource', this.parseResource, this); + + this._jsonCache = {}; // fullPath: contents as JSON object + this._ycbCache = {}; // fullPath: YCB config object + this._ycbDims = this._readYcbDimensions(); + }, + + + destructor: function() { + // TODO: needed to break cycle so we don't leak memory? + this.rs = null; + }, + + + getDimensions: function() { + return this.rs.cloneObj(this._ycbDims); + }, + + + /** + * Reads and parses a JSON file + * + * @method readConfigJSON + * @param fullPath {string} path to JSON file + * @return {mixed} contents of JSON file + */ + // TODO: async interface + readConfigJSON: function(fullPath) { + var json, + contents; + if (!libpath.existsSync(fullPath)) { + return {}; + } + json = this._jsonCache[fullPath]; + if (!json) { + try { + contents = libfs.readFileSync(fullPath, 'utf-8'); + json = JSON.parse(contents); + } catch (e) { + throw new Error('Error parsing JSON file: ' + fullPath); + } + this._jsonCache[fullPath] = json; + } + return json; + }, + + + /** + * reads a configuration file that is in YCB format + * + * @method readConfigYCB + * @param ctx {object} runtime context + * @param fullPath {string} path to the YCB file + * @return {object} the contextualized configuration + */ + // TODO: async interface + readConfigYCB: function(fullPath, ctx) { + var cacheKey, + json, + ycb; + + ctx = this.rs.mergeRecursive(this.rs.getStaticContext(), ctx); + + ycb = this._ycbCache[fullPath]; + if (!ycb) { + json = this.readConfigJSON(fullPath); + json = this._ycbDims.concat(json); + ycb = new libycb.Ycb(json); + this._ycbCache[fullPath] = ycb; + } + return ycb.read(ctx, {}); + }, + + + findResourceByConvention: function(source, mojitType) { + var fs = source.fs, + use = false; + + // we only care about files + if (!fs.isFile) { + return; + } + // we don't care about files in subdirectories + if ('.' !== fs.subDir) { + return; + } + // we only care about json files + if ('.json' !== fs.ext) { + return; + } + // use package.json for the app and the mojit + if ('package' === fs.basename && 'bundle' !== fs.rootType) { + use = true; + } + // use all configs in the application + if ('app' === fs.rootType) { + use = true; + } + // use configs from non-shared mojit resources + if (mojitType && 'shared' !== mojitType) { + use = true; + } + if (!use) { + return; + } + + return new Y.Do.AlterReturn(null, { + type: 'config' + }); + }, + + + parseResource: function(source, type, subtype, mojitType) { + var baseParts, + res; + + if ('config' !== type) { + return; + } + + baseParts = source.fs.basename.split('.'); + res = { + source: source, + type: 'config', + affinity: 'common', + selector: '*' + }; + if ('app' !== source.fs.rootType) { + res.mojit = mojitType; + } + if (baseParts.length !== 1) { + Y.log('invalid config filename. skipping ' + source.fs.fullPath, 'warn', NAME); + return; + } + res.name = libpath.join(source.fs.subDir, baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + return new Y.Do.Halt(null, res); + }, + + + /** + * Read the application's dimensions.json file for YCB processing. If not + * available, fall back to the framework's default dimensions.json. + * + * @method _readYcbDimensions + * @return {array} contents of the dimensions.json file + * @private + */ + _readYcbDimensions: function() { + var path = libpath.join(this.appRoot, 'dimensions.json'); + if (!libpath.existsSync(path)) { + path = libpath.join(this.mojitoRoot, 'dimensions.json'); + } + return this.readConfigJSON(path); + } + + + }); + Y.namespace('mojito.addons.rs'); + Y.mojito.addons.rs.config = RSAddonConfig; + +}, '0.0.1', { requires: ['plugin', 'oop']}); diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js new file mode 100644 index 000000000..f392f6d48 --- /dev/null +++ b/source/lib/app/addons/rs/selector.server.js @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ + +YUI.add('addon-rs-selector', function(Y, NAME) { + + var libpath = require('path'), + libycb = require(libpath.join(__dirname, '../../../libs/ycb')); + + function RSAddonSelector() { + RSAddonSelector.superclass.constructor.apply(this, arguments); + } + RSAddonSelector.NS = 'selector'; + RSAddonSelector.DEPS = ['config']; + RSAddonSelector.ATTRS = {}; + + Y.extend(RSAddonSelector, Y.Plugin.Base, { + + initializer: function(config) { + var dims, + json; + this.rs = config.host; + this.appRoot = config.appRoot; + this.mojitoRoot = config.mojitoRoot; + + dims = this.rs.config.getDimensions(); + json = this.rs.config.readConfigJSON(libpath.join(this.appRoot, 'application.json')); + json = dims.concat(json); + // TODO: use rs.config for this too + this._appConfigYCB = new libycb.Ycb(json); + }, + + + destructor: function() { + // TODO: needed to break cycle so we don't leak memory? + this.rs = null; + }, + + + getListFromContext: function(ctx) { + var sels = ['*']; + var p, part, parts; + // TODO: use rs.config for this too + parts = this._appConfigYCB.readNoMerge(ctx, {}); + for (p = 0; p < parts.length; p++) { + part = parts[p]; + if (part.selector) { + sels.unshift(part.selector); + } + } + return sels; + } + + + }); + Y.namespace('mojito.addons.rs'); + Y.mojito.addons.rs.selector = RSAddonSelector; + +}, '0.0.1', { requires: ['plugin', 'oop']}); diff --git a/source/lib/store-new.server.js b/source/lib/store-new.server.js deleted file mode 100644 index f0317874f..000000000 --- a/source/lib/store-new.server.js +++ /dev/null @@ -1,1499 +0,0 @@ -/* - * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved. - * Copyrights licensed under the New BSD License. - * See the accompanying LICENSE file for terms. - */ - - -/*jslint - anon:true, sloppy:true, regexp: true, continue: true, nomen:true, node:true -*/ - -/** - * The ResourceStore manages information about the "resources" in a Mojito - * application. These resources are things that have representation on the - * filesystem. - * - * You generally don't need to worry about this class (and its addons) unless - * you are extending Mojito. - * - * Each resource can have many different versions. This is not talking about - * revisions, which is how the resource changes over time. It is instead - * talking about how there can be a version of the resource just for iphones, - * one just for android, a fallback, etc. - * - * The metadata kept about each resource is normalized to the follow keys: - *
- *
source (object)
- *
where the source came from. (not shipped to the client.) - *
- *
fs (object)
- *
filesystem details
- *
pkg (object)
- *
packaging details
- *
- *
- *
mojit (string)
- *
which mojit this applies to, if any. ("shared" means the resource is available to all mojits.)
- *
type (string)
- *
resource type
- *
subtype (string)
- *
not all types of subtypes
- *
name (string)
- *
common to all versions of the resource
- *
id (string)
- *
unique ID. common to all versions of the resource. (typically {type}-{subtype}-{name}.)
- *
yui (object)
- *
for resources that are YUI modules
- *
- * - * The following are only used in the metadata for each resource version - * (The metadata for resolved resources won't have these, since they're intrinsically - * part of the resolved resource.) - *
- *
affinity (string)
- *
runtime affinity. either server, client, or common
- *
selector (string)
- *
version selector
- * - * - * @module ResourceStore - * @main - */ -YUI.add('resource-store', function(Y, NAME) { - - var libfs = require('fs'), - libglob = require('./glob'), - libpath = require('path'), - libwalker = require('./package-walker.server'), - - isNotAlphaNum = /[^a-zA-Z0-9]/, - - mojitoRoot = __dirname, - - CONVENTION_SUBDIR_TYPES = { - // subdir: type - 'actions': 'action', - 'binders': 'binder', - 'commands': 'command', - 'middleware': 'middleware', - 'models': 'model', - 'views': 'view' - }, - CONVENTION_SUBDIR_TYPE_IS_JS = { - 'action': true, - 'binder': true, - 'model': true - }; - - - - // The Affinity object is to manage the use of the affinity string in - // filenames. Some files have affinities that have multiple parts - // (e.g. "server-tests"). - function Affinity(affinity) { - var parts; - if (affinity.indexOf('-') === -1) { - this.affinity = affinity; - } else { - parts = affinity.split('-'); - this.affinity = parts[0]; - this.type = parts[1]; - } - } - Affinity.prototype = { - toString: function() { - return this.affinity; - } - }; - - - - /** - * @class ResourceStore.server - * @constructor - * @requires addon-rs-config, addon-rs-selector - * @param config {object} - * @param root {string} directory to manage (usually the application directory) - * @param context {object} static context - * @param appConfig {object} overrides for `application.json` - */ - function ResourceStore(config) { - ResourceStore.superclass.constructor.apply(this, arguments); - } - ResourceStore.NAME = 'ResourceStore'; - ResourceStore.ATTRS = {}; - - - Y.extend(ResourceStore, Y.Base, { - - initializer: function(cfg) { - this._config = cfg; - this._jsonCache = {}; // fullPath: contents as JSON object - this._ycbCache = {}; // fullPath: context: YCB config object - - this._appRVs = {}; // res.type: array of resource versions - this._mojitRVs = {}; // mojitType: array of resource versions - this._appResources = {}; // env: posl: res.type: array of resources - this._mojitResources = {}; // env: posl: mojitType: array of resources - - Y.Object.each(Y.mojito.addons.rs, function(fn, name) { - this.plug(fn, { appRoot:cfg.root, mojitoRoot:mojitoRoot }); - }, this); - }, - destructor: function() {}, - - - /** - * Returns the static (non-runtime-sensitive) context - * @method getStaticContext - * @return {object} the context - */ - getStaticContext: function() { - return this.cloneObj(this._config.context); - }, - - - /** - * Returns the static (non-runtime-sensitive) version of the application.json. - * @method getStaticAppConfig - * @return {object} the configuration from applications.json - */ - getStaticAppConfig: function() { - return this.cloneObj(this._appConfigStatic); - }, - - - /** - * Returns a contextualized application configuration. - * @method getAppConfig - * @param ctx {object} the context - * @return {object} the application configuration contextualized by the "ctx" argument. - */ - getAppConfig: function(ctx) { - var appConfig, - ycb; - - if (this._appConfigStatic && (!ctx || !Object.keys(ctx).length)) { - return this.cloneObj(this._appConfigStatic); - } - - // start with the base - appConfig = this.cloneObj(this._fwConfig.appConfigBase); - - // apply the read values from the file - ycb = this.config.readConfigYCB(libpath.join(this._config.root, 'application.json'), ctx); - this.mergeRecursive(appConfig, ycb); - - // apply the passed-in overrides - this.mergeRecursive(appConfig, this.cloneObj(this._config.appConfig)); - - return appConfig; - }, - - - /** - * Preloads everything in the app, and as well pertinent parts of - * the framework. - * - * @method preload - * @return {nothing} - */ - preload: function() { - this._fwConfig = this.config.readConfigJSON(libpath.join(mojitoRoot, 'config.json')); - this._appConfigStatic = this.getAppConfig({}); - this._preloadMeta(); - this.resolveResourceVersions(); - }, - - - /** - * preload metadata about all resources in the application (and Mojito framework) - * - * @private - * @method _preloadMeta - * @return {nothing} work down via other called methods - */ - _preloadMeta: function() { - var me = this, - walker, - walkedMojito = false, - dir, - info; - walker = new libwalker.BreadthFirst(this._config.root); - walker.walk(function(err, info) { - if (err) { - throw err; - } - if ('mojito' === info.pkg.name) { - walkedMojito = true; - } - me._preloadPackage(info); - }); - - // user might not have installed mojito as a dependency of their - // application. (they -should- have but might not have.) - // FUTURE: instead walk -all- global packages? - if (!walkedMojito) { - dir = libpath.join(mojitoRoot, '..'); - info = { - depth: 999, - parents: [], - dir: dir - }; - info.pkg = this.config.readConfigJSON(libpath.join(dir, 'package.json')); - - // special case for weird packaging situations - if (!Object.keys(info.pkg).length) { - info.dir = mojitoRoot; - info.pkg = { - name: 'mojito', - version: '0.666.666', - yahoo: { - mojito: { - type: 'bundle', - location: 'app' - } - } - }; - } - - this._preloadPackage(info); - } - }, - - - /** - * preloads metadata about resources in a package - * (but not subpackages in its node_modules/) - * - * @private - * @method _preloadPackage - * @param info {object} metadata about the package - * @return {nothing} work down via other called methods - */ - _preloadPackage: function(info) { - var dir, - pkg; - // FUTURE: use info.inherit to scope mojit dependencies - /* - console.log('--PACKAGE-- ' + info.depth + ' ' + info.pkg.name + '@' + info.pkg.version - + ' \t' + (info.pkg.yahoo && info.pkg.yahoo.mojito && info.pkg.yahoo.mojito.type) - + ' \t[' + info.parents.join(',') + ']' - // + ' \t-- ' + JSON.stringify(info.inherit) - ); - */ - pkg = { - name: info.pkg.name, - version: info.pkg.version, - depth: info.depth - }; - if (0 === info.depth) { - // the actual application is handled specially - this._preloadApp(pkg); - return; - } - if (!info.pkg.yahoo || !info.pkg.yahoo.mojito) { - return; - } - switch (info.pkg.yahoo.mojito.type) { - case 'bundle': - dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); - this._preloadDirBundle(dir, pkg); - break; - case 'mojit': - dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); - this._preloadDirMojit(dir, 'pkg', pkg); - break; - default: - Y.log('Unknown package type "' + info.pkg.yahoo.mojito.type + '"', 'warn', NAME); - break; - } - }, - - - /** - * preloads metadata about resources in the application directory - * (but not node_modules/) - * - * @private - * @method _preloadApp - * @param pkg {object} metadata (name and version) about the app's package - * @return {nothing} work down via other called methods - */ - _preloadApp: function(pkg) { - var ress, - r, - res, - list, - i; - - ress = this._findResourcesByConvention(this._config.root, 'app', pkg, 'shared'); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if ('mojit' !== res.type) { - // ignore app-level mojits found by convention, since they'll be loaded below - this.addResourceVersion(ress[r]); - } - } - - // load mojitsDirs - list = this._globList(this._config.root, this._appConfigStatic.mojitsDirs); - for (i = 0; i < list.length; i += 1) { - this._preloadDirMojits(list[i], 'app', pkg); - } - - // load mojitDirs - list = this._globList(this._config.root, this._appConfigStatic.mojitDirs || []); - for (i = 0; i < list.length; i += 1) { - this._preloadDirMojit(list[i], 'app', pkg); - } - }, - - - /** - * preloads metadata about resource in a directory - * - * @method _preloadDirBundle - * @param dir {string} directory path - * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods - * @private - */ - _preloadDirBundle: function(dir, pkg) { - var ress, - r, - res; - // FUTURE: support configuration too - - ress = this._findResourcesByConvention(dir, 'bundle', pkg, 'shared'); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - this.addResourceVersion(res); - } - this._preloadDirMojits(libpath.join(dir, 'mojits'), 'bundle', pkg); - }, - - - /** - * preloads a directory containing many mojits - * - * @private - * @method _preloadDirMojits - * @param dir {string} directory path - * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" - * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods - */ - _preloadDirMojits: function(dir, dirType, pkg) { - var i, - realDirs, - children, - childName, - childPath; - - if ('/' !== dir.charAt(0)) { - dir = libpath.join(this._config.root, dir); - } - - if (!libpath.existsSync(dir)) { - return; - } - - children = this._sortedReaddirSync(dir); - for (i = 0; i < children.length; i += 1) { - childName = children[i]; - if ('.' === childName.substring(0, 1)) { - continue; - } - childPath = libpath.join(dir, childName); - this._preloadDirMojit(childPath, dirType, pkg); - } - }, - - - /** - * preloads a directory that represents a single mojit - * - * @private - * @method _preloadDirMojit - * @param dir {string} directory path - * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" - * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods - */ - _preloadDirMojit: function(dir, dirType, pkg) { - var mojitType, - packageJson, - definitionJson, - ress, - r, - res; - - if ('/' !== dir.charAt(0)) { - dir = libpath.join(this._config.root, dir); - } - - if (!libpath.existsSync(dir)) { - return; - } - - mojitType = libpath.basename(dir); - packageJson = this.config.readConfigJSON(libpath.join(dir, 'package.json')); - if (packageJson) { - if (packageJson.name) { - mojitType = packageJson.name; - } - // FUTURE: check NPM "engine" - // TODO: register mojit's package.json as a static asset, in "static handler" plugin - } - - definitionJson = this.config.readConfigYCB(libpath.join(dir, 'definition.json'), {}); - if (definitionJson.appLevel) { - mojitType = 'shared'; - } - - res = { - source: { - fs: { - fullPath: dir, - rootDir: dir, - rootType: dirType, - subDir: '.', - subDirArray: ['.'], - basename: libpath.basename(dir), - isFile: false, - ext: null - }, - pkg: pkg - }, - mojit: null, - type: 'mojit', - subtype: null, - name: mojitType, - id: 'mojit--' + mojitType, - affinity: 'common', - selector: '*' - }; - this.addResourceVersion(res); - - ress = this._findResourcesByConvention(dir, 'mojit', pkg, mojitType); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - // just in case, only add those resources that really do belong to us - if (res.mojit === mojitType) { - this.addResourceVersion(res); - } - // FUTURE: else warn? - } - }, - - - /** - * Called by the ResourceStore to decide if a file should be considered - * a resource. You most often don't want to call this directly, but - * instead to hook into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.afterHostMethod('findResourceByConvention', this._myFindResourceByConvention, this); - * ``` - * - * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. - * This method figures out the type (and subtype) of a file, and `parseResource()` turns - * the file into an actual resource. - * - * @method findResourceByConvention - * @param source {object} the same as the `source` part of a resource - * @param mojitType {string} the name of the mojit - * @return {boolean|object} If the source is a directory, a boolean can be returned. - * True indicates that the directory contents should be scanned, while false - * indicates that the directory should be skipped. - * If the source does represent a resource, then an object with the following - * fields should be returned; - * @param type {string} type of the resource - * @param subtype {string} optional subtype of the resource - * @param skipSubdirParts {integer} number of path parts of `source.fs.subDir` to skip - */ - findResourceByConvention: function(source, mojitType) { - var fs = source.fs, - baseParts = fs.basename.split('.'), - type; - - if (!fs.isFile && '.' === fs.subDir && CONVENTION_SUBDIR_TYPES[fs.basename]) { - return true; - } - type = CONVENTION_SUBDIR_TYPES[fs.subDirArray[0]]; - if (!fs.isFile && type) { - return true; - } - if (fs.isFile && type && fs.subDirArray.length >= 1) { - if (CONVENTION_SUBDIR_TYPE_IS_JS[type] && '.js' !== fs.ext) { - return false; - } - return { - type: type, - skipSubdirParts: 1 - }; - } - - // special case: addons - if (!fs.isFile && '.' === fs.subDir && 'addons' === fs.basename) { - return true; - } - if (!fs.isFile && fs.subDirArray.length < 2 && 'addons' === fs.subDirArray[0]) { - return true; - } - if (fs.isFile && fs.subDirArray.length >= 1 && 'addons' === fs.subDirArray[0]) { - if ('.js' !== fs.ext) { - return false; - } - return { - type: 'addon', - subtype: fs.subDirArray[1], - skipSubdirParts: 2 - }; - } - - // special case: archetypes - if (!fs.isFile && '.' === fs.subDir && 'archetypes' === fs.basename) { - return true; - } - if (!fs.isFile && fs.subDirArray.length < 2 && 'archetypes' === fs.subDirArray[0]) { - return true; - } - if (!fs.isFile && fs.subDirArray.length == 2 && 'archetypes' === fs.subDirArray[0]) { - return { - type: 'archetype', - subtype: fs.subDirArray[1], - skipSubdirParts: 2 - }; - } - - // special case: assets - if (!fs.isFile && '.' === fs.subDir && 'assets' === fs.basename) { - return true; - } - if (!fs.isFile && 'assets' === fs.subDirArray[0]) { - return true; - } - if (fs.isFile && 'assets' === fs.subDirArray[0] && fs.subDirArray.length >= 1) { - return { - type: 'asset', - subtype: fs.ext.substr(1), - skipSubdirParts: 1 - }; - } - - // special case: controller - if (fs.isFile && '.' === fs.subDir && 'controller' === baseParts[0]) { - if ('.js' !== fs.ext) { - return false; - } - return { - type: 'controller' - }; - } - - // special case: mojit - if (!fs.isFile && '.' === fs.subDir && 'mojits' === fs.basename) { - // don't bother finding mojits here, since they're loaded explicitly in - // the app and bundle in different ways - return false; - } - - // unknown path - return true; - }, - - - /** - * Called by the ResourceStore to turn a file into a resource. - * You most often don't want to call this directly, but instead to hook - * into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.beforeHostMethod('parseResource', this._myParseResource, this); - * ``` - * - * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. - * `findResourceByConvention()` figures out the type (and subtype) of a file, and - * this method turns the file into an actual resource. - * - * @method parseResource - * @param source {object} the same as the `source` part of a resource - * @param type {string} the resource type of the file - * @param subtype {string} the optional resource subtype of the file - * @param mojitType {string} the name of the mojit - * @return {object|undefined} the resource version - */ - parseResource: function(source, type, subtype, mojitType) { - var fs = source.fs, - baseParts = fs.basename.split('.'), - res; - - // app-level resources - if ('archetype' === type || 'command' === type || 'middleware' === type) { - if ('mojit' === fs.rootType) { - Y.log(type + ' cannot be defined in a mojit. skipping ' + fs.fullPath, 'warn', NAME); - return; - } - res = { - source: source, - mojit: null, - type: type, - subtype: subtype, - name: fs.basename, - affinity: 'server', - selector: '*' - }; - res.id = [res.type, res.subtype, res.name].join('-'); - return res; - } - - // mojit parts with format {name}.{affinity}.{selector} - if ( - 'action' === type || - 'addon' === type || - 'controller' === type || - 'model' === type - ) { - res = { - source: source, - mojit: mojitType, - type: type, - subtype: subtype, - affinity: 'server', - selector: '*' - }; - if (baseParts.length >= 3) { - res.selector = baseParts.pop(); - } - if (baseParts.length >= 2) { - res.affinity = baseParts.pop(); - } - if (baseParts.length !== 1) { - Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); - return; - } - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); - res.id = [res.type, res.subtype, res.name].join('-'); - return res; - } - - // mojit parts with format {name}.{selector} - if ('asset' === type || 'binder' === type) { - res = { - source: source, - mojit: mojitType, - type: type, - subtype: subtype, - affinity: 'common', - selector: '*' - }; - if (baseParts.length >= 2) { - res.selector = baseParts.pop(); - } - if (baseParts.length !== 1) { - Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); - return; - } - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); - res.id = [res.type, res.subtype, res.name].join('-'); - return res; - } - - // special case: view - if ('view' === type) { - res = { - source: source, - mojit: mojitType, - type: type, - subtype: subtype, - viewOutputFormat: fs.ext.substr(1), - viewEngine: baseParts.pop(), - affinity: 'common', - selector: '*' - }; - if (baseParts.length >= 2) { - res.selector = baseParts.pop(); - } - if (baseParts.length !== 1) { - Y.log('invalid view filename. skipping ' + fs.fullPath, 'warn', NAME); - return; - } - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); - res.id = [res.type, res.subtype, res.name].join('-'); - return res; - } - - // just ignore unknown types - return; - }, - - - /** - * Called by the ResourceStore to register a resource version. - * You most often don't want to call this directly, but instead to hook - * into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.beforeHostMethod('parseResource', this._myParseResource, this); - * ``` - * - * @method addResourceVersion - * @param res {object} the resource version - * @return {nothing} - */ - addResourceVersion: function(res) { - res.affinity = new Affinity(res.affinity); - if (res.mojit) { - if (!this._mojitRVs[res.mojit]) { - this._mojitRVs[res.mojit] = []; - } - this._mojitRVs[res.mojit].push(res); - } else { - if (!this._appRVs[res.type]) { - this._appRVs[res.type] = []; - } - this._appRVs[res.type].push(res); - } - }, - - - /** - * For each possible runtime configuration (based on context), pre-calculates - * which versions of the resources will be used. - * The priority (highest to lowest): - * source, - * selector, - * affinity (env or "common"). - * - * @method resolveResourceVersions - * @return {nothing} - */ - resolveResourceVersions: function() { - var c, ctx, ctxs, - poslKey, posl, posls = {}, - e, env, envs = [ 'client', 'server' ], - affinities, selectors, sourceBase, - type, ress, - p; - - ctxs = this._listAllContexts(); - for (var c = 0; c < ctxs.length; c++) { - ctx = ctxs[c]; - posl = this.selector.getListFromContext(ctx); - posls[JSON.stringify(posl)] = posl; - } - - for (e = 0; e < envs.length; e += 1) { - env = envs[e]; - - affinities = {}; // affinity: priority modifier - affinities[env] = 1; - affinities.common = 0; - - for (poslKey in posls) { - if (posls.hasOwnProperty(poslKey)) { - posl = posls[poslKey]; - selectors = {}; // selector: priority modifier - for (p = 0; p < posl.length; p += 1) { - selectors[posl[p]] = (posl.length - p - 1) * 2; - } - sourceBase = posl.length * 2; - //console.log('-- source base ' + sourceBase); - //console.log(selectors); - //console.log(affinities); - - if (!this._appResources[env]) { - this._appResources[env] = {} - } - if (!this._appResources[env][poslKey]) { - this._appResources[env][poslKey] = {} - } - for (type in this._appRVs) { - if (this._appRVs.hasOwnProperty(type)) { - this._appResources[env][poslKey][type] = - this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs[type] ]); - } - } - - if (!this._mojitResources[env]) { - this._mojitResources[env] = {} - } - if (!this._mojitResources[env][poslKey]) { - this._mojitResources[env][poslKey] = {} - } - for (type in this._mojitRVs) { - if ('shared' === type) { - continue; - } - if (this._mojitRVs.hasOwnProperty(type)) { - this._mojitResources[env][poslKey][type] = - this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); - // TODO: fire event that mojit has been resolved - } - } - } - } - } - }, - - - /** - * Resolves versions for a list of resources. - * The priority is based on passed-in configuration. See - * resolveResourceVersions() for details. - * - * @private - * @method _resolveVersions - * @param affinities {object} lookup hash for priority adjustment for each affinity - * @param selectors {object} lookup hash for priority adjustment for each selector - * @param sourceBase {int} multiplier for order in priority list - * @param srcs {array of arrays} resource versions to resolve - * @return {array} list of resolved resources - */ - _resolveVersions: function(affinities, selectors, sourceBase, srcs) { - var s, src, - r, res, - priority, - versions = {}, // id: priority: resource - out = [], - resid, - highest; - - for (s = 0; s < srcs.length; s += 1) { - src = srcs[s]; - for (r = 0; r < src.length; r += 1) { - res = src[r]; - if (!selectors.hasOwnProperty(res.selector)) { - continue; - } - if (!affinities.hasOwnProperty(res.affinity)) { - continue; - } - // TODO: conditionally skip optional affinities - priority = (s * sourceBase) + - selectors[res.selector] + affinities[res.affinity]; - //console.log('--DEBUG-- pri=' + priority + ' --' - // + ' src' + s + '=' + (s * sourceBase) - // + ' ' + res.selector + '=' + selectors[res.selector] - // + ' ' + res.affinity + '=' + affinities[res.affinity] - // + ' -- ' + res.id); - if (!versions[res.id]) { - versions[res.id] = {}; - } - versions[res.id][priority] = res; - } - } - for (resid in versions) { - if (versions.hasOwnProperty(resid)) { - highest = Math.max.apply(Math, Object.keys(versions[resid])) - //console.log('--DEBUG-- highest=' + highest + ' -- ' + resid); - out.push(versions[resid][highest]); - } - } - return out; - }, - - - /** - * Generates a list of all possible context (which is a lot!). - * @private - * @method _listAllContext - * @return {array of objects} all possible contexts - */ - _listAllContexts: function() { - var dims = this.config.getDimensions(), - nctxs, c, ctxs = [], - dn, dname, dnames, - dv, dval, dvals, - e, each, mod, - // only because we might want to change it at some point - // (not including it helps reduce the number of contexts) - SKIP_RUNTIME = true; - - dims = dims[0].dimensions; - dims = this._flattenDims(dims); - dnames = Object.keys(dims); - - nctxs = 1; - for (dn = 0; dn < dnames.length; dn++) { - dname = dnames[dn]; - if (SKIP_RUNTIME && dname === 'runtime') { - continue; - } - dvals = dims[dname]; - if (dname !== 'runtime') { - // we never have indeterminant runtime - dvals.push('*'); - } - nctxs *= dvals.length; - } - - for (c = 0; c < nctxs; c++) { - ctxs[c] = {}; - } - mod = 1; - for (dn = 0; dn < dnames.length; dn++) { - dname = dnames[dn]; - if (SKIP_RUNTIME && dname === 'runtime') { - continue; - } - dvals = dims[dname]; - mod *= dvals.length; - each = nctxs / mod; - - e = each; - dv = 0; - for (c = 0; c < nctxs; --e, c++) { - if (0 === e) { - e = each; - dv++; - dv = dv % dvals.length; - } - dval = dvals[dv]; - if ('*' !== dval) { - ctxs[c][dname] = dval; - } - } - } - return ctxs; - }, - - - /** - * Flattens dimensions so that the structure of the dimension values doesn't matter. - * @private - * @method _flattenDims - * @param dims {object} dimensions structure - * @return {object} - */ - _flattenDims: function(dims) { - var d, dim, - name, out = {}; - for (d = 0; d < dims.length; d++) { - dim = dims[d]; - name = Object.keys(dim)[0]; - out[name] = this._listKeys(dim[name]); - } - return out; - }, - - - /** - * Recursively finds all keys for the object (plus child objects). - * @private - * @method _listKeys - * @param obj {object} - * @return {array} list of all keys in the object (no matter how deep) - */ - _listKeys: function(obj) { - var k, keys = []; - for (k in obj) { - if (obj.hasOwnProperty(k)) { - keys.push(k); - if ('object' === typeof obj[k]) { - keys = keys.concat(this._listKeys(obj[k])); - } - } - } - return keys; - }, - - - /** - * Finds resources based on our conventions - * -doesn't- load mojits or their contents. That's done elsewhere. - * - * @private - * @method _findResourcesByConvention - * @param dir {string} directory from which to find resources - * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" - * @param pkg {object} metadata (name and version) about the package - * @param mojitType {string|null} name of mojit to which the resource belongs - * @return {array} list of resources - */ - _findResourcesByConvention: function(dir, dirType, pkg, mojitType) { - var me = this, - ress = []; - //console.log('-- FIND RESOURCES BY CONVENTION -- ' + pkg.name + '@' + pkg.version + ' -- ' + mojitType); - - this._walkDirRecursive(dir, function(error, subdir, file, isFile) { - var source, ret, res; - - if ('node_modules' === file) { - return false; - } - if ('libs' === file) { - return false; - } - if ('tests' === file && 'test' !== me._appConfigStatic.env) { - return false; - } - - source = { - fs: { - fullPath: libpath.join(dir, subdir, file), - rootDir: dir, - rootType: dirType, - subDir: subdir, - subDirArray: subdir.split('/'), - isFile: isFile, - ext: libpath.extname(file) - }, - pkg: pkg - }; - source.fs.basename = libpath.basename(file, source.fs.ext); - - if (me._skipBadPath(source.fs)) { - return false; - } - - ret = me.findResourceByConvention(source, mojitType); - if ('object' === typeof ret) { - if (ret.skipSubdirParts) { - source.fs.subDirArray = source.fs.subDirArray.slice(ret.skipSubdirParts); - source.fs.subDir = source.fs.subDirArray.join('/') || '.'; - } - res = me.parseResource(source, ret.type, ret.subtype, mojitType); - if ('object' === typeof res) { - ress.push(res); - } - // don't recurse into resources that are directories - return false; - } - return ret; - }); - - return ress; - }, - - - /** - * Indicates whether file should be skipped based on its path - * - * @private - * @method _skipBadPath - * @param pathParts {object} the "source.fs" part of the resource - * @return {boolean} true indicates that the file should be skipped - */ - _skipBadPath: function(fs) { - if (fs.isFile && fs.ext.substr(1).match(isNotAlphaNum)) { - return true; - } - return false; - }, - - - /** - * A wrapper for fs.readdirSync() that guarantees ordering. The order in - * which the file system is walked is significant within the resource - * store, e.g., when looking up a matching context. - * - * @private - * @method _sortedReaddirSync - * @param path {string} directory to read - * @return {array} files in the directory - */ - _sortedReaddirSync: function(path) { - var out = libfs.readdirSync(path); - return out.sort(); - }, - - - /** - * Recursively walks a directory - * - * @private - * @method _walkDirRecursive - * @param dir {string} directory to start at - * @param cb {function(error, subdir, name, isFile)} callback called for each file - * @param _subdir {string} INTERNAL argument, please ignore - * @return {nothing} value returned via callback - */ - _walkDirRecursive: function(dir, cb, _subdir) { - var subdir, - fulldir, - children, - i, - childName, - childPath, - childFullPath, - childStat; - - subdir = _subdir || '.'; - fulldir = libpath.join(dir, subdir); - if (!libpath.existsSync(fulldir)) { - return; - } - - children = this._sortedReaddirSync(fulldir); - for (i = 0; i < children.length; i += 1) { - childName = children[i]; - if ('.' === childName.substring(0, 1)) { - continue; - } - if ('node_modules' === childName) { - continue; - } - childPath = libpath.join(subdir, childName); - childFullPath = libpath.join(dir, childPath); - childStat = libfs.statSync(childFullPath); - if (childStat.isFile()) { - cb(null, subdir, childName, true); - } else if (childStat.isDirectory()) { - if (cb(null, subdir, childName, false)) { - this._walkDirRecursive(dir, cb, childPath); - } - } - } - }, - - - /** - * takes a list of globs and turns it into a list of matching paths - * @private - * @param prefix {string} prefix for every path in the list - * @param list {array} list of globs - * @return {array} list of paths matching the globs - */ - _globList: function(prefix, list) { - var found = [], - i, - glob; - for (i = 0; i < list.length; i += 1) { - glob = list[i]; - if ('/' !== glob.charAt(0)) { - glob = libpath.join(prefix, glob); - } - libglob.globSync(glob, {}, found); - } - return found; - }, - - - /** - * Recursively merge one object onto another. - * From http://stackoverflow.com/questions/171251/ - * how-can-i-merge-properties-of-two-javascript-objects-dynamically/ - * 383245#383245. - * - * @method mergeRecursive - * @param dest {object} object to merge into - * @param src {object} object to merge onto "dest" - * @param matchType {boolean} controls whether a non-object in the src is - * allowed to clobber a non-object in the dest (if a different type) - * @return {object} the modified "dest" object is also returned directly - */ - mergeRecursive: function(dest, src, typeMatch) { - var p; - for (p in src) { - if (src.hasOwnProperty(p)) { - // Property in destination object set; update its value. - if (src[p] && src[p].constructor === Object) { - if (!dest[p]) { - dest[p] = {}; - } - dest[p] = this.mergeRecursive(dest[p], src[p]); - } else { - if (dest[p] && typeMatch) { - if (typeof dest[p] === typeof src[p]) { - dest[p] = src[p]; - } - } else { - dest[p] = src[p]; - } - } - } - } - return dest; - }, - - - /** - * @method cloneObj - * @param o {mixed} - * @return {mixed} deep copy of argument - */ - cloneObj: function(o) { - var newO, - i; - - if (typeof o !== 'object') { - return o; - } - if (!o) { - return o; - } - - if ('[object Array]' === Object.prototype.toString.apply(o)) { - newO = []; - for (i = 0; i < o.length; i += 1) { - newO[i] = this.cloneObj(o[i]); - } - return newO; - } - - newO = {}; - for (i in o) { - if (o.hasOwnProperty(i)) { - newO[i] = this.cloneObj(o[i]); - } - } - return newO; - } - - - }); - - Y.namespace('mojito'); - Y.mojito.ResourceStore = ResourceStore; - - -}, '0.0.1', { requires: [ - 'base', - 'oop', - 'addon-rs-config', - 'addon-rs-selector' -]}); - - - -YUI.add('addon-rs-config', function(Y, NAME) { - - var libfs = require('fs'), - libpath = require('path'), - libycb = require('./libs/ycb'); - - function RSAddonConfig() { - RSAddonConfig.superclass.constructor.apply(this, arguments); - } - RSAddonConfig.NS = 'config'; - RSAddonConfig.ATTRS = {}; - - Y.extend(RSAddonConfig, Y.Plugin.Base, { - - initializer: function(config) { - this.rs = config.host; - this.appRoot = config.appRoot; - this.mojitoRoot = config.mojitoRoot; - this.afterHostMethod('findResourceByConvention', this.findResourceByConvention, this); - this.beforeHostMethod('parseResource', this.parseResource, this); - - this._jsonCache = {}; // fullPath: contents as JSON object - this._ycbCache = {}; // fullPath: YCB config object - this._ycbDims = this._readYcbDimensions(); - }, - - - destructor: function() { - // TODO: needed to break cycle so we don't leak memory? - this.rs = null; - }, - - - getDimensions: function() { - return this.rs.cloneObj(this._ycbDims); - }, - - - /** - * Reads and parses a JSON file - * - * @method readConfigJSON - * @param fullPath {string} path to JSON file - * @return {mixed} contents of JSON file - */ - // TODO: async interface - readConfigJSON: function(fullPath) { - var json, - contents; - if (!libpath.existsSync(fullPath)) { - return {}; - } - json = this._jsonCache[fullPath]; - if (!json) { - try { - contents = libfs.readFileSync(fullPath, 'utf-8'); - json = JSON.parse(contents); - } catch (e) { - throw new Error('Error parsing JSON file: ' + fullPath); - } - this._jsonCache[fullPath] = json; - } - return json; - }, - - - /** - * reads a configuration file that is in YCB format - * - * @method readConfigYCB - * @param ctx {object} runtime context - * @param fullPath {string} path to the YCB file - * @return {object} the contextualized configuration - */ - // TODO: async interface - readConfigYCB: function(fullPath, ctx) { - var cacheKey, - json, - ycb; - - ctx = this.rs.mergeRecursive(this.rs.getStaticContext(), ctx); - - ycb = this._ycbCache[fullPath]; - if (!ycb) { - json = this.readConfigJSON(fullPath); - json = this._ycbDims.concat(json); - ycb = new libycb.Ycb(json); - this._ycbCache[fullPath] = ycb; - } - return ycb.read(ctx, {}); - }, - - - findResourceByConvention: function(source, mojitType) { - var fs = source.fs, - use = false; - - // we only care about files - if (!fs.isFile) { - return; - } - // we don't care about files in subdirectories - if ('.' !== fs.subDir) { - return; - } - // we only care about json files - if ('.json' !== fs.ext) { - return; - } - // use package.json for the app and the mojit - if ('package' === fs.basename && 'bundle' !== fs.rootType) { - use = true; - } - // use all configs in the application - if ('app' === fs.rootType) { - use = true; - } - // use configs from non-shared mojit resources - if (mojitType && 'shared' !== mojitType) { - use = true; - } - if (!use) { - return; - } - - return new Y.Do.AlterReturn(null, { - type: 'config' - }); - }, - - - parseResource: function(source, type, subtype, mojitType) { - var baseParts, - res; - - if ('config' !== type) { - return; - } - - baseParts = source.fs.basename.split('.'); - res = { - source: source, - type: 'config', - affinity: 'common', - selector: '*' - }; - if ('app' !== source.fs.rootType) { - res.mojit = mojitType; - } - if (baseParts.length !== 1) { - Y.log('invalid config filename. skipping ' + source.fs.fullPath, 'warn', NAME); - return; - } - res.name = libpath.join(source.fs.subDir, baseParts.join('.')); - res.id = [res.type, res.subtype, res.name].join('-'); - return new Y.Do.Halt(null, res); - }, - - - /** - * Read the application's dimensions.json file for YCB processing. If not - * available, fall back to the framework's default dimensions.json. - * - * @method _readYcbDimensions - * @return {array} contents of the dimensions.json file - * @private - */ - _readYcbDimensions: function() { - var path = libpath.join(this.appRoot, 'dimensions.json'); - if (!libpath.existsSync(path)) { - path = libpath.join(this.mojitoRoot, 'dimensions.json'); - } - return this.readConfigJSON(path); - } - - - }); - Y.namespace('mojito.addons.rs'); - Y.mojito.addons.rs.config = RSAddonConfig; - -}, '0.0.1', { requires: ['plugin', 'oop']}); - - - -YUI.add('addon-rs-selector', function(Y, NAME) { - - var libpath = require('path'), - libycb = require('./libs/ycb'); - - function RSAddonSelector() { - RSAddonSelector.superclass.constructor.apply(this, arguments); - } - RSAddonSelector.NS = 'selector'; - RSAddonSelector.DEPS = ['config']; - RSAddonSelector.ATTRS = {}; - - Y.extend(RSAddonSelector, Y.Plugin.Base, { - - initializer: function(config) { - var dims, - json; - this.rs = config.host; - this.appRoot = config.appRoot; - this.mojitoRoot = config.mojitoRoot; - - dims = this.rs.config.getDimensions(); - json = this.rs.config.readConfigJSON(libpath.join(this.appRoot, 'application.json')); - json = dims.concat(json); - this._appConfigYCB = new libycb.Ycb(json); - }, - - - destructor: function() { - // TODO: needed to break cycle so we don't leak memory? - this.rs = null; - }, - - - getListFromContext: function(ctx) { - var sels = ['*']; - var p, part, parts; - parts = this._appConfigYCB.readNoMerge(ctx, {}); - for (p = 0; p < parts.length; p++) { - part = parts[p]; - if (part.selector) { - sels.unshift(part.selector); - } - } - return sels; - } - - - }); - Y.namespace('mojito.addons.rs'); - Y.mojito.addons.rs.selector = RSAddonSelector; - -}, '0.0.1', { requires: ['plugin', 'oop']}); - - - diff --git a/source/lib/store.server.js b/source/lib/store.server.js index cfdc0f2f7..542efe920 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -4,4392 +4,1321 @@ * See the accompanying LICENSE file for terms. */ + /*jslint anon:true, sloppy:true, regexp: true, continue: true, nomen:true, node:true */ - -var libfs = require('fs'), - libglob = require('./glob'), - libpath = require('path'), - libqs = require('querystring'), - libvm = require('vm'), - libwalker = require('./package-walker.server'), - libycb = require('./libs/ycb'), - - isPositiveInt = /^[0-9]+$/, - isSpaceOpencurly = / \{/g, - isSpaceClosecurly = / \}/g, - isNotAlphaNum = /[^a-zA-Z0-9]/, - - // fallback logger - // This is the logger that is used until setLogger() is called. - logger = { - log: function(msg, lvl, who) { - var log = console.log; - - if (lvl === 'warn' || lvl === 'error') { - // console.warn provides unbuffered output and avoids message - // truncation when process.exit() is called after logging - log = console.warn; - } - log('[' + lvl + '] ' + who + ': ' + msg); - } - }, - - // nodejs-yui3 has global state about which modules are loaded. Use - // multiple require()'d instances as a wall to prevent cross-contamination - // when using loader for dependency calculations. - utilYUI = require('yui').YUI, - serverYUI = require('yui').YUI, - clientYUI = require('yui').YUI, - - Y = utilYUI({ useSync: true }).use('intl'), - - mojitoRoot = __dirname, - - NAME = 'MojitoServer'; - -// TODO: move string constants here. - - -// The Affinity object is to manage the use of the affinity string in -// filenames. Some files have affinities that have multiple parts -// (e.g. "server-tests"). -function Affinity(affinity) { - this._init(affinity); -} - - -Affinity.prototype = { - _init: function(aff) { - var parts; - - if (aff.indexOf('-') === -1) { - this.affinity = aff; - } else { - parts = aff.split('-'); - this.affinity = parts[0]; - this.type = parts[1]; - } - }, - toString: function() { - return this.affinity; - } -}; - - - /** - * The Resource Store manages information about the "resources" in a Mojito + * The ResourceStore manages information about the "resources" in a Mojito * application. These resources are things that have representation on the * filesystem. * + * You generally don't need to worry about this class (and its addons) unless + * you are extending Mojito. + * * Each resource can have many different versions. This is not talking about * revisions, which is how the resource changes over time. It is instead * talking about how there can be a version of the resource just for iphones, * one just for android, a fallback, etc. * - * There are various types of resources: - *
- *   config      -- a piece of configuration, sometimes for another resource
- *   controller  -- the controller for a mojit
- *   model       -- a model for a mojit
- *   view        -- a view for a mojit
- *   binder      -- a binder for a mojit
- *   action      -- an action to augment the controller
- *   asset       -- an asset (css, js, image, etc)
- *   addon       -- an addon to the mojito system
- *   spec        -- the configuration for a mojit instance
- *   yui-lang    -- a YUI3 language bundle
- *   yui-module  -- a YUI3 module (that isn't one of the above)
- * 
- * - * The metadata kept about each resource is "normalized" to the follow keys: - * (not all resources will have all keys) - * (some types will have additional keys) - * - *
- *   - id
- *       context-insensitive ID of the resource
- *       said another way, all versions of a resource have the same ID
- *
- *   - type
- *       see above
- *
- *   - fsPath
- *       the path on the filesystem
- *
- *   - staticHandlerURL
- *       the URL that will cause the asset handler to serve the resource
- *       for resources that can be deployed by reference to the client
- *
- *   - name
- *       specific to type
- *
- *   - configType
- *       for type=config
- *       the type of the configuration
- *
- *   - viewEngine
- *       for type=view
- *       `mu`, `dust`, etc
+ * The metadata kept about each resource is normalized to the follow keys:
+ * 
+ *
source (object)
+ *
where the source came from. (not shipped to the client.) + *
+ *
fs (object)
+ *
filesystem details
+ *
pkg (object)
+ *
packaging details
+ *
+ *
+ *
mojit (string)
+ *
which mojit this applies to, if any. ("shared" means the resource is available to all mojits.)
+ *
type (string)
+ *
resource type
+ *
subtype (string)
+ *
not all types of subtypes
+ *
name (string)
+ *
common to all versions of the resource
+ *
id (string)
+ *
unique ID. common to all versions of the resource. (typically {type}-{subtype}-{name}.)
+ *
yui (object)
+ *
for resources that are YUI modules
+ *
* - * - viewOutputFormat - * for type=view - * output format that the view will generate - * `xml`, `html`, etc + * The following are only used in the metadata for each resource version + * (The metadata for resolved resources won't have these, since they're intrinsically + * part of the resolved resource.) + *
+ *
affinity (string)
+ *
runtime affinity. either server, client, or common
+ *
selector (string)
+ *
version selector
+ * * - * - assetType - * for type=asset - * `css`, `js`, `png`, `swf`, etc - * - * - addonType - * for type=addon - * the mojito subsystem to which the addon should be added - * - * - yuiModuleName - * for any resource delivered as a YUI3 module - * the YUI3 module name - * - * - yuiModuleVersion - * for any resource delivered as a YUI3 module - * the YUI3 module version - * - * - yuiModuleMeta - * for any resource delivered as a YUI3 module - * the YUI3 module metadata - * (requires, langs, etc) - * - * - yuiSortedPaths - * for any resource delivered as a YUI3 module - * a list of YUI modules required by the module, - * with transitive dependencies resolved - * format: { yui-module-name: URL-to-load-yui-module } - *
- * - * @module MojitoServer - * @class ResourceStore.server - * @constructor - * @param root {string} directory where application is found - * @param libs {object} dependent libraries -- this param is mainly used - * during unit testing + * @module ResourceStore + * @main */ -function ServerStore(root, libs) { - //logger.log('ctor(' + root + ')'); - this._root = root; - this._version = '0.1.0'; - this._shortRoot = libpath.basename(root); - this._libs = libs || {}; - - // the static version of the appConfig (application.json) - // It's "static" because it's determined at server-start time, and doesn't - // change after that. - this._appConfigStatic = null; - - // full paths to routes files, to aid detection - this._preload_routes = {}; - - // Each of these is a complex datastructure, the first key of which is the - // resource ID ("resid"). (For mojitMeta, the first key is the mojit type - // and the second key is resid.) - // Under resid the next key is the affinity ("client", "server", or - // "common".) - // Under affinity is a datastructure that tracks all versions of the resource. - // There is a special key "contexts" which lists all the contexts that the - // resource has been specialized for. "contexts" is an object. The key is a - // string that identifies the context, and the value is a partial context that - // describes the specialization. (An example might be "device=iphone" for - // the key and { device:'iphone' } for the value.) - // The rest of the keys are the context strings (as found in "contexts"), and - // the values are the metadata about the resource versions. - // [resid][affinity].contexts - // [resid][affinity][ctxKey] = { metadata } - // (These are mostly populated by the _preloadResource() method.) - this._preload = { - appMeta: {}, // application-level - sharedMeta: {}, // shared between each mojit - mojitMeta: {} // individual to each mojit - }; - - // These are similar to the _preload above, except the affinity has been resolved - // down for each environment ("client" or "server"). Also, the ctxKey has been - // moved above resid to optimize lookup during runtime. - this._appMetaNC = {}; // [resid] = { parts } - this._appMeta = {}; // [env][ctxKey][resid] = { parts } - this._sharedMeta = {}; // [env][ctxKey][resid] = { parts } - this._mojitMeta = {}; // [env][type][ctxKey][resid] = { parts } - this._mojitYuiRequired = {}; // [env][type][ctxKey] = [ YUI module names ] - this._mojitYuiSorted = {}; // [env][type][ctxKey] = [ YUI module names ] - this._mojitYuiSortedPaths = {}; // [env][type][ctxKey][module] = path - - this._jsonCache = {}; // fullpath: contents as JSON object - this._ycbCache = {}; // fullpath: context: YCB config object - - this._staticURLs = {}; // url => fullpath - this._dynamicURLs = {}; // url => dynamic content - this._mojitAssetRoots = {}; // mojitType => URL prefix - this._mojitLangs = {}; // mojitType => [en-US,en-GB,en] - this._mojitPaths = {}; // mojitType => filesystem directory of mojit - - this._expandInstanceCache = { // [env][cacheKey] = instance - client: {}, - server: {} - }; +YUI.add('resource-store', function(Y, NAME) { + + var libfs = require('fs'), + libglob = require('./glob'), + libpath = require('path'), + libwalker = require('./package-walker.server'), + + isNotAlphaNum = /[^a-zA-Z0-9]/, + + mojitoRoot = __dirname, + + CONVENTION_SUBDIR_TYPES = { + // subdir: type + 'actions': 'action', + 'binders': 'binder', + 'commands': 'command', + 'middleware': 'middleware', + 'models': 'model', + 'views': 'view' + }, + CONVENTION_SUBDIR_TYPE_IS_JS = { + 'action': true, + 'binder': true, + 'model': true + }, + ADDON_SUBTYPES_APPLEVEL = { + 'rs': true + }; - // TODO: bake this into the refactoring work. - // this stuff is mainly so that we can send mocks during testing - if (!this._libs.fs) { - this._libs.fs = libfs; - } - if (!this._libs.path) { - this._libs.path = libpath; - } - if (libycb && (!this._libs.ycb)) { - this._libs.ycb = libycb; - } -} -ServerStore.prototype = { + // The Affinity object is to manage the use of the affinity string in + // filenames. Some files have affinities that have multiple parts + // (e.g. "server-tests"). + function Affinity(affinity) { + var parts; + if (affinity.indexOf('-') === -1) { + this.affinity = affinity; + } else { + parts = affinity.split('-'); + this.affinity = parts[0]; + this.type = parts[1]; + } + } + Affinity.prototype = { + toString: function() { + return this.affinity; + } + }; - // =========================================== - // ================== PUBLIC ================= /** - * Preloads everything in the app, and as well pertinent parts of - * the framework. - * - * @method preload - * @param {object} appContext the base context for reading configuration. - * @param {object} appConfig overrides for the app config. - * @return {nothing} + * @class ResourceStore.server + * @constructor + * @requires addon-rs-config, addon-rs-selector + * @param config {object} + * @param root {string} directory to manage (usually the application directory) + * @param context {object} static context + * @param appConfig {object} overrides for `application.json` */ - preload: function(appContext, appConfig) { - //logger.log('preload()'); - var type, - ctxKey, - resid, - res, - n; - - if (!this._preload) { - // we've already been preloaded - // This situation mainly happens in the commandline scripts. - return; - } + function ResourceStore(config) { + ResourceStore.superclass.constructor.apply(this, arguments); + } + ResourceStore.NAME = 'ResourceStore'; + ResourceStore.ATTRS = {}; - this._fwConfig = this._readConfigJSON(libpath.join(mojitoRoot, - 'config.json')); - this._ycbDims = this._readYcbDimensions(); - this._validYCBDims = this._precalcValidYCBDimensions( - this._ycbDims[0].dimensions - ); - this._defaultYCBContext = appContext || {}; - - // need to read the statically configured appConfig now so that values - // are available during generation of the static URLs - this._appConfigStatic = this._readAppConfigStatic(); - - // generates URL's about each spec in application.json - this._urlsForAppSpecs(); - - // merge app configuration overrides - if (appConfig) { - for (n in appConfig) { - if (appConfig.hasOwnProperty(n)) { - logger.log('overriding application config value: ' + n, - 'warn'); - this._appConfigStatic[n] = appConfig[n]; - } - } - } - // generates metadata about each resource - this._preloadMeta(); + Y.extend(ResourceStore, Y.Base, { - // takes the preloaded info about each resource and resolves - // version priority (.server. more specific that .common. etc) - this._cookdown(); + initializer: function(cfg) { + this._config = cfg; + this._jsonCache = {}; // fullPath: contents as JSON object + this._ycbCache = {}; // fullPath: context: YCB config object - // preread configs - this._prereadConfigs(this._appMeta.server); - for (type in this._mojitMeta.server) { - if (this._mojitMeta.server.hasOwnProperty(type)) { - this._prereadConfigs(this._mojitMeta.server[type]); - } - } + this._appRVs = {}; // res.type: array of resource versions + this._mojitRVs = {}; // mojitType: array of resource versions + this._appResources = {}; // env: posl: res.type: array of resources + this._mojitResources = {}; // env: posl: mojitType: array of resources - // We need to run this function even for "ondemand", since it calculates - // additional implied dependencies, such as a binder on MojitoClient, - // or a controller on the view-engine needed to render its views. - this._precalcYuiDependencies(); - - // binders are client-side-only resources, yet we need to know about - // them when talking about the 'server' environment - for (type in this._mojitMeta.client) { - if (this._mojitMeta.client.hasOwnProperty(type)) { - - for (ctxKey in this._mojitMeta.client[type]) { - if (this._mojitMeta.client[type].hasOwnProperty(ctxKey)) { - - for (resid in this._mojitMeta.client[type][ctxKey]) { - if (this._mojitMeta.client[type][ctxKey]. - hasOwnProperty(resid)) { - res = this._mojitMeta.client[type - ][ctxKey][resid]; - if (res.type !== 'binder') { - continue; - } - this._mojitMeta.server[type - ][ctxKey][resid] = res; - } - } - } + // We'll start with just our "config" addon. + this._yuiUseSync( { + 'addon-rs-config': { + fullpath: libpath.join(__dirname, 'app/addons/rs/config.server.js'), } - } - } - }, - - - /** - * Sets the logger object. - * - * @method setLogger - * @param l {object} object containing a log(message,level,source) function - * @return {nothing} - */ - setLogger: function(l) { - logger = l; - }, + }); + this.plug(Y.mojito.addons.rs.config, { appRoot:this._config.root, mojitoRoot:mojitoRoot }); + this._fwConfig = this.config.readConfigJSON(libpath.join(mojitoRoot, 'config.json')); + this._appConfigStatic = this.getAppConfig({}); + }, + destructor: function() {}, - /** - * Returns, via callback, the fully expanded mojit instance specification. - * - * @method getSpec - * @param env {string} either "client" or "server" - * @param id {string} the ID of the spec to return - * @param context {object} the runtime context for the spec - * @param callback {function(err,spec)} callback used to return the results (or error) - * @return {nothing} results returned via the callback parameter - */ - getSpec: function(env, id, context, callback) { - this.expandInstanceForEnv(env, {base: id}, context, function(err, obj) { - if (err) { - callback(err); - return; - } + /** + * Returns the static (non-runtime-sensitive) context + * @method getStaticContext + * @return {object} the context + */ + getStaticContext: function() { + return this.cloneObj(this._config.context); + }, - if (env === 'client' && obj) { - delete obj.assets; - } - callback(null, obj); - }); - }, + /** + * Returns the static (non-runtime-sensitive) version of the application.json. + * @method getStaticAppConfig + * @return {object} the configuration from applications.json + */ + getStaticAppConfig: function() { + return this.cloneObj(this._appConfigStatic); + }, - /** - * Returns, via callback, the details of the mojit type. - * - * @method getType - * @param env {string} either "client" or "server" - * @param type {string} the mojit type - * @param context {object} the runtime context for the spec - * @param callback {function(err,spec)} callback used to return the results (or error) - * @return {nothing} results returned via the callback parameter - */ - getType: function(env, type, context, callback) { + /** + * Returns a contextualized application configuration. + * @method getAppConfig + * @param ctx {object} the context + * @return {object} the application configuration contextualized by the "ctx" argument. + */ + getAppConfig: function(ctx) { + var appConfig, + ycb; - this.expandInstanceForEnv(env, {type: type}, context, function(err, - obj) { - if (err) { - callback(err); - return; + if (this._appConfigStatic && (!ctx || !Object.keys(ctx).length)) { + return this.cloneObj(this._appConfigStatic); } - if (env === 'client' && obj) { - delete obj.assets; - } + // start with the base + appConfig = this.cloneObj(this._fwConfig.appConfigBase); - callback(null, obj); - }); - }, + // apply the read values from the file + ycb = this.config.readConfigYCB(libpath.join(this._config.root, 'application.json'), ctx); + this.mergeRecursive(appConfig, ycb); - /** - * This just calls expandInstanceForEnv() with `env` set to `server`. - * - * @method expandInstance - * @param instance {map} Partial instance to expand. - * @param ctx {object} The request context. - * @param cb {function(err,instance)} callback used to return the results (or error) - * @return {nothing} results returned via the callback parameter - */ - expandInstance: function(instance, ctx, cb) { - this.expandInstanceForEnv('server', instance, ctx, cb); - return; - }, + // apply the passed-in overrides + this.mergeRecursive(appConfig, this.cloneObj(this._config.appConfig)); + return appConfig; + }, - /** - * This method takes a partial instance and expands it to all details needed - * to run the mojit. - * - * Only `base` or `type` fields are required. You should only specify one. - * - *
-     *  instance: {
-     *      base: string
-     *          // specifies a "base" instance which this instance will extend
-     *          // the value refers to a key of `specs` in `application.json`
-     *      type: string
-     *          // specifies the mojit type
-     *      action: "",
-     *          // specifies a default action if the instance isn't dispatched
-     *          // with a specific one.
-     *      config: object
-     *          // the config for the mojit
-     *          // this will be augmented (appropriately) with the mojit type
-     *          // defaults found in the type's `defaults.json`
-     *      appConfig: object
-     *          // the application config (appropriate for the context)
-     *      assetRoot: "",
-     *          // path to directory containing assets
-     *          // the path will be a URL if `env` is `client` otherwise it's a
-     *          // filesystem path
-     *      definition: object
-     *          // the body of the `defintion.json` for the mojit type
-     *      defaults: object
-     *          // the body of the `defaults.json` for the mojit type
-     *      yui: {
-     *          // details for generating a YUI sandbox for this instance
-     *          config: {
-     *              // configuration details for the YUI.GlobalConfig.groups (or
-     *              // an equivalent).
-     *              // The module paths are given as `fullpath` and contain
-     *              // either a URL if `env' is `client` or a filesystem path if
-     *              // `env` is `server`
-     *          },
-     *          requires: []
-     *              // list of YUI modules that this instance requires
-     *      }
-     *      actions: array
-     *          // list of paths to the YUI modules containing actions
-     *      controller: string
-     *          // path to controller
-     *          // the path will be a URL if `env` is `client` otherwise it's a
-     *          // filesystem path
-     *      lang:
-     *          // path to YUI module of the language bundle
-     *          // the path will be a URL if `env` is `client` otherwise it's a
-     *          // filesystem path
-     *      models: object
-     *          // list of models used by the mojit type
-     *          // the key is the model name, and the value is the path to the
-     *          // model file
-     *          // the path will be a URL if `env` is `client` otherwise it's a
-     *          // filesystem path
-     *      views: {
-     *          // list of views in the mojit type
-     *          // the key is the view name, and the value is details about the
-     *          // view
-     *          view-name: {
-     *              "content-path": "",
-     *                  // the path to use to load the body of the view
-     *                  // the path will be a URL if `env` is `client` otherwise
-     *                  // it's a filesystem path
-     *              "engine": "",
-     *                  // which engine is used to render the view
-     *              "binder-path": "",
-     *                  // the path to the binder
-     *                  // the path will be a URL if `env` is `client` otherwise
-     *                  // it's a filesystem path
-     *              "binder-module": ""
-     *                  // the YUI module name of the binder
-     *          }
-     *      }
-     *  }
-     * 
- * - * @method expandInstanceForEnv - * @param env {string} "client" or "server" - * @param instance {object} partial instance to expand - * @param ctx {object} the runtime context for the instance - * @param cb {function(err,instance)} callback used to return the results (or error) - * @return {nothing} results returned via the callback parameter - */ - expandInstanceForEnv: function(env, instance, ctx, cb) { - //logger.log('expandInstanceForEnv(' + env + ',' + - // (instance.id||'@'+instance.type) + ')'); - var self = this, - base, - appConfig = this.getAppConfig(ctx, 'application'), - cacheKey = JSON.stringify(instance) + JSON.stringify( - this._getValidYCBContext(ctx) - ), - cacheValue = this._expandInstanceCache[env][cacheKey]; - - if (cacheValue) { - cb(null, this._cloneObj(cacheValue)); - return; - } - function gotBase(out, fromBase) { - var spec; - - // type details rebuilt every time - delete out.actions; - delete out.assetsRoot; - delete out.assets; - delete out.controller; - delete out.defaults; - delete out.definition; - delete out.models; - delete out.views; - delete out.yui; - - out.config = out.config || {}; - out.action = out.action || 'index'; - if (!out.instanceId) { - out.instanceId = Y.guid(); - //DEBUGGING: out.instanceId += '-instance-server-' + out.type; - } - // DEPRECATED, kept in case a user is using it. - out.guid = out.instanceId; + /** + * Preloads everything in the app, and as well pertinent parts of + * the framework. + * + * @method preload + * @return {nothing} + */ + preload: function() { + // We need to do an initial sweep to find the resource store addons. + this.preloadResourceVersions(); + // And then use them. + this.loadAddons(); + // Then, do another sweep so that the loaded addons can be used. + this.preloadResourceVersions(); + this.resolveResourceVersions(); + }, + + + /** + * Augments this resource store with addons that we know about. + * To find the addons, call `preloadResourceVersions()` first. + * @method loadAddons + * @return {nothing} + */ + loadAddons: function() { + var modules = {}, + r, + res; - try { - self.getMojitTypeDetails(env, ctx, out.type, out); - } catch (err) { - return cb(err); + Y.Object.each(Y.mojito.addons.rs, function(fn, name) { + this.unplug(name); + }, this); + + for (r = 0; r < this._appRVs.addon.length; r += 1) { + res = this._appRVs.addon[r]; + if ('rs' === res.subtype) { + // FUTURE: ideally we shouldn't proscribe the YUI module name of RS addons + // (We can/should introspect the file for the YUI module name.) + modules['addon-rs-' + res.name] = { + fullpath: res.source.fs.fullPath + }; + } } + this._yuiUseSync(modules); - // apply type defaults to config - if ((!fromBase) && out.defaults && out.defaults.config) { - spec = self._cloneObj(out.defaults.config); - self._mergeRecursive(spec, out.config); - out.config = spec; - } + Y.Object.each(Y.mojito.addons.rs, function(fn, name) { + this.plug(fn, { appRoot:this._config.root, mojitoRoot:mojitoRoot }); + }, this); + }, - if (!out.appConfig) { - out.appConfig = appConfig; - delete out.appConfig.specs; - } - self._expandInstanceCache[env][cacheKey] = self._cloneObj(out); - cb(null, out); - } - if (instance.base) { - if (appConfig.specs) { - base = appConfig.specs[instance.base]; - } - if (!base) { - return cb(new Error('Unknown "base" of "' + instance.base + - '"')); - } - // The base will need to carry it's ID with it. - base.id = instance.base; - this.expandInstanceForEnv(env, base, ctx, function(err, - baseInstance) { + /** + * Preload metadata about all resource versions in the application (and Mojito framework) + * + * @method preloadResourceVersions + * @return {nothing} work down via other called methods + */ + preloadResourceVersions: function() { + var me = this, + walker, + walkedMojito = false, + dir, + info; + + this._appRVs = {}; + this._mojitRVs = {}; + + walker = new libwalker.BreadthFirst(this._config.root); + walker.walk(function(err, info) { if (err) { - return cb(err); + throw err; + } + if ('mojito' === info.pkg.name) { + walkedMojito = true; } - var temp = baseInstance; - self._mergeRecursive(temp, instance); - gotBase(temp, true); + me._preloadPackage(info); }); - } else { - gotBase(this._cloneObj(instance), false); - } - }, + // user might not have installed mojito as a dependency of their + // application. (they -should- have but might not have.) + // FUTURE: instead walk -all- global packages? + if (!walkedMojito) { + dir = libpath.join(mojitoRoot, '..'); + info = { + depth: 999, + parents: [], + dir: dir + }; + info.pkg = this.config.readConfigJSON(libpath.join(dir, 'package.json')); + + // special case for weird packaging situations + if (!Object.keys(info.pkg).length) { + info.dir = mojitoRoot; + info.pkg = { + name: 'mojito', + version: '0.666.666', + yahoo: { + mojito: { + type: 'bundle', + location: 'app' + } + } + }; + } - /** - * gets application configuration - * - * @method getAppConfig - * @param ctx {object} the runtime context under which to load the config - * @param name {string} type of config to read: - * - definition: reads ./application.json - * - package: reads ./package.json - * - routes: reads ./routes.json (or whatever was configured in - * appConfig('definition').routesFiles) - * @return {object} config object - */ - getAppConfig: function(ctx, name) { - //logger.log('getAppConfig('+name+')'); - var resid, - res, - ycb; - - if ('application' === name && (!ctx || !Object.keys(ctx).length)) { - return this._cloneObj(this._appConfigStatic); - } - - resid = 'config-' + name; - res = this._getContextualizedResource(this._appMeta.server, ctx, resid); - if (!res) { - return {}; - } - ycb = this._readConfigYCB(ctx, res.fsPath, true); - return this._cloneObj(ycb); - }, - + this._preloadPackage(info); + } + }, - /** - * Returns the routes configured in the application. - * - * @method getRoutes - * @param ctx {object} runtime context under which to load the routes - * @return {object} routes - */ - getRoutes: function(ctx) { - //logger.log('getRoutes()'); - var ress, - resid, - res, - r, - routes = {}; - - // TODO: [Issue 100] trapped this error. It only appears when there is - // no application.json - try { - ress = this._getResourceListForContext(this._appMeta.server, ctx); - } catch (err) { - //logger.log(err); - ress = {}; - } - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - res = ress[resid]; - if ('routes' !== res.configType) { - continue; - } - r = this._readConfigYCB(ctx, res.fsPath); - this._mergeRecursive(routes, r); + /** + * preloads metadata about resources in a package + * (but not subpackages in its node_modules/) + * + * @private + * @method _preloadPackage + * @param info {object} metadata about the package + * @return {nothing} work down via other called methods + */ + _preloadPackage: function(info) { + var dir, + pkg; + // FUTURE: use info.inherit to scope mojit dependencies + /* + console.log('--PACKAGE-- ' + info.depth + ' ' + info.pkg.name + '@' + info.pkg.version + + ' \t' + (info.pkg.yahoo && info.pkg.yahoo.mojito && info.pkg.yahoo.mojito.type) + + ' \t[' + info.parents.join(',') + ']' + // + ' \t-- ' + JSON.stringify(info.inherit) + ); + */ + pkg = { + name: info.pkg.name, + version: info.pkg.version, + depth: info.depth + }; + if (0 === info.depth) { + // the actual application is handled specially + this._preloadApp(pkg); + return; } - } - if (!Object.keys(routes).length) { - routes = this._cloneObj(this._fwConfig.defaultRoutes); - } - return routes; - }, - + if (!info.pkg.yahoo || !info.pkg.yahoo.mojito) { + return; + } + switch (info.pkg.yahoo.mojito.type) { + case 'bundle': + dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); + this._preloadDirBundle(dir, pkg); + break; + case 'mojit': + dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); + this._preloadDirMojit(dir, 'pkg', pkg); + break; + default: + Y.log('Unknown package type "' + info.pkg.yahoo.mojito.type + '"', 'warn', NAME); + break; + } + }, - /** - * Returns the filesystem location of the static URL. - * Returns undefined if given URL isn't part of the app. - * - * @method fileFromStaticHandlerURL - * @param url {string} static URL - * @return {string} path on filesystem of specified URL, or undefined - */ - fileFromStaticHandlerURL: function(url) { - //logger.log('fileFromStaticHandlerURL('+url+')'); - return this._staticURLs[url]; - }, + /** + * preloads metadata about resources in the application directory + * (but not node_modules/) + * + * @private + * @method _preloadApp + * @param pkg {object} metadata (name and version) about the app's package + * @return {nothing} work down via other called methods + */ + _preloadApp: function(pkg) { + var ress, + r, + res, + list, + i; - /** - * Returns the YUI configuration object which tells YUI about the - * YUI modules in all the mojits. - * - * @method getYuiConfigAllMojits - * @param env {string} "client" or "server" - * @param ctx {object} runtime context for YUI configuration - * @return {object} YUI configuration for all mojits - */ - getYuiConfigAllMojits: function(env, ctx) { - // TODO: use getMojitTypeDetails() to generate this. - //logger.log('getYuiConfigAllMojits('+env+')'); - var modules = {}, - type, - ress, - resid, - res, - ctxKey; - - for (type in this._mojitMeta[env]) { - if (this._mojitMeta[env].hasOwnProperty(type)) { - ress = this._getResourceListForContext( - this._mojitMeta[env][type], - ctx - ); - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - res = ress[resid]; - if (!res.yuiModuleName) { - continue; - } - if (res.sharedMojit) { - continue; - } - modules[res.yuiModuleName] = { - fullpath: ('client' === env) ? - res.staticHandlerURL : - res.fsPath, - requires: res.yuiModuleMeta.requires - }; - } + ress = this._findResourcesByConvention(this._config.root, 'app', pkg, 'shared'); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if ('mojit' !== res.type) { + // ignore app-level mojits found by convention, since they'll be loaded below + this.addResourceVersion(ress[r]); } + } - // add all langs - ress = this._getLangList(this._mojitMeta[env][type]); - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - res = ress[resid]; - modules[res.yuiModuleName] = { - fullpath: ('client' === env) ? - res.staticHandlerURL : - res.fsPath, - requires: res.yuiModuleMeta.requires - }; - } - } + // load mojitsDirs + list = this._globList(this._config.root, this._appConfigStatic.mojitsDirs); + for (i = 0; i < list.length; i += 1) { + this._preloadDirMojits(list[i], 'app', pkg); } - } - return {modules: modules}; - }, + // load mojitDirs + list = this._globList(this._config.root, this._appConfigStatic.mojitDirs || []); + for (i = 0; i < list.length; i += 1) { + this._preloadDirMojit(list[i], 'app', pkg); + } + }, - /** - * Returns the YUI configuration object which tells YUI about the - * YUI modules in the Mojito framework. - * - * @method getYuiConfigFw - * @param env {string} "client" or "server" - * @param ctx {object} runtime context for YUI configuration - * @return {object} YUI configuration for Mojito framework - */ - getYuiConfigFw: function(env, ctx) { - //logger.log('getYuiConfigFw('+env+')'); - var modules = {}, - ress, - resid, - res; - - if (!this._sharedMeta[env]) { - return {modules: {}}; - } - ress = this._getResourceListForContext(this._sharedMeta[env], ctx); - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - res = ress[resid]; - if (!res.yuiModuleName) { - continue; - } - if ('mojito' !== res.pkg.name) { - continue; - } - modules[res.yuiModuleName] = { - fullpath: ('client' === env) ? - res.staticHandlerURL : - res.fsPath, - requires: res.yuiModuleMeta.requires - }; + /** + * preloads metadata about resource in a directory + * + * @method _preloadDirBundle + * @param dir {string} directory path + * @param pkg {object} metadata (name and version) about the package + * @return {nothing} work down via other called methods + * @private + */ + _preloadDirBundle: function(dir, pkg) { + var ress, + r, + res; + // FUTURE: support configuration too + + ress = this._findResourcesByConvention(dir, 'bundle', pkg, 'shared'); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + this.addResourceVersion(res); + } + this._preloadDirMojits(libpath.join(dir, 'mojits'), 'bundle', pkg); + }, + + + /** + * preloads a directory containing many mojits + * + * @private + * @method _preloadDirMojits + * @param dir {string} directory path + * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" + * @param pkg {object} metadata (name and version) about the package + * @return {nothing} work down via other called methods + */ + _preloadDirMojits: function(dir, dirType, pkg) { + var i, + realDirs, + children, + childName, + childPath; + + if ('/' !== dir.charAt(0)) { + dir = libpath.join(this._config.root, dir); } - } - return {modules: modules}; - }, + if (!libpath.existsSync(dir)) { + return; + } - /** - * Returns the YUI configuration object which tells YUI about the - * YUI modules in the application (which aren't part of a mojit). - * - * @method getYiConfigApp - * @param env {string} "client" or "server" - * @param ctx {object} runtime context for YUI configuration - * @return {object} YUI configuration for the app-level modules - */ - getYuiConfigApp: function(env, ctx) { - //logger.log('getYuiConfigApp('+env+')'); - var modules = {}, - ress, - resid, - res; - - if (!this._sharedMeta[env]) { - return {modules: {}}; - } - ress = this._getResourceListForContext(this._sharedMeta[env], ctx); - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - res = ress[resid]; - if (!res.yuiModuleName) { - continue; - } - if ('mojito' === res.pkg.name) { + children = this._sortedReaddirSync(dir); + for (i = 0; i < children.length; i += 1) { + childName = children[i]; + if ('.' === childName.substring(0, 1)) { continue; } - modules[res.yuiModuleName] = { - fullpath: ('client' === env) ? - res.staticHandlerURL : - res.fsPath, - requires: res.yuiModuleMeta.requires - }; + childPath = libpath.join(dir, childName); + this._preloadDirMojit(childPath, dirType, pkg); } - } - return {modules: modules}; - }, - + }, - /** - * returns a serializeable object used to initialize Mojito on the client - * - * FUTURE: [Issue 105] Cache the output of this function - * cache key: all of ctx - * - * @method serializeClientStore - * @param context {object} runtime context - * @param instance {array} DEPRECATED: list of instances to deploy to the client - * (only instances with IDs will be deployable) - * @return {object} object that should be serialized and used to initialize the MojitoClient - */ - serializeClientStore: function(ctx, instances) { - //logger.log('serializeClientStore()'); - var i, - id, - instance, - type, - types = {}, - out = { - appConfig: {}, - specs: {}, // instance details - mojits: {}, // type details - routes: {} - }; - out.appConfig = this.getAppConfig(ctx, 'application'); + /** + * preloads a directory that represents a single mojit + * + * @private + * @method _preloadDirMojit + * @param dir {string} directory path + * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" + * @param pkg {object} metadata (name and version) about the package + * @return {nothing} work down via other called methods + */ + _preloadDirMojit: function(dir, dirType, pkg) { + var mojitType, + packageJson, + definitionJson, + ress, + r, + res; - for (i = 0; i < instances.length; i += 1) { - instance = instances[i]; - types[instance.type] = true; - id = instance.id; - if (id) { - out.specs[id] = out.appConfig.specs[id]; + if ('/' !== dir.charAt(0)) { + dir = libpath.join(this._config.root, dir); } - } - for (type in types) { - if (types.hasOwnProperty(type)) { - out.mojits[type] = {}; - this.getMojitTypeDetails('client', ctx, type, out.mojits[type]); + if (!libpath.existsSync(dir)) { + return; } - } - - out.routes = this.getRoutes(ctx); - - // these aren't needed on the client - delete out.appConfig.mojitsDirs; - delete out.appConfig.routesFiles; - delete out.appConfig.specs; - return out; - }, - - - /** - * Returns a list of all mojit types in the application. - * - * @method listAllMojits - * @param env {string} "client" or "server" - * @return {array} list of mojit types - */ - listAllMojits: function(env) { - var mojitType, - list = []; + mojitType = libpath.basename(dir); + packageJson = this.config.readConfigJSON(libpath.join(dir, 'package.json')); + if (packageJson) { + if (packageJson.name) { + mojitType = packageJson.name; + } + // FUTURE: check NPM "engine" + // TODO: register mojit's package.json as a static asset, in "static handler" plugin + } - for (mojitType in this._mojitMeta[env]) { - if (this._mojitMeta[env].hasOwnProperty(mojitType)) { - list.push(mojitType); + definitionJson = this.config.readConfigYCB(libpath.join(dir, 'definition.json'), {}); + if (definitionJson.appLevel) { + mojitType = 'shared'; } - } - return list; - }, + res = { + source: { + fs: { + fullPath: dir, + rootDir: dir, + rootType: dirType, + subDir: '.', + subDirArray: ['.'], + basename: libpath.basename(dir), + isFile: false, + ext: null + }, + pkg: pkg + }, + mojit: null, + type: 'mojit', + subtype: null, + name: mojitType, + id: 'mojit--' + mojitType, + affinity: 'common', + selector: '*' + }; + this.addResourceVersion(res); + + ress = this._findResourcesByConvention(dir, 'mojit', pkg, mojitType); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + // just in case, only add those resources that really do belong to us + if (res.mojit === mojitType) { + this.addResourceVersion(res); + } + // FUTURE: else warn? + } + }, + + + /** + * Called by the ResourceStore to decide if a file should be considered + * a resource. You most often don't want to call this directly, but + * instead to hook into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.afterHostMethod('findResourceByConvention', this._myFindResourceByConvention, this); + * ``` + * + * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. + * This method figures out the type (and subtype) of a file, and `parseResource()` turns + * the file into an actual resource. + * + * @method findResourceByConvention + * @param source {object} the same as the `source` part of a resource + * @param mojitType {string} the name of the mojit + * @return {boolean|object} If the source is a directory, a boolean can be returned. + * True indicates that the directory contents should be scanned, while false + * indicates that the directory should be skipped. + * If the source does represent a resource, then an object with the following + * fields should be returned; + * @param type {string} type of the resource + * @param subtype {string} optional subtype of the resource + * @param skipSubdirParts {integer} number of path parts of `source.fs.subDir` to skip + */ + findResourceByConvention: function(source, mojitType) { + var fs = source.fs, + baseParts = fs.basename.split('.'), + type; - /** - * Returns details about all mojits in the application. - * - * @method getAllMojits - * @param env {string} "client" or "server" - * @param ctx {object} runtime context - * @return {object} keys are mojit type names, values are details about each mojit - */ - getAllMojits: function(env, ctx) { + if (!fs.isFile && '.' === fs.subDir && CONVENTION_SUBDIR_TYPES[fs.basename]) { + return true; + } + type = CONVENTION_SUBDIR_TYPES[fs.subDirArray[0]]; + if (!fs.isFile && type) { + return true; + } + if (fs.isFile && type && fs.subDirArray.length >= 1) { + if (CONVENTION_SUBDIR_TYPE_IS_JS[type] && '.js' !== fs.ext) { + return false; + } + return { + type: type, + skipSubdirParts: 1 + }; + } - var mojits, - mojit, - list = {}; + // special case: addons + if (!fs.isFile && '.' === fs.subDir && 'addons' === fs.basename) { + return true; + } + if (!fs.isFile && fs.subDirArray.length < 2 && 'addons' === fs.subDirArray[0]) { + return true; + } + if (fs.isFile && fs.subDirArray.length >= 1 && 'addons' === fs.subDirArray[0]) { + if ('.js' !== fs.ext) { + return false; + } + return { + type: 'addon', + subtype: fs.subDirArray[1], + skipSubdirParts: 2 + }; + } - if (!ctx) { - ctx = {}; - } + // special case: archetypes + if (!fs.isFile && '.' === fs.subDir && 'archetypes' === fs.basename) { + return true; + } + if (!fs.isFile && fs.subDirArray.length < 2 && 'archetypes' === fs.subDirArray[0]) { + return true; + } + if (!fs.isFile && fs.subDirArray.length == 2 && 'archetypes' === fs.subDirArray[0]) { + return { + type: 'archetype', + subtype: fs.subDirArray[1], + skipSubdirParts: 2 + }; + } - mojits = this.listAllMojits(env); + // special case: assets + if (!fs.isFile && '.' === fs.subDir && 'assets' === fs.basename) { + return true; + } + if (!fs.isFile && 'assets' === fs.subDirArray[0]) { + return true; + } + if (fs.isFile && 'assets' === fs.subDirArray[0] && fs.subDirArray.length >= 1) { + return { + type: 'asset', + subtype: fs.ext.substr(1), + skipSubdirParts: 1 + }; + } - for (mojit in mojits) { - if (mojits.hasOwnProperty(mojit)) { - list[mojits[mojit]] = - this.getMojitTypeDetails(env, ctx, mojits[mojit]); + // special case: controller + if (fs.isFile && '.' === fs.subDir && 'controller' === baseParts[0]) { + if ('.js' !== fs.ext) { + return false; + } + return { + type: 'controller' + }; } - } - return list; - }, + // special case: mojit + if (!fs.isFile && '.' === fs.subDir && 'mojits' === fs.basename) { + // don't bother finding mojits here, since they're loaded explicitly in + // the app and bundle in different ways + return false; + } + // unknown path + return true; + }, + + + /** + * Called by the ResourceStore to turn a file into a resource. + * You most often don't want to call this directly, but instead to hook + * into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.beforeHostMethod('parseResource', this._myParseResource, this); + * ``` + * + * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. + * `findResourceByConvention()` figures out the type (and subtype) of a file, and + * this method turns the file into an actual resource. + * + * @method parseResource + * @param source {object} the same as the `source` part of a resource + * @param type {string} the resource type of the file + * @param subtype {string} the optional resource subtype of the file + * @param mojitType {string} the name of the mojit + * @return {object|undefined} the resource version + */ + parseResource: function(source, type, subtype, mojitType) { + var fs = source.fs, + baseParts = fs.basename.split('.'), + res; - /** - * Given a set of known contexts, finds the best match for a runtime context. - * Gives special consideration to the "lang" key in the contexts. - * - * @method _findBestContext - * @param currentContext {object} runtime context - * @param contexts {object} a mapping of context key to context - * @return {string} null or the context key of the best match - * @private - */ - _findBestContext: function(currentContext, contexts) { - var availableLangs = [], - bestCtxKey, - bestLang, - context, - ctxKey, - i, - matchingKeys = []; - - // Collect languages from matching contexts - // We're done if we find an exact match - for (ctxKey in contexts) { - if (contexts.hasOwnProperty(ctxKey)) { - context = contexts[ctxKey]; - if (this._matchContext(currentContext, context)) { - if (context.lang) { - if (currentContext.lang === context.lang) { - bestCtxKey = ctxKey; - break; - } - availableLangs.push(context.lang); - } - matchingKeys.push(ctxKey); + // app-level resources + if ('archetype' === type || 'command' === type || 'middleware' === type) { + if ('mojit' === fs.rootType) { + Y.log(type + ' cannot be defined in a mojit. skipping ' + fs.fullPath, 'warn', NAME); + return; } + res = { + source: source, + mojit: null, + type: type, + subtype: subtype, + name: fs.basename, + affinity: 'server', + selector: '*' + }; + res.id = [res.type, res.subtype, res.name].join('-'); + return res; } - } - // If no exact match, find the next best language - if (!bestCtxKey && availableLangs && availableLangs.length && - currentContext && currentContext.lang) { - bestLang = Y.Intl.lookupBestLang(currentContext.lang, - availableLangs); - if (bestLang) { - for (i = 0; i < matchingKeys.length; i += 1) { - if (contexts[matchingKeys[i]].lang === bestLang) { - bestCtxKey = matchingKeys[i]; - break; - } + // mojit parts with format {name}.{affinity}.{selector} + if ( + 'action' === type || + 'addon' === type || + 'controller' === type || + 'model' === type + ) { + res = { + source: source, + mojit: mojitType, + type: type, + subtype: subtype, + affinity: 'server', + selector: '*' + }; + if (baseParts.length >= 3) { + res.selector = baseParts.pop(); + } + if (baseParts.length >= 2) { + res.affinity = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); + return; } + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + // special case + if ('addon' === type && ADDON_SUBTYPES_APPLEVEL[res.subtype]) { + res.mojit = null; + } + return res; } - } - - return bestCtxKey || - (matchingKeys.length && matchingKeys[0]) || - null; - }, + // mojit parts with format {name}.{selector} + if ('asset' === type || 'binder' === type) { + res = { + source: source, + mojit: mojitType, + type: type, + subtype: subtype, + affinity: 'common', + selector: '*' + }; + if (baseParts.length >= 2) { + res.selector = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); + return; + } + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + return res; + } - /** - * Returns details about a mojit type. - * - * @method getMojitTypeDetails - * @param env {string} "client" or "server" - * @param ctx {object} runtime context - * @param mojitType {string} mojit type - * @param dest {object} object in which to place the results - * @return {object} returns the "dest" parameter, which has had details added to it - */ - getMojitTypeDetails: function(env, ctx, mojitType, dest) { - //logger.log('getMojitTypeDetails('+env+',ctx,'+mojitType+')'); - var ress, - resid, - res, - name, - engine, - engines = {}, - ctxKey, - assumeRollups = this._appConfigStatic.assumeRollups, - usePrecomputed = -1 !== this._appConfigStatic.yui. - dependencyCalculations.indexOf('precomputed'), - useOnDemand = -1 !== this._appConfigStatic.yui. - dependencyCalculations.indexOf('ondemand'), - module, - lddf, // lang/datatype-date-format - lddfPath; - - if (!usePrecomputed) { - useOnDemand = true; - } - - if (!dest) { - dest = {}; - } - - if (!dest.actions) { - dest.actions = []; - } - if (!dest.assets) { - dest.assets = {}; - } - if (!dest.models) { - dest.models = {}; - } - if (!dest.modelYUIModuleNames) { - dest.modelYUIModuleNames = {}; - } - if (!dest.views) { - dest.views = {}; - } - if (!dest.yui) { - dest.yui = {config: {}, requires: []}; - } - if (!dest.yui.config) { - dest.yui.config = {modules: {}}; - } - if (!dest.yui.config.modules) { - dest.yui.config.modules = {}; - } - if (!dest.yui.requires) { - dest.yui.requires = []; - } - if (!dest.yui.langs) { - dest.yui.langs = {}; - } - - if (usePrecomputed) { - ctxKey = this._findBestContext(ctx, - this._mojitYuiSorted[env][mojitType].contexts); - dest.yui.requires = - this._mojitYuiRequired[env][mojitType][ctxKey] || []; - dest.yui.sorted = - this._cloneObj( - this._mojitYuiSorted[env][mojitType][ctxKey] || [] - ); - dest.yui.sortedPaths = - this._cloneObj( - this._mojitYuiSortedPaths[env][mojitType][ctxKey] || {} - ); - } - - dest.assetsRoot = this._mojitAssetRoots[mojitType]; - dest.definition = this._getMojitConfig('server', ctx, mojitType, - 'definition'); - dest.defaults = this._getMojitConfig('server', ctx, mojitType, - 'defaults'); - - ress = this._getResourceListForContext(this._mojitMeta[env][mojitType], - ctx); - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - - res = ress[resid]; - - if (res.type === 'action') { - if (env === 'client') { - if (assumeRollups) { - dest.actions.push(res.rollupURL); - } else { - dest.actions.push(res.staticHandlerURL); - } - } else { - dest.actions.push(res.fsPath); - } - } - - if (res.type === 'asset') { - name = res.name; - if (env === 'client') { - if (assumeRollups) { - dest.assets[name] = res.rollupURL; - } else { - dest.assets[name] = res.staticHandlerURL; - } - } else { - dest.assets[name] = res.fsPath; - } - } - - if (res.type === 'binder') { - if (!dest.views[res.name]) { - dest.views[res.name] = {}; - } - dest.views[res.name]['binder-url'] = res.staticHandlerURL; - if (env === 'client') { - if (assumeRollups) { - dest.views[res.name]['binder-path'] = res.rollupURL; - } else { - dest.views[res.name]['binder-path'] = - res.staticHandlerURL; - } - } else { - dest.views[res.name]['binder-path'] = res.fsPath; - } - dest.views[res.name]['binder-module'] = res.yuiModuleName; - dest.views[res.name]['binder-yui-sorted'] = - res.yuiSortedPaths; - if ('server' === env) { - // don't do any other type of server-side processing for - // the binder ESPECIALLY don't add it to dest.yui.* - continue; - } - } - - if (res.type === 'controller') { - // We need the YUI Module name of the contoller so we can - // select a language for it - dest.controllerModuleName = res.yuiModuleName; - if (env === 'client') { - if (assumeRollups) { - dest.controller = res.rollupURL; - } else { - dest.controller = res.staticHandlerURL; - } - } else { - dest.controller = res.fsPath; - } - } - - if (res.type === 'model') { - if (env === 'client') { - if (assumeRollups) { - dest.models[res.name] = res.rollupURL; - } else { - dest.models[res.name] = res.staticHandlerURL; - } - } else { - dest.models[res.name] = res.fsPath; - } - if (res.yuiModuleName) { - dest.modelYUIModuleNames[res.yuiModuleName] = true; - //logger.log("Processing Models:" + res.name + ":" + - // res.yuiModuleName, 'mojito', NAME); - } - } - - if (res.type === 'view') { - if (!dest.views[res.name]) { - dest.views[res.name] = {}; - } - if (env === 'client') { - if (assumeRollups) { - dest.views[res.name]['content-path'] = - res.rollupURL; - } else { - dest.views[res.name]['content-path'] = - res.staticHandlerURL; - } - } else { - dest.views[res.name]['content-path'] = res.fsPath; - } - dest.views[res.name].engine = res.viewEngine; - engines[res.viewEngine] = true; - } - - if (res.type === 'yui-lang') { - dest.yui.langs[res.langCode] = res.yuiModuleName; - } - - if (res.yuiModuleName) { - if (res.addonType === 'view-engines') { - // we'll only load the viewEngines that we need - continue; - } - if (useOnDemand) { - dest.yui.requires.push(res.yuiModuleName); - } - if (res.sharedMojit) { - continue; - } - dest.yui.config.modules[res.yuiModuleName] = { - fullpath: undefined, - requires: res.yuiModuleMeta.requires || [] - }; - if (env === 'client') { - if (assumeRollups) { - dest.yui.config.modules[res.yuiModuleName - ].fullpath = res.rollupURL; - } else { - dest.yui.config.modules[res.yuiModuleName - ].fullpath = res.staticHandlerURL; - } - } else { - dest.yui.config.modules[res.yuiModuleName].fullpath = - res.fsPath; - } - - // If we have "lang" use it - if (this._mojitLangs[res.yuiModuleName]) { - this._mojitLangs[res.yuiModuleName].sort(); - dest.yui.config.modules[res.yuiModuleName].lang = - this._mojitLangs[res.yuiModuleName]; - } - } - } - } - for (engine in engines) { - if (engines.hasOwnProperty(engine)) { - resid = 'addon-view-engines-' + engine; - res = this._getContextualizedResource( - this._mojitMeta[env][mojitType], - ctx, - resid - ); - dest.yui.config.modules[res.yuiModuleName] = { - fullpath: ('client' === env) ? - res.staticHandlerURL : - res.fsPath, - requires: res.yuiModuleMeta.requires || [] - }; - if (useOnDemand) { - dest.yui.requires.push(res.yuiModuleName); - } - } - } - - // We need to include -all- the langs since we don't know which will - // actually be used. dispatch() will cull that down to the right one. - if (usePrecomputed) { - for (name in dest.yui.langs) { - if (dest.yui.langs.hasOwnProperty(name)) { - module = dest.yui.langs[name]; - lddf = 'lang/datatype-date-format_' + (name || 'en'); - if (dest.yui.sortedPaths[lddf]) { - lddfPath = dest.yui.sortedPaths[lddf].replace('_' + - name, '_MOJITOPLACEHOLDER'); - } - if (!dest.yui.sortedPaths[module]) { - dest.yui.sorted.push(module); - dest.yui.sortedPaths[module] = - dest.yui.config.modules[module].fullpath; - } - } - } - if (lddfPath) { - for (name in dest.yui.langs) { - if (dest.yui.langs.hasOwnProperty(name)) { - lddf = 'lang/datatype-date-format_' + (name || 'en'); - if (!dest.yui.sortedPaths[lddf]) { - dest.yui.sorted.push(lddf); - dest.yui.sortedPaths[lddf] = - lddfPath.replace('MOJITOPLACEHOLDER', - (name || 'en')); - } - } - } - } - } - - return dest; - }, - - - /** - * Returns details on how to make rollups for app-level resources. - * - * @method getRollupsApp - * @param env {string} "client" or "server" - * @param ctx {object} runtime context - * @return {object} object describing where to put the rollup and what it should contain - */ - getRollupsApp: function(env, ctx) { - var dest = libpath.join(this._root, 'rollup.' + env + '.js'), - srcs = [], - ress, - resid, - res; - - ress = this._getResourceListForContext(this._sharedMeta[env], ctx); - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - res = ress[resid]; - if (!res.yuiModuleName) { - continue; - } - srcs.push(res.fsPath); - } - } - return { - dest: dest, - srcs: srcs - }; - }, - - - /** - * Returns details on how to make rollups for mojit-level resources. - * - * This example comes from GSG5. - * { FlickrDetail: - * dest: '/blah/blah/mojits/FlickrDetail/rollup.client.js' - * srcs: [ - * '/blah/blah/mojits/FlickrDetail/controller.common.js', - * '/blah/blah/mojits/FlickrDetail/binders/index.js', - * '/blah/blah/mojits/FlickrDetail/lang/FlickrDetail_de.js', - * '/blah/blah/mojits/FlickrDetail/lang/FlickrDetail_en-US.js' - * ] - * } - * - * @method getRollupsMojits - * @param env {string} "client" or "server" - * @param ctx {object} runtime context - * @return {object} object describing where to put the rollup and what it should contain - */ - getRollupsMojits: function(env, ctx) { - var mojitName, - ress, - resid, - res, - dest, - srcs, - rollups = {}, - mojitoMojits = libpath.join(mojitoRoot, 'mojits'); - - for (mojitName in this._mojitMeta[env]) { - if (this._mojitMeta[env].hasOwnProperty(mojitName)) { - srcs = []; - ress = this._getResourceListForContext( - this._mojitMeta[env][mojitName], - ctx - ); - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - res = ress[resid]; - if (res.sharedMojit) { - continue; - } - if (!res.yuiModuleName) { - continue; - } - srcs.push(res.fsPath); - } - } - dest = libpath.join(this._mojitPaths[mojitName], 'rollup.' + - env + '.js'); - if (dest.indexOf(mojitoMojits) === 0) { - // we shouldn't write to the mojits that ship with Mojito - dest = false; - } - if (dest && srcs.length) { - rollups[mojitName] = { - dest: dest, - srcs: srcs - }; - } - } - } - return rollups; - }, - - - /** - * Returns details on how to make inline CSS for mojits. - * - * This example comes from (a modified) GSG5. - * [ { - * context: { device: 'iphone' }, - * mojitName: 'FlickrDetail', - * yuiModuleName: 'inlinecss/FlickrDetail', - * dest: '/blah/mojits/FlickrDetail/autoload/compiled' + - * '/css.iphone.client.js', - * srcs: { - * '/static/FlickrDetail/assets/index.css': - * ' /blah/mojits/FlickrDetail/assets/index.iphone.css', - * '/static/FlickrDetail/assets/message.css': - * ' /blah/mojits/FlickrDetail/assets/message.css' - * } - * ] - * - * @method getInlineCssMojits - * @param env {string} "client" or "server" - * @param ctxFilter {object} (optional) runtime context to restrict results to - * @return {array} object describing where to put the inline CSS file and what it should contain - */ - getInlineCssMojits: function(env, ctxFilter) { - var mojitName, - ctxKey, - ctx, - ress, - resid, - res, - selector, - dest, - srcs, - inlines = [], - mojitoMojits = libpath.join(mojitoRoot, 'mojits'); - - // This will make our test later a little easier. - if (typeof ctxFilter === 'object' && !Object.keys(ctxFilter).length) { - ctxFilter = null; - } - - for (mojitName in this._mojitMeta[env]) { - if (this._mojitMeta[env].hasOwnProperty(mojitName)) { - for (ctxKey in this._mojitMeta[env][mojitName].contexts) { - if (this._mojitMeta[env][mojitName - ].contexts.hasOwnProperty(ctxKey)) { - ctx = this._mojitMeta[env][mojitName].contexts[ctxKey]; - if (ctxFilter && !this._matchContext(ctx, ctxFilter)) { - continue; - } - - ress = this._cloneObj(this._mojitMeta[env - ][mojitName]['*']); - if ('*' !== ctxKey) { - ress = this._mergeRecursive(ress, - this._mojitMeta[env][mojitName][ctxKey]); - } - srcs = []; - for (resid in ress) { - if (ress.hasOwnProperty(resid)) { - res = ress[resid]; - if ((res.type !== 'asset') || - (res.assetType !== 'css')) { - continue; - } - srcs[res.staticHandlerURL] = res.fsPath; - } - } - selector = this._selectorFromContext(ctx); - dest = 'autoload/compiled/inlinecss' + (selector ? '.' + - selector : '') + '.common.js'; - dest = libpath.join(this._mojitPaths[mojitName], dest); - if (dest.indexOf(mojitoMojits) === 0) { - // we shouldn't write to the mojits that ship with - // Mojito - continue; - } - if (Object.keys(srcs).length) { - inlines.push({ - context: ctx, - mojitName: mojitName, - yuiModuleName: 'inlinecss/' + mojitName, - dest: dest, - srcs: srcs - }); - } - - } // has - } // foreach ctxKey - } // has - } // foreach mojit - return inlines; - }, - - - // =========================================== - // ================= PRIVATE ================= - - /** - * the "static" version of the application.json is the version that has - * the context applied that was given at server-start time. - * - * @method _readAppConfigStatic - * @return {object} static config - * @private - */ - _readAppConfigStatic: function() { - var path = libpath.join(this._root, 'application.json'), - config = this._cloneObj(this._fwConfig.appConfigBase), - appConfig; - - if (this._libs.path.existsSync(path)) { - appConfig = this._readConfigYCB({}, path); - this._mergeRecursive(config, appConfig); - } - - return config; - }, - - - /** - * Read the application's dimensions.json file for YCB processing. If not - * available, fall back to the framework's default dimensions.json. - * - * @method _readYcbDimensions - * @return {array} contents of the dimensions.json file - * @private - */ - _readYcbDimensions: function() { - var libpath = this._libs.path, - path = libpath.join(this._root, 'dimensions.json'), - dims; - - if (!libpath.existsSync(path)) { - path = libpath.join(mojitoRoot, 'dimensions.json'); - } - - dims = this._readConfigJSON(path); - if (!this._isValidYcbDimensions(dims)) { - throw new Error('Invalid dimensions.json: ' + path); - } - return dims; - }, - - - /** - * Perform some light validation for YCB dimensions JSON objects. It should - * look something like this: - * [{ - * "dimensions": [ - * "dim1": {}, - * "dim2": {}, - * ... - * ] - * }] - * - * @method _isValidYcbDimensions - * @param {array} dimensions - * @return {boolean} - * @private - */ - _isValidYcbDimensions: function(dims) { - var isArray = Y.Lang.isArray; - - return isArray(dims) && - dims.length === 1 && - isArray(dims[0].dimensions) && - dims[0].dimensions.length > 0; - }, - - - /** - * preload metadata about all resources in the application (and Mojito framework) - * - * @method _preloadMeta - * @return {nothing} work down via other called methods - * @private - */ - _preloadMeta: function() { - var me = this, - walker, - walkedMojito = false, - dir, - info; - walker = new libwalker.BreadthFirst(this._root); - walker.walk(function(err, info) { - if (err) { - throw err; - } - if ('mojito' === info.pkg.name) { - walkedMojito = true; - } - me._preloadPackage(info); - }); - - // user might not have installed mojito as a dependency of their - // application. (they -should- have but might not have.) - if (!walkedMojito) { - dir = libpath.join(mojitoRoot, '..'); - info = { - depth: 999, - parents: [], - dir: dir - }; - info.pkg = this._readMojitConfigFile(libpath.join(dir, 'package.json'), false); - - // special case for weird packaging situations - if (!Object.keys(info.pkg).length) { - info.dir = mojitoRoot; - info.pkg = { - name: 'mojito', - version: '0.666.666', - yahoo: { - mojito: { - type: 'bundle', - location: 'app' - } - } - }; - } - - this._preloadPackage(info); - } - }, - - - /** - * preloads metadata about resources in the application directory - * (but not node_modules/) - * - * @method _preloadApp - * @param pkg {object} metadata (name and version) about the app's package - * @return {nothing} work down via other called methods - * @private - */ - _preloadApp: function(pkg) { - var i, - path; - - // mark routes, to aid in detecting them later - for (i = 0; i < this._appConfigStatic.routesFiles.length; i += 1) { - path = this._appConfigStatic.routesFiles[i]; - if ('/' !== path.charAt(0)) { - path = libpath.join(this._root, path); - } - if (!libpath.existsSync(path)) { - logger.log('missing routes file. skipping ' + path, 'warn', NAME); - continue; - } - this._preload_routes[path] = true; - } - - this._preloadDirBundle(this._root, pkg, true); - - // load mojitsDirs - for (i = 0; i < this._appConfigStatic.mojitsDirs.length; i += 1) { - path = this._appConfigStatic.mojitsDirs[i]; - this._preloadDirMojits(path, pkg); - } - - // load mojitDirs - if (this._appConfigStatic.mojitDirs) { - for (i = 0; i < this._appConfigStatic.mojitDirs.length; i += 1) { - path = this._appConfigStatic.mojitDirs[i]; - this._preloadDirMojit(path, pkg, path); - } - } - }, - - - /** - * preloads metadata about resources in a package - * (but not subpackages in its node_modules/) - * - * @method _preloadPackage - * @param info {object} metadata about the package - * @return {nothing} work down via other called methods - * @private - */ - _preloadPackage: function(info) { - var dir, - pkg; - // FUTURE: use info.inherit to scope mojit dependencies - /* - console.log('--PACKAGE-- ' + info.depth + ' ' + info.pkg.name + '@' + info.pkg.version - + ' \t' + (info.pkg.yahoo && info.pkg.yahoo.mojito && info.pkg.yahoo.mojito.type) - + ' \t[' + info.parents.join(',') + ']' - // + ' \t-- ' + JSON.stringify(info.inherit) - ); - */ - pkg = { - name: info.pkg.name, - version: info.pkg.version - }; - if (0 === info.depth) { - // the actual application is handled specially - this._preloadApp(pkg); - return; - } - if (!info.pkg.yahoo || !info.pkg.yahoo.mojito) { - return; - } - switch (info.pkg.yahoo.mojito.type) { - case 'bundle': - dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); - this._preloadDirBundle(dir, pkg, false); - this._preloadDirMojits(libpath.join(dir, 'mojits'), pkg); - break; - case 'mojit': - dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); - this._preloadDirMojit(dir, pkg, info.dir); - break; - default: - logger.log('Unknown package type "' + info.pkg.yahoo.mojito.type + '"', 'warn', NAME); - break; - } - }, - - - /** - * preloads metadata about resource in a directory - * - * @method _preloadDirBundle - * @param dir {string} directory path - * @param pkg {object} metadata (name and version) about the package - * @param loadConfig {boolean} whether to also preload metadata about the configuration files - * @return {nothing} work down via other called methods - * @private - */ - _preloadDirBundle: function(dir, pkg, loadConfig) { - var i, - res, - resources; - resources = this._findResourcesByConvention(dir, pkg, 'shared'); - for (i = 0; i < resources.length; i += 1) { - res = resources[i]; - switch (res.type) { - case 'config': - if (!loadConfig) { - if ('package' !== res.name) { - logger.log('config file "' + res.shortPath + '" not used here. skipping.', 'warn', NAME); - } - break; - } - this._preloadResource(res, null); - break; - - // app-level - case 'archetype': - case 'command': - case 'middleware': - this._preloadResource(res, null); - break; - - // mojit-level - case 'action': - case 'addon': - case 'asset': - case 'binder': - case 'controller': - case 'model': - case 'spec': - case 'view': - case 'yui-lang': - case 'yui-module': - this._preloadResource(res, 'shared'); - break; - - default: - logger.log('unknown resource type "' + res.type + '". skipping ' + res.fsPath, 'warn', NAME); - break; - } // switch - } // for each resource - }, - - - /** - * @method _parseResourceArchetype - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceArchetype: function(res, subtype) { - var dir, - ext, - file; - - // archetypes don't support our ".affinity." or ".selector." filename syntax - dir = libpath.dirname(res.shortPath); - ext = libpath.extname(res.shortPath); - file = libpath.basename(res.shortPath, ext); - - res.name = libpath.join(dir, file); - res.type = 'archetype'; - res.subtype = subtype; - res.id = 'archetype-' + res.subtype + '-' + res.name; - return res; - }, - - - /** - * @method _parseResourceCommand - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceCommand: function(res) { - var dir, - ext, - file; - - // commands don't support our ".affinity." or ".selector." filename syntax - dir = libpath.dirname(res.shortPath); - ext = libpath.extname(res.shortPath); - file = libpath.basename(res.shortPath, ext); - - res.name = libpath.join(dir, file); - res.type = 'command'; - res.id = 'command-' + res.name; - return res; - }, - - - /** - * @method _parseResourceMiddleware - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceMiddleware: function(res) { - var dir, - ext, - file; - - // middleware doesn't support our ".affinity." or ".selector." filename syntax - dir = libpath.dirname(res.shortPath); - ext = libpath.extname(res.shortPath); - file = libpath.basename(res.shortPath, ext); - - res.name = libpath.join(dir, file); - res.type = 'middleware'; - res.id = 'middleware-' + res.name; - return res; - }, - - - /** - * @method _parseResourceConfig - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceConfig: function(res, mojitType) { - var dir, - ext, - file, - pathParts; - - // configs don't support our ".affinity." or ".selector." filename syntax - dir = libpath.dirname(res.shortPath); - ext = libpath.extname(res.shortPath); - file = libpath.basename(res.shortPath, ext); - pathParts = { - affinity: new Affinity('server'), - contextKey: '*', - contextParts: {}, - ext: ext - }; - - if ('.json' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - if (this._preload_routes[res.fsPath]) { - res.configType = 'routes'; - } - res.name = libpath.join(dir, file); - res.type = 'config'; - res.id = 'config-' + res.name; - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceAction - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceAction: function(res, mojitType) { - var pathParts = this._parsePath(res, 'server'); - if ('.js' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - res.name = libpath.join(libpath.dirname(res.shortPath), pathParts.shortFile); - res.type = 'action'; - res.id = 'action-' + res.name; - this._precalcYuiModule(res); - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceAddon - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceAddon: function(res, mojitType, subtype) { - var pathParts = this._parsePath(res, 'server'); - if ('.js' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - res.name = libpath.join(libpath.dirname(res.shortPath), pathParts.shortFile); - res.type = 'addon'; - res.addonType = subtype; - res.id = 'addon-' + res.addonType + '-' + res.name; - this._precalcYuiModule(res); - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceAsset - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceAsset: function(res, mojitType) { - var dir, - ext, - file, - fileParts, - pathParts; - - // binders don't support our ".affinity." filename syntax - dir = libpath.dirname(res.shortPath); - ext = libpath.extname(res.shortPath); - file = libpath.basename(res.shortPath, ext); - fileParts = file.split('.'); - - pathParts = { - affinity: new Affinity('common'), - contextKey: '*', - contextParts: {}, - ext: ext - }; - if (fileParts.length >= 2) { - pathParts.contextParts.device = fileParts.pop(); - pathParts.contextKey = libqs.stringify(pathParts.contextParts); - } - pathParts.shortFile = fileParts.join('.'); - if (this._skipBadPath(pathParts)) { - return; - } - - res.name = libpath.join(dir, pathParts.shortFile) + pathParts.ext; - res.type = 'asset'; - res.assetType = pathParts.ext.substr(1); - res.id = 'asset-' + res.name; - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceBinder - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceBinder: function(res, mojitType) { - var dir, - ext, - file, - fileParts, - pathParts; - - // binders don't support our ".affinity." filename syntax - dir = libpath.dirname(res.shortPath); - ext = libpath.extname(res.shortPath); - file = libpath.basename(res.shortPath, ext); - fileParts = file.split('.'); - - pathParts = { - affinity: new Affinity('client'), - contextKey: '*', - contextParts: {}, - ext: ext - }; - if (fileParts.length >= 2) { - pathParts.contextParts.device = fileParts.pop(); - pathParts.contextKey = libqs.stringify(pathParts.contextParts); - } - pathParts.shortFile = fileParts.join('.'); - if ('.js' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - - res.name = libpath.join(libpath.dirname(res.shortPath), pathParts.shortFile); - res.type = 'binder'; - res.id = 'binder-' + res.name; - this._precalcYuiModule(res); - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceController - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceController: function(res, mojitType) { - var pathParts = this._parsePath(res, 'server'); - if ('.js' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - res.name = 'controller'; - res.type = 'controller'; - res.id = 'controller'; - this._precalcYuiModule(res); - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceModel - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceModel: function(res, mojitType) { - var pathParts = this._parsePath(res, 'server'); - if ('.js' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - res.name = libpath.join(libpath.dirname(res.shortPath), pathParts.shortFile); - res.type = 'model'; - res.id = 'model-' + res.name; - this._precalcYuiModule(res); - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceSpec - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceSpec: function(res, mojitType) { - var dir, - ext, - file, - pathParts, - appConfig, - prefix; - - // specs don't support our ".affinity." or ".selector." filename syntax - dir = libpath.dirname(res.shortPath); - ext = libpath.extname(res.shortPath); - file = libpath.basename(res.shortPath, ext); - pathParts = { - affinity: new Affinity('server'), - contextKey: '*', - contextParts: {}, - ext: ext - }; - pathParts.shortFile = file; - if ('.json' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - - res.name = libpath.join(libpath.dirname(res.shortPath), pathParts.shortFile); - res.type = 'spec'; - res.id = 'spec-' + res.name; - - appConfig = this._appConfigStatic.staticHandling || {}; - prefix = '/static'; - if (typeof appConfig.prefix !== 'undefined') { - prefix = appConfig.prefix ? '/' + appConfig.prefix : ''; - } - - // namespaced by mojitType - res.specName = mojitType; - if (res.name !== 'default') { - res.specName += ':' + res.name; - } - - res.dynamicHandlerURL = prefix + '/' + mojitType + '/specs/' + res.shortPath; - return res; - }, - - - /** - * @method _parseResourceView - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceView: function(res, mojitType) { - var pathParts, - fileParts; - // views don't support our ".affinity." filename syntax - pathParts = { - affinity: new Affinity('common'), - contextKey: '*', - contextParts: {}, - ext: libpath.extname(res.shortPath) - }; - fileParts = libpath.basename(res.shortPath).split('.'); - res.viewOutputFormat = fileParts.pop(); - res.viewEngine = fileParts.pop(); - if (fileParts.length >= 2) { - pathParts.contextParts.device = fileParts.pop(); - pathParts.contextKey = libqs.stringify(pathParts.contextParts); - } - pathParts.shortFile = fileParts.join('.'); - - if (fileParts.length !== 1) { - logger.log('invalid view filename. skipping ' + res.fsPath, 'warn', NAME); - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - - res.name = libpath.join(libpath.dirname(res.shortPath), pathParts.shortFile); - res.type = 'view'; - res.id = 'view-' + res.name; - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceYuiLang - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceYuiLang: function(res, mojitType) { - var pathParts, - shortName; - // language bundles don't support our ".affinity." filename syntax - pathParts = { - affinity: new Affinity('common'), - contextKey: '*', - contextParts: {}, - ext: libpath.extname(res.shortPath) - }; - if ('.js' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - pathParts.shortFile = libpath.basename(res.shortPath, pathParts.ext); - if (pathParts.shortFile === mojitType) { - res.langCode = ''; - } else if (mojitType === pathParts.shortFile.substr(0, mojitType.length)) { - res.langCode = pathParts.shortFile.substr(mojitType.length + 1); - } else { - logger.log('invalid YUI lang file format. skipping ' + res.fsPath, 'error', NAME); - return; - } - if (res.langCode) { - pathParts.contextParts.lang = res.langCode; - pathParts.contextKey = libqs.stringify(pathParts.contextParts); - } - - res.name = res.langCode; - res.type = 'yui-lang'; - res.id = 'yui-lang-' + res.langCode; - - if (!this._mojitLangs[mojitType]) { - this._mojitLangs[mojitType] = []; - } - if (res.langCode) { - this._mojitLangs[mojitType].push(res.langCode); - } - - this._precalcYuiModule(res); - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * @method _parseResourceYuiModule - * @param res {object} partial resource - * @return {object|null} parsed resource, or null if shouldn't be used - * @private - */ - _parseResourceYuiModule: function(res, mojitType) { - var pathParts = this._parsePath(res); - if ('.js' !== pathParts.ext) { - return; - } - if (this._skipBadPath(pathParts)) { - return; - } - - if (pathParts.affinity.type) { - // This allows the app config to specify that some "client" affinity - // code should not be deployed if it has an "optional" subtype - if (this._appConfigStatic.deferAllOptionalAutoloads && - pathParts.affinity.type === 'optional') { - pathParts.affinity = 'none'; - } else if (pathParts.affinity.type === 'tests' && - this._appConfigStatic.env !== 'test') { - // this filters tests from being included with deployed - // applications - pathParts.affinity = 'none'; - } - } - if ('none' === pathParts.affinity) { - return; - } - - this._precalcYuiModule(res); - res.name = res.yuiModuleName; - res.type = 'yui-module'; - res.id = 'yui-module-' + res.name; - this._precalcStaticURL(res, mojitType); - res.pathParts = pathParts; - return res; - }, - - - /** - * preloads a directory containing many mojits - * - * @method _preloadDirMojits - * @param dir {string} directory path - * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods - * @private - */ - _preloadDirMojits: function(dir, pkg) { - var i, - realDirs, - children, - childName, - childPath; - - if ('/' !== dir.charAt(0)) { - dir = libpath.join(this._root, dir); - } - - // handle globbing - if (dir.indexOf('*') >= 0) { - realDirs = []; - libglob.globSync(dir, {}, realDirs); - if (!realDirs.length) { - logger.log('Failed to find any mojitsDirs matching ' + dir, 'error', NAME); - return; - } - for (i = 0; i < realDirs.length; i += 1) { - this._preloadDirMojits(realDirs[i], pkg); - } - return; - } - - if (!this._libs.path.existsSync(dir)) { - return; - } - - children = this._sortedReaddirSync(dir); - for (i = 0; i < children.length; i += 1) { - childName = children[i]; - if ('.' === childName.substring(0, 1)) { - continue; - } - childPath = libpath.join(dir, childName); - this._preloadDirMojit(childPath, pkg, childPath); - } - }, - - - /** - * preloads a directory that represents a single mojit - * - * @method _preloadDirMojit - * @param dir {string} directory path - * @param pkg {object} metadata (name and version) about the package - * @param pkgDir {string} directory of the packaging for this mojit - * @return {nothing} work down via other called methods - * @private - */ - _preloadDirMojit: function(dir, pkg, pkgDir) { - var i, - realDirs, - resources, - res, - mojitType, - packageJson, - definitionJson, - appConfig, - prefix, - url; - - if ('/' !== dir.charAt(0)) { - dir = libpath.join(this._root, dir); - } - - // handle globbing - if (dir.indexOf('*') >= 0) { - realDirs = []; - libglob.globSync(dir, {}, realDirs); - if (!realDirs.length) { - logger.log('Failed to find any mojitDirs matching ' + dir, 'error', NAME); - return; - } - for (i = 0; i < realDirs.length; i += 1) { - this._preloadDirMojit(realDirs[i], pkg, pkgDir); - } - return; - } - - if (!this._libs.path.existsSync(dir)) { - return; - } - - mojitType = libpath.basename(dir); - packageJson = this._readMojitConfigFile(libpath.join(pkgDir, 'package.json'), false); - if (packageJson) { - if (packageJson.name) { - mojitType = packageJson.name; - } - if (pkg.name !== 'mojito') { - // TODO: deprecate. NPM "engine" is better - if (!this._mojitoVersionMatch(packageJson, this._version)) { - logger.log('Mojito version mismatch: mojit skipped in "' + - dir + '"', 'warn', NAME); - return; - } - - this._mojitPackageAsAsset(dir, mojitType, packageJson); - } - } - this._mojitPaths[mojitType] = dir; - - definitionJson = this._readMojitConfigFile(libpath.join(dir, 'definition.json'), true); - if (definitionJson.appLevel) { - mojitType = 'shared'; - } - - if ('shared' !== mojitType) { - // TODO: [Issue 109] re-use logic from _precalcStaticURL() for - // prefix (so that all options are supported) - appConfig = this._appConfigStatic.staticHandling || {}; - prefix = '/static'; - if (typeof appConfig.prefix !== 'undefined') { - prefix = appConfig.prefix ? '/' + appConfig.prefix : ''; - } - url = prefix + '/' + mojitType + '/definition.json'; - this._dynamicURLs[url] = libpath.join(dir, 'definition.json'); - } - - resources = this._findResourcesByConvention(dir, pkg, mojitType); - for (i = 0; i < resources.length; i += 1) { - res = resources[i]; - switch (res.type) { - // app-level - case 'archetype': - case 'command': - case 'middleware': - logger.log('app-level resources not allowed here. skipping ' + res.fsPath, 'warn', NAME); - this._preloadResource(res, mojitType); - break; - - // mojit-level - case 'action': - case 'addon': - case 'asset': - case 'binder': - case 'controller': - case 'model': - case 'spec': - case 'view': - case 'yui-lang': - case 'yui-module': - this._preloadResource(res, mojitType); - break; - case 'config': - if ('shared' !== mojitType) { - this._preloadResource(res, mojitType); - } - break; - - default: - logger.log('unknown resource type "' + res.type + '". skipping ' + res.fsPath, 'warn', NAME); - break; - } // switch - } - }, - - - /** - * Finds resources based on our conventions - * -doesn't- load mojits or their contents. That's done elsewhere. - * - * actions/{name}.**.js - * addons/{subtype}/{name}.**.js - * archetypes/{subtype}/{name}/ - * assets/{everything} - * binders/{name}.**.js - * commands/{name}.js - * controller.**.js - * lang/{name}.**.js - * middleware/{name}.js - * models/{name}.**.js - * views/{name}.**.{ext} - * yui_modules/{name}.**.js - * - * @method _findResourcesByConvention - * @param dir {string} directory from which to find resources - * @param pkg {object} metadata (name and version) about the package - * @param mojitType {string|null} name of mojit to which the resource belongs - * @return {array} list of resources - * @private - */ - _findResourcesByConvention: function(dir, pkg, mojitType) { - var me = this, - resources = []; - //console.log('-- FIND RESOURCES BY CONVENTION -- ' + pkg.name + '@' + pkg.version + ' -- ' + mojitType); - - this._walkDirRecursive(dir, function(error, subdir, file, isFile) { - var pathParts, - fileParts, - ext, - subtype, - res; - - if ('node_modules' === file) { - return false; - } - if ('libs' === file) { - return false; - } - if ('tests' === file && 'test' !== me._appConfigStatic.env) { - return false; - } - - pathParts = libpath.join(subdir, file).split('/'); - if (!isFile) { - - // mojits are loaded another way later - if ('.' === subdir && 'mojits' === file) { - return false; - } - - if (pathParts.length === 3 && 'archetypes' === pathParts[0]) { - subtype = pathParts[1]; - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(2).join('/') - }; - res = me._parseResourceArchetype(res, subtype); - if (res) { - resources.push(res); - } - // no need to recurse into the archetype at this time - return false; - } - - // otherwise, just recurse - return true; - } - - fileParts = file.split('.'); - ext = fileParts[fileParts.length - 1]; - - if ('.' === subdir && 'json' === ext) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: libpath.join(subdir, file) - }; - res = me._parseResourceConfig(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'commands' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceCommand(res); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'middleware' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceMiddleware(res); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'actions' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceAction(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 3 && 'addons' === pathParts[0]) { - subtype = pathParts[1]; - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(2).join('/') - }; - res = me._parseResourceAddon(res, mojitType, subtype); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'assets' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceAsset(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'binders' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceBinder(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if ('.' === subdir && 'controller' === fileParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: libpath.join(subdir, file) - }; - res = me._parseResourceController(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'lang' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceYuiLang(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'models' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceModel(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'specs' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceSpec(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && 'views' === pathParts[0]) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceView(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - if (pathParts.length >= 2 && ('yui_modules' === pathParts[0] - || 'autoload' === pathParts[0] - || 'tests' === pathParts[0])) { - res = { - pkg: pkg, - fsPath: libpath.join(dir, subdir, file), - shortPath: pathParts.slice(1).join('/') - }; - res = me._parseResourceYuiModule(res, mojitType); - if (res) { - resources.push(res); - } - return; - } - - // unknown file, just skip. (this includes config files which are - // handled separately.) - return; - }); - - return resources; - }, - - - /** - * @method _parsePath - * @param res {object} partial resource - * @return {object} metadata: ext, shortFile, affinity, contextKey, and contextParts - * @private - */ - _parsePath: function(res, defaultAffinity) { - var out = {}, - fileParts, - device; - - out.contextKey = '*'; - out.contextParts = {}; - out.affinity = defaultAffinity || 'server'; - out.ext = libpath.extname(res.shortPath); - - fileParts = libpath.basename(res.shortPath, out.ext).split('.'); - if (fileParts.length >= 3) { - device = fileParts.pop(); - } - if (fileParts.length >= 2) { - out.affinity = fileParts.pop(); - } - out.shortFile = fileParts.join('.'); - - out.affinity = new Affinity(out.affinity); - if (device) { - out.contextParts.device = device; - } - if (!this._objectIsEmpty(out.contextParts)) { - out.contextKey = libqs.stringify(out.contextParts); - } - return out; - }, - - - /** - * utility that registers the resource for later parts of the algorithm - * - * @method _preloadResource - * @param res {object} metadata about the resource - * @param mojitType {string} which mojit, if applicatable - * @return {nothing} - * @private - */ - _preloadResource: function(res, mojitType) { - var dest, - config, - using, - skipping; - - if ('spec' === res.type) { - config = this._readConfigYCB({}, res.fsPath); - if (!this._appConfigStatic.specs) { - this._appConfigStatic.specs = {}; - } - this._appConfigStatic.specs[res.specName] = config; - this._dynamicURLs[res.dynamicHandlerURL] = res.fsPath; - return; - } - - // non-contextualized resources - // (... which are most app-level resources) - if (!res.pathParts) { - this._appMetaNC[res.id] = res; - return; - } - - if (!mojitType) { - dest = this._preload.appMeta; - } else if ('shared' === mojitType) { - res.sharedMojit = true; - dest = this._preload.sharedMeta; - } else { - if (!this._preload.mojitMeta[mojitType]) { - this._preload.mojitMeta[mojitType] = {}; - } - dest = this._preload.mojitMeta[mojitType]; - } - - if (!dest[res.id]) { - dest[res.id] = {}; - } - dest = dest[res.id]; - if (!dest[res.pathParts.affinity]) { - dest[res.pathParts.affinity] = {}; - } - dest = dest[res.pathParts.affinity]; - if (!dest.contexts) { - dest.contexts = {}; - } - if (dest[res.pathParts.contextKey]) { - using = 'from pkg ' + dest[res.pathParts.contextKey].pkg.name + '@' + - dest[res.pathParts.contextKey].pkg.version; - skipping = 'from pkg ' + res.pkg.name + '@' + res.pkg.version; - if (using === skipping) { - using = dest[res.pathParts.contextKey].shortPath; - skipping = res.shortPath; - } - if (using === skipping) { - using = dest[res.pathParts.contextKey].fsPath; - skipping = res.fsPath; - } - logger.log('ALREADY EXISTS: ' + res.type + ' ' + res.shortPath + - (mojitType ? ' -- in mojit ' + mojitType : '') + - ' -- using ' + using + ' -- skipping ' + skipping, - 'info', NAME); - return; - } - dest.contexts[res.pathParts.contextKey] = res.pathParts.contextParts; - dest[res.pathParts.contextKey] = res; - delete res.pathParts; - - if (res.staticHandlerURL) { - this._staticURLs[res.staticHandlerURL] = res.staticHandlerFsPath; - delete res.staticHandlerFsPath; - } - }, - - - /** - * Note: this MUST be called before _parseResourceSpec() - * - * Generates URL's about each spec in application.json - * - * @method _urlsForAppSpecs - * @return {nothing} - * @private - */ - _urlsForAppSpecs: function() { - var specs = this._appConfigStatic.specs || {}, - appConfig, - prefix, - id, - url; - - appConfig = this._appConfigStatic.staticHandling || {}; - prefix = '/static'; - if (typeof appConfig.prefix !== 'undefined') { - prefix = appConfig.prefix ? '/' + appConfig.prefix : ''; - } - - for (id in specs) { - if (specs.hasOwnProperty(id)) { - // Set the URL of the spec - // TODO: use appconfig.staticHandling.appName - url = prefix + '/' + id + '/specs/default.json'; - this._dynamicURLs[url] = 'application.json'; - } - } - }, - - - /** - * prereads the configuration file, if possible - * (configuration files in YCB format cannot be preread) - * - * @method _prereadConfigs - * @param src {object} contextualized resources - * @return {nothing} - * @private - */ - _prereadConfigs: function(src) { - var ctxKey, - res, - resid; - - if ((!src) || (!Object.keys(src))) { - return; - } - for (ctxKey in src.contexts) { - if (src.contexts.hasOwnProperty(ctxKey)) { - for (resid in src[ctxKey]) { - if (src[ctxKey].hasOwnProperty(resid)) { - res = src[ctxKey][resid]; - if ('config' === res.type && - !this._jsonCache[res.fsPath]) { - //logger.log('prereading ' + res.fsPath, 'info', - // NAME); - this._jsonCache[res.fsPath] = - this._readConfigJSON(res.fsPath); - } - } - } - } - } - }, - - - /** - * Reads and parses a JSON file - * - * @method _readConfigJSON - * @param fullpath {string} path to JSON file - * @return {mixed} contents of JSON file - * @private - */ - _readConfigJSON: function(fullpath) { - //logger.log('_readConfigJSON(' + fullpath + ')'); - var json, - contents = this._libs.fs.readFileSync(fullpath, 'utf-8'); - - try { - json = JSON.parse(contents); - } catch (e) { - logger.log(this._reportJavaScriptSyntaxErrors(contents, fullpath), - 'warn', NAME); - throw new Error('Error parsing JSON file: ' + fullpath); - } - return json; - }, - - - /** - * Create a lookup table for validating YCB dimensions and values. The - * table looks like this: - * - *
-     * {
-     *   "dim1": {
-     *     "val1": null,
-     *     "val2": null,
-     *     ...
-     *   },
-     *   ...
-     * }
-     * 
- * - * @method _precalcValidYCBDimensions - * @param dimensions {object} Top-level YCB "dimensions" object - * @return object - * @private - */ - _precalcValidYCBDimensions: function(dimensions) { - var validDims = {}, - name, - i; - - for (i = 0; i < dimensions.length; i += 1) { - for (name in dimensions[i]) { - if (dimensions[i].hasOwnProperty(name)) { - validDims[name] = {}; - this._flattenYCBDimension(validDims[name], - dimensions[i][name]); - } - } - } - - return validDims; - }, - - - /** - * Flatten the keys in a nested structure into a single object. The first - * argument is modified. All values are set to null. - * - * @method _flattenYCBDimensions - * @param keys {object} The accumulator for keys. - * @param obj {object} - * @private - */ - _flattenYCBDimension: function(keys, obj) { - var name; - - for (name in obj) { - if (obj.hasOwnProperty(name)) { - keys[name] = null; - if (typeof obj[name] === 'object') { - this._flattenYCBDimension(keys, obj[name]); - } - } - } - }, - - - /** - * Return a context that contains only valid dimensions and values. - * - * @method _getValidYCBContext - * @param ctx {object} runtime context - * @return {object} filtered runtime context - * @private - */ - _getValidYCBContext: function(ctx) { - var validDims = this._validYCBDims, - validCtx = {}, - name, - value; - - for (name in ctx) { - if (ctx.hasOwnProperty(name)) { - value = ctx[name]; - if (validDims[name] && validDims[name].hasOwnProperty(value)) { - validCtx[name] = value; - } - } - } - return validCtx; - }, - - - /** - * reads a configuration file that is in YCB format - * - * @method _readConfigYCB - * @param ctx {object} runtime context - * @param fullpath {string} path to the YCB file - * @param isAppConfig {boolean} indicates whether the file being read is the application.json - * @return {object} the contextualized configuration - * @private - */ - _readConfigYCB: function(ctx, fullpath, isAppConfig) { - var cacheKey, - appConfigStatic, - jsonCache = this._jsonCache, - json, - ycbCache = this._ycbCache, - ycb; - - ctx = this._getValidYCBContext(ctx); - - //cache key only needs to account for dynamic context - cacheKey = JSON.stringify(ctx); - - //logger.log('_readConfigYCB('+fullpath+')', 'mojito', NAME); - - if (!fullpath) { - //logger.log('unknown fullpath', 'mojito', NAME); - return {}; - } - - if (!ycbCache[fullpath]) { - ycbCache[fullpath] = {}; - } - - ycb = ycbCache[fullpath][cacheKey]; - if (!ycb) { - json = jsonCache[fullpath]; - if (!json) { - json = this._readConfigJSON(fullpath); - jsonCache[fullpath] = json; - } - json = this._ycbDims.concat(json); - - ctx = this._mergeRecursive(this._cloneObj(this._defaultYCBContext), - ctx); - - // libycb.read() will distructively modify its first argument - ycb = libycb.read(this._cloneObj(json), ctx); - if (isAppConfig) { - appConfigStatic = this._cloneObj(this._appConfigStatic); - ycb = this._mergeRecursive(appConfigStatic, ycb); - } - ycbCache[fullpath][cacheKey] = ycb; - } - return ycb; - }, - - - /** - * Reads a configuration file for a mojit - * - * @method _readMojitConfigFile - * @param path {string} path to the file - * @param ycb {boolean} indicates whether the file should be read using the YCB library - * @return {object} the configuration - * @private - */ - _readMojitConfigFile: function(path, ycb) { - //logger.log('_readMojitConfigFile(' + path + ',' + ycb + ')'); - var json, - contents; - - if (!this._libs.path.existsSync(path)) { - return {}; - } - if (ycb) { - return this._readConfigYCB({}, path); - } - try { - contents = this._libs.fs.readFileSync(path, 'utf-8'); - json = JSON.parse(contents); - } catch (e) { - logger.log(this._reportJavaScriptSyntaxErrors(contents, path), - 'warn', NAME); - throw new Error('Error reading or parsing JSON file: ' + path); - } - return json; - }, - - - /** - * Registers the mojit's package.json as a static resource - * - * @method _mojitPackageAsAsset - * @param dir {string} directory of mojit - * @param mojitType {string} name of mojit - * @param packageJson {object} contents of mojit's package.json - * @return {nothing} - * @private - */ - _mojitPackageAsAsset: function(dir, mojitType, packageJson) { - var pkg, - fakeResource; - - // FUTURE: deprecate config.mojito in package.json - pkg = (packageJson.yahoo && packageJson.yahoo.mojito && - packageJson.yahoo.mojito['package']) || - (packageJson.config && packageJson.config.mojito && - packageJson.config.mojito['package']); - - if (pkg === 'public') { - // We have to check if the "package.json" files wants to do this - fakeResource = { - type: 'package', - fsPath: libpath.join(dir, 'package.json'), - shortPath: 'package.json' - }; - this._precalcStaticURL(fakeResource, mojitType); - if (fakeResource.staticHandlerURL) { - this._staticURLs[fakeResource.staticHandlerURL] = - fakeResource.staticHandlerFsPath; + // special case: view + if ('view' === type) { + res = { + source: source, + mojit: mojitType, + type: type, + subtype: subtype, + viewOutputFormat: fs.ext.substr(1), + viewEngine: baseParts.pop(), + affinity: 'common', + selector: '*' + }; + if (baseParts.length >= 2) { + res.selector = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid view filename. skipping ' + fs.fullPath, 'warn', NAME); + return; + } + res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + return res; } - } - }, - - - /** - * Checks to see if the version of Mojito specified in a mojit's - * package.json matches the current verison of Mojito. - * - * @method _mojitoVersionMatch - * @param pack {object} contents of the mojit's package.json file - * @param version {string} current version of mojito - * @return {boolean} returns true if the mojit can be used - * @private - */ - _mojitoVersionMatch: function(pack, version) { - var packageVersion; - - // There was no package.json file so assume version is OK - if (Object.keys(pack).length === 0) { - return true; - } - - // If we have a version make sure it is less than the given one - packageVersion = (pack.yahoo && pack.yahoo.mojito && - pack.yahoo.mojito.version) || - (pack.config && pack.config.mojito && pack.config.mojito.version); - if (packageVersion) { - if (packageVersion === '*') { - return true; + // just ignore unknown types + return; + }, + + + /** + * Called by the ResourceStore to register a resource version. + * You most often don't want to call this directly, but instead to hook + * into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.beforeHostMethod('parseResource', this._myParseResource, this); + * ``` + * + * @method addResourceVersion + * @param res {object} the resource version + * @return {nothing} + */ + addResourceVersion: function(res) { + res.affinity = new Affinity(res.affinity); + if (res.mojit) { + if (!this._mojitRVs[res.mojit]) { + this._mojitRVs[res.mojit] = []; + } + this._mojitRVs[res.mojit].push(res); + } else { + if (!this._appRVs[res.type]) { + this._appRVs[res.type] = []; + } + this._appRVs[res.type].push(res); } - // TODO: [Issue 95] Put real version checking code here. - return packageVersion <= version; - } - - // No version is set so return false as we don't know what this is - return false; - }, + }, - /** - * Takes the preloaded info and resolves ("cooks down") affinity, etc. - * - * This function is a doozy. This is where all the magic happens as far - * as which version of each resource is used. The results are stored in - * this._fwMeta, _appMeta, _mojitMeta, etc. The primary key of these is - * the environment ("client" or "server"). The secondary key is the - * context key -- a string representation of the partial context. - * - * We do that to have fast lookup during runtime. - * - * The algorithm chooses first from the most specific source: mojit- - * specific has higher precedence than shared. Within each of those, - * the environment-specific version ("client" or "server") has higher - * precedence than the "common" affinity. - * - * We do this for each context key (partial context). We resolve - * context inheritance (for example no-context versus device=iphone) - * at runtime (in getMojitTypeDetails()). - * - * (Half of the above algorithm is implemented here, and half in - * _cookdownMerge() which is a utility for this method.) - * - * @method _cookdown - * @return {nothing} - * @private - */ - _cookdown: function() { - //logger.log('_cookdown()'); - var env, - envs = ['client', 'server'], - type, // mojit type - i, - merged, // results of _cookdownMerge() - resid, - resids, // array (really object keys) of all resource IDs - ctxKey; - - // example of _preload.mojitMeta: - // [type][resid][affinity] = { - // "contexts": { "device=iphone": { "device":"iphone"} }, - // "device=iphone": { - // "type": ... - // "fsPath": ... - // } - // } - // resid was generated during _preloadFile*(), for example: - // controller is 'controller' - // model is 'model-foo' - // view is 'view-index' - // - // TODO 2011-06-16: [Issue 109] break down into smaller pieces - // (at least for more fine-grained unit testing). - for (type in this._preload.mojitMeta) { - if (this._preload.mojitMeta.hasOwnProperty(type)) { - // TODO: LINT [Issue 110] detect dupe resids. - resids = {}; - - // need resids from all sources - for (resid in this._preload.sharedMeta) { - if (this._preload.sharedMeta.hasOwnProperty(resid)) { - resids[resid] = true; - } - } - for (resid in this._preload.mojitMeta[type]) { - if (this._preload.mojitMeta[type].hasOwnProperty(resid)) { - resids[resid] = true; - } - } + /** + * For each possible runtime configuration (based on context), pre-calculates + * which versions of the resources will be used. + * The priority (highest to lowest): + * source, + * selector, + * affinity (env or "common"). + * + * @method resolveResourceVersions + * @return {nothing} + */ + resolveResourceVersions: function() { + var c, ctx, ctxs, + poslKey, posl, posls = {}, + e, env, envs = [ 'client', 'server' ], + affinities, selectors, sourceBase, + type, ress, + p; + + ctxs = this._listAllContexts(); + for (var c = 0; c < ctxs.length; c++) { + ctx = ctxs[c]; + posl = this.selector.getListFromContext(ctx); + posls[JSON.stringify(posl)] = posl; + } + + for (e = 0; e < envs.length; e += 1) { + env = envs[e]; + + affinities = {}; // affinity: priority modifier + affinities[env] = 1; + affinities.common = 0; + + for (poslKey in posls) { + if (posls.hasOwnProperty(poslKey)) { + posl = posls[poslKey]; + selectors = {}; // selector: priority modifier + for (p = 0; p < posl.length; p += 1) { + selectors[posl[p]] = (posl.length - p - 1) * 2; + } + sourceBase = posl.length * 2; + //console.log('-- source base ' + sourceBase); + //console.log(selectors); + //console.log(affinities); - for (resid in resids) { - if (resids.hasOwnProperty(resid)) { - for (i = 0; i < envs.length; i += 1) { - env = envs[i]; - merged = this._cookdownMerge(env, [ - // ORDER IS IMPORTANT - this._preload.sharedMeta[resid], - this._preload.mojitMeta[type][resid] - ]); - if (merged) { - if (!this._mojitMeta[env]) { - this._mojitMeta[env] = {}; - } - if (!this._mojitMeta[env][type]) { - this._mojitMeta[env][type] = {}; - } - if (!this._mojitMeta[env][type].contexts) { - this._mojitMeta[env][type].contexts = {}; - } - for (ctxKey in merged.contexts) { - if (merged.contexts. - hasOwnProperty(ctxKey)) { - this._mojitMeta[env][type - ].contexts[ctxKey] = - merged.contexts[ctxKey]; - if (!this._mojitMeta[env - ][type][ctxKey]) { - this._mojitMeta[env - ][type][ctxKey] = {}; - } - // TODO: give example of - // data structure. - this._mojitMeta[env - ][type][ctxKey][resid] = - merged[ctxKey]; - } - } - } // have merged - } // foreach env - } - } // foreach resid - } - } // foreach type - - for (resid in this._preload.appMeta) { - if (this._preload.appMeta.hasOwnProperty(resid)) { - for (i = 0; i < envs.length; i += 1) { - env = envs[i]; - merged = this._cookdownMerge(env, - [this._preload.appMeta[resid]]); - if (merged) { - if (!this._appMeta[env]) { - this._appMeta[env] = {}; + if (!this._appResources[env]) { + this._appResources[env] = {} } - if (!this._appMeta[env].contexts) { - this._appMeta[env].contexts = {}; + if (!this._appResources[env][poslKey]) { + this._appResources[env][poslKey] = {} } - for (ctxKey in merged.contexts) { - if (merged.contexts.hasOwnProperty(ctxKey)) { - this._appMeta[env].contexts[ctxKey] = - merged.contexts[ctxKey]; - if (!this._appMeta[env][ctxKey]) { - this._appMeta[env][ctxKey] = {}; - } - this._appMeta[env][ctxKey][resid] = - merged[ctxKey]; + for (type in this._appRVs) { + if (this._appRVs.hasOwnProperty(type)) { + this._appResources[env][poslKey][type] = + this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs[type] ]); } } - } - } - } - } - for (resid in this._preload.sharedMeta) { - if (this._preload.sharedMeta.hasOwnProperty(resid)) { - for (i = 0; i < envs.length; i += 1) { - env = envs[i]; - merged = this._cookdownMerge(env, - [this._preload.sharedMeta[resid]]); - if (merged) { - if (!this._sharedMeta[env]) { - this._sharedMeta[env] = {}; + if (!this._mojitResources[env]) { + this._mojitResources[env] = {} } - if (!this._sharedMeta[env].contexts) { - this._sharedMeta[env].contexts = {}; + if (!this._mojitResources[env][poslKey]) { + this._mojitResources[env][poslKey] = {} } - for (ctxKey in merged.contexts) { - if (merged.contexts.hasOwnProperty(ctxKey)) { - this._sharedMeta[env].contexts[ctxKey] = - merged.contexts[ctxKey]; - if (!this._sharedMeta[env][ctxKey]) { - this._sharedMeta[env][ctxKey] = {}; - } - this._sharedMeta[env][ctxKey][resid] = - merged[ctxKey]; + for (type in this._mojitRVs) { + if ('shared' === type) { + continue; + } + if (this._mojitRVs.hasOwnProperty(type)) { + this._mojitResources[env][poslKey][type] = + this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); + // TODO: fire event that mojit has been resolved } } } } } - } - - delete this._preload_routes; - delete this._preload; - }, - - - /** - * This is a utility for _cookdown(). See docs on that for details. - * - * The general idea is that we start with the lowest priority items - * and let higher priority items clobber. - * - * @method _cookdownMerge - * @param env {string} "client" or "server" - * @param srcs {array} ORDER MATTERS! list of resources to merge - * @return {TODO} TODO - * @private - */ - _cookdownMerge: function(env, srcs) { - var merged = { contexts: {} }, - affinities = ['common', env], // priority order - s, - src, - lastS = srcs.length - 1, - found = false, // TODO: when is found==false? - a, - affinity, - ctx, - res, - ctxKey; - - for (s = 0; s < srcs.length; s += 1) { - src = srcs[s]; - if (!src) { - continue; - } - for (a = 0; a < affinities.length; a += 1) { - affinity = affinities[a]; - if (!src[affinity]) { - continue; - } - for (ctxKey in src[affinity].contexts) { - if (src[affinity].contexts.hasOwnProperty(ctxKey)) { - merged.contexts[ctxKey] = - src[affinity].contexts[ctxKey]; - res = this._cloneObj(src[affinity][ctxKey]); - if (('config' === res.type) && (s !== lastS)) { - // only pull in configs from the last source - continue; - } - merged.type = res.type; - merged[ctxKey] = res; - found = true; - } - } - } - } - if (!found) { - return null; - } - return merged; - }, - - - /** - * Calculates, at server start time, the YUI module dependencies - * for mojit controllers and binders - * - * @method _precalcYuiDependencies - * @return {nothing} - * @private - */ - _precalcYuiDependencies: function() { - var e, - i, - env, - envs = ['client', 'server'], - mojitType, - ctxKey, - module, - parts, - required, - resid, - res, - sorted, - ctxs; - - for (e = 0; e < envs.length; e += 1) { - env = envs[e]; - - // mojit-specific - // -------------- - if (!this._mojitYuiSorted[env]) { - this._mojitYuiRequired[env] = {}; - this._mojitYuiSorted[env] = {}; - this._mojitYuiSortedPaths[env] = {}; - } - for (mojitType in this._mojitMeta[env]) { - if (this._mojitMeta[env].hasOwnProperty(mojitType)) { - - if (!this._mojitYuiSorted[env][mojitType]) { - this._mojitYuiRequired[env][mojitType] = {}; - this._mojitYuiSorted[env][mojitType] = {}; - this._mojitYuiSortedPaths[env][mojitType] = {}; - } - for (ctxKey in this._mojitMeta[env][mojitType].contexts) { - if (this._mojitMeta[env - ][mojitType].contexts.hasOwnProperty(ctxKey)) { - - // we handle non-context version below - if ('*' === ctxKey) { - continue; - } - if (!this._mojitYuiSorted[env - ][mojitType].contexts) { - this._mojitYuiRequired[env - ][mojitType].contexts = {}; - this._mojitYuiSorted[env - ][mojitType].contexts = {}; - this._mojitYuiSortedPaths[env - ][mojitType].contexts = {}; - } + }, - parts = {}; - this._precalcYuiDependencies_getDepParts(env, - this._mojitMeta[env][mojitType]['*'], parts); - this._precalcYuiDependencies_getDepParts(env, - this._mojitMeta[env][mojitType][ctxKey], parts); - if (parts.controller && - parts.modules['inlinecss/' + mojitType]) { - parts.modules[parts.controller.yuiModuleName]. - requires.push('inlinecss/' + mojitType); - } - this._mojitYuiSorted[env - ][mojitType].contexts[ctxKey] = - this._mojitMeta[env - ][mojitType].contexts[ctxKey]; - this._mojitYuiRequired[env - ][mojitType].contexts[ctxKey] = - this._mojitMeta[env - ][mojitType].contexts[ctxKey]; - if (parts.controller) { - parts.required[parts.controller.yuiModuleName] = - true; - // dependencies necessary to dispatch the mojit - parts.required['mojito-dispatcher'] = true; - sorted = this._sortYUIModules( - this._mojitMeta[env][mojitType - ].contexts[ctxKey], - env, - this._appConfigStatic.yui, - mojitType, - parts.modules, - parts.required - ); - this._mojitYuiRequired[env - ][mojitType][ctxKey] = - Object.keys(parts.required); - this._mojitYuiSorted[env - ][mojitType][ctxKey] = sorted.sorted; - this._mojitYuiSortedPaths[env - ][mojitType][ctxKey] = sorted.paths; - } - - // also calculate sortedPaths for each individual binder - if ('client' === env) { - for (resid in this._mojitMeta[env - ][mojitType][ctxKey]) { - if (this._mojitMeta[env - ][mojitType][ctxKey - ].hasOwnProperty(resid)) { - res = this._mojitMeta[env - ][mojitType][ctxKey][resid]; - if (res.type !== 'binder') { - continue; - } - required = {}; - required[res.yuiModuleName] = true; - // all binders have this dependency, - // even if not explicitly given - required['mojito-client'] = true; - // view engines are needed to support - // mojitProxy.render() - for (i in parts.viewEngines) { - if (parts.viewEngines. - hasOwnProperty(i)) { - required[parts.viewEngines[i]] = - true; - } - } - sorted = this._sortYUIModules( - this._mojitMeta[env][mojitType - ].contexts[ctxKey], - env, - this._appConfigStatic.yui, - mojitType, - parts.modules, - required - ); - res.yuiSortedPaths = sorted.paths; - } - } - } // env==client - } - } // foreach context (except '*') - - // here's where we handle the non-context version - if (this._mojitMeta[env][mojitType]['*']) { - if (!this._mojitYuiSorted[env][mojitType].contexts) { - this._mojitYuiRequired[env - ][mojitType].contexts = {}; - this._mojitYuiSorted[env][mojitType].contexts = {}; - this._mojitYuiSortedPaths[env - ][mojitType].contexts = {}; - } - parts = {}; - this._precalcYuiDependencies_getDepParts(env, - this._mojitMeta[env][mojitType]['*'], - parts); - if (parts.controller && parts.modules['inlinecss/' + - mojitType]) { - parts.modules[parts.controller.yuiModuleName]. - requires.push('inlinecss/' + mojitType); - } - this._mojitYuiSorted[env][mojitType].contexts['*'] = - this._mojitMeta[env][mojitType].contexts['*']; - this._mojitYuiRequired[env][mojitType].contexts['*'] = - this._mojitMeta[env][mojitType].contexts['*']; - if (parts.controller) { - parts.required[parts.controller.yuiModuleName] = - true; - // dependencies necessary to dispatch the mojit - parts.required['mojito-dispatcher'] = true; - sorted = this._sortYUIModules( - this._mojitMeta[env][mojitType].contexts['*'], - env, - this._appConfigStatic.yui, - mojitType, - parts.modules, - parts.required - ); - this._mojitYuiRequired[env][mojitType]['*'] = - Object.keys(parts.required); - this._mojitYuiSorted[env][mojitType]['*'] = - sorted.sorted; - this._mojitYuiSortedPaths[env][mojitType]['*'] = - sorted.paths; - } - // also calculate sortedPaths for each individual binder - if ('client' === env) { - for (resid in this._mojitMeta[env - ][mojitType]['*']) { - if (this._mojitMeta[env][mojitType - ]['*'].hasOwnProperty(resid)) { - res = this._mojitMeta[env][mojitType - ]['*'][resid]; - if (res.type !== 'binder') { - continue; - } - required = {}; - required[res.yuiModuleName] = true; - // all binders have this dependency, even if - // not explicitly given - required['mojito-client'] = true; - // view engines are needed to support - // mojitProxy.render() - for (i in parts.viewEngines) { - if (parts.viewEngines. - hasOwnProperty(i) - ) { - required[parts.viewEngines[i]] = - true; - } - } - sorted = this._sortYUIModules( - this._mojitMeta[env][mojitType - ].contexts['*'], - env, - this._appConfigStatic.yui, - mojitType, - parts.modules, - required - ); - res.yuiSortedPaths = sorted.paths; - } - } - } // env==client - } // context=='*' - } - } // foreach mojitType - } // foreach env - }, - - - // Fills dest with: - // .controller resource for the controller - // .modules hash yuiModuleName: { - // fullpath: where to load the module from - // requires: list of required modules - // } - // .required hash yuiModuleName:true of modules required by the - // source controller - // .viewEngines hash name:yuiModuleName of view engines - // - // @method _precalcYuiDependencies_getDepParts - // @param env {string} "client" or "server" - // @param source {object} list of resources - // @param dest {object} where to add results - // @return {nothing} results put in "dest" argument - // @private - _precalcYuiDependencies_getDepParts: function(env, source, dest) { - var resid, - res, - viewEngine; - - if (!source) { - return; - } - dest.required = dest.required || {}; - dest.viewEngines = dest.viewEngines || {}; - dest.modules = dest.modules || {}; - - // all mojits essentially have this dependency implicitly (even it not - // given explicitly) - dest.required.mojito = true; - - for (resid in source) { - if (source.hasOwnProperty(resid)) { - res = source[resid]; - if ('view' === res.type) { - viewEngine = source['addon-view-engines-' + res.viewEngine]; - if (viewEngine) { - dest.required[viewEngine.yuiModuleName] = true; - dest.viewEngines[res.viewEngine] = - viewEngine.yuiModuleName; - } - } - if (res.yuiModuleName) { - // The binder is part of the resources for the server, - // but shouldn't be added as a runtime dependency. - if ('server' === env && 'binder' === res.type) { + /** + * Resolves versions for a list of resources. + * The priority is based on passed-in configuration. See + * resolveResourceVersions() for details. + * + * @private + * @method _resolveVersions + * @param affinities {object} lookup hash for priority adjustment for each affinity + * @param selectors {object} lookup hash for priority adjustment for each selector + * @param sourceBase {int} multiplier for order in priority list + * @param srcs {array of arrays} resource versions to resolve + * @return {array} list of resolved resources + */ + _resolveVersions: function(affinities, selectors, sourceBase, srcs) { + var s, src, + r, res, + priority, + versions = {}, // id: priority: resource + out = [], + resid, + highest; + + for (s = 0; s < srcs.length; s += 1) { + src = srcs[s]; + for (r = 0; r < src.length; r += 1) { + res = src[r]; + if (!selectors.hasOwnProperty(res.selector)) { continue; } - if (!res.sharedMojit) { - dest.required[res.yuiModuleName] = true; + if (!affinities.hasOwnProperty(res.affinity)) { + continue; } - dest.modules[res.yuiModuleName] = { - requires: res.yuiModuleMeta.requires, - fullpath: (('client' === env) ? - res.staticHandlerURL : - res.fsPath) - }; - if (('controller' === res.type)) { - dest.controller = res; + // TODO: conditionally skip optional affinities + priority = (s * sourceBase) + + selectors[res.selector] + affinities[res.affinity]; + //console.log('--DEBUG-- pri=' + priority + ' --' + // + ' src' + s + '=' + (s * sourceBase) + // + ' ' + res.selector + '=' + selectors[res.selector] + // + ' ' + res.affinity + '=' + affinities[res.affinity] + // + ' -- ' + res.id); + if (!versions[res.id]) { + versions[res.id] = {}; } + versions[res.id][priority] = res; } } - } - // TODO: move other dynamic dependency adjustments - // (compiled views inlinecss) here. - }, - - - /** - * Uses YUI Loader to sort a list of YUI modules. - * - * @method _sortYUIModules - * @param ctx {object} runtime context - * @param env {string} runtime environment ("client" or "server") - * @param yuiConfig {object} configuration for YUI - * @param mojitType {string} name of mojit - * @param modules {object} YUI configuration for all modules - * @param required {object} lookup hash of modules that are required - * @return {object} list of load-order sorted module names, and object - * listing paths used to load those modules - * @private - */ - _sortYUIModules: function(ctx, env, yuiConfig, mojitType, modules, - required) { - var YUI = serverYUI, - Y, - loader, - sortedPaths = {}, - usePrecomputed = -1 !== this._appConfigStatic.yui. - dependencyCalculations.indexOf('precomputed'), - useOnDemand = -1 !== this._appConfigStatic.yui. - dependencyCalculations.indexOf('ondemand'), - j, - module, - info; - - if (!usePrecomputed) { - useOnDemand = true; - } - - if ('client' === env) { - // Use clientYUI to avoid cross-contamination with serverYUI - YUI = clientYUI; - } - - // We don't actually need the full list, just the base required modules. - // YUI.Loader() will do the rest at runtime. - if (useOnDemand) { - for (module in required) { - if (required.hasOwnProperty(module)) { - sortedPaths[module] = modules[module].fullpath; + for (resid in versions) { + if (versions.hasOwnProperty(resid)) { + highest = Math.max.apply(Math, Object.keys(versions[resid])) + //console.log('--DEBUG-- highest=' + highest + ' -- ' + resid); + out.push(versions[resid][highest]); } } - return { sorted: Object.keys(required), paths: sortedPaths }; - } - - Y = YUI({ useSync: true }).use('loader-base'); - Y.applyConfig({ useSync: false }); - - // We need to clear YUI's cached dependencies, since there's no - // guarantee that the previously calculated dependencies have been done - // using the same context as this calculation. - delete YUI.Env._renderedMods; + return out; + }, - //Use ignoreRegistered here instead of the old `delete YUI.Env._renderedMods` hack - loader = new Y.Loader({ lang: ctx.lang, ignoreRegistered: true }); - //Only override the default if it's required - if (yuiConfig && yuiConfig.base) { - loader.base = yuiConfig.base; - } - loader.addGroup({modules: modules}, mojitType); - loader.calculate({required: required}); - for (j = 0; j < loader.sorted.length; j += 1) { - module = loader.sorted[j]; - info = loader.moduleInfo[module]; - if (info) { - // modules with "nodejs" in their name are tweaks on other modules - if ('client' === env && module.indexOf('nodejs') !== -1) { + /** + * Generates a list of all possible context (which is a lot!). + * @private + * @method _listAllContext + * @return {array of objects} all possible contexts + */ + _listAllContexts: function() { + var dims = this.config.getDimensions(), + nctxs, c, ctxs = [], + dn, dname, dnames, + dv, dval, dvals, + e, each, mod, + // only because we might want to change it at some point + // (not including it helps reduce the number of contexts) + SKIP_RUNTIME = true; + + dims = dims[0].dimensions; + dims = this._flattenDims(dims); + dnames = Object.keys(dims); + + nctxs = 1; + for (dn = 0; dn < dnames.length; dn++) { + dname = dnames[dn]; + if (SKIP_RUNTIME && dname === 'runtime') { continue; } - sortedPaths[module] = info.fullpath || loader._url(info.path); + dvals = dims[dname]; + if (dname !== 'runtime') { + // we never have indeterminant runtime + dvals.push('*'); + } + nctxs *= dvals.length; } - } - return { sorted: loader.sorted, paths: sortedPaths }; - }, - - - /** - * Calculates the static handling URL for a resource. - * - * @method _precalcStaticURL - * @param res {object} metadata about the resource - * @param mojitType {string} mojit type, can be undefined for non-mojit-specific resources - * @return {nothing} new metadata added to the "res" argument - * @private - */ - _precalcStaticURL: function(res, mojitType) { - /* alternate approach which shows power of precalculating the URL and - * then passing it around everywhere - res.staticHandlerURL = '/static/' + Math.floor(Math.random() * - 1000000000); - return; - */ - var url, - parts = [], - path, - i, - config = this._appConfigStatic.staticHandling || {}, - prefix = config.prefix, - appName = config.appName || this._shortRoot, - frameworkName = config.frameworkName || 'mojito', - rollupParts = [], - rollupFsPath; - - // TODO: [Issue 111] magic constants should should come from fw.json. - - /* - Server only framework mojits like DaliProxy and HTMLFrameMojit should - never have static URLs associated with them, so we skip them. This never - used to be an issue until we added the "assumeRollups" functionality to - preload JSON specs for specified mojits during the compile step (mojito - compile json) for Livestand. I think we need to reevaluate this entire - process so we don't have such a fragile condition below. - */ - // TODO: reevaluate this entire process so we don't have such a fragile - // condition below. - if (mojitType === 'DaliProxy' || mojitType === 'HTMLFrameMojit') { - return; - } - switch (res.type) { - case 'action': - path = libpath.join('actions', res.shortPath); - break; - case 'addon': - path = libpath.join('addons', res.addonType, res.shortPath); - break; - case 'asset': - path = libpath.join('assets', res.shortPath); - break; - case 'binder': - path = libpath.join('binders', res.shortPath); - break; - case 'controller': - path = res.shortPath; - break; - case 'model': - path = libpath.join('models', res.shortPath); - break; - case 'view': - path = libpath.join('views', res.shortPath); - break; - case 'yui-lang': - path = libpath.join('lang', res.shortPath); - break; - case 'yui-module': - // FUTURE: change this to 'yui_modules' - path = libpath.join('autoload', res.shortPath); - break; - case 'package': - path = res.shortPath; - break; - default: - return; - } - - if (!config.hasOwnProperty('prefix')) { - prefix = 'static'; - } - if (prefix) { - parts.push(prefix); - rollupParts.push(prefix); - } - - if ('shared' === mojitType) { - if (res.pkg && 'mojito' === res.pkg.name) { - parts.push(frameworkName); - if (config.useRollups && res.yuiModuleName) { - // fw resources are put into app-level rollup - rollupParts.push(appName); - rollupFsPath = libpath.join(this._root, 'rollup.client.js'); + for (c = 0; c < nctxs; c++) { + ctxs[c] = {}; + } + mod = 1; + for (dn = 0; dn < dnames.length; dn++) { + dname = dnames[dn]; + if (SKIP_RUNTIME && dname === 'runtime') { + continue; } - } else { - parts.push(appName); - if (config.useRollups && res.yuiModuleName) { - rollupParts.push(appName); - rollupFsPath = libpath.join(this._root, 'rollup.client.js'); + dvals = dims[dname]; + mod *= dvals.length; + each = nctxs / mod; + + e = each; + dv = 0; + for (c = 0; c < nctxs; --e, c++) { + if (0 === e) { + e = each; + dv++; + dv = dv % dvals.length; + } + dval = dvals[dv]; + if ('*' !== dval) { + ctxs[c][dname] = dval; + } } } - } else { - parts.push(mojitType); - if (config.useRollups && res.yuiModuleName) { - rollupParts.push(mojitType); - rollupFsPath = libpath.join(this._mojitPaths[mojitType], - 'rollup.client.js'); - } - } - if (mojitType) { - if (!this._mojitAssetRoots[mojitType]) { - this._mojitAssetRoots[mojitType] = '/' + - libpath.join(parts.join('/'), 'assets'); - } - } + return ctxs; + }, - // only use rollup URL if rollup file exists or we are assuming rollups - if ((rollupFsPath && this._libs.path.existsSync(rollupFsPath) && - config.useRollups) || this._appConfigStatic.assumeRollups) { - // useful for debugging: path += '?orig=' + path; - res.rollupURL = '/' + libpath.join(rollupParts.join('/'), - 'rollup.client.js'); - url = res.rollupURL; - res.staticHandlerFsPath = rollupFsPath; - } - if (!url) { - url = '/' + libpath.join(parts.join('/'), path); - res.staticHandlerFsPath = res.fsPath; - } - res.staticHandlerURL = url; - }, - - /** - * Attempt to gather YUI-module details. - * - * @method _precalcYuiModule - * @param res {object} metadata about the resource - * @return {nothing} new metadata added to the "res" argument - * @private - */ - _precalcYuiModule: function(res) { - var file = this._libs.fs.readFileSync(res.fsPath, 'utf8'), - ctx = { - console: { - log: function() {} - }, - window: {}, - document: {}, - YUI: { - add: function(name, fn, version, meta) { - res.yuiModuleName = name; - res.yuiModuleVersion = version; - res.yuiModuleMeta = meta || {}; + /** + * Flattens dimensions so that the structure of the dimension values doesn't matter. + * @private + * @method _flattenDims + * @param dims {object} dimensions structure + * @return {object} + */ + _flattenDims: function(dims) { + var d, dim, + name, out = {}; + for (d = 0; d < dims.length; d++) { + dim = dims[d]; + name = Object.keys(dim)[0]; + out[name] = this._listKeys(dim[name]); + } + return out; + }, + + + /** + * Recursively finds all keys for the object (plus child objects). + * @private + * @method _listKeys + * @param obj {object} + * @return {array} list of all keys in the object (no matter how deep) + */ + _listKeys: function(obj) { + var k, keys = []; + for (k in obj) { + if (obj.hasOwnProperty(k)) { + keys.push(k); + if ('object' === typeof obj[k]) { + keys = keys.concat(this._listKeys(obj[k])); } } - }; - - try { - libvm.runInNewContext(file, ctx, res.fsPath); - } catch (e) { - if (e.stack.indexOf('SyntaxError:') === 0) { - // the stack of a SyntaxError thrown by runInNewContext() lacks - // filename, line and column numbers for the error. Also, it - // does not write to stderr as the docs claim. - - // see "Lack of error message in vm.* stuff" from 2011-04-29 at - // http://groups.google.com/group/nodejs/browse_thread/ - // thread/2075b964a3f7dd79/bd0df1ae36829813 - - logger.log(this._reportJavaScriptSyntaxErrors(file)); - logger.log(e.message + ' in file: ' + res.fsPath, 'error', - NAME); - - } else { - logger.log(e.message + '\n' + e.stack, 'error', NAME); } - process.exit(-1); - } - }, - + return keys; + }, - /** - * Reads one the configuration files for a mojit - * - * @method _getMojitConfig - * @param env {string} "client" or "server" - * @param ctx {object} runtime context - * @param mojitType {string} name of mojit - * @param name {string} config resource name, either "definition" or "defaults" - * @private - */ - _getMojitConfig: function(env, ctx, mojitType, name) { - //logger.log('_getMojitConfig('+env+','+mojitType+','+name+')'); - var resid, - res; - - if (!this._mojitMeta[env][mojitType]) { - throw new Error('Cannot find meta data for mojit type \"' + mojitType + - '\"'); - } - resid = 'config-' + name; - res = this._getContextualizedResource(this._mojitMeta[env][mojitType], ctx, - resid); - if (!res) { - return {}; - } - return this._readConfigYCB(ctx, res.fsPath); - }, + /** + * Finds resources based on our conventions + * -doesn't- load mojits or their contents. That's done elsewhere. + * + * @private + * @method _findResourcesByConvention + * @param dir {string} directory from which to find resources + * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" + * @param pkg {object} metadata (name and version) about the package + * @param mojitType {string|null} name of mojit to which the resource belongs + * @return {array} list of resources + */ + _findResourcesByConvention: function(dir, dirType, pkg, mojitType) { + var me = this, + ress = []; + //console.log('-- FIND RESOURCES BY CONVENTION -- ' + pkg.name + '@' + pkg.version + ' -- ' + mojitType); + this._walkDirRecursive(dir, function(error, subdir, file, isFile) { + var source, ret, res; - /** - * Returns whether a runtime context matches a partial context - * - * @method _matchContext - * @param ctx {object} runtime context - * @param ctxParts {object} partial context - * @private - */ - _matchContext: function(ctx, ctxParts) { - var k; - - for (k in ctxParts) { - if (ctxParts.hasOwnProperty(k)) { - // FUTURE -- handle "lang" slightly specially ("en" should match - // "en-US") - // For now we will skip the "lang" check as it could change on - // the fly in the client and we need all the "lang" files - // available in our YUI instance. - if (k !== 'lang' && ctx[k] !== ctxParts[k]) { + if ('node_modules' === file) { return false; } - } - } - return true; - }, - - - /** - * Returns a list of resource metadata that match the context - * - * @method _getResourceListForContext - * @param src {object} list of contextualized resources, key is contextKey - * @param ctx {object} context to match - * @return {object} list of resources, key is resource ID - * @private - */ - _getResourceListForContext: function(src, ctx) { - var list = {}, // resid: resource - resid, - ctxKey; - - if (src['*']) { - for (resid in src['*']) { - if (src['*'].hasOwnProperty(resid)) { - list[resid] = src['*'][resid]; + if ('libs' === file) { + return false; } - } - } - for (ctxKey in src.contexts) { - if (src.contexts.hasOwnProperty(ctxKey)) { - if ('*' === ctxKey) { - continue; + if ('tests' === file && 'test' !== me._appConfigStatic.env) { + return false; } - if (!this._matchContext(ctx, src.contexts[ctxKey])) { - continue; + + source = { + fs: { + fullPath: libpath.join(dir, subdir, file), + rootDir: dir, + rootType: dirType, + subDir: subdir, + subDirArray: subdir.split('/'), + isFile: isFile, + ext: libpath.extname(file) + }, + pkg: pkg + }; + source.fs.basename = libpath.basename(file, source.fs.ext); + + if (me._skipBadPath(source.fs)) { + return false; } - for (resid in src[ctxKey]) { - if (src[ctxKey].hasOwnProperty(resid)) { - list[resid] = src[ctxKey][resid]; + + ret = me.findResourceByConvention(source, mojitType); + if ('object' === typeof ret) { + if (ret.skipSubdirParts) { + source.fs.subDirArray = source.fs.subDirArray.slice(ret.skipSubdirParts); + source.fs.subDir = source.fs.subDirArray.join('/') || '.'; } + res = me.parseResource(source, ret.type, ret.subtype, mojitType); + if ('object' === typeof res) { + ress.push(res); + } + // don't recurse into resources that are directories + return false; } - } - } - return list; - }, + return ret; + }); + return ress; + }, - /** - * Returns a list of the language resources - * doesn't discriminate based on context: returns all langs for all - * contexts. - * - * @method _getLangList - * @param src {object} list of contextualized resources, key is contextKey - * @return {object} list of language resources, key is resource ID - * @private - */ - _getLangList: function(src) { - var list = {}, // resid: res - ctxKey, - resid, - res; - - for (ctxKey in src.contexts) { - if (src.contexts.hasOwnProperty(ctxKey)) { - for (resid in src[ctxKey]) { - if (src[ctxKey].hasOwnProperty(resid)) { - res = src[ctxKey][resid]; - if ('yui-lang' === res.type) { - list[resid] = res; - } - } - } - } - } - return list; - }, + /** + * Indicates whether file should be skipped based on its path + * + * @private + * @method _skipBadPath + * @param pathParts {object} the "source.fs" part of the resource + * @return {boolean} true indicates that the file should be skipped + */ + _skipBadPath: function(fs) { + if (fs.isFile && fs.ext.substr(1).match(isNotAlphaNum)) { + return true; + } + return false; + }, - /** - * Returns the metadata for a resource specific for a particular runtime context - * - * @method _getContextualizedResource - * @param src {object} list of contextualized resources, key is contextKey - * @param ctx {object} context to match - * @param resid {string} ID of resource to find - * @return {object} resource metadata - * @private - */ - _getContextualizedResource: function(src, ctx, resid) { - var ctxKey, - res; - // TODO: [Issue 100] Review, for when there is no app.json file. - if (!src || !src.contexts) { - return {}; - } + /** + * A wrapper for fs.readdirSync() that guarantees ordering. The order in + * which the file system is walked is significant within the resource + * store, e.g., when looking up a matching context. + * + * @private + * @method _sortedReaddirSync + * @param path {string} directory to read + * @return {array} files in the directory + */ + _sortedReaddirSync: function(path) { + var out = libfs.readdirSync(path); + return out.sort(); + }, + + + /** + * Recursively walks a directory + * + * @private + * @method _walkDirRecursive + * @param dir {string} directory to start at + * @param cb {function(error, subdir, name, isFile)} callback called for each file + * @param _subdir {string} INTERNAL argument, please ignore + * @return {nothing} value returned via callback + */ + _walkDirRecursive: function(dir, cb, _subdir) { + var subdir, + fulldir, + children, + i, + childName, + childPath, + childFullPath, + childStat; + + subdir = _subdir || '.'; + fulldir = libpath.join(dir, subdir); + if (!libpath.existsSync(fulldir)) { + return; + } - for (ctxKey in src.contexts) { - if (src.contexts.hasOwnProperty(ctxKey)) { - // look for specific first - if ('*' === ctxKey) { + children = this._sortedReaddirSync(fulldir); + for (i = 0; i < children.length; i += 1) { + childName = children[i]; + if ('.' === childName.substring(0, 1)) { continue; } - if (!this._matchContext(ctx, src.contexts[ctxKey])) { + if ('node_modules' === childName) { continue; } - res = src[ctxKey][resid]; - if (res) { - return res; - } - } - } - // fallback - return src['*'][resid]; - }, - - - /** - * Indicates whether file should be skipped based on its path - * - * @method _skipBadPath - * @param pathParts {object} return value of _parsePath() (or the equivalent) - * @return {boolean} true indicates that the file should be skipped - * @private - */ - _skipBadPath: function(pathParts) { - var ext = pathParts.ext.substring(1); - if (ext.match(isNotAlphaNum)) { - return true; - } - return false; - }, - - - /** - * Generate a report of syntax errors for JavaScript code. This is also - * very useful to find syntax errors in JSON documents. - * - * @method _reportJavaScriptSyntaxErrors - * @param {string} js the JavaScript - * @param {string} filename OPTIONAL. the name of the file containing the - * JavaScript - * @return {string} if errors were found, a multi-line error report - * @private - */ - _reportJavaScriptSyntaxErrors: function(js, filename) { - - // use a really lenient JSLINT to find syntax errors - - var jslint = require('./management/fulljslint').jslint, - opts = { - // turn off all the usual checks - devel: true, - browser: true, - node: true, - rhino: true, - widget: true, - windows: true, - bitwise: true, - regexp: true, - confusion: true, - undef: true, - 'continue': true, - unparam: true, - debug: true, - sloppy: true, - eqeq: true, - sub: true, - es5: true, - vars: true, - evil: true, - white: true, - forin: true, - css: true, - newcap: true, - cap: true, - nomen: true, - on: true, - plusplus: true, - fragment: true, - - // prevent well-known globals from showing up as errors - predef: [ - // CommonJS - 'exports', - // YUI - 'YUI', 'YUI_config', 'YAHOO', 'YAHOO_config', 'Y', - // Node - 'global', 'process', 'require', '__filename', 'module', - // Browser - 'document', 'navigator', 'console', 'self', 'window' - ] - }, - // identify errors about undefined globals - nameIsNotDefined = / is not defined\.$/, - success, - report = [], - len, - e, - i; - - success = jslint(js, opts); - if (!success) { - len = jslint.errors.length; - for (i = 0; i < len; i += 1) { - e = jslint.errors[i]; - if (e && e.reason && !nameIsNotDefined.test(e.reason)) { - report.push(e.line + ',' + e.character + ': ' + e.reason); - report.push(' ' + - (e.evidence || '').replace(/^\s+|\s+$/, '')); + childPath = libpath.join(subdir, childName); + childFullPath = libpath.join(dir, childPath); + childStat = libfs.statSync(childFullPath); + if (childStat.isFile()) { + cb(null, subdir, childName, true); + } else if (childStat.isDirectory()) { + if (cb(null, subdir, childName, false)) { + this._walkDirRecursive(dir, cb, childPath); + } } } - } - - if (filename && report.length) { - report.unshift('Syntax errors detected in ' + filename); - } + }, - return report.join('\n'); - }, - - /** - * Returns the selector for the runtime context - * - * @method _selectorFromContext - * @param ctx {object} runtime context - * @return {string|null} selector for context - * @private - */ - _selectorFromContext: function(ctx) { - if (ctx.device) { - return ctx.device; - } - return null; - }, - - - /** - * @method _objectIsEmpty - * @param o {object} - * @return {boolean} true if the object is empty - * @private - */ - _objectIsEmpty: function(o) { - if (!o) { - return true; - } - return (0 === Object.keys(o).length); - }, - - - // from http://stackoverflow.com/questions/171251/ - // how-can-i-merge-properties-of-two-javascript-objects-dynamically/ - // 383245#383245 - /** - * Recursively merge one object onto another - * - * @method _mergeRecursive - * @param dest {object} object to merge into - * @param src {object} object to merge onto "dest" - * @param matchType {boolean} controls whether a non-object in the src is - * allowed to clobber a non-object in the dest (if a different type) - * @return {object} the modified "dest" object is also returned directly - * @private - */ - _mergeRecursive: function(dest, src, typeMatch) { - var p; - - for (p in src) { - if (src.hasOwnProperty(p)) { - // Property in destination object set; update its value. - if (src[p] && src[p].constructor === Object) { - if (!dest[p]) { - dest[p] = {}; - } - dest[p] = this._mergeRecursive(dest[p], src[p]); - } else { - if (dest[p] && typeMatch) { - if (typeof dest[p] === typeof src[p]) { - dest[p] = src[p]; + /** + * takes a list of globs and turns it into a list of matching paths + * @private + * @param prefix {string} prefix for every path in the list + * @param list {array} list of globs + * @return {array} list of paths matching the globs + */ + _globList: function(prefix, list) { + var found = [], + i, + glob; + for (i = 0; i < list.length; i += 1) { + glob = list[i]; + if ('/' !== glob.charAt(0)) { + glob = libpath.join(prefix, glob); + } + libglob.globSync(glob, {}, found); + } + return found; + }, + + + /** + * Augments this resource store's Y object with the specified YUI modules. + * @private + * @method _yuiUseSync + * @param modules {object} YUI module configuration information + * @return {nothing} + */ + _yuiUseSync: function(modules) { + Y.applyConfig({ + useSync: true, + modules: modules + }); + Y.use.apply(Y, Object.keys(modules)); + Y.applyConfig({ useSync: false }); + }, + + + /** + * Recursively merge one object onto another. + * From http://stackoverflow.com/questions/171251/ + * how-can-i-merge-properties-of-two-javascript-objects-dynamically/ + * 383245#383245. + * + * @method mergeRecursive + * @param dest {object} object to merge into + * @param src {object} object to merge onto "dest" + * @param matchType {boolean} controls whether a non-object in the src is + * allowed to clobber a non-object in the dest (if a different type) + * @return {object} the modified "dest" object is also returned directly + */ + mergeRecursive: function(dest, src, typeMatch) { + var p; + for (p in src) { + if (src.hasOwnProperty(p)) { + // Property in destination object set; update its value. + if (src[p] && src[p].constructor === Object) { + if (!dest[p]) { + dest[p] = {}; } + dest[p] = this.mergeRecursive(dest[p], src[p]); } else { - dest[p] = src[p]; + if (dest[p] && typeMatch) { + if (typeof dest[p] === typeof src[p]) { + dest[p] = src[p]; + } + } else { + dest[p] = src[p]; + } } } } - } - return dest; - }, - + return dest; + }, - /** - * @method _cloneObj - * @param o {mixed} - * @return {mixed} deep copy of argument - * @private - */ - _cloneObj: function(o) { - var newO, - i; - if (typeof o !== 'object') { - return o; - } - if (!o) { - return o; - } + /** + * @method cloneObj + * @param o {mixed} + * @return {mixed} deep copy of argument + */ + cloneObj: function(o) { + var newO, + i; - if ('[object Array]' === Object.prototype.toString.apply(o)) { - newO = []; - for (i = 0; i < o.length; i += 1) { - newO[i] = this._cloneObj(o[i]); + if (typeof o !== 'object') { + return o; } - return newO; - } - - newO = {}; - for (i in o) { - if (o.hasOwnProperty(i)) { - newO[i] = this._cloneObj(o[i]); + if (!o) { + return o; } - } - return newO; - }, - - - /** - * A wrapper for fs.readdirSync() that guarantees ordering. The order in - * which the file system is walked is significant within the resource - * store, e.g., when looking up a matching context. - * - * @method _sortedReaddirSync - * @param path {string} directory to read - * @return {array} files in the directory - * @private - */ - _sortedReaddirSync: function(path) { - var out = this._libs.fs.readdirSync(path); - return out.sort(); - }, - - - /** - * Recursively walks a directory - * - * @method _walkDirRecursive - * @param dir {string} directory to start at - * @param cb {function(error, subdir, name, isFile)} callback called for each file - * @param _subdir {string} INTERNAL argument, please ignore - * @return {nothing} value returned via callback - * @private - */ - _walkDirRecursive: function(dir, cb, _subdir) { - var subdir, - fulldir, - children, - i, - childName, - childPath, - childFullPath, - childStat; - - subdir = _subdir || '.'; - fulldir = libpath.join(dir, subdir); - if (!this._libs.path.existsSync(fulldir)) { - return; - } - children = this._sortedReaddirSync(fulldir); - for (i = 0; i < children.length; i += 1) { - childName = children[i]; - if ('.' === childName.substring(0, 1)) { - continue; - } - if ('node_modules' === childName) { - continue; + if ('[object Array]' === Object.prototype.toString.apply(o)) { + newO = []; + for (i = 0; i < o.length; i += 1) { + newO[i] = this.cloneObj(o[i]); + } + return newO; } - childPath = libpath.join(subdir, childName); - childFullPath = libpath.join(dir, childPath); - childStat = this._libs.fs.statSync(childFullPath); - if (childStat.isFile()) { - cb(null, subdir, childName, true); - } else if (childStat.isDirectory()) { - if (cb(null, subdir, childName, false)) { - this._walkDirRecursive(dir, cb, childPath); + + newO = {}; + for (i in o) { + if (o.hasOwnProperty(i)) { + newO[i] = this.cloneObj(o[i]); } } + return newO; } - } -}; + }); + + Y.namespace('mojito'); + Y.mojito.ResourceStore = ResourceStore; -module.exports = ServerStore; +}, '0.0.1', { requires: [ + 'base', + 'oop' +]}); From d6765bc6e9ffd02c095ab49b74edb12a74a3139b Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 29 May 2012 15:10:17 -0700 Subject: [PATCH 011/119] resource and resource version getters --- source/lib/store.server.js | 102 ++++++++++++++++++++++++++++++------- 1 file changed, 83 insertions(+), 19 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 542efe920..1f7675a69 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -60,7 +60,7 @@ * @module ResourceStore * @main */ -YUI.add('resource-store', function(Y, NAME) { +YUI.add('mojito-resource-store', function(Y, NAME) { var libfs = require('fs'), libglob = require('./glob'), @@ -135,9 +135,9 @@ YUI.add('resource-store', function(Y, NAME) { this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: context: YCB config object - this._appRVs = {}; // res.type: array of resource versions + this._appRVs = []; // array of resource versions this._mojitRVs = {}; // mojitType: array of resource versions - this._appResources = {}; // env: posl: res.type: array of resources + this._appResources = {}; // env: posl: array of resources this._mojitResources = {}; // env: posl: mojitType: array of resources // We'll start with just our "config" addon. @@ -228,6 +228,7 @@ YUI.add('resource-store', function(Y, NAME) { */ loadAddons: function() { var modules = {}, + ress, r, res; @@ -235,8 +236,9 @@ YUI.add('resource-store', function(Y, NAME) { this.unplug(name); }, this); - for (r = 0; r < this._appRVs.addon.length; r += 1) { - res = this._appRVs.addon[r]; + ress = this.getResourceVersions({type:'addon', subtype:'rs'}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; if ('rs' === res.subtype) { // FUTURE: ideally we shouldn't proscribe the YUI module name of RS addons // (We can/should introspect the file for the YUI module name.) @@ -266,7 +268,7 @@ YUI.add('resource-store', function(Y, NAME) { dir, info; - this._appRVs = {}; + this._appRVs = []; this._mojitRVs = {}; walker = new libwalker.BreadthFirst(this._config.root); @@ -312,6 +314,78 @@ YUI.add('resource-store', function(Y, NAME) { }, + /** + * Returns a list of resource versions that match the filter. + * @param filter {object} limit returned resource versions to only those whose keys/values match the filter + * @return {array of objects} list of matching resource versions + */ + getResourceVersions: function(filter) { + var source, + out = [], + r, + res, + k, + use; + + source = filter.mojit ? this._mojitRVs[filter.mojit] : this._appRVs; + for (r = 0; r < source.length; r += 1) { + res = source[r]; + use = true; + for (k in filter) { + if (filter.hasOwnProperty(k)) { + if (res[k] !== filter[k]) { + use = false; + break; + } + } + } + if (use) { + out.push(res); + } + } + return out; + }, + + + /** + * Returns a list of resources that match the filter. + * @param env {string} the runtime environment + * @param ctx {object} the context + * @param filter {object} limit returned resources to only those whose keys/values match the filter + * @return {array of objects} list of matching resources + */ + getResources: function(env, ctx, filter) { + var posl, + source, + out = [], + r, + res, + k, + use; + + posl = JSON.stringify(this.selector.getListFromContext(ctx)); + source = filter.mojit ? + this._mojitResources[env][posl][filter.mojit] : + this._appResources[env][posl]; + for (r = 0; r < source.length; r += 1) { + res = source[r]; + use = true; + for (k in filter) { + if (filter.hasOwnProperty(k)) { + if (res[k] !== filter[k]) { + use = false; + break; + } + } + } + if (use) { + out.push(res); + } + } + return out; + }, + + /** * preloads metadata about resources in a package * (but not subpackages in its node_modules/) @@ -802,10 +876,7 @@ YUI.add('resource-store', function(Y, NAME) { } this._mojitRVs[res.mojit].push(res); } else { - if (!this._appRVs[res.type]) { - this._appRVs[res.type] = []; - } - this._appRVs[res.type].push(res); + this._appRVs.push(res); } }, @@ -858,15 +929,8 @@ YUI.add('resource-store', function(Y, NAME) { if (!this._appResources[env]) { this._appResources[env] = {} } - if (!this._appResources[env][poslKey]) { - this._appResources[env][poslKey] = {} - } - for (type in this._appRVs) { - if (this._appRVs.hasOwnProperty(type)) { - this._appResources[env][poslKey][type] = - this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs[type] ]); - } - } + this._appResources[env][poslKey] = + this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs ]); if (!this._mojitResources[env]) { this._mojitResources[env] = {} From 1f4b70b7c657a248aa2204d27a24959528ded8bb Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 29 May 2012 15:10:35 -0700 Subject: [PATCH 012/119] routes addon --- source/lib/app/addons/rs/routes.server.js | 73 +++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 source/lib/app/addons/rs/routes.server.js diff --git a/source/lib/app/addons/rs/routes.server.js b/source/lib/app/addons/rs/routes.server.js new file mode 100644 index 000000000..f0948bc87 --- /dev/null +++ b/source/lib/app/addons/rs/routes.server.js @@ -0,0 +1,73 @@ +/* + * Copyright (c) 2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ + +YUI.add('addon-rs-routes', function(Y, NAME) { + + var libpath = require('path'), + libycb = require(libpath.join(__dirname, '../../../libs/ycb')); + + function RSAddonRoutes() { + RSAddonRoutes.superclass.constructor.apply(this, arguments); + } + RSAddonRoutes.NS = 'routes'; + RSAddonRoutes.DEPS = ['config']; + RSAddonRoutes.ATTRS = {}; + + Y.extend(RSAddonRoutes, Y.Plugin.Base, { + + initializer: function(config) { + this.rs = config.host; + this.appRoot = config.appRoot; + }, + + + destructor: function() { + // TODO: needed to break cycle so we don't leak memory? + this.rs = null; + }, + + + read: function(env, ctx, cb) { + ctx.runtime = env; + var appConfig = this.rs.getAppConfig(ctx), + routesFiles = appConfig.routesFiles, + p, + path, + fixedPaths = {}, + out = {}, + ress, + r, + res, + path, + routes; + + for (p = 0; p < routesFiles.length; p += 1) { + path = routesFiles[p]; + // relative paths are relative to the application + if ('/' !== path.charAt(1)) { + path = libpath.join(this.appRoot, path); + } + fixedPaths[path] = true; + } + + ress = this.rs.getResources(env, ctx, {type:'config'}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (fixedPaths[res.source.fs.fullPath]) { + routes = this.rs.config.readConfigYCB(res.source.fs.fullPath, ctx); + out = Y.merge(out, routes); + } + } + + cb(null, out); + } + + + }); + Y.namespace('mojito.addons.rs'); + Y.mojito.addons.rs.routes = RSAddonRoutes; + +}, '0.0.1', { requires: ['plugin', 'oop']}); From f563a9dcf4b602553fd4fea87c4553f0fdd81e86 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 29 May 2012 17:29:04 -0700 Subject: [PATCH 013/119] first phase of yui RS addon --- source/lib/app/addons/rs/yui.server.js | 168 +++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 source/lib/app/addons/rs/yui.server.js diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js new file mode 100644 index 000000000..3dbb98448 --- /dev/null +++ b/source/lib/app/addons/rs/yui.server.js @@ -0,0 +1,168 @@ +/* + * Copyright (c) 2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ + +YUI.add('addon-rs-yui', function(Y, NAME) { + + var libfs = require('fs'), + libpath = require('path'), + libvm = require('vm'); + + function RSAddonYUI() { + RSAddonYUI.superclass.constructor.apply(this, arguments); + } + RSAddonYUI.NS = 'yui'; + RSAddonYUI.ATTRS = {}; + + Y.extend(RSAddonYUI, Y.Plugin.Base, { + + initializer: function(config) { + this.rs = config.host; + this.appRoot = config.appRoot; + this.mojitoRoot = config.mojitoRoot; + this.afterHostMethod('findResourceByConvention', this.findResourceByConvention, this); + this.beforeHostMethod('parseResource', this.parseResource, this); + this.beforeHostMethod('addResourceVersion', this.addResourceVersion, this); + }, + + + destructor: function() { + // TODO: needed to break cycle so we don't leak memory? + this.rs = null; + }, + + + findResourceByConvention: function(source, mojitType) { + var fs = source.fs; + + if (!fs.isFile) { + return; + } + if ('.js' !== fs.ext) { + return; + } + + if (fs.subDirArray.length >= 1 && ('autoload' === fs.subDirArray[0] || 'yui_modules' === fs.subDirArray[0])) { + return new Y.Do.AlterReturn(null, { + type: 'yui-module' + }); + } + + if (fs.subDirArray.length >= 1 && 'lang' === fs.subDirArray[0]) { + return new Y.Do.AlterReturn(null, { + type: 'yui-lang', + skipSubdirParts: 1 + }); + } + }, + + + parseResource: function(source, type, subtype, mojitType) { + var fs = source.fs, + baseParts, + res; + + if ('yui-lang' === type) { + res = { + source: source, + mojit: mojitType, + type: 'yui-lang', + affinity: 'common', + selector: '*' + }; + if (!res.yui) { + res.yui = {}; + } + if (fs.basename === mojitType) { + res.yui.lang = ''; + } else if (mojitType === fs.basename.substr(0, mojitType.length)) { + res.yui.lang = fs.basename.substr(mojitType.length + 1); + } else { + logger.log('invalid YUI lang file format. skipping ' + fs.fullPath, 'error', NAME); + } + res.name = res.yui.lang; + res.id = [res.type, res.subtype, res.name].join('-'); + return new Y.Do.Halt(null, res); + } + + if ('yui-module' === type) { + baseParts = fs.basename.split('.'), + res = { + source: source, + mojit: mojitType, + type: 'yui-module', + affinity: 'server', + selector: '*' + }; + if (baseParts.length >= 3) { + res.selector = baseParts.pop(); + } + if (baseParts.length >= 2) { + res.affinity = baseParts.pop(); + } + if (baseParts.length !== 1) { + Y.log('invalid yui-module filename. skipping ' + fs.fullPath, 'warn', NAME); + return; + } + this._parseYUIModule(res); + res.name = res.yui.name; + res.id = [res.type, res.subtype, res.name].join('-'); + return new Y.Do.Halt(null, res); + } + }, + + + addResourceVersion: function(res) { + if ('.js' !== res.source.fs.ext) { + return; + } + if (res.yui && res.yui.name) { + // work done already + return; + } + // ASSUMPTION: no app-level resources are YUI modules + if (!res.mojit) { + return; + } + this._parseYUIModule(res); + }, + + + _parseYUIModule: function(res) { + var file, + ctx, + yui = {}; + file = libfs.readFileSync(res.source.fs.fullPath, 'utf8'); + ctx = { + console: { + log: function() {} + }, + window: {}, + document: {}, + YUI: { + add: function(name, fn, version, meta) { + yui.name = name; + yui.version = version; + yui.meta = meta || {}; + } + } + }; + try { + libvm.runInNewContext(file, ctx, res.source.fs.fullPath); + } catch (e) { + yui = null; + Y.log(e.message + '\n' + e.stack, 'error', NAME); + } + if (yui) { + res.yui = Y.merge(res.yui || {}, yui); + } + } + + + }); + Y.namespace('mojito.addons.rs'); + Y.mojito.addons.rs.yui = RSAddonYUI; + +}, '0.0.1', { requires: ['plugin', 'oop']}); From 55b7c4da7e4b2ff634482fba36437e09bf4697c9 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 30 May 2012 10:21:08 -0700 Subject: [PATCH 014/119] query for mojit resources also includes shared resources --- source/lib/store.server.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 1f7675a69..af721bb31 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -367,6 +367,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { source = filter.mojit ? this._mojitResources[env][posl][filter.mojit] : this._appResources[env][posl]; + // this is taken care of already, and will trip up mojit-level + // resources that are actually shared + delete filter.mojit; for (r = 0; r < source.length; r += 1) { res = source[r]; use = true; From 4fde110636c0440b2f03c7fbc7eb75882d5c1e22 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 5 Jun 2012 21:07:50 -0700 Subject: [PATCH 015/119] new store wired into index.js --- source/lib/index.js | 84 ++++++++++++++++++--------------- source/lib/store.server.js | 97 +++++++++++++++++++++++++++++++++++--- 2 files changed, 138 insertions(+), 43 deletions(-) diff --git a/source/lib/index.js b/source/lib/index.js index 105b08d17..7a906efde 100644 --- a/source/lib/index.js +++ b/source/lib/index.js @@ -34,7 +34,6 @@ var MOJITO_MIDDLEWARE = [ // TODO: [Issue 80] go back to connect? var express = require('express'), - ResourceStore = require('./store.server'), OutputHandler = require('./output-handler.server'), libpath = require('path'); @@ -47,18 +46,14 @@ global._mojito = {}; // This configures YUI with both the Mojito framework and all the // YUI modules in the application. -function configureYUI(YUI, store, load) { +function configureYUI(Y, store, load) { var fw, app, module; fw = store.getYuiConfigFw('server', {}); + Y.applyConfig(fw); app = store.getYuiConfigApp('server', {}); - YUI.applyConfig({ - groups: { - 'mojito-fw': fw, - 'mojito-app': app - } - }); + Y.applyConfig(app); // also pre-load fw and app modules for (module in fw.modules) { if (fw.modules.hasOwnProperty(module)) { @@ -127,14 +122,52 @@ MojitoServer.prototype = { options.context = {}; } - store = new ResourceStore(options.dir); + // all logging that comes from YUI comes from here + // We need to do this early, since creating a Y instance appears to copy + // the function. + YUI.applyConfig({ logFn: function(msg, lvl, src) { + // translating YUI logs so they are categorized outside the rest + // of Mojito's log levels + var args = Array.prototype.slice.call(arguments); + if (!this.mojito || src === 'yui' || src === 'loader' || + src === 'get') { + if ((!logger) && (!logConfig.yui)) { + return; + } + args[1] = 'YUI-' + lvl.toUpperCase(); + } + if (logger) { + logger.log.apply(logger, args); + } else { + console.log(serverLog.options.formatter(msg, lvl, src, + new Date().getTime(), serverLog.options)); + } + } + }); + + Y = YUI({ core: CORE_YUI_MODULES, useSync: true }); + + Y.applyConfig({ + modules: { + 'mojito-resource-store': { + fullpath: libpath.join(__dirname, 'store.server.js') + } + } + }); + Y.applyConfig({ useSync: true }); + Y.use('mojito-resource-store'); + store = new Y.mojito.ResourceStore({ + root: options.dir, + context: options.context, + appConfig: options.appConfig + }); // share the resource store as a property of the application instance // (useful for the Mojito CLI) app.store = store; - store.preload(options.context, options.appConfig); - appConfig = store.getAppConfig(null, 'application'); + store.preload(); + appConfig = store.getAppConfig(null); // TODO: extract function if (appConfig.log && appConfig.log.server) { @@ -151,30 +184,6 @@ MojitoServer.prototype = { } } - configureYUI(YUI, store, CORE_MOJITO_MODULES); - - // all logging that comes from YUI comes from here - // We need to do this early, since creating a Y instance appears to copy - // the function. - YUI.GlobalConfig.logFn = function(msg, lvl, src) { - // translating YUI logs so they are categorized outside the rest - // of Mojito's log levels - var args = Array.prototype.slice.call(arguments); - if (!this.mojito || src === 'yui' || src === 'loader' || - src === 'get') { - if ((!logger) && (!logConfig.yui)) { - return; - } - args[1] = 'YUI-' + lvl.toUpperCase(); - } - if (logger) { - logger.log.apply(logger, args); - } else { - console.log(serverLog.options.formatter(msg, lvl, src, - new Date().getTime(), serverLog.options)); - } - }; - // merge application log options over top defaults Object.keys(logConfig).forEach(function(k) { if (logConfig[k] !== undefined) { @@ -182,15 +191,16 @@ MojitoServer.prototype = { } }); - Y = YUI({ core: CORE_YUI_MODULES, useSync: true }); + configureYUI(Y, store, CORE_MOJITO_MODULES); // Load logger early so that we can plug it in before the other loading // happens. + Y.applyConfig({ useSync: true }); Y.use('mojito-logger'); // TODO: extract function logger = new Y.mojito.Logger(serverLog.options); - store.setLogger(logger); + Y.applyConfig({ useSync: true }); Y.use.apply(Y, CORE_MOJITO_MODULES); Y.applyConfig({ useSync: false }); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index af721bb31..c81bd002b 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -174,6 +174,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + // TODO DOCS + getFrameworkConfig: function() { + return this.cloneObj(this._fwConfig); + }, + + /** * Returns a contextualized application configuration. * @method getAppConfig @@ -364,9 +370,20 @@ YUI.add('mojito-resource-store', function(Y, NAME) { use; posl = JSON.stringify(this.selector.getListFromContext(ctx)); - source = filter.mojit ? - this._mojitResources[env][posl][filter.mojit] : - this._appResources[env][posl]; + if (filter.mojit) { + if (!this._mojitResources[env] || + !this._mojitResources[env][posl] || + !this._mojitResources[env][posl][filter.mojit]) { + return []; + } + source = this._mojitResources[env][posl][filter.mojit]; + } else { + if (!this._appResources[env] || + !this._appResources[env][posl]) { + return []; + } + source = this._appResources[env][posl]; + } // this is taken care of already, and will trip up mojit-level // resources that are actually shared delete filter.mojit; @@ -389,6 +406,77 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + // TODO DOCS + listAllMojits: function() { + var mojitType, + list = []; + for (mojitType in this._mojitRVs) { + if (this._mojitRVs.hasOwnProperty(mojitType)) { + if ('shared' !== mojitType) { + list.push(mojitType); + } + } + } + return list; + }, + + + // TODO DOCS + // TODO -- move to yui addon + getYuiConfigFw: function(env, ctx) { + var r, + res, + ress, + modules = {}; + ress = this.getResources(env, ctx, { mojit:'shared' }); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (!res.yui || !res.yui.name) { + continue; + } + if ('mojito' !== res.source.pkg.name) { + continue; + } + modules[res.yui.name] = { + fullpath: ('client' === env) ? + res.staticHandlerURL : + res.source.fs.fullPath, + requires: + (res.yui.meta && res.yui.meta.requires) || [] + }; + } + return { modules: modules }; + }, + + + // TODO DOCS + // TODO -- move to yui addon + getYuiConfigApp: function(env, ctx) { + var r, + res, + ress, + modules = {}; + ress = this.getResources(env, ctx, { mojit:'shared' }); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (!res.yui || !res.yui.name) { + continue; + } + if ('mojito' === res.source.pkg.name) { + continue; + } + modules[res.yui.name] = { + fullpath: ('client' === env) ? + res.staticHandlerURL : + res.source.fs.fullPath, + requires: + (res.yui.meta && res.yui.meta.requires) || [] + }; + } + return { modules: modules }; + }, + + /** * preloads metadata about resources in a package * (but not subpackages in its node_modules/) @@ -942,9 +1030,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._mojitResources[env][poslKey] = {} } for (type in this._mojitRVs) { - if ('shared' === type) { - continue; - } if (this._mojitRVs.hasOwnProperty(type)) { this._mojitResources[env][poslKey][type] = this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); From 65315baf84f8405479c89a14e3aaecfd79c9309c Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 5 Jun 2012 21:11:06 -0700 Subject: [PATCH 016/119] updated for new store.getAppConfig() API --- source/lib/app/addons/ac/deploy.server.js | 5 ++--- source/lib/app/autoload/action-context.common.js | 2 +- source/lib/app/autoload/controller-context.common.js | 2 +- source/lib/app/autoload/dispatch.common.js | 2 +- source/lib/app/autoload/resource-store-adapter.common.js | 4 ++-- source/lib/app/autoload/store.client.js | 2 +- source/lib/app/commands/build.js | 3 +-- source/lib/app/middleware/mojito-handler-static.js | 2 +- source/lib/app/middleware/mojito-handler-tunnel.js | 2 +- source/lib/app/middleware/mojito-router.js | 2 +- 10 files changed, 12 insertions(+), 14 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index 9fa21f6d0..31cf114b4 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -81,8 +81,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { var store = this.rs, contextServer = this.ac.context, - appConfigServer = store.getAppConfig(contextServer, - 'application'), + appConfigServer = store.getAppConfig(contextServer), contextClient, appConfigClient, yuiConfig = {}, @@ -110,7 +109,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { contextClient = Y.mojito.util.copy(contextServer); contextClient.runtime = 'client'; - appConfigClient = store.getAppConfig(contextClient, 'application'); + appConfigClient = store.getAppConfig(contextClient); clientConfig.context = contextClient; if (appConfigClient.yui && appConfigClient.yui.config) { diff --git a/source/lib/app/autoload/action-context.common.js b/source/lib/app/autoload/action-context.common.js index 24f746cbb..ecfc86672 100644 --- a/source/lib/app/autoload/action-context.common.js +++ b/source/lib/app/autoload/action-context.common.js @@ -277,7 +277,7 @@ YUI.add('mojito-action-context', function(Y, NAME) { // TODO: should rework to be 'getAppConfig()' and 'getAppRoutes()' and // not property access through a hash. this.app = { - config: store.getAppConfig(this.context, 'application'), + config: store.getAppConfig(this.context), routes: store.getRoutes(this.context) }; diff --git a/source/lib/app/autoload/controller-context.common.js b/source/lib/app/autoload/controller-context.common.js index d77db76e7..ba63eb9c0 100644 --- a/source/lib/app/autoload/controller-context.common.js +++ b/source/lib/app/autoload/controller-context.common.js @@ -34,7 +34,7 @@ YUI.add('mojito-controller-context', function(Y, NAME) { // do a shallow merge of app-level and mojit-level configs // mojit config properties take precedence - configApp = this.store.getAppConfig({}, 'application').config, + configApp = this.store.getAppConfig({}).config, configCombo = Y.merge(configApp, instance.config), // Y.mojito.controller for legacy, multi-instance. diff --git a/source/lib/app/autoload/dispatch.common.js b/source/lib/app/autoload/dispatch.common.js index e2f6357e9..655b32ed1 100644 --- a/source/lib/app/autoload/dispatch.common.js +++ b/source/lib/app/autoload/dispatch.common.js @@ -395,7 +395,7 @@ YUI.add('mojito-dispatcher', function(Y, NAME) { logger.log('Dispatcher created', 'debug', NAME); - appConfigStatic = store.getAppConfig({}, 'application'); + appConfigStatic = store.getAppConfig({}); appShareYUIInstance = (false !== appConfigStatic.shareYUIInstance); usePrecomputed = appConfigStatic.yui && (-1 !== diff --git a/source/lib/app/autoload/resource-store-adapter.common.js b/source/lib/app/autoload/resource-store-adapter.common.js index efbacee35..d3437fc75 100644 --- a/source/lib/app/autoload/resource-store-adapter.common.js +++ b/source/lib/app/autoload/resource-store-adapter.common.js @@ -130,8 +130,8 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { }, - getAppConfig: function(context, name) { - return this.store.getAppConfig(context, name); + getAppConfig: function(context) { + return this.store.getAppConfig(context); }, diff --git a/source/lib/app/autoload/store.client.js b/source/lib/app/autoload/store.client.js index ae6031bc4..6667c6cff 100644 --- a/source/lib/app/autoload/store.client.js +++ b/source/lib/app/autoload/store.client.js @@ -191,7 +191,7 @@ YUI.add('mojito-client-store', function(Y, NAME) { /* * TODO: REVIEW RE [Issue 78] */ - getAppConfig: function(context, name) { + getAppConfig: function(context) { return this.appConfig; }, diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 6fd63bbb1..60cd0964c 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -166,8 +166,7 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, store.preload(); - appConfig = store.getAppConfig(libqs.parse(cmdOptions.context), - 'application'); + appConfig = store.getAppConfig(libqs.parse(cmdOptions.context)); tunnelPrefix = appConfig.tunnelPrefix || '/tunnel'; console.log('Building a "' + type + '" of the Mojito application at "' + diff --git a/source/lib/app/middleware/mojito-handler-static.js b/source/lib/app/middleware/mojito-handler-static.js index c64157a8e..78a9225b2 100644 --- a/source/lib/app/middleware/mojito-handler-static.js +++ b/source/lib/app/middleware/mojito-handler-static.js @@ -164,7 +164,7 @@ function clearCache(key) { */ function staticProvider(store, globalLogger) { logger = globalLogger; - var appConfig = store.getAppConfig(null, 'application'), + var appConfig = store.getAppConfig(null), options = appConfig.staticHandling || {}, cache = options.cache, maxAge = options.maxAge; diff --git a/source/lib/app/middleware/mojito-handler-tunnel.js b/source/lib/app/middleware/mojito-handler-tunnel.js index 67f874b21..f21d0d59f 100644 --- a/source/lib/app/middleware/mojito-handler-tunnel.js +++ b/source/lib/app/middleware/mojito-handler-tunnel.js @@ -38,7 +38,7 @@ TunnelServer.prototype = { logger = globalLogger; //console.log('creating handle'); this._store = store; - config = store.getAppConfig({}, 'application'); + config = store.getAppConfig({}); this.tunnelPrefix = (config && config.tunnelPrefix) ? config.tunnelPrefix : '/tunnel'; diff --git a/source/lib/app/middleware/mojito-router.js b/source/lib/app/middleware/mojito-router.js index efbf0bb66..6e62d0b95 100644 --- a/source/lib/app/middleware/mojito-router.js +++ b/source/lib/app/middleware/mojito-router.js @@ -52,7 +52,7 @@ Router.prototype = { routes = store.getRoutes(context), routeMaker = new RouteMakerClass(routes), query = liburl.parse(req.url, true).query, - appConfig = store.getAppConfig(context, 'application'), + appConfig = store.getAppConfig(context), url, routeMatch; From ba5017068be12f6f0c63b8296ae156cf8a36dfeb Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 5 Jun 2012 21:36:31 -0700 Subject: [PATCH 017/119] uses the public store API to get the framework config --- source/lib/app/addons/ac/deploy.server.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index 31cf114b4..dbb025f2a 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -85,6 +85,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { contextClient, appConfigClient, yuiConfig = {}, + fwConfig, yuiModules, loader, yuiCombo, @@ -172,6 +173,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { yuiModules = ['yui']; yuiJsUrlContains.yui = true; if (useOnDemand) { + fwConfig = store.store.getFrameworkConfig(); yuiModules.push('get'); yuiJsUrlContains.get = true; yuiModules.push('loader-base'); @@ -180,10 +182,8 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { yuiJsUrlContains['loader-rollup'] = true; yuiModules.push('loader-yui3'); yuiJsUrlContains['loader-yui3'] = true; - for (i = 0; i < store.store._fwConfig. - ondemandBaseYuiModules.length; i += 1) { - module = - store.store._fwConfig.ondemandBaseYuiModules[i]; + for (i = 0; i < fwConfig.ondemandBaseYuiModules.length; i += 1) { + module = fwConfig.ondemandBaseYuiModules[i]; yuiModules.push(module); yuiJsUrlContains[module] = true; } From 325bb3fca5c4d6512a8476bad04aae243df6ade6 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 6 Jun 2012 12:58:22 -0700 Subject: [PATCH 018/119] `mojito test` runs (tests don't pass yet) --- source/lib/app/addons/rs/yui.server.js | 3 +- .../autoload/resource-store-adapter.common.js | 11 +- source/lib/app/commands/test.js | 46 ++--- source/lib/store.server.js | 34 ++++ .../app/addons/ac/partial-tests.common.js | 11 -- .../resource-store-adapter-tests.common.js | 138 ++++++-------- .../lib/tests/autoload/store.server-tests.js | 168 ++++++++---------- 7 files changed, 190 insertions(+), 221 deletions(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index 3dbb98448..cf7f2787d 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -46,7 +46,8 @@ YUI.add('addon-rs-yui', function(Y, NAME) { if (fs.subDirArray.length >= 1 && ('autoload' === fs.subDirArray[0] || 'yui_modules' === fs.subDirArray[0])) { return new Y.Do.AlterReturn(null, { - type: 'yui-module' + type: 'yui-module', + skipSubdirParts: 1 }); } diff --git a/source/lib/app/autoload/resource-store-adapter.common.js b/source/lib/app/autoload/resource-store-adapter.common.js index d3437fc75..d35b63a6f 100644 --- a/source/lib/app/autoload/resource-store-adapter.common.js +++ b/source/lib/app/autoload/resource-store-adapter.common.js @@ -18,8 +18,7 @@ */ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { - var APP_ROOT_PATH = '', - logger; + var logger; Y.mojito.ResourceStoreAdapter = { @@ -36,13 +35,11 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { logger.log('resource store adapter init', 'mojito', NAME); - APP_ROOT_PATH = resourceStore._root; - this.ENV = env; this.store = resourceStore; - this._root = resourceStore._root; - this._staticURLs = resourceStore._staticURLs; + this._root = this.store._config.root; + this._staticURLs = this.store._staticURLs; return this; }, @@ -126,7 +123,7 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { getAppPath: function() { - return APP_ROOT_PATH; + return this._root; }, diff --git a/source/lib/app/commands/test.js b/source/lib/app/commands/test.js index d9dc6a7bc..d01bba41b 100644 --- a/source/lib/app/commands/test.js +++ b/source/lib/app/commands/test.js @@ -25,8 +25,6 @@ var pathlib = require('path'), fwTestsRoot = pathlib.join(targetMojitoPath, 'lib/tests'), - ResourceStore, - resultsDir = 'artifacts/test', resultsFile = pathlib.join(resultsDir, 'result.xml'), coverageDir = pathlib.join(resultsDir, 'coverage'), @@ -116,15 +114,10 @@ function collectRunResults(results) { } } -function configureYUI(YUI, store, load) { - YUI.applyConfig({ - useSync: true, - groups: { - 'mojito-fw': store.getYuiConfigFw('server', {}), - 'mojito-app': store.getYuiConfigApp('server', {}), - 'mojito-mojits': store.getYuiConfigAllMojits('server,', {}) - } - }); +function configureYUI(Y, store) { + Y.applyConfig(store.getYuiConfigFw('server', {})); + Y.applyConfig(store.getYuiConfigApp('server', {})); + Y.applyConfig(store.getYuiConfigAllMojits('server', {})); } @@ -421,7 +414,7 @@ function processResults() { function executeTestsWithinY(tests, cb) { - var YUIInst, + var Y, suiteName = ''; function handleEvent(event) { @@ -471,14 +464,14 @@ function executeTestsWithinY(tests, cb) { TestRunner.clear(); // create new YUI instance using tests and mojito - YUIInst = YUI({core: [ + Y = YUI({core: [ 'get', 'features', 'intl-base', 'mojito' ]}); - YUIInst.use.apply(YUIInst, tests); + Y.use.apply(Y, tests); } @@ -665,7 +658,8 @@ runTests = function(opts) { testModuleNames = ['mojito', 'mojito-test']; testRunner = function(testPath) { - var testConfigs, + var Y, + testConfigs, sourceConfigs; if (testType === 'mojit') { @@ -688,13 +682,25 @@ runTests = function(opts) { modules: testConfigs }); } else { - ResourceStore = require(pathlib.join(targetMojitoPath, - 'lib/store.server.js')); - store = new ResourceStore(testPath); + Y = YUI(); + Y.applyConfig({ + useSync: true, + modules: { + 'mojito-resource-store': { + fullpath: pathlib.join(__dirname, '../../store.server.js') + } + } + }); + Y.use('mojito-resource-store'); + store = new Y.mojito.ResourceStore({ + root: testPath, + context: {}, + appConfig: { env: 'test' } + }); - store.preload({}, { env: 'test' }); + store.preload(); - configureYUI(YUI, store, testModuleNames); + configureYUI(YUI, store); if (testType === 'fw') { testConfigs = store.getYuiConfigApp('server', {}).modules; diff --git a/source/lib/store.server.js b/source/lib/store.server.js index c81bd002b..fdf9bc4a9 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -132,6 +132,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { initializer: function(cfg) { this._config = cfg; + this._config.context = this._config.context || {}; + this._config.appConfig = this._config.appConfig || {}; this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: context: YCB config object @@ -476,6 +478,38 @@ YUI.add('mojito-resource-store', function(Y, NAME) { return { modules: modules }; }, + + // TODO DOCS + // TODO -- move to yui addon + getYuiConfigAllMojits: function(env, ctx) { + var m, mojit, mojits; + var r, res, ress; + var modules = {}; + mojits = this.listAllMojits(); + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; + ress = this.getResources(env, ctx, { mojit: mojit }); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (!res.yui || !res.yui.name) { + continue; + } + if (res.mojit !== mojit) { + // generally only happens if res.mojit is 'shared' + continue; + } + modules[res.yui.name] = { + fullpath: ('client' === env) ? + res.staticHandlerURL : + res.source.fs.fullPath, + requires: + (res.yui.meta && res.yui.meta.requires) || [] + }; + } + } + return { modules: modules }; + }, + /** * preloads metadata about resources in a package diff --git a/source/lib/tests/autoload/app/addons/ac/partial-tests.common.js b/source/lib/tests/autoload/app/addons/ac/partial-tests.common.js index e491da874..fc84a8c9c 100644 --- a/source/lib/tests/autoload/app/addons/ac/partial-tests.common.js +++ b/source/lib/tests/autoload/app/addons/ac/partial-tests.common.js @@ -8,8 +8,6 @@ YUI.add('mojito-partial-addon-tests', function(Y, NAME) { var suite = new YUITest.TestSuite(NAME), path = require('path'), fixtures = path.join(__dirname, '../../../../fixtures/store'), - ResourceStore = require(path.join(__dirname, - '../../../../../store.server')), Assert = YUITest.Assert, ObjectAssert = YUITest.ObjectAssert, Mock = YUITest.Mock, @@ -29,9 +27,6 @@ YUI.add('mojito-partial-addon-tests', function(Y, NAME) { } }; - var store = new ResourceStore(fixtures); - store.setLogger(logger); - var mockCallback = Mock(); Mock.expect(mockCallback, { method: 'callback', @@ -59,9 +54,6 @@ YUI.add('mojito-partial-addon-tests', function(Y, NAME) { } }; - var store = new ResourceStore(fixtures); - store.setLogger(logger); - var addon = new Y.mojito.addons.ac.partial(command, null, ac); var mockRenderer = Mock(); @@ -98,9 +90,6 @@ YUI.add('mojito-partial-addon-tests', function(Y, NAME) { } }; - var store = new ResourceStore(fixtures); - store.setLogger(logger); - var mockCallback = Mock(); Mock.expect(mockCallback, { method: 'callback', diff --git a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js index 99178b842..070328421 100644 --- a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js +++ b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js @@ -8,7 +8,6 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { var suite = new YUITest.TestSuite(NAME), path = require('path'), fixtures = path.join(__dirname, '../../../fixtures/store'), - ResourceStore = require(path.join(__dirname, '../../../../store.server')), dummyLog = {log: function() {}}, A = YUITest.Assert; @@ -17,46 +16,38 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { name: 'Resource Store Adapter Tests', 'pre load': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - - //Y.log(JSON.stringify(store,null,4)); - A.isTrue(store.getAppPath() === fixtures); }, 'server app config value': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var config = store.getAppConfig(null, 'application'); - A.isTrue(config.testKey1 === 'testVal1'); }, 'server mojit config value': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {base:'test1'}; - store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.id === 'test1'); A.isTrue(instance.type === 'test_mojit_1'); A.isTrue(instance.config.testKey4 === 'testVal4'); @@ -64,18 +55,15 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config value via type': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'test_mojit_1'}; - store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.type === 'test_mojit_1'); A.isTrue(instance.config.testKey4 === 'testVal4'); A.isTrue(instance.config.testKey6.testKey7 === 'testVal7'); @@ -83,21 +71,18 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config value via type & overrride': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = { type:'test_mojit_1', config:{testKey4: 'other'} }; - store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.type === 'test_mojit_1'); A.isTrue(instance.config.testKey4 === 'other'); A.isTrue(instance.config.testKey5 === 'testVal5'); @@ -105,145 +90,123 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config assets': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'test_mojit_1'}; - store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.assets['css/main.css'] !== undefined); A.isTrue(instance.assets['js/main.js'] !== undefined); }); }, 'server mojit config views': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'test_mojit_1'}; - store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.views['test_1'] !== undefined); A.isTrue(instance.views['test_2'] !== undefined); }); }, 'server mojit config models': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'test_mojit_1'}; - store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.models['test_1'] !== undefined); A.isTrue(instance.models['test_2'] !== undefined); }); }, 'server mojit config actions': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'test_mojit_1'}; - store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.actions.length === 2); }); }, 'server mojit config appConfig': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var command = { type:'test_mojit_1', appConfig:{ testKey3: 'other' } }; - store.expandInstance(command, {}, function(err, instance){ - A.isTrue(instance.appConfig.testKey2 === 'testVal2'); A.isTrue(instance.appConfig.testKey3 === 'other'); }); }, 'TODO: server mojit config definition override': function() { - A.skip(); return; - var store = new ResourceStore(fixtures); - + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var command = {type:'test_mojit_1'}; - store.expandInstance(command, {}, function(err, instance){ - A.isTrue(instance.models['other_1'] === '/path/to/other_1'); }); }, 'server mojit instance definition override': function() { - - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var command = { type:'test_mojit_1', models: { 'other_2': '/path/to/other_2' } }; - store.expandInstance(command, {}, function(err, instance){ - A.isTrue(instance.models['other_2'] === '/path/to/other_2'); }); }, 'server mojit type name can come from package.json': function() { - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit2'}; store.expandInstance(instance, {}, function(err, instance){ A.isNotUndefined(instance.controller); @@ -253,25 +216,25 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit is NOT loaded becuase of pacakge mojito version miss-match': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - A.isTrue(typeof store._staticURLs['/static/test_mojit_4/package.json'] === 'undefined'); A.isTrue(typeof store._staticURLs['/static/TestMojit4/package.json'] === 'undefined'); }, 'server mojit is loaded becuase of pacakge mojito version match': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit2'}; store.expandInstance(instance, {}, function(err, instance){ A.areSame('/static/TestMojit2/assets', instance.assetsRoot); @@ -279,46 +242,46 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server a mojits package.json file is NOT publicly accessible': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - A.isTrue(typeof store._staticURLs['/static/TestMojit2/package.json'] === 'undefined'); }, 'server a mojits package.json file is publicly accessible': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - A.isTrue(typeof store._staticURLs['/static/TestMojit3/package.json'] === 'string'); }, 'server a mojit is NOT loaded because it has a package.json file with no mojito config': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - A.isTrue(typeof store._staticURLs['/static/TestMojit5/package.json'] === 'undefined'); }, 'server mojit view index.mu.html is loaded correctly': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit3'}; store.expandInstance(instance, {}, function(err, instance){ A.areSame('index.mu.html', instance.views.index['content-path'].split('/').pop()); @@ -326,13 +289,13 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit view index.iphone.mu.html is loaded correctly': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit3'}; store.expandInstance(instance, {device:'iphone'}, function(err, instance){ A.areSame('index.iphone.mu.html', instance.views.index['content-path'].split('/').pop()); @@ -340,13 +303,13 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit view index1.mu.html is loaded correctly': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit3', action: 'index1'}; store.expandInstance(instance, {device:'forotheriphone'}, function(err, instance){ A.areSame('index1.forotheriphone.mu.html', instance.views.index1['content-path'].split('/').pop()); @@ -354,13 +317,13 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit view index1.iphone.mu.html is loaded correctly': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit3', action: 'index1'}; store.expandInstance(instance, {device:'otheriphone'}, function(err, instance){ A.areSame('index1.otheriphone.mu.html', instance.views.index1['content-path'].split('/').pop()); @@ -368,30 +331,28 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'test getSpec() from specs dir': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - store.getSpec('server', 'TestMojit2', {}, function(err, instance){ - A.isTrue(instance.type === 'TestMojit2'); A.isTrue(instance.config.testKey1 === 'testVal1'); }); }, 'test getType()': function(){ - var resourceStore = new ResourceStore(fixtures), + var resourceStore, store; + resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - store.getType('server', 'test_mojit_1', {}, function(err, instance){ - A.isTrue(instance.type === 'test_mojit_1'); A.isTrue(instance.config.testKey4 === 'testVal4'); A.isTrue(instance.config.testKey6.testKey7 === 'testVal7'); @@ -402,4 +363,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { YUITest.TestRunner.add(suite); -}, '0.0.1', {requires: ['mojito-resource-store-adapter']}); +}, '0.0.1', {requires: [ + 'mojito-resource-store', + 'mojito-resource-store-adapter' +]}); diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index cab184860..cf560bc2b 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -9,7 +9,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { libpath = require('path'), fixtures = libpath.join(__dirname, '../fixtures/store'), mojitoRoot = libpath.join(__dirname, '../..'), - ResourceStore = require(libpath.join(__dirname, '../../store.server')), Mock = YUITest.Mock, A = YUITest.Assert, AA = YUITest.ArrayAssert; @@ -19,41 +18,36 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { name: 'Store tests', 'pre load': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); //Y.log(JSON.stringify(store,null,4)); - A.isTrue(store._root === fixtures); + A.isTrue(store._config.root === fixtures); }, 'pre load no application.json file': function() { - var fixtures = libpath.join(__dirname, '../fixtures/store_no_app_config'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); //Y.log(JSON.stringify(store,null,4)); - A.isTrue(store._root === fixtures); + A.isTrue(store._config.root === fixtures); }, 'server app config value': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - var config = store.getAppConfig(null, 'application'); + var config = store.getAppConfig(null); A.isTrue(config.testKey1 === 'testVal1'); }, 'server mojit config value': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {base:'test1'}; store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.id === 'test1', 'wrong ID'); A.isTrue(instance.type === 'test_mojit_1', 'wrong type'); A.isTrue(instance.config.testKey4 === 'testVal4', 'missing key from definition.json'); @@ -61,13 +55,11 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit config value via type': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.type === 'test_mojit_1', 'wrong ID'); A.isTrue(instance.config.testKey4 === 'testVal4', 'missing config from definition.json'); A.isTrue(instance.config.testKey6.testKey7 === 'testVal7', 'missing deep config from definition.json'); @@ -75,8 +67,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit config value via type & overrride': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = { @@ -84,7 +75,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { config:{testKey4: 'other'} }; store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.type === 'test_mojit_1', 'wrong ID'); A.isTrue(instance.config.testKey4 === 'other', 'missing config from definition.json'); A.isTrue(instance.config.testKey5 === 'testVal5', 'missing deep config from definition.json'); @@ -92,14 +82,12 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance assets': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance) { A.areSame('/static/test_mojit_1/assets', instance.assetsRoot); - // we'll skip the favicon.ico that ships with Mojito // (it's not availble when running --coverage anyway) A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/assets/css/main.css'), instance.assets['css/main.css']); @@ -108,8 +96,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance views & binders': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'test_mojit_1'}; @@ -139,8 +126,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance models': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'test_mojit_1'}; @@ -160,8 +146,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance actions': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'test_mojit_1'}; @@ -174,8 +159,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance yui': function() { - - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'TestMojit2'}; @@ -219,9 +203,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance yui - precomputed': function() { - var fixtures = libpath.join(__dirname, '../fixtures/precomputed'); - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = { type:'PagedFlickr' }; @@ -270,9 +253,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance yui - ondemand': function() { - var fixtures = libpath.join(__dirname, '../fixtures/ondemand'); - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = { type:'PagedFlickr' }; @@ -309,9 +291,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance yui - precomputed+ondemand': function() { - var fixtures = libpath.join(__dirname, '../fixtures/precomputed-ondemand'); - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = { type:'PagedFlickr' }; @@ -372,13 +353,13 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'dynamic handling of mojit definition.json': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/definition.json'), store._dynamicURLs['/static/test_mojit_1/definition.json']); }, 'server mojit type name can come from package.json': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'TestMojit2'}; @@ -390,7 +371,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit is NOT loaded because of package mojito version miss-match': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); A.isTrue(typeof store._staticURLs['/static/test_mojit_4/package.json'] === 'undefined'); @@ -398,7 +379,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit is loaded because of package mojito version match': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'TestMojit2'}; @@ -408,28 +389,28 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server a mojits package.json file is NOT publicly accessible': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); A.isTrue(typeof store._staticURLs['/static/TestMojit2/package.json'] === 'undefined'); }, 'server a mojits package.json file is publicly accessible': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); A.isTrue(typeof store._staticURLs['/static/TestMojit3/package.json'] === 'string'); }, 'server a mojit is NOT loaded because it has a package.json file with no mojito config': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); A.isTrue(typeof store._staticURLs['/static/TestMojit5/package.json'] === 'undefined'); }, 'server mojit view index.mu.html is loaded correctly': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'TestMojit3'}; @@ -439,7 +420,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit view index.iphone.mu.html is loaded correctly': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'TestMojit3'}; @@ -449,7 +430,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit view index1.mu.html is loaded correctly': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'TestMojit3'}; @@ -459,7 +440,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit view index1.iphone.mu.html is loaded correctly': function(){ - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = {type:'TestMojit3'}; @@ -469,7 +450,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'app-level mojits': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = { type: 'test_mojit_1' }; store.expandInstance(instance, {}, function(err, instance) { @@ -478,7 +459,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'mojitDirs setting': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = { type: 'soloMojit' }; store.expandInstance(instance, {}, function(err, instance) { @@ -487,7 +468,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'expandInstance caching': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var instance = 'foo'; var context = {}; @@ -498,7 +479,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'rollups mojits': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var rollups = store.getRollupsMojits('client', {}); @@ -514,7 +495,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'rollups app': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var rollup = store.getRollupsApp('client', {}); @@ -530,7 +511,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'inline css mojits': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var inlines = store.getInlineCssMojits('client', {}); @@ -562,7 +543,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'multi preload, and setLogger()': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); var logsBefore, logs = 0; store.setLogger({ log: function() { logs++; @@ -574,7 +555,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call getSpec()': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); store.getSpec('server', 'test1', {}, function(err, instance) { A.areSame('test_mojit_1', instance.type); @@ -585,7 +566,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call getType()': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); store.getType('server', 'test_mojit_1', {}, function(err, instance) { A.areSame('test_mojit_1', instance.type); @@ -596,7 +577,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'instance with base pointing to non-existant spec': function() { - var store = new ResourceStore(fixtures), + var store = new Y.mojito.ResourceStore({ root: fixtures }), spec = { base: 'nonexistant' }; store.preload(); store.expandInstance(spec, {}, function(err, instance) { @@ -606,11 +587,11 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'getAppConfig() returns contextualized info': function() { - var store = new ResourceStore(fixtures), + var store = new Y.mojito.ResourceStore({ root: fixtures }), config, context = { runtime: 'server' }; store.preload(context); - config = store.getAppConfig(null, 'application'); + config = store.getAppConfig(null); A.isObject(config); A.areSame('testVal1-server', config.testKey1, 'testKey1 wasnt contextualized to the server'); A.areSame('testVal2', config.testKey2, 'testKey2 gotten from the wrong context'); @@ -618,17 +599,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isUndefined(config.testKey4, 'testKey4 gotten from the wrong context'); }, - 'getAppConfig() for something other than "definition"': function() { - var store = new ResourceStore(fixtures); - store.preload(); - var config = store.getAppConfig({}, 'routes'); - A.isObject(config); - A.isObject(config.flickr_by_page); - A.isObject(config.flickr_base); - }, - 'call getRoutes()': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var routes = store.getRoutes({}); A.isObject(routes, 'no routes at all'); @@ -637,14 +609,14 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call fileFromStaticHandlerURL()': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var fullpath = store.fileFromStaticHandlerURL('/static/TestMojit2/controller.server.js'); A.areSame(libpath.join(fixtures, 'mojits/test_mojit_2/controller.server.js'), fullpath); }, 'call serializeClientStore()': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var client = store.serializeClientStore({}, []); A.isObject(client, 'config is missing'); @@ -663,7 +635,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call listAllMojits()': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var list = store.listAllMojits('server'); A.areSame(9, list.length, 'found the wrong number of mojits'); @@ -679,7 +651,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call getAllMojits()': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var mojits = store.getAllMojits('server', {}); A.areSame(9, Object.keys(mojits).length, 'Found the wrong number of mojits'); @@ -706,7 +678,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'stuff with ctx{lang:}, in language fallback': function() { var fixtures = libpath.join(__dirname, '../fixtures/gsg5'), - store = new ResourceStore(fixtures), + store = new Y.mojito.ResourceStore({ root: fixtures }), ctx, spec; store.preload(); @@ -759,7 +731,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'bad files': function() { var fixtures = libpath.join(__dirname, '../fixtures/badfiles'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var spec = { type: 'M' }; store.expandInstance(spec, {}, function(err, instance) { @@ -772,7 +744,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'malformed JSON for config file': function() { var fixtures = libpath.join(__dirname, '../fixtures/badfiles2'), - store = new ResourceStore(fixtures), + store = new Y.mojito.ResourceStore({ root: fixtures }), logCalled = 0; store.setLogger({ log: function() { logCalled++; @@ -789,7 +761,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'JSON config file not YCB': function() { var fixtures = libpath.join(__dirname, '../fixtures/badfiles3'), - store = new ResourceStore(fixtures), + store = new Y.mojito.ResourceStore({ root: fixtures }), r, logCalled = 0; store.setLogger({ log: function(msg, lvl, who) { logCalled++; @@ -802,7 +774,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'appConfig deferAllOptionalAutoloads': function() { var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var spec = { type: 'PagedFlickr' }; store.expandInstance(spec, {}, function(err, instance) { @@ -821,7 +793,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'appConfig staticHandling.prefix': function() { var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var spec = { type: 'PagedFlickr' }; store.expandInstance(spec, {}, function(err, instance) { @@ -831,7 +803,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'controller with selector': function() { var fixtures = libpath.join(__dirname, '../fixtures/gsg5'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var spec = { type: 'PagedFlickr' }; var ctx = { device: 'iphone' }; @@ -842,7 +814,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'binder with selector': function() { var fixtures = libpath.join(__dirname, '../fixtures/gsg5'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var spec = { type: 'PagedFlickr' }; var ctx = { device: 'iphone' }; @@ -852,7 +824,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'app with rollups': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var spec = { type: 'rollups' }; store.expandInstanceForEnv('client', spec, {}, function(err, instance) { @@ -868,7 +840,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'appConfig yui.base': function() { var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var spec = { type: 'PagedFlickr' }; store.expandInstance(spec, {}, function(err, instance) { @@ -891,7 +863,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { returns: ['d', 'c', 'a', 'b'] }); - var store = new ResourceStore(fixtures, { fs: mockfs }); +// TODO -- store._libs = { fs: mockfs } + var store = new Y.mojito.ResourceStore({ root: fixtures }); var files = store._sortedReaddirSync('dir'); AA.itemsAreSame(['a', 'b', 'c', 'd'], files); @@ -933,7 +906,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { returns: true }); - store = new ResourceStore(fixtures, { path: mockpath }); +// TODO -- store._libs = { path: mockpath } + store = new Y.mojito.ResourceStore({ root: fixtures }); store._readConfigJSON = Y.bind(mockstore._readConfigJSON, mockstore); store._isValidYcbDimensions = Y.bind(mockstore._isValidYcbDimensions, mockstore); @@ -962,7 +936,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { returns: true }); - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store._readConfigJSON = Y.bind(mockstore._readConfigJSON, mockstore); store._isValidYcbDimensions = Y.bind(mockstore._isValidYcbDimensions, mockstore); @@ -981,7 +955,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { returns: [] //the invalid dimensions.json }); - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); store._readConfigJSON = Y.bind(mockstore._readConfigJSON, mockstore); try { @@ -996,37 +970,37 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { '_isValidYcbDimensions() allows dimensions array with single key': function() { var dims = [{ dimensions: [{ dimKey: {} }] }]; - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); A.isTrue(store._isValidYcbDimensions(dims)); }, '_isValidYcbDimensions() spots empty array': function() { var dims = []; - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); A.isFalse(store._isValidYcbDimensions(dims)); }, '_isValidYcbDimensions() spots empty dimensions': function() { var dims = [{ dimensions: [] }]; - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); A.isFalse(store._isValidYcbDimensions(dims)); }, '_isValidYcbDimensions() spots too many top-level items': function() { var dims = [{ dimensions: [{ dimKey: {} }] }, { extraDimensions: [] }]; - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); A.isFalse(store._isValidYcbDimensions(dims)); }, '_skipBadPath() does just that': function() { - var store = new ResourceStore(fixtures); + var store = new Y.mojito.ResourceStore({ root: fixtures }); A.areSame(true, store._skipBadPath({ ext: '.js~' })); A.areSame(false, store._skipBadPath({ ext: '.js' })); }, 'load node_modules': function() { var fixtures = libpath.join(__dirname, '../fixtures/packages'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); if (!store._mojitMeta.server.a && !store._mojitMeta.server.aa && !store._mojitMeta.server.ba) { @@ -1060,7 +1034,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'find and parse resources by convention': function() { var fixtures = libpath.join(__dirname, '../fixtures/conventions'), - store = new ResourceStore(fixtures); + store = new Y.mojito.ResourceStore({ root: fixtures }); // fake out some parts of preload(), which we're trying to avoid store._fwConfig = store._readConfigJSON(libpath.join(mojitoRoot, 'config.json')); @@ -1428,4 +1402,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { YUITest.TestRunner.add(suite); -}, '0.0.1', {requires: ['oop', 'mojito-resource-store-adapter']}); +}, '0.0.1', {requires: [ + 'oop', + 'mojito-resource-store', + 'mojito-resource-store-adapter' +]}); From 9f9f91fb5d7b6a2b3a1ae2466fddccae0ef93151 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 6 Jun 2012 15:21:20 -0700 Subject: [PATCH 019/119] delinted --- source/lib/app/addons/rs/config.server.js | 3 + source/lib/app/addons/rs/routes.server.js | 6 +- source/lib/app/addons/rs/selector.server.js | 11 ++- source/lib/app/addons/rs/yui.server.js | 7 +- source/lib/app/commands/gv.js | 2 +- source/lib/app/commands/help.js | 2 +- source/lib/app/commands/info.js | 2 +- source/lib/app/commands/jslint.js | 3 +- source/lib/app/commands/start.js | 2 +- source/lib/index.js | 29 +++--- source/lib/store.server.js | 102 +++++++++++--------- 11 files changed, 96 insertions(+), 73 deletions(-) diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js index 6ca583e66..26d33c30c 100644 --- a/source/lib/app/addons/rs/config.server.js +++ b/source/lib/app/addons/rs/config.server.js @@ -4,6 +4,9 @@ * See the accompanying LICENSE file for terms. */ +/*jslint anon:true, sloppy:true, nomen:true*/ +/*global YUI*/ + YUI.add('addon-rs-config', function(Y, NAME) { var libfs = require('fs'), diff --git a/source/lib/app/addons/rs/routes.server.js b/source/lib/app/addons/rs/routes.server.js index f0948bc87..ae9205bf9 100644 --- a/source/lib/app/addons/rs/routes.server.js +++ b/source/lib/app/addons/rs/routes.server.js @@ -4,6 +4,9 @@ * See the accompanying LICENSE file for terms. */ +/*jslint anon:true, sloppy:true, nomen:true*/ +/*global YUI*/ + YUI.add('addon-rs-routes', function(Y, NAME) { var libpath = require('path'), @@ -41,7 +44,6 @@ YUI.add('addon-rs-routes', function(Y, NAME) { ress, r, res, - path, routes; for (p = 0; p < routesFiles.length; p += 1) { @@ -53,7 +55,7 @@ YUI.add('addon-rs-routes', function(Y, NAME) { fixedPaths[path] = true; } - ress = this.rs.getResources(env, ctx, {type:'config'}); + ress = this.rs.getResources(env, ctx, {type: 'config'}); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (fixedPaths[res.source.fs.fullPath]) { diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js index f392f6d48..f6e130dd7 100644 --- a/source/lib/app/addons/rs/selector.server.js +++ b/source/lib/app/addons/rs/selector.server.js @@ -4,6 +4,9 @@ * See the accompanying LICENSE file for terms. */ +/*jslint anon:true, sloppy:true, nomen:true*/ +/*global YUI*/ + YUI.add('addon-rs-selector', function(Y, NAME) { var libpath = require('path'), @@ -40,11 +43,13 @@ YUI.add('addon-rs-selector', function(Y, NAME) { getListFromContext: function(ctx) { - var sels = ['*']; - var p, part, parts; + var sels = ['*'], + p, + part, + parts; // TODO: use rs.config for this too parts = this._appConfigYCB.readNoMerge(ctx, {}); - for (p = 0; p < parts.length; p++) { + for (p = 0; p < parts.length; p += 1) { part = parts[p]; if (part.selector) { sels.unshift(part.selector); diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index cf7f2787d..55f61c745 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -4,6 +4,9 @@ * See the accompanying LICENSE file for terms. */ +/*jslint anon:true, sloppy:true, nomen:true*/ +/*global YUI*/ + YUI.add('addon-rs-yui', function(Y, NAME) { var libfs = require('fs'), @@ -81,7 +84,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { } else if (mojitType === fs.basename.substr(0, mojitType.length)) { res.yui.lang = fs.basename.substr(mojitType.length + 1); } else { - logger.log('invalid YUI lang file format. skipping ' + fs.fullPath, 'error', NAME); + Y.log('invalid YUI lang file format. skipping ' + fs.fullPath, 'error', NAME); } res.name = res.yui.lang; res.id = [res.type, res.subtype, res.name].join('-'); @@ -89,7 +92,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { } if ('yui-module' === type) { - baseParts = fs.basename.split('.'), + baseParts = fs.basename.split('.'); res = { source: source, mojit: mojitType, diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index 41ffc230e..a2f83c7ea 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -5,7 +5,7 @@ */ -/*jslint anon:true, sloppy:true*/ +/*jslint anon:true, nomen:true, sloppy:true*/ // TODO: diff --git a/source/lib/app/commands/help.js b/source/lib/app/commands/help.js index e9bb56ef2..9d0289791 100644 --- a/source/lib/app/commands/help.js +++ b/source/lib/app/commands/help.js @@ -51,7 +51,7 @@ function helpCommand(command) { } catch (e) { try { help('./' + command); - } catch(e) { + } catch (ee) { console.log('No such command: ' + command); helpTop(); } diff --git a/source/lib/app/commands/info.js b/source/lib/app/commands/info.js index 90c6b28c0..a8863a37a 100644 --- a/source/lib/app/commands/info.js +++ b/source/lib/app/commands/info.js @@ -5,7 +5,7 @@ */ -/*jslint anon:true, sloppy:true*/ +/*jslint anon:true, nomen:true, sloppy:true*/ var fs = require('fs'), diff --git a/source/lib/app/commands/jslint.js b/source/lib/app/commands/jslint.js index 80a8baae3..31d50b250 100644 --- a/source/lib/app/commands/jslint.js +++ b/source/lib/app/commands/jslint.js @@ -130,9 +130,10 @@ function OutputFile(filename) { * and returning the number of errors encountered. */ function lintOneFile(infile, outfile) { - var jslint = require('../fulljslint').jslint, + var jslint = require('../../management/fulljslint').jslint, OPTS = { 'continue': true, // Tolerate continue + node: true, predef: [ // CommonJS 'exports', diff --git a/source/lib/app/commands/start.js b/source/lib/app/commands/start.js index 2b279b92f..c50429651 100644 --- a/source/lib/app/commands/start.js +++ b/source/lib/app/commands/start.js @@ -5,7 +5,7 @@ */ -/*jslint anon:true, sloppy:true*/ +/*jslint anon:true, nomen:true, sloppy:true*/ var path = require('path'), diff --git a/source/lib/index.js b/source/lib/index.js index 7a906efde..79cf858e4 100644 --- a/source/lib/index.js +++ b/source/lib/index.js @@ -126,24 +126,23 @@ MojitoServer.prototype = { // We need to do this early, since creating a Y instance appears to copy // the function. YUI.applyConfig({ logFn: function(msg, lvl, src) { - // translating YUI logs so they are categorized outside the rest - // of Mojito's log levels - var args = Array.prototype.slice.call(arguments); - if (!this.mojito || src === 'yui' || src === 'loader' || + // translating YUI logs so they are categorized outside the rest + // of Mojito's log levels + var args = Array.prototype.slice.call(arguments); + if (!this.mojito || src === 'yui' || src === 'loader' || src === 'get') { - if ((!logger) && (!logConfig.yui)) { - return; - } - args[1] = 'YUI-' + lvl.toUpperCase(); - } - if (logger) { - logger.log.apply(logger, args); - } else { - console.log(serverLog.options.formatter(msg, lvl, src, - new Date().getTime(), serverLog.options)); + if ((!logger) && (!logConfig.yui)) { + return; } + args[1] = 'YUI-' + lvl.toUpperCase(); } - }); + if (logger) { + logger.log.apply(logger, args); + } else { + console.log(serverLog.options.formatter(msg, lvl, src, + new Date().getTime(), serverLog.options)); + } + }}); Y = YUI({ core: CORE_YUI_MODULES, useSync: true }); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index fdf9bc4a9..2e710139c 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -143,12 +143,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._mojitResources = {}; // env: posl: mojitType: array of resources // We'll start with just our "config" addon. - this._yuiUseSync( { + this._yuiUseSync({ 'addon-rs-config': { - fullpath: libpath.join(__dirname, 'app/addons/rs/config.server.js'), + fullpath: libpath.join(__dirname, 'app/addons/rs/config.server.js') } }); - this.plug(Y.mojito.addons.rs.config, { appRoot:this._config.root, mojitoRoot:mojitoRoot }); + this.plug(Y.mojito.addons.rs.config, { appRoot: this._config.root, mojitoRoot: mojitoRoot }); this._fwConfig = this.config.readConfigJSON(libpath.join(mojitoRoot, 'config.json')); this._appConfigStatic = this.getAppConfig({}); @@ -244,7 +244,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this.unplug(name); }, this); - ress = this.getResourceVersions({type:'addon', subtype:'rs'}); + ress = this.getResourceVersions({type: 'addon', subtype: 'rs'}); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if ('rs' === res.subtype) { @@ -258,7 +258,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._yuiUseSync(modules); Y.Object.each(Y.mojito.addons.rs, function(fn, name) { - this.plug(fn, { appRoot:this._config.root, mojitoRoot:mojitoRoot }); + this.plug(fn, { appRoot: this._config.root, mojitoRoot: mojitoRoot }); }, this); }, @@ -374,14 +374,14 @@ YUI.add('mojito-resource-store', function(Y, NAME) { posl = JSON.stringify(this.selector.getListFromContext(ctx)); if (filter.mojit) { if (!this._mojitResources[env] || - !this._mojitResources[env][posl] || - !this._mojitResources[env][posl][filter.mojit]) { + !this._mojitResources[env][posl] || + !this._mojitResources[env][posl][filter.mojit]) { return []; } source = this._mojitResources[env][posl][filter.mojit]; } else { if (!this._appResources[env] || - !this._appResources[env][posl]) { + !this._appResources[env][posl]) { return []; } source = this._appResources[env][posl]; @@ -430,7 +430,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { res, ress, modules = {}; - ress = this.getResources(env, ctx, { mojit:'shared' }); + ress = this.getResources(env, ctx, { mojit: 'shared' }); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (!res.yui || !res.yui.name) { @@ -441,8 +441,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } modules[res.yui.name] = { fullpath: ('client' === env) ? - res.staticHandlerURL : - res.source.fs.fullPath, + res.staticHandlerURL : + res.source.fs.fullPath, requires: (res.yui.meta && res.yui.meta.requires) || [] }; @@ -458,7 +458,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { res, ress, modules = {}; - ress = this.getResources(env, ctx, { mojit:'shared' }); + ress = this.getResources(env, ctx, { mojit: 'shared' }); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (!res.yui || !res.yui.name) { @@ -469,8 +469,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } modules[res.yui.name] = { fullpath: ('client' === env) ? - res.staticHandlerURL : - res.source.fs.fullPath, + res.staticHandlerURL : + res.source.fs.fullPath, requires: (res.yui.meta && res.yui.meta.requires) || [] }; @@ -478,13 +478,17 @@ YUI.add('mojito-resource-store', function(Y, NAME) { return { modules: modules }; }, - + // TODO DOCS // TODO -- move to yui addon getYuiConfigAllMojits: function(env, ctx) { - var m, mojit, mojits; - var r, res, ress; - var modules = {}; + var m, + mojit, + mojits, + r, + res, + ress, + modules = {}; mojits = this.listAllMojits(); for (m = 0; m < mojits.length; m += 1) { mojit = mojits[m]; @@ -500,8 +504,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } modules[res.yui.name] = { fullpath: ('client' === env) ? - res.staticHandlerURL : - res.source.fs.fullPath, + res.staticHandlerURL : + res.source.fs.fullPath, requires: (res.yui.meta && res.yui.meta.requires) || [] }; @@ -808,7 +812,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (!fs.isFile && fs.subDirArray.length < 2 && 'archetypes' === fs.subDirArray[0]) { return true; } - if (!fs.isFile && fs.subDirArray.length == 2 && 'archetypes' === fs.subDirArray[0]) { + if (!fs.isFile && fs.subDirArray.length === 2 && 'archetypes' === fs.subDirArray[0]) { return { type: 'archetype', subtype: fs.subDirArray[1], @@ -897,12 +901,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } // mojit parts with format {name}.{affinity}.{selector} - if ( - 'action' === type || - 'addon' === type || - 'controller' === type || - 'model' === type - ) { + if ('action' === type || + 'addon' === type || + 'controller' === type || + 'model' === type) { res = { source: source, mojit: mojitType, @@ -949,8 +951,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); - return res; - } + return res; + } // special case: view if ('view' === type) { @@ -1026,7 +1028,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { p; ctxs = this._listAllContexts(); - for (var c = 0; c < ctxs.length; c++) { + for (c = 0; c < ctxs.length; c += 1) { ctx = ctxs[c]; posl = this.selector.getListFromContext(ctx); posls[JSON.stringify(posl)] = posl; @@ -1052,20 +1054,20 @@ YUI.add('mojito-resource-store', function(Y, NAME) { //console.log(affinities); if (!this._appResources[env]) { - this._appResources[env] = {} + this._appResources[env] = {}; } - this._appResources[env][poslKey] = + this._appResources[env][poslKey] = this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs ]); if (!this._mojitResources[env]) { - this._mojitResources[env] = {} + this._mojitResources[env] = {}; } if (!this._mojitResources[env][poslKey]) { - this._mojitResources[env][poslKey] = {} + this._mojitResources[env][poslKey] = {}; } for (type in this._mojitRVs) { if (this._mojitRVs.hasOwnProperty(type)) { - this._mojitResources[env][poslKey][type] = + this._mojitResources[env][poslKey][type] = this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); // TODO: fire event that mojit has been resolved } @@ -1124,7 +1126,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } for (resid in versions) { if (versions.hasOwnProperty(resid)) { - highest = Math.max.apply(Math, Object.keys(versions[resid])) + highest = Math.max.apply(Math, Object.keys(versions[resid])); //console.log('--DEBUG-- highest=' + highest + ' -- ' + resid); out.push(versions[resid][highest]); } @@ -1141,10 +1143,18 @@ YUI.add('mojito-resource-store', function(Y, NAME) { */ _listAllContexts: function() { var dims = this.config.getDimensions(), - nctxs, c, ctxs = [], - dn, dname, dnames, - dv, dval, dvals, - e, each, mod, + nctxs, + c, + ctxs = [], + dn, + dname, + dnames, + dv, + dval, + dvals, + e, + each, + mod, // only because we might want to change it at some point // (not including it helps reduce the number of contexts) SKIP_RUNTIME = true; @@ -1154,7 +1164,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { dnames = Object.keys(dims); nctxs = 1; - for (dn = 0; dn < dnames.length; dn++) { + for (dn = 0; dn < dnames.length; dn += 1) { dname = dnames[dn]; if (SKIP_RUNTIME && dname === 'runtime') { continue; @@ -1167,11 +1177,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { nctxs *= dvals.length; } - for (c = 0; c < nctxs; c++) { + for (c = 0; c < nctxs; c += 1) { ctxs[c] = {}; } mod = 1; - for (dn = 0; dn < dnames.length; dn++) { + for (dn = 0; dn < dnames.length; dn += 1) { dname = dnames[dn]; if (SKIP_RUNTIME && dname === 'runtime') { continue; @@ -1182,10 +1192,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { e = each; dv = 0; - for (c = 0; c < nctxs; --e, c++) { + for (c = 0; c < nctxs; e -= 1, c += 1) { if (0 === e) { e = each; - dv++; + dv += 1; dv = dv % dvals.length; } dval = dvals[dv]; @@ -1208,7 +1218,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { _flattenDims: function(dims) { var d, dim, name, out = {}; - for (d = 0; d < dims.length; d++) { + for (d = 0; d < dims.length; d += 1) { dim = dims[d]; name = Object.keys(dim)[0]; out[name] = this._listKeys(dim[name]); From 5a7ccbc56afdb616430c210c62d18c52f3f317a6 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 7 Jun 2012 08:19:34 -0700 Subject: [PATCH 020/119] updated to match new resource store API --- .../app/addons/ac/deploy-tests.server.js | 36 ++++++++++++++----- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js index 0304fd28a..8817a03b3 100644 --- a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js +++ b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js @@ -10,9 +10,9 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { A = YUITest.Assert, AA = YUITest.ArrayAssert, OA = YUITest.ObjectAssert; - + suite.add(new YUITest.TestCase({ - + name: 'basics', setUp: function() { @@ -50,7 +50,11 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { serializeClientStore: function() { return 'clientstore'; }, - store: { _fwConfig: { ondemandBaseYuiModules:[] } }, + store: { + getFrameworkConfig: function() { + return { ondemandBaseYuiModules:[] }; + } + }, getYuiConfigFw: function() { return {}; }, getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { @@ -130,7 +134,11 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { serializeClientStore: function() { return 'clientstore'; }, - store: { _fwConfig: { ondemandBaseYuiModules:[] } }, + store: { + getFrameworkConfig: function() { + return { ondemandBaseYuiModules:[] }; + } + }, getYuiConfigFw: function() { return {}; }, getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { @@ -218,7 +226,11 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { serializeClientStore: function() { return 'clientstore'; }, - store: { _fwConfig: { ondemandBaseYuiModules:[] } }, + store: { + getFrameworkConfig: function() { + return { ondemandBaseYuiModules:[] }; + } + }, getYuiConfigFw: function() { return {}; }, getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { @@ -297,7 +309,11 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { serializeClientStore: function() { return 'clientstore'; }, - store: { _fwConfig: { ondemandBaseYuiModules:[] } }, + store: { + getFrameworkConfig: function() { + return { ondemandBaseYuiModules:[] }; + } + }, getYuiConfigFw: function() { return {}; }, getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { @@ -376,7 +392,11 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { serializeClientStore: function() { return 'clientstore'; }, - store: { _fwConfig: { ondemandBaseYuiModules:[] } }, + store: { + getFrameworkConfig: function() { + return { ondemandBaseYuiModules:[] }; + } + }, getYuiConfigFw: function() { return {}; }, getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { @@ -420,7 +440,7 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { })); YUITest.TestRunner.add(suite); - + }, '0.0.1', {requires: [ 'mojito-deploy-addon' From 8987a606e90443696bc1bee1826bea976d64740a Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 7 Jun 2012 09:26:23 -0700 Subject: [PATCH 021/119] everyone who wants the contents of a JSON file gets their own copy, to mangle as they see fit --- source/lib/app/addons/rs/config.server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js index 26d33c30c..e1317192f 100644 --- a/source/lib/app/addons/rs/config.server.js +++ b/source/lib/app/addons/rs/config.server.js @@ -69,7 +69,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { } this._jsonCache[fullPath] = json; } - return json; + return this.rs.cloneObj(json); }, From 0b4c8a231e369bb2d8b40f1df2f0b671c5ebcd64 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 7 Jun 2012 14:02:21 -0700 Subject: [PATCH 022/119] start of config RS addon tests --- .../app/addons/rs/config-tests.server.js | 229 ++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 source/lib/tests/autoload/app/addons/rs/config-tests.server.js diff --git a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js new file mode 100644 index 000000000..77952da19 --- /dev/null +++ b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js @@ -0,0 +1,229 @@ +/* + * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ +YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { + + var suite = new YUITest.TestSuite(NAME), + libfs = require('fs'), + libpath = require('path'), + mojitoRoot = libpath.join(__dirname, '../../../../../'), + A = YUITest.Assert, + OA = YUITest.ObjectAssert, + AA = YUITest.ArrayAssert; + + + function MockRS(config) { + MockRS.superclass.constructor.apply(this, arguments); + } + MockRS.NAME = 'MockResourceStore'; + MockRS.ATTRS = {}; + Y.extend(MockRS, Y.Base, { + + initializer: function(cfg) { + this._config = cfg || {}; + }, + + cloneObj: function(o) { + return Y.clone(o); + }, + + getStaticContext: function() { + return this._config.context || {}; + }, + + mergeRecursive: function(a, b) { + return Y.mix(a, b, true, [], 0, true); + }, + + findResourceByConvention: function(source, mojitType) { + // no-op + }, + + parseResource: function(source, type, subtype, mojitType) { + // no-op + } + + }); + + + function readJSON(dir, file) { + var path = libpath.join(dir, file); + var contents = libfs.readFileSync(path, 'utf-8'); + return JSON.parse(contents); + } + + + function cmp(x, y, msg, path) { + if (Y.Lang.isArray(x)) { + A.isArray(x, msg || 'first arg should be an array'); + A.isArray(y, msg || 'second arg should be an array'); + A.areSame(x.length, y.length, msg || 'arrays are different lengths'); + for (var i = 0; i < x.length; i += 1) { + cmp(x[i], y[i], msg); + } + return; + } + if (Y.Lang.isObject(x)) { + A.isObject(x, msg || 'first arg should be an object'); +if (! Y.Lang.isObject(y)) { + console.log(x); + console.log(y); +} + A.isObject(y, msg || 'second arg should be an object'); + A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); + for (var i in x) { + if (x.hasOwnProperty(i)) { + cmp(x[i], y[i], msg); + } + } + return; + } + A.areSame(x, y, msg || 'args should be the same'); + } + + + function makeSource(dir, dirType, subdir, file, isFile) { + var source = { + fs: { + fullPath: libpath.join(dir, subdir, file), + rootDir: dir, + rootType: dirType, + subDir: subdir, + subDirArray: subdir.split('/'), + isFile: isFile, + ext: libpath.extname(file) + }, + pkg: { + name: 'unittest', + version: '999.666.999', + depth: 999 + } + }; + source.fs.basename = libpath.basename(file, source.fs.ext); + return source; + } + + + suite.add(new YUITest.TestCase({ + + name: 'config rs addon tests', + + 'read dimensions': function() { + // from mojito + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + var have = store.config.getDimensions(); + var want = readJSON(mojitoRoot, 'dimensions.json'); + cmp(want, have); + + // app-specified + fixtures = libpath.join(__dirname, '../../../../fixtures/ycb'); + store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + have = store.config.getDimensions(); + want = readJSON(fixtures, 'dimensions.json'); + cmp(want, have); + }, + + + 'find config resources': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + // skip non-json files + var source = makeSource(fixtures, 'app', '.', 'server.js', true); + var have = store.findResourceByConvention(source, null); + var want = undefined; + cmp(have, want, 'skip non-json files'); + + // include all json files in the app + source = makeSource(fixtures, 'app', '.', 'x.json', true); + have = store.findResourceByConvention(source, null); + want = { type: 'config' }; + cmp(have, want, 'include all json files in the app'); + + // ... explicitly including package.json + source = makeSource(fixtures, 'app', '.', 'package.json', true); + have = store.findResourceByConvention(source, null); + want = { type: 'config' }; + cmp(have, want, 'include package.json in the app'); + + // exclude all json files in a bundle + source = makeSource(fixtures, 'bundle', '.', 'x.json', true); + have = store.findResourceByConvention(source, null); + want = undefined; + cmp(have, want, 'exclude all json files in a bundle'); + + // ... explicitly excluding package.json + source = makeSource(fixtures, 'bundle', '.', 'package.json', true); + have = store.findResourceByConvention(source, null); + want = undefined; + cmp(have, want, 'exclude package.json in a bundle'); + + // include all json files in a mojit + source = makeSource(fixtures, 'mojit', '.', 'x.json', true); + have = store.findResourceByConvention(source, 'foo'); + want = { type: 'config' }; + cmp(have, want, 'include all json files in a mojit'); + + // ... except for the 'shared' mojit + source = makeSource(fixtures, 'mojit', '.', 'x.json', true); + have = store.findResourceByConvention(source, 'shared'); + want = undefined; + cmp(have, want, 'exclude all json files in the "shared" mojit'); + + // ... explicitly including package.json + source = makeSource(fixtures, 'mojit', '.', 'package.json', true); + have = store.findResourceByConvention(source, 'shared'); + want = { type: 'config' }; + cmp(have, want, 'include package.json in the "shared" mojit'); + }, + + + 'parse found resource': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var source = makeSource(fixtures, 'app', '.', 'application.json', true); + var res = store.parseResource(source, 'config'); + A.isNotUndefined(res); + cmp(res.source, source); + A.areSame('config', res.type); + A.areSame('common', res.affinity); + A.areSame('*', res.selector); + A.areSame('application', res.name); + A.areSame('config--application', res.id); + A.isUndefined(res.mojit); + + source = makeSource(fixtures, 'mojit', '.', 'defaults.json', true); + res = store.parseResource(source, 'config', undefined, 'x'); + A.isNotUndefined(res); + cmp(res.source, source); + A.areSame('config', res.type); + A.areSame('common', res.affinity); + A.areSame('*', res.selector); + A.areSame('defaults', res.name); + A.areSame('config--defaults', res.id); + A.areSame('x', res.mojit); + }, + + + 'TODO: read JSON files': function() { + A.skip(); + }, + + + 'TODO: read YCB files': function() { + A.skip(); + } + + })); + + YUITest.TestRunner.add(suite); + +}, '0.0.1', {requires: ['base', 'oop', 'addon-rs-config']}); From 69333e2f048848969aca2e7ca9db2e81c0ad12c6 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 8 Jun 2012 12:49:12 -0700 Subject: [PATCH 023/119] rest of tests for addon-rs-config --- .../app/addons/rs/config-tests.server.js | 94 +++++++++-- .../lib/tests/autoload/store.server-tests.js | 151 ------------------ 2 files changed, 84 insertions(+), 161 deletions(-) diff --git a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js index 77952da19..9bffd2a69 100644 --- a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js @@ -33,8 +33,28 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { return this._config.context || {}; }, - mergeRecursive: function(a, b) { - return Y.mix(a, b, true, [], 0, true); + mergeRecursive: function(dest, src, typeMatch) { + var p; + for (p in src) { + if (src.hasOwnProperty(p)) { + // Property in destination object set; update its value. + if (src[p] && src[p].constructor === Object) { + if (!dest[p]) { + dest[p] = {}; + } + dest[p] = this.mergeRecursive(dest[p], src[p]); + } else { + if (dest[p] && typeMatch) { + if (typeof dest[p] === typeof src[p]) { + dest[p] = src[p]; + } + } else { + dest[p] = src[p]; + } + } + } + } + return dest; }, findResourceByConvention: function(source, mojitType) { @@ -67,10 +87,6 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { } if (Y.Lang.isObject(x)) { A.isObject(x, msg || 'first arg should be an object'); -if (! Y.Lang.isObject(y)) { - console.log(x); - console.log(y); -} A.isObject(y, msg || 'second arg should be an object'); A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); for (var i in x) { @@ -213,14 +229,72 @@ if (! Y.Lang.isObject(y)) { }, - 'TODO: read JSON files': function() { - A.skip(); + 'read JSON files': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var path = libpath.join(fixtures, 'application.json'); + var have = store.config.readConfigJSON(path); + var want = readJSON(fixtures, 'application.json'); + cmp(have, want); + }, + + + 'read YCB files': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var path = libpath.join(fixtures, 'application.json'); + var have = store.config.readConfigYCB(path, { runtime: 'server' }); + var want = { + "mojitDirs": [ + "soloMojit" + ], + "staticHandling": { + "useRollups": true + }, + "testKey1": "testVal1-server", + "testKey2": "testVal2", + "testKey3": "testVal3", + "specs": { + "test1": { + "type": "test_mojit_1" + } + }, + "pathos": "portended" + }; + cmp(have, want); }, - 'TODO: read YCB files': function() { - A.skip(); + 'malformed JSON for config file': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/badfiles2'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var path = libpath.join(fixtures, 'routes.json'); + try { + store.config.readConfigJSON(path); + } + catch (err) { + A.areSame('Error parsing JSON file:', err.message.substr(0, 24)); + } + }, + + + 'JSON config file not YCB': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/badfiles3'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var path = libpath.join(fixtures, 'routes.json'); + var have = store.config.readConfigYCB(path, {}); + var want = {}; + cmp(have, want); } + })); diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index cf560bc2b..58bc3c3a8 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -742,36 +742,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }); }, - 'malformed JSON for config file': function() { - var fixtures = libpath.join(__dirname, '../fixtures/badfiles2'), - store = new Y.mojito.ResourceStore({ root: fixtures }), - logCalled = 0; - store.setLogger({ log: function() { - logCalled++; - }}); - try { - store.preload(); - } - catch (err) { - A.areSame('Error parsing JSON file:', err.message.substr(0, 24)); - A.areSame(1, logCalled); - return; - } - }, - - 'JSON config file not YCB': function() { - var fixtures = libpath.join(__dirname, '../fixtures/badfiles3'), - store = new Y.mojito.ResourceStore({ root: fixtures }), - r, logCalled = 0; - store.setLogger({ log: function(msg, lvl, who) { - logCalled++; - }}); - store.preload(); - r = store.getRoutes(); - A.areSame(0, logCalled); - A.isNotUndefined(r._default_path); - }, - 'appConfig deferAllOptionalAutoloads': function() { var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'), store = new Y.mojito.ResourceStore({ root: fixtures }); @@ -871,127 +841,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { Mock.verify(mockfs); }, - '_readYcbDimensions() uses application dimensions.json': function() { - var store, - mockpath = Mock(), - joinCount = 0; - - Mock.expect(mockpath, { - method: 'join', - args: [Mock.Value.String, 'dimensions.json'], - returns: 'joinedpath_app', - callCount: 1, - run: function(base, ignored) { - A.areSame(store._root, base); - } - }); - - Mock.expect(mockpath, { - method: 'existsSync', - args: ['joinedpath_app'], - returns: true - }); - - var mockstore = Mock(); - - Mock.expect(mockstore, { - method: '_readConfigJSON', - args: ['joinedpath_app'], - returns: [] - }); - - Mock.expect(mockstore, { - method: '_isValidYcbDimensions', - args: [Mock.Value.Object], - returns: true - }); - -// TODO -- store._libs = { path: mockpath } - store = new Y.mojito.ResourceStore({ root: fixtures }); - store._readConfigJSON = Y.bind(mockstore._readConfigJSON, mockstore); - store._isValidYcbDimensions = Y.bind(mockstore._isValidYcbDimensions, mockstore); - - var dims = store._readYcbDimensions(); - AA.isEmpty(dims, 'Expected the empty array returned by the mocked method'); - - Mock.verify(mockstore); - Mock.verify(mockpath); - }, - - '_readYcbDimensions() falls back when application dimensions.json missing': function() { - var store; - - var mockstore = Mock(); - - Mock.expect(mockstore, { - method: '_readConfigJSON', - args: ['joinedpath_fw'], - args: [libpath.join(__dirname, '../../dimensions.json')], - returns: [] - }); - - Mock.expect(mockstore, { - method: '_isValidYcbDimensions', - args: [Mock.Value.Object], - returns: true - }); - - store = new Y.mojito.ResourceStore({ root: fixtures }); - store._readConfigJSON = Y.bind(mockstore._readConfigJSON, mockstore); - store._isValidYcbDimensions = Y.bind(mockstore._isValidYcbDimensions, mockstore); - - var dims = store._readYcbDimensions(); - AA.isEmpty(dims, 'Expected the empty array returned by the mocked method'); - - Mock.verify(mockstore); - }, - - '_readYcbDimensions() throws an error when dimensions.json is invalid': function() { - var mockstore = Mock(); - - Mock.expect(mockstore, { - method: '_readConfigJSON', - args: [Mock.Value.String], - returns: [] //the invalid dimensions.json - }); - - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store._readConfigJSON = Y.bind(mockstore._readConfigJSON, mockstore); - - try { - store._readYcbDimensions(); - A.fail('Expected an exception'); - } catch (e) { - A.areSame('Invalid dimensions.json: ' + libpath.join(mojitoRoot, 'dimensions.json'), e.message); - } - - Mock.verify(mockstore); - }, - - '_isValidYcbDimensions() allows dimensions array with single key': function() { - var dims = [{ dimensions: [{ dimKey: {} }] }]; - var store = new Y.mojito.ResourceStore({ root: fixtures }); - A.isTrue(store._isValidYcbDimensions(dims)); - }, - - '_isValidYcbDimensions() spots empty array': function() { - var dims = []; - var store = new Y.mojito.ResourceStore({ root: fixtures }); - A.isFalse(store._isValidYcbDimensions(dims)); - }, - - '_isValidYcbDimensions() spots empty dimensions': function() { - var dims = [{ dimensions: [] }]; - var store = new Y.mojito.ResourceStore({ root: fixtures }); - A.isFalse(store._isValidYcbDimensions(dims)); - }, - - '_isValidYcbDimensions() spots too many top-level items': function() { - var dims = [{ dimensions: [{ dimKey: {} }] }, { extraDimensions: [] }]; - var store = new Y.mojito.ResourceStore({ root: fixtures }); - A.isFalse(store._isValidYcbDimensions(dims)); - }, - '_skipBadPath() does just that': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); A.areSame(true, store._skipBadPath({ ext: '.js~' })); From 7b98d0b30cc63721b8b5dd65a98cf22aa83f56de Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 8 Jun 2012 14:14:04 -0700 Subject: [PATCH 024/119] unit test for addon-rs-routes --- .../app/addons/rs/routes-tests.server.js | 156 ++++++++++++++++++ .../lib/tests/autoload/store.server-tests.js | 9 - 2 files changed, 156 insertions(+), 9 deletions(-) create mode 100644 source/lib/tests/autoload/app/addons/rs/routes-tests.server.js diff --git a/source/lib/tests/autoload/app/addons/rs/routes-tests.server.js b/source/lib/tests/autoload/app/addons/rs/routes-tests.server.js new file mode 100644 index 000000000..19e1c2320 --- /dev/null +++ b/source/lib/tests/autoload/app/addons/rs/routes-tests.server.js @@ -0,0 +1,156 @@ +/* + * Copyright (c) 2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ +YUI.add('mojito-addon-rs-routes-tests', function(Y, NAME) { + + var suite = new YUITest.TestSuite(NAME), + libpath = require('path'), + mojitoRoot = libpath.join(__dirname, '../../../../../'), + A = YUITest.Assert; + + + function MockRS(config) { + MockRS.superclass.constructor.apply(this, arguments); + } + MockRS.NAME = 'MockResourceStore'; + MockRS.ATTRS = {}; + Y.extend(MockRS, Y.Base, { + + initializer: function(cfg) { + this._config = cfg || {}; + }, + + getAppConfig: function(ctx) { + return { routesFiles: [ 'routes.json' ] }; + }, + + getResources: function(env, ctx, filter) { + var res = {}; + res.source = makeSource(this._config.root, 'app', '.', 'routes.json', true); + return [ res ]; + }, + + cloneObj: function(o) { + return Y.clone(o); + }, + + getStaticContext: function() { + return this._config.context || {}; + }, + + mergeRecursive: function(dest, src, typeMatch) { + var p; + for (p in src) { + if (src.hasOwnProperty(p)) { + // Property in destination object set; update its value. + if (src[p] && src[p].constructor === Object) { + if (!dest[p]) { + dest[p] = {}; + } + dest[p] = this.mergeRecursive(dest[p], src[p]); + } else { + if (dest[p] && typeMatch) { + if (typeof dest[p] === typeof src[p]) { + dest[p] = src[p]; + } + } else { + dest[p] = src[p]; + } + } + } + } + return dest; + } + + }); + + + function cmp(x, y, msg, path) { + if (Y.Lang.isArray(x)) { + A.isArray(x, msg || 'first arg should be an array'); + A.isArray(y, msg || 'second arg should be an array'); + A.areSame(x.length, y.length, msg || 'arrays are different lengths'); + for (var i = 0; i < x.length; i += 1) { + cmp(x[i], y[i], msg); + } + return; + } + if (Y.Lang.isObject(x)) { + A.isObject(x, msg || 'first arg should be an object'); + A.isObject(y, msg || 'second arg should be an object'); + A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); + for (var i in x) { + if (x.hasOwnProperty(i)) { + cmp(x[i], y[i], msg); + } + } + return; + } + A.areSame(x, y, msg || 'args should be the same'); + } + + + function makeSource(dir, dirType, subdir, file, isFile) { + var source = { + fs: { + fullPath: libpath.join(dir, subdir, file), + rootDir: dir, + rootType: dirType, + subDir: subdir, + subDirArray: subdir.split('/'), + isFile: isFile, + ext: libpath.extname(file) + }, + pkg: { + name: 'unittest', + version: '999.666.999', + depth: 999 + } + }; + source.fs.basename = libpath.basename(file, source.fs.ext); + return source; + } + + + suite.add(new YUITest.TestCase({ + + name: 'config rs addon tests', + + 'read routes': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + store.plug(Y.mojito.addons.rs.routes, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store.routes.read('server', {}, function(err, have) { + var want = { + flickr_by_page: { + verbs: [ "get" ], + path: "/flickr/page/:page/image/:image", + call: "flickr.index" + }, + flickr_base: { + verbs: [ "get" ], + path: "/flickr", + param: "page=1&image=0", + call: "flickr.index" + }, + detail: undefined + }; + cmp(have, want); + }); + } + + + })); + + YUITest.TestRunner.add(suite); + +}, '0.0.1', {requires: [ + 'base', + 'oop', + 'addon-rs-config', + 'addon-rs-routes' +]}); diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 58bc3c3a8..27919795d 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -599,15 +599,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isUndefined(config.testKey4, 'testKey4 gotten from the wrong context'); }, - 'call getRoutes()': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var routes = store.getRoutes({}); - A.isObject(routes, 'no routes at all'); - A.isObject(routes.flickr_by_page, 'missing route flickr_by_page'); - A.isObject(routes.flickr_base, 'missing route flickr_base'); - }, - 'call fileFromStaticHandlerURL()': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); From 666de01f0f6583912096be7601001be2bbd242e6 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sat, 9 Jun 2012 15:28:52 -0700 Subject: [PATCH 025/119] addon-rs-routes was a little silly, so moved back into the store core --- source/lib/app/addons/rs/routes.server.js | 75 --------- source/lib/store.server.js | 40 +++++ .../app/addons/rs/routes-tests.server.js | 156 ------------------ .../lib/tests/autoload/store.server-tests.js | 9 + 4 files changed, 49 insertions(+), 231 deletions(-) delete mode 100644 source/lib/app/addons/rs/routes.server.js delete mode 100644 source/lib/tests/autoload/app/addons/rs/routes-tests.server.js diff --git a/source/lib/app/addons/rs/routes.server.js b/source/lib/app/addons/rs/routes.server.js deleted file mode 100644 index ae9205bf9..000000000 --- a/source/lib/app/addons/rs/routes.server.js +++ /dev/null @@ -1,75 +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, sloppy:true, nomen:true*/ -/*global YUI*/ - -YUI.add('addon-rs-routes', function(Y, NAME) { - - var libpath = require('path'), - libycb = require(libpath.join(__dirname, '../../../libs/ycb')); - - function RSAddonRoutes() { - RSAddonRoutes.superclass.constructor.apply(this, arguments); - } - RSAddonRoutes.NS = 'routes'; - RSAddonRoutes.DEPS = ['config']; - RSAddonRoutes.ATTRS = {}; - - Y.extend(RSAddonRoutes, Y.Plugin.Base, { - - initializer: function(config) { - this.rs = config.host; - this.appRoot = config.appRoot; - }, - - - destructor: function() { - // TODO: needed to break cycle so we don't leak memory? - this.rs = null; - }, - - - read: function(env, ctx, cb) { - ctx.runtime = env; - var appConfig = this.rs.getAppConfig(ctx), - routesFiles = appConfig.routesFiles, - p, - path, - fixedPaths = {}, - out = {}, - ress, - r, - res, - routes; - - for (p = 0; p < routesFiles.length; p += 1) { - path = routesFiles[p]; - // relative paths are relative to the application - if ('/' !== path.charAt(1)) { - path = libpath.join(this.appRoot, path); - } - fixedPaths[path] = true; - } - - ress = this.rs.getResources(env, ctx, {type: 'config'}); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if (fixedPaths[res.source.fs.fullPath]) { - routes = this.rs.config.readConfigYCB(res.source.fs.fullPath, ctx); - out = Y.merge(out, routes); - } - } - - cb(null, out); - } - - - }); - Y.namespace('mojito.addons.rs'); - Y.mojito.addons.rs.routes = RSAddonRoutes; - -}, '0.0.1', { requires: ['plugin', 'oop']}); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 2e710139c..8e2721287 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -515,6 +515,46 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + /** + * Returns the routes configured in the application. + * + * @param ctx {object} the context + * @return {object} routes + */ + getRoutes: function(ctx) { + var appConfig = this.getAppConfig(ctx), + routesFiles = appConfig.routesFiles, + p, + path, + fixedPaths = {}, + out = {}, + ress, + r, + res, + routes; + + for (p = 0; p < routesFiles.length; p += 1) { + path = routesFiles[p]; + // relative paths are relative to the application + if ('/' !== path.charAt(1)) { + path = libpath.join(this._config.root, path); + } + fixedPaths[path] = true; + } + + ress = this.getResources('server', ctx, {type: 'config'}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (fixedPaths[res.source.fs.fullPath]) { + routes = this.config.readConfigYCB(res.source.fs.fullPath, ctx); + out = Y.merge(out, routes); + } + } + + return out; + }, + + /** * preloads metadata about resources in a package * (but not subpackages in its node_modules/) diff --git a/source/lib/tests/autoload/app/addons/rs/routes-tests.server.js b/source/lib/tests/autoload/app/addons/rs/routes-tests.server.js deleted file mode 100644 index 19e1c2320..000000000 --- a/source/lib/tests/autoload/app/addons/rs/routes-tests.server.js +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (c) 2012, Yahoo! Inc. All rights reserved. - * Copyrights licensed under the New BSD License. - * See the accompanying LICENSE file for terms. - */ -YUI.add('mojito-addon-rs-routes-tests', function(Y, NAME) { - - var suite = new YUITest.TestSuite(NAME), - libpath = require('path'), - mojitoRoot = libpath.join(__dirname, '../../../../../'), - A = YUITest.Assert; - - - function MockRS(config) { - MockRS.superclass.constructor.apply(this, arguments); - } - MockRS.NAME = 'MockResourceStore'; - MockRS.ATTRS = {}; - Y.extend(MockRS, Y.Base, { - - initializer: function(cfg) { - this._config = cfg || {}; - }, - - getAppConfig: function(ctx) { - return { routesFiles: [ 'routes.json' ] }; - }, - - getResources: function(env, ctx, filter) { - var res = {}; - res.source = makeSource(this._config.root, 'app', '.', 'routes.json', true); - return [ res ]; - }, - - cloneObj: function(o) { - return Y.clone(o); - }, - - getStaticContext: function() { - return this._config.context || {}; - }, - - mergeRecursive: function(dest, src, typeMatch) { - var p; - for (p in src) { - if (src.hasOwnProperty(p)) { - // Property in destination object set; update its value. - if (src[p] && src[p].constructor === Object) { - if (!dest[p]) { - dest[p] = {}; - } - dest[p] = this.mergeRecursive(dest[p], src[p]); - } else { - if (dest[p] && typeMatch) { - if (typeof dest[p] === typeof src[p]) { - dest[p] = src[p]; - } - } else { - dest[p] = src[p]; - } - } - } - } - return dest; - } - - }); - - - function cmp(x, y, msg, path) { - if (Y.Lang.isArray(x)) { - A.isArray(x, msg || 'first arg should be an array'); - A.isArray(y, msg || 'second arg should be an array'); - A.areSame(x.length, y.length, msg || 'arrays are different lengths'); - for (var i = 0; i < x.length; i += 1) { - cmp(x[i], y[i], msg); - } - return; - } - if (Y.Lang.isObject(x)) { - A.isObject(x, msg || 'first arg should be an object'); - A.isObject(y, msg || 'second arg should be an object'); - A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); - for (var i in x) { - if (x.hasOwnProperty(i)) { - cmp(x[i], y[i], msg); - } - } - return; - } - A.areSame(x, y, msg || 'args should be the same'); - } - - - function makeSource(dir, dirType, subdir, file, isFile) { - var source = { - fs: { - fullPath: libpath.join(dir, subdir, file), - rootDir: dir, - rootType: dirType, - subDir: subdir, - subDirArray: subdir.split('/'), - isFile: isFile, - ext: libpath.extname(file) - }, - pkg: { - name: 'unittest', - version: '999.666.999', - depth: 999 - } - }; - source.fs.basename = libpath.basename(file, source.fs.ext); - return source; - } - - - suite.add(new YUITest.TestCase({ - - name: 'config rs addon tests', - - 'read routes': function() { - var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); - var store = new MockRS({ root: fixtures }); - store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); - store.plug(Y.mojito.addons.rs.routes, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); - - store.routes.read('server', {}, function(err, have) { - var want = { - flickr_by_page: { - verbs: [ "get" ], - path: "/flickr/page/:page/image/:image", - call: "flickr.index" - }, - flickr_base: { - verbs: [ "get" ], - path: "/flickr", - param: "page=1&image=0", - call: "flickr.index" - }, - detail: undefined - }; - cmp(have, want); - }); - } - - - })); - - YUITest.TestRunner.add(suite); - -}, '0.0.1', {requires: [ - 'base', - 'oop', - 'addon-rs-config', - 'addon-rs-routes' -]}); diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 27919795d..58bc3c3a8 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -599,6 +599,15 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isUndefined(config.testKey4, 'testKey4 gotten from the wrong context'); }, + 'call getRoutes()': function() { + var store = new Y.mojito.ResourceStore({ root: fixtures }); + store.preload(); + var routes = store.getRoutes({}); + A.isObject(routes, 'no routes at all'); + A.isObject(routes.flickr_by_page, 'missing route flickr_by_page'); + A.isObject(routes.flickr_base, 'missing route flickr_base'); + }, + 'call fileFromStaticHandlerURL()': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); From 1c22f2516245c22832f5d9144b4b128ad041924b Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sat, 9 Jun 2012 15:45:19 -0700 Subject: [PATCH 026/119] unit test for addon-rs-selector --- .../app/addons/rs/config-tests.server.js | 1 + .../app/addons/rs/selector-tests.server.js | 104 ++++++++++++++++++ .../lib/tests/fixtures/store/application.json | 12 ++ 3 files changed, 117 insertions(+) create mode 100644 source/lib/tests/autoload/app/addons/rs/selector-tests.server.js diff --git a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js index 9bffd2a69..eeeec682b 100644 --- a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js @@ -263,6 +263,7 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { "type": "test_mojit_1" } }, + "selector": "shelves", "pathos": "portended" }; cmp(have, want); diff --git a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js new file mode 100644 index 000000000..ca261ab25 --- /dev/null +++ b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ +YUI.add('mojito-addon-rs-selector-tests', function(Y, NAME) { + + var suite = new YUITest.TestSuite(NAME), + libpath = require('path'), + mojitoRoot = libpath.join(__dirname, '../../../../../'), + A = YUITest.Assert; + + + function MockRS(config) { + MockRS.superclass.constructor.apply(this, arguments); + } + MockRS.NAME = 'MockResourceStore'; + MockRS.ATTRS = {}; + Y.extend(MockRS, Y.Base, { + initializer: function(cfg) { + this._config = cfg || {}; + }, + cloneObj: function(o) { + return Y.clone(o); + } + }); + + + function cmp(x, y, msg, path) { + if (Y.Lang.isArray(x)) { + A.isArray(x, msg || 'first arg should be an array'); + A.isArray(y, msg || 'second arg should be an array'); + A.areSame(x.length, y.length, msg || 'arrays are different lengths'); + for (var i = 0; i < x.length; i += 1) { + cmp(x[i], y[i], msg); + } + return; + } + if (Y.Lang.isObject(x)) { + A.isObject(x, msg || 'first arg should be an object'); + A.isObject(y, msg || 'second arg should be an object'); + A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); + for (var i in x) { + if (x.hasOwnProperty(i)) { + cmp(x[i], y[i], msg); + } + } + return; + } + A.areSame(x, y, msg || 'args should be the same'); + } + + + suite.add(new YUITest.TestCase({ + + name: 'selector rs addon tests', + + 'read dimensions': function() { + // from mojito + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + store.plug(Y.mojito.addons.rs.selector, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var have = store.selector.getListFromContext({}); + var want = ['*']; + cmp(have, want); + + var have = store.selector.getListFromContext({runtime:'client'}); + var want = ['right', '*']; + cmp(have, want); + + var have = store.selector.getListFromContext({runtime:'server'}); + var want = ['shelves', '*']; + cmp(have, want); + + var have = store.selector.getListFromContext({device:'android'}); + var want = ['droid', '*']; + cmp(have, want); + + var have = store.selector.getListFromContext({runtime:'server', device:'android'}); + var want = ['shelves', 'droid', '*']; + cmp(have, want); + + var have = store.selector.getListFromContext({device:'android', environment:'dev'}); + var want = ['devdroid', 'droid', '*']; + cmp(have, want); + + var have = store.selector.getListFromContext({runtime:'server', device:'android', environment:'dev'}); + var want = ['shelves', 'devdroid', 'droid', '*']; + cmp(have, want); + } + + + })); + + YUITest.TestRunner.add(suite); + +}, '0.0.1', {requires: [ + 'base', + 'oop', + 'addon-rs-config', + 'addon-rs-selector' +]}); diff --git a/source/lib/tests/fixtures/store/application.json b/source/lib/tests/fixtures/store/application.json index 024372df2..031942067 100644 --- a/source/lib/tests/fixtures/store/application.json +++ b/source/lib/tests/fixtures/store/application.json @@ -20,13 +20,25 @@ { "settings": [ "runtime:server" ], + "selector": "shelves", "testKey1": "testVal1-server", "pathos": "portended" }, { "settings": [ "runtime:client" ], + "selector": "right", "testKey2": "testVal2-client", "testKey4": "testVal4-client" + }, + { + "settings": [ "device:android" ], + + "selector": "droid" + }, + { + "settings": [ "device:android", "environment:dev" ], + + "selector": "devdroid" } ] From 6e4c26072ee115f75a6af6411fb2529a00050804 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sat, 9 Jun 2012 17:13:16 -0700 Subject: [PATCH 027/119] unit test for addon-rs-yui --- .../app/addons/rs/yui-tests.server.js | 224 ++++++++++++++++++ 1 file changed, 224 insertions(+) create mode 100644 source/lib/tests/autoload/app/addons/rs/yui-tests.server.js diff --git a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js new file mode 100644 index 000000000..b2a401f8e --- /dev/null +++ b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js @@ -0,0 +1,224 @@ +/* + * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ +YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { + + var suite = new YUITest.TestSuite(NAME), + libpath = require('path'), + mojitoRoot = libpath.join(__dirname, '../../../../../'), + A = YUITest.Assert; + + + function MockRS(config) { + MockRS.superclass.constructor.apply(this, arguments); + } + MockRS.NAME = 'MockResourceStore'; + MockRS.ATTRS = {}; + Y.extend(MockRS, Y.Base, { + + initializer: function(cfg) { + this._config = cfg || {}; + this.RVs = {}; + }, + + findResourceByConvention: function(source, mojitType) { + // no-op + }, + + parseResource: function(source, type, subtype, mojitType) { + // no-op + }, + + addResourceVersion: function(res) { + this.RVs[[res.affinity, res.selector, res.id].join('/')] = res; + } + + }); + + + function cmp(x, y, msg, path) { + if (Y.Lang.isArray(x)) { + A.isArray(x, msg || 'first arg should be an array'); + A.isArray(y, msg || 'second arg should be an array'); + A.areSame(x.length, y.length, msg || 'arrays are different lengths'); + for (var i = 0; i < x.length; i += 1) { + cmp(x[i], y[i], msg); + } + return; + } + if (Y.Lang.isObject(x)) { + A.isObject(x, msg || 'first arg should be an object'); + A.isObject(y, msg || 'second arg should be an object'); + A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); + for (var i in x) { + if (x.hasOwnProperty(i)) { + cmp(x[i], y[i], msg); + } + } + return; + } + A.areSame(x, y, msg || 'args should be the same'); + } + + + function makeSource(dir, dirType, subdir, file, isFile) { + var source = { + fs: { + fullPath: libpath.join(dir, subdir, file), + rootDir: dir, + rootType: dirType, + subDir: subdir, + subDirArray: subdir.split('/'), + isFile: isFile, + ext: libpath.extname(file) + }, + pkg: { + name: 'unittest', + version: '999.666.999', + depth: 999 + } + }; + source.fs.basename = libpath.basename(file, source.fs.ext); + return source; + } + + + suite.add(new YUITest.TestCase({ + + name: 'yui rs addon tests', + + 'find yui resources': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var source = makeSource(fixtures, 'app', 'autoload', 'x.server.txt', true); + var have = store.findResourceByConvention(source, null); + var want = undefined; + cmp(have, want); + + source = makeSource(fixtures, 'app', 'blix', 'x.server.js', true); + have = store.findResourceByConvention(source, null); + want = undefined; + cmp(have, want); + + source = makeSource(fixtures, 'app', 'autoload', 'x.server.js', true); + have = store.findResourceByConvention(source, null); + want = { type: 'yui-module', skipSubdirParts: 1 }; + cmp(have, want); + + source = makeSource(fixtures, 'app', 'yui_modules', 'x.server.js', true); + have = store.findResourceByConvention(source, null); + want = { type: 'yui-module', skipSubdirParts: 1 }; + cmp(have, want); + + source = makeSource(fixtures, 'app', 'lang', 'x.server.js', true); + have = store.findResourceByConvention(source, null); + want = { type: 'yui-lang', skipSubdirParts: 1 }; + cmp(have, want); + }, + + + 'parse found resource': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/conventions'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var source = makeSource(fixtures, 'app', 'autoload', 'm.common.js', true); + var res = store.parseResource(source, 'yui-module'); + A.isNotUndefined(res); + cmp(res.source, source); + A.areSame('yui-module', res.type); + A.areSame('common', res.affinity); + A.areSame('*', res.selector); + A.areSame('m', res.name); + A.areSame('yui-module--m', res.id); + A.isUndefined(res.mojit); + + source = makeSource(fixtures, 'app', 'autoload', 'm.common.iphone.js', true); + res = store.parseResource(source, 'yui-module'); + A.isNotUndefined(res); + cmp(res.source, source); + A.areSame('yui-module', res.type); + A.areSame('common', res.affinity); + A.areSame('iphone', res.selector); + A.areSame('m', res.name); + A.areSame('yui-module--m', res.id); + A.isUndefined(res.mojit); + + source = makeSource(fixtures, 'app', 'yui_modules', 'x.common.js', true); + res = store.parseResource(source, 'yui-module'); + A.isNotUndefined(res); + cmp(res.source, source); + A.areSame('yui-module', res.type); + A.areSame('common', res.affinity); + A.areSame('*', res.selector); + A.areSame('x', res.name); + A.areSame('yui-module--x', res.id); + A.isUndefined(res.mojit); + + source = makeSource(fixtures, 'bundle', 'lang', 'testing.js', true); + res = store.parseResource(source, 'yui-lang', undefined, 'testing'); + A.isNotUndefined(res); + cmp(res.source, source); + A.areSame('yui-lang', res.type); + A.areSame('common', res.affinity); + A.areSame('*', res.selector); + A.areSame('', res.name); + A.areSame('yui-lang--', res.id); + A.areSame('testing', res.mojit); + + source = makeSource(fixtures, 'bundle', 'lang', 'testing_de.js', true); + res = store.parseResource(source, 'yui-lang', undefined, 'testing'); + A.isNotUndefined(res); + cmp(res.source, source); + A.areSame('yui-lang', res.type); + A.areSame('common', res.affinity); + A.areSame('*', res.selector); + A.areSame('de', res.name); + A.areSame('yui-lang--de', res.id); + A.areSame('testing', res.mojit); + + source = makeSource(fixtures, 'bundle', 'lang', 'testing_en-US.js', true); + res = store.parseResource(source, 'yui-lang', undefined, 'testing'); + A.isNotUndefined(res); + cmp(res.source, source); + A.areSame('yui-lang', res.type); + A.areSame('common', res.affinity); + A.areSame('*', res.selector); + A.areSame('en-US', res.name); + A.areSame('yui-lang--en-US', res.id); + A.areSame('testing', res.mojit); + }, + + + 'parse other resources': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/conventions'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var source = makeSource(fixtures+'/mojits/X', 'mojit', '.', 'controller.common.js', true); + var res = { + source: source, + mojit: 'X', + type: 'controller', + name: 'controller', + id: 'controller--controller', + affinity: 'common', + selector: '*' + }; + store.addResourceVersion(res); + res = store.RVs['common/*/controller--controller']; + cmp(res.source, source); + A.isNotUndefined(res.yui); + A.areSame('X', res.yui.name); + } + + + })); + + YUITest.TestRunner.add(suite); + +}, '0.0.1', {requires: ['base', 'oop', 'addon-rs-yui']}); From cc73e60e6692e59b7b26daa869a0a77a837bfbe6 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 10 Jun 2012 15:10:01 -0700 Subject: [PATCH 028/119] addon-rs-url (and unit tests) --- source/lib/app/addons/rs/url.server.js | 136 +++++++++ source/lib/store.server.js | 3 + .../app/addons/rs/url-tests.server.js | 288 ++++++++++++++++++ 3 files changed, 427 insertions(+) create mode 100644 source/lib/app/addons/rs/url.server.js create mode 100644 source/lib/tests/autoload/app/addons/rs/url-tests.server.js diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js new file mode 100644 index 000000000..db11d5fb6 --- /dev/null +++ b/source/lib/app/addons/rs/url.server.js @@ -0,0 +1,136 @@ +/* + * 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, sloppy:true, nomen:true*/ +/*global YUI*/ + +YUI.add('addon-rs-url', function(Y, NAME) { + + var libfs = require('fs'), + libpath = require('path'); + + function RSAddonUrl() { + RSAddonUrl.superclass.constructor.apply(this, arguments); + } + RSAddonUrl.NS = 'url'; + RSAddonUrl.ATTRS = {}; + + Y.extend(RSAddonUrl, Y.Plugin.Base, { + + initializer: function(config) { + var appConfig; + this.rs = config.host; + this.appRoot = config.appRoot; + this.mojitoRoot = config.mojitoRoot; + this.URLpaths = {}; + this.afterHostMethod('preloadResourceVersions', this.preloadResourceVersions, this); + + appConfig = this.rs.getStaticAppConfig(); + this.config = appConfig.staticHandling || {}; + this.config.appName = this.config.appName || libpath.basename(this.appRoot); + this.config.frameworkName = this.config.frameworkName || 'mojito'; + if (!this.config.hasOwnProperty('prefix')) { + this.config.prefix = 'static'; + } + // FUTURE: deprecate appConfig.assumeRollups + this.assumeRollups = this.config.assumeRollups || appConfig.assumeRollups; + }, + + + destructor: function() { + // TODO: needed to break cycle so we don't leak memory? + this.rs = null; + }, + + + preloadResourceVersions: function() { + var mojits = this.rs.listAllMojits(), + m, + mojit, + mojitRes, + mojitControllerRess, + ress, + r, + res; + + mojits.push('shared'); + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; + mojitRes = this.rs.getResourceVersions({id: 'mojit--' + mojit})[0]; + + // Server-only framework mojits like DaliProxy and HTMLFrameMojit + // should never have URLs associated with them. This never used + // to be an issue until we added the "assumeRollups" feature to + // preload JSON specs for specific mojits during the compile step + // (`mojito compile json`) for Livestand. + if ('shared' !== mojit && 'mojito' === mojitRes.source.pkg.name) { + mojitControllerRess = this.rs.getResourceVersions({mojit: mojit, id: 'controller--controller'}); + if (mojitControllerRess.length === 1 && + mojitControllerRess[0].affinity.affinity === 'server') { + continue; + } + } + + ress = this.rs.getResourceVersions({mojit: mojit}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + this._calcResourceURL(res, mojitRes); + } + } + }, + + + _calcResourceURL: function(res, mojitRes) { + var fs = res.source.fs, + relativePath = fs.fullPath.substr(fs.rootDir.length + 1), + urlParts = [], + rollupParts = [], + rollupFsPath; + + if (this.config.prefix) { + urlParts.push(this.config.prefix); + rollupParts.push(this.config.prefix); + } + + if ('shared' === res.mojit) { + if ('mojito' === res.source.pkg.name) { + urlParts.push(this.config.frameworkName); + } else { + urlParts.push(this.config.appName); + } + // fw resources are also put into the app-level rollup + if (res.yui && res.yui.name) { + rollupParts.push(this.config.appName); + rollupParts.push('rollup.client.js'); + rollupFsPath = libpath.join(this.appRoot, 'rollup.client.js'); + } + } else { + urlParts.push(res.mojit); + if (res.yui && res.yui.name) { + rollupParts.push(res.mojit); + rollupParts.push('rollup.client.js'); + rollupFsPath = libpath.join(mojitRes.source.fs.fullPath, 'rollup.client.js'); + } + } + + urlParts.push(relativePath); + + if (rollupFsPath && (this.assumeRollups || libpath.existsSync(rollupFsPath))) { + res.url = '/' + rollupParts.join('/'); + this.URLpaths[res.url] = rollupFsPath; + fs.rollupPath = rollupFsPath; + } else { + res.url = '/' + urlParts.join('/'); + this.URLpaths[res.url] = fs.fullPath; + } + } + + + }); + Y.namespace('mojito.addons.rs'); + Y.mojito.addons.rs.url = RSAddonUrl; + +}, '0.0.1', { requires: ['plugin', 'oop']}); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 8e2721287..ddbb78e4b 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -336,6 +336,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { use; source = filter.mojit ? this._mojitRVs[filter.mojit] : this._appRVs; + if (!source) { + return []; + } for (r = 0; r < source.length; r += 1) { res = source[r]; use = true; diff --git a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js new file mode 100644 index 000000000..3bef1e98a --- /dev/null +++ b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ +YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { + + var suite = new YUITest.TestSuite(NAME), + libfs = require('fs'), + libpath = require('path'), + mojitoRoot = libpath.join(__dirname, '../../../../../'), + A = YUITest.Assert, + OA = YUITest.ObjectAssert, + AA = YUITest.ArrayAssert; + + + function MockRS(config) { + MockRS.superclass.constructor.apply(this, arguments); + } + MockRS.NAME = 'MockResourceStore'; + MockRS.ATTRS = {}; + Y.extend(MockRS, Y.Base, { + + initializer: function(cfg) { + this._config = cfg || {}; + this._mojits = {}; + this._appRVs = []; + this._mojitRVs = {}; + }, + + getStaticAppConfig: function() { + return Y.clone(this._config.appConfig); + }, + + listAllMojits: function() { + return Object.keys(this._mojits); + }, + + getResourceVersions: function(filter) { + var source, + out = [], + r, + res, + k, + use; + source = filter.mojit ? this._mojitRVs[filter.mojit] : this._appRVs; + if (!source) { + return []; + } + for (r = 0; r < source.length; r += 1) { + res = source[r]; + use = true; + for (k in filter) { + if (filter.hasOwnProperty(k)) { + if (res[k] !== filter[k]) { + use = false; + break; + } + } + } + if (use) { + out.push(res); + } + } + return out; + }, + + preloadResourceVersions: function() { + // no-op + return true; + }, + + _makeResource: function(pkg, mojit, type, name, affinity, yuiName) { + if (mojit && mojit !== 'shared') { + this._mojits[mojit] = true; + } + var res = { + source: { + fs: { + fullPath: 'path/for/' + type + '--' + name + '.' + affinity + '.ext', + rootDir: 'path/for' + }, + pkg: { + name: pkg + } + }, + mojit: mojit, + type: type, + name: name, + id: type + '--' + name, + affinity: { affinity: affinity } + } + if (yuiName) { + res.yui = { name: yuiName }; + } + if (mojit) { + if (!this._mojitRVs[mojit]) { + this._mojitRVs[mojit] = []; + } + this._mojitRVs[mojit].push(res); + } else { + this._appRVs.push(res); + } + } + + }); + + + function cmp(x, y, msg, path) { + if (Y.Lang.isArray(x)) { + A.isArray(x, msg || 'first arg should be an array'); + A.isArray(y, msg || 'second arg should be an array'); + A.areSame(x.length, y.length, msg || 'arrays are different lengths'); + for (var i = 0; i < x.length; i += 1) { + cmp(x[i], y[i], msg); + } + return; + } + if (Y.Lang.isObject(x)) { + A.isObject(x, msg || 'first arg should be an object'); + A.isObject(y, msg || 'second arg should be an object'); + A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); + for (var i in x) { + if (x.hasOwnProperty(i)) { + cmp(x[i], y[i], msg); + } + } + return; + } + A.areSame(x, y, msg || 'args should be the same'); + } + + + function makeSource(dir, dirType, subdir, file, isFile) { + var source = { + fs: { + fullPath: libpath.join(dir, subdir, file), + rootDir: dir, + rootType: dirType, + subDir: subdir, + subDirArray: subdir.split('/'), + isFile: isFile, + ext: libpath.extname(file) + }, + pkg: { + name: 'unittest', + version: '999.666.999', + depth: 999 + } + }; + source.fs.basename = libpath.basename(file, source.fs.ext); + return source; + } + + + suite.add(new YUITest.TestCase({ + + name: 'url rs addon tests', + + 'skip mojito-provided server-only mojits': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ + root: fixtures, + appConfig: {} + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store._makeResource('mojito', null, 'mojit', 'X', 'common'); + store._makeResource('mojito', 'X', 'controller', 'controller', 'server'); + store.preloadResourceVersions(); + A.isUndefined(store._mojitRVs.X[0].url); + }, + + + 'include mojito-provided non-server-only mojits': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ + root: fixtures, + appConfig: {} + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store._makeResource('mojito', null, 'mojit', 'X', 'common'); + store._makeResource('mojito', 'X', 'controller', 'controller', 'common'); + store._makeResource('mojito', null, 'mojit', 'Y', 'common'); + store._makeResource('mojito', 'Y', 'controller', 'controller', 'client'); + store._makeResource('mojito', 'Y', 'controller', 'controller', 'server'); + store.preloadResourceVersions(); + A.areSame(1, store._mojitRVs.X.length); + A.areSame('/static/X/controller--controller.common.ext', store._mojitRVs.X[0].url); + A.areSame(2, store._mojitRVs.Y.length); + A.areSame('/static/Y/controller--controller.client.ext', store._mojitRVs.Y[0].url); + A.areSame('/static/Y/controller--controller.server.ext', store._mojitRVs.Y[1].url); + }, + + + 'resources in "shared" mojit': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ + root: fixtures, + appConfig: {} + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store._makeResource('mojito', 'shared', 'x', 'y', 'common'); + store._makeResource('orange', 'shared', 'x', 'y', 'common'); + store.preloadResourceVersions(); + A.areSame('/static/mojito/x--y.common.ext', store._mojitRVs.shared[0].url); + A.areSame('/static/store/x--y.common.ext', store._mojitRVs.shared[1].url); + }, + + + 'normal mojit resources': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ + root: fixtures, + appConfig: {} + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store._makeResource('orange', null, 'mojit', 'X', 'common'); + store._makeResource('orange', 'X', 'x', 'y', 'common'); + store._makeResource('orange', null, 'mojit', 'Y', 'common'); + store._makeResource('orange', 'Y', 'x', 'y', 'common'); + store.preloadResourceVersions(); + A.areSame('/static/X/x--y.common.ext', store._mojitRVs.X[0].url); + A.areSame('/static/Y/x--y.common.ext', store._mojitRVs.Y[0].url); + }, + + + 'configuration via appConfig': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ + root: fixtures, + appConfig: { + staticHandling: { + prefix: '', + frameworkName: 'FFF', + appName: 'AAA' + } + } + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store._makeResource('mojito', 'shared', 'x', 'y', 'common'); + store._makeResource('orange', 'shared', 'x', 'y', 'common'); + store.preloadResourceVersions(); + A.areSame('/FFF/x--y.common.ext', store._mojitRVs.shared[0].url); + A.areSame('/AAA/x--y.common.ext', store._mojitRVs.shared[1].url); + }, + + + 'assume rollups': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ + root: fixtures, + appConfig: { + staticHandling: { assumeRollups: true } + } + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store._makeResource('mojito', 'shared', 'x', 'y', 'common', 'red'); + store._makeResource('orange', 'shared', 'x', 'y', 'common', 'red'); + store._makeResource('orange', null, 'mojit', 'X', 'common'); + store._makeResource('orange', 'X', 'x', 'y', 'common', 'red'); + store._makeResource('orange', null, 'mojit', 'Y', 'common'); + store._makeResource('orange', 'Y', 'x', 'y', 'common', 'red'); + store._makeResource('orange', 'Y', 'not', 'yui', 'common'); + store.preloadResourceVersions(); + A.areSame('/static/store/rollup.client.js', store._mojitRVs.shared[0].url); + A.areSame(libpath.join(fixtures, 'rollup.client.js'), store._mojitRVs.shared[0].source.fs.rollupPath); + A.areSame('/static/store/rollup.client.js', store._mojitRVs.shared[1].url); + A.areSame(libpath.join(fixtures, 'rollup.client.js'), store._mojitRVs.shared[1].source.fs.rollupPath); + A.areSame('/static/X/rollup.client.js', store._mojitRVs.X[0].url); + A.areSame('path/for/mojit--X.common.ext/rollup.client.js', store._mojitRVs.X[0].source.fs.rollupPath); + A.areSame('/static/Y/rollup.client.js', store._mojitRVs.Y[0].url); + A.areSame('path/for/mojit--Y.common.ext/rollup.client.js', store._mojitRVs.Y[0].source.fs.rollupPath); + A.areSame('/static/Y/not--yui.common.ext', store._mojitRVs.Y[1].url); + A.isUndefined(store._mojitRVs.Y[1].source.fs.rollupPath); + } + + + })); + + YUITest.TestRunner.add(suite); + +}, '0.0.1', {requires: ['base', 'oop', 'addon-rs-url']}); From 00730bb1658e3c3d0ad5db8ea079720b25cf4f44 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 10 Jun 2012 17:33:59 -0700 Subject: [PATCH 029/119] reorganization and documentation --- source/lib/store.server.js | 913 +++++++++++++++++++------------------ 1 file changed, 480 insertions(+), 433 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index ddbb78e4b..b456aa43f 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -72,7 +72,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { mojitoRoot = __dirname, CONVENTION_SUBDIR_TYPES = { - // subdir: type + // subdir: resource type 'actions': 'action', 'binders': 'binder', 'commands': 'command', @@ -85,6 +85,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { 'binder': true, 'model': true }, + // which addon subtypes are app-level ADDON_SUBTYPES_APPLEVEL = { 'rs': true }; @@ -156,6 +157,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { destructor: function() {}, + //==================================================================== + // PUBLIC METHODS + + /** * Returns the static (non-runtime-sensitive) context * @method getStaticContext @@ -176,7 +181,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, - // TODO DOCS + /** + * Returns Mojito's built-in configuration. + * @method getFrameworkConfig + * @return {object} the configuration for mojito + */ getFrameworkConfig: function() { return this.cloneObj(this._fwConfig); }, @@ -211,12 +220,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** - * Preloads everything in the app, and as well pertinent parts of - * the framework. - * - * @method preload - * @return {nothing} - */ + * Preloads everything in the app, and as well pertinent parts of + * the framework. + * + * @method preload + * @return {nothing} + */ preload: function() { // We need to do an initial sweep to find the resource store addons. this.preloadResourceVersions(); @@ -228,102 +237,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, - /** - * Augments this resource store with addons that we know about. - * To find the addons, call `preloadResourceVersions()` first. - * @method loadAddons - * @return {nothing} - */ - loadAddons: function() { - var modules = {}, - ress, - r, - res; - - Y.Object.each(Y.mojito.addons.rs, function(fn, name) { - this.unplug(name); - }, this); - - ress = this.getResourceVersions({type: 'addon', subtype: 'rs'}); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if ('rs' === res.subtype) { - // FUTURE: ideally we shouldn't proscribe the YUI module name of RS addons - // (We can/should introspect the file for the YUI module name.) - modules['addon-rs-' + res.name] = { - fullpath: res.source.fs.fullPath - }; - } - } - this._yuiUseSync(modules); - - Y.Object.each(Y.mojito.addons.rs, function(fn, name) { - this.plug(fn, { appRoot: this._config.root, mojitoRoot: mojitoRoot }); - }, this); - }, - - - /** - * Preload metadata about all resource versions in the application (and Mojito framework) - * - * @method preloadResourceVersions - * @return {nothing} work down via other called methods - */ - preloadResourceVersions: function() { - var me = this, - walker, - walkedMojito = false, - dir, - info; - - this._appRVs = []; - this._mojitRVs = {}; - - walker = new libwalker.BreadthFirst(this._config.root); - walker.walk(function(err, info) { - if (err) { - throw err; - } - if ('mojito' === info.pkg.name) { - walkedMojito = true; - } - me._preloadPackage(info); - }); - - // user might not have installed mojito as a dependency of their - // application. (they -should- have but might not have.) - // FUTURE: instead walk -all- global packages? - if (!walkedMojito) { - dir = libpath.join(mojitoRoot, '..'); - info = { - depth: 999, - parents: [], - dir: dir - }; - info.pkg = this.config.readConfigJSON(libpath.join(dir, 'package.json')); - - // special case for weird packaging situations - if (!Object.keys(info.pkg).length) { - info.dir = mojitoRoot; - info.pkg = { - name: 'mojito', - version: '0.666.666', - yahoo: { - mojito: { - type: 'bundle', - location: 'app' - } - } - }; - } - - this._preloadPackage(info); - } - }, - - /** * Returns a list of resource versions that match the filter. + * (To get the list of resource versions from all mojits, you'll need + * to call `listAllMojits()` and iterate over that list, calling this + * method with `mojit:` in the filter.) + * * @param filter {object} limit returned resource versions to only those whose keys/values match the filter * @return {array of objects} list of matching resource versions */ @@ -360,6 +279,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * Returns a list of resources that match the filter. + * (To get the list of resources from all mojits, you'll need to call + * `listAllMojits()` and iterate over that list, calling this method + * with `mojit:` in the filter.) + * * @param env {string} the runtime environment * @param ctx {object} the context * @param filter {object} limit returned resources to only those whose keys/values match the filter @@ -411,7 +334,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, - // TODO DOCS + /** + * Returns a list of all mojits in the app, except for the "shared" mojit. + * @method listAllMojits + * @return {array} list of mojits + */ listAllMojits: function() { var mojitType, list = []; @@ -559,227 +486,188 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** - * preloads metadata about resources in a package - * (but not subpackages in its node_modules/) + * Recursively merge one object onto another. + * From http://stackoverflow.com/questions/171251/ + * how-can-i-merge-properties-of-two-javascript-objects-dynamically/ + * 383245#383245. * - * @private - * @method _preloadPackage - * @param info {object} metadata about the package - * @return {nothing} work down via other called methods + * @method mergeRecursive + * @param dest {object} object to merge into + * @param src {object} object to merge onto "dest" + * @param matchType {boolean} controls whether a non-object in the src is + * allowed to clobber a non-object in the dest (if a different type) + * @return {object} the modified "dest" object is also returned directly */ - _preloadPackage: function(info) { - var dir, - pkg; - // FUTURE: use info.inherit to scope mojit dependencies - /* - console.log('--PACKAGE-- ' + info.depth + ' ' + info.pkg.name + '@' + info.pkg.version - + ' \t' + (info.pkg.yahoo && info.pkg.yahoo.mojito && info.pkg.yahoo.mojito.type) - + ' \t[' + info.parents.join(',') + ']' - // + ' \t-- ' + JSON.stringify(info.inherit) - ); - */ - pkg = { - name: info.pkg.name, - version: info.pkg.version, - depth: info.depth - }; - if (0 === info.depth) { - // the actual application is handled specially - this._preloadApp(pkg); - return; - } - if (!info.pkg.yahoo || !info.pkg.yahoo.mojito) { - return; - } - switch (info.pkg.yahoo.mojito.type) { - case 'bundle': - dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); - this._preloadDirBundle(dir, pkg); - break; - case 'mojit': - dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); - this._preloadDirMojit(dir, 'pkg', pkg); - break; - default: - Y.log('Unknown package type "' + info.pkg.yahoo.mojito.type + '"', 'warn', NAME); - break; + mergeRecursive: function(dest, src, typeMatch) { + var p; + for (p in src) { + if (src.hasOwnProperty(p)) { + // Property in destination object set; update its value. + if (src[p] && src[p].constructor === Object) { + if (!dest[p]) { + dest[p] = {}; + } + dest[p] = this.mergeRecursive(dest[p], src[p]); + } else { + if (dest[p] && typeMatch) { + if (typeof dest[p] === typeof src[p]) { + dest[p] = src[p]; + } + } else { + dest[p] = src[p]; + } + } + } } + return dest; }, /** - * preloads metadata about resources in the application directory - * (but not node_modules/) - * - * @private - * @method _preloadApp - * @param pkg {object} metadata (name and version) about the app's package - * @return {nothing} work down via other called methods + * @method cloneObj + * @param o {mixed} + * @return {mixed} deep copy of argument */ - _preloadApp: function(pkg) { - var ress, - r, - res, - list, + cloneObj: function(o) { + var newO, i; - ress = this._findResourcesByConvention(this._config.root, 'app', pkg, 'shared'); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if ('mojit' !== res.type) { - // ignore app-level mojits found by convention, since they'll be loaded below - this.addResourceVersion(ress[r]); - } + if (typeof o !== 'object') { + return o; + } + if (!o) { + return o; } - // load mojitsDirs - list = this._globList(this._config.root, this._appConfigStatic.mojitsDirs); - for (i = 0; i < list.length; i += 1) { - this._preloadDirMojits(list[i], 'app', pkg); + if ('[object Array]' === Object.prototype.toString.apply(o)) { + newO = []; + for (i = 0; i < o.length; i += 1) { + newO[i] = this.cloneObj(o[i]); + } + return newO; } - // load mojitDirs - list = this._globList(this._config.root, this._appConfigStatic.mojitDirs || []); - for (i = 0; i < list.length; i += 1) { - this._preloadDirMojit(list[i], 'app', pkg); + newO = {}; + for (i in o) { + if (o.hasOwnProperty(i)) { + newO[i] = this.cloneObj(o[i]); + } } + return newO; }, + //==================================================================== + // CALLBACK METHODS + // These are called at various points in the algorithm of public + // methods. They are public so that they can be hooked into via AOP. + + /** - * preloads metadata about resource in a directory + * Augments this resource store with addons that we know about. + * To find the addons, call `preloadResourceVersions()` first. * - * @method _preloadDirBundle - * @param dir {string} directory path - * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods - * @private + * You most often don't want to call this directly, but instead to hook + * into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.afterHostMethod('loadAddons', this._myLoadAddons, this); + * ``` + * + * @method loadAddons + * @return {nothing} */ - _preloadDirBundle: function(dir, pkg) { - var ress, + loadAddons: function() { + var modules = {}, + ress, r, res; - // FUTURE: support configuration too - ress = this._findResourcesByConvention(dir, 'bundle', pkg, 'shared'); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - this.addResourceVersion(res); - } - this._preloadDirMojits(libpath.join(dir, 'mojits'), 'bundle', pkg); - }, - - - /** - * preloads a directory containing many mojits - * - * @private - * @method _preloadDirMojits - * @param dir {string} directory path - * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" - * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods - */ - _preloadDirMojits: function(dir, dirType, pkg) { - var i, - realDirs, - children, - childName, - childPath; - - if ('/' !== dir.charAt(0)) { - dir = libpath.join(this._config.root, dir); - } - - if (!libpath.existsSync(dir)) { - return; - } + Y.Object.each(Y.mojito.addons.rs, function(fn, name) { + this.unplug(name); + }, this); - children = this._sortedReaddirSync(dir); - for (i = 0; i < children.length; i += 1) { - childName = children[i]; - if ('.' === childName.substring(0, 1)) { - continue; + ress = this.getResourceVersions({type: 'addon', subtype: 'rs'}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if ('rs' === res.subtype) { + // FUTURE: ideally we shouldn't proscribe the YUI module name of RS addons + // (We can/should introspect the file for the YUI module name.) + modules['addon-rs-' + res.name] = { + fullpath: res.source.fs.fullPath + }; } - childPath = libpath.join(dir, childName); - this._preloadDirMojit(childPath, dirType, pkg); } + this._yuiUseSync(modules); + + Y.Object.each(Y.mojito.addons.rs, function(fn, name) { + this.plug(fn, { appRoot: this._config.root, mojitoRoot: mojitoRoot }); + }, this); }, /** - * preloads a directory that represents a single mojit + * Preload metadata about all resource versions in the application + * (and Mojito framework). * - * @private - * @method _preloadDirMojit - * @param dir {string} directory path - * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" - * @param pkg {object} metadata (name and version) about the package + * + * You most often don't want to call this directly, but instead to hook + * into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.afterHostMethod('preloadResourceVersions', this._myPreloadResourceVersions, this); + * ``` + * + * @method preloadResourceVersions * @return {nothing} work down via other called methods */ - _preloadDirMojit: function(dir, dirType, pkg) { - var mojitType, - packageJson, - definitionJson, - ress, - r, - res; - - if ('/' !== dir.charAt(0)) { - dir = libpath.join(this._config.root, dir); - } + preloadResourceVersions: function() { + var me = this, + walker, + walkedMojito = false, + dir, + info; - if (!libpath.existsSync(dir)) { - return; - } + this._appRVs = []; + this._mojitRVs = {}; - mojitType = libpath.basename(dir); - packageJson = this.config.readConfigJSON(libpath.join(dir, 'package.json')); - if (packageJson) { - if (packageJson.name) { - mojitType = packageJson.name; + walker = new libwalker.BreadthFirst(this._config.root); + walker.walk(function(err, info) { + if (err) { + throw err; } - // FUTURE: check NPM "engine" - // TODO: register mojit's package.json as a static asset, in "static handler" plugin - } - - definitionJson = this.config.readConfigYCB(libpath.join(dir, 'definition.json'), {}); - if (definitionJson.appLevel) { - mojitType = 'shared'; - } + if ('mojito' === info.pkg.name) { + walkedMojito = true; + } + me._preloadPackage(info); + }); - res = { - source: { - fs: { - fullPath: dir, - rootDir: dir, - rootType: dirType, - subDir: '.', - subDirArray: ['.'], - basename: libpath.basename(dir), - isFile: false, - ext: null - }, - pkg: pkg - }, - mojit: null, - type: 'mojit', - subtype: null, - name: mojitType, - id: 'mojit--' + mojitType, - affinity: 'common', - selector: '*' - }; - this.addResourceVersion(res); + // user might not have installed mojito as a dependency of their + // application. (they -should- have but might not have.) + // FUTURE: instead walk -all- global packages? + if (!walkedMojito) { + dir = libpath.join(mojitoRoot, '..'); + info = { + depth: 999, + parents: [], + dir: dir + }; + info.pkg = this.config.readConfigJSON(libpath.join(dir, 'package.json')); - ress = this._findResourcesByConvention(dir, 'mojit', pkg, mojitType); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - // just in case, only add those resources that really do belong to us - if (res.mojit === mojitType) { - this.addResourceVersion(res); + // special case for weird packaging situations + if (!Object.keys(info.pkg).length) { + info.dir = mojitoRoot; + info.pkg = { + name: 'mojito', + version: '0.666.666', + yahoo: { + mojito: { + type: 'bundle', + location: 'app' + } + } + }; } - // FUTURE: else warn? + + this._preloadPackage(info); } }, @@ -1021,102 +909,333 @@ YUI.add('mojito-resource-store', function(Y, NAME) { return res; } - // just ignore unknown types - return; + // just ignore unknown types + return; + }, + + + /** + * Called by the ResourceStore to register a resource version. + * You most often don't want to call this directly, but instead to hook + * into it using the AOP mechanism of `Y.Plugin.Base`: + * ``` + * this.beforeHostMethod('parseResource', this._myParseResource, this); + * ``` + * + * @method addResourceVersion + * @param res {object} the resource version + * @return {nothing} + */ + addResourceVersion: function(res) { + res.affinity = new Affinity(res.affinity); + if (res.mojit) { + if (!this._mojitRVs[res.mojit]) { + this._mojitRVs[res.mojit] = []; + } + this._mojitRVs[res.mojit].push(res); + } else { + this._appRVs.push(res); + } + }, + + + /** + * For each possible runtime configuration (based on context), pre-calculates + * which versions of the resources will be used. + * The priority (highest to lowest): + * source, + * selector, + * affinity (env or "common"). + * + * @method resolveResourceVersions + * @return {nothing} + */ + resolveResourceVersions: function() { + var c, ctx, ctxs, + poslKey, posl, posls = {}, + e, env, envs = [ 'client', 'server' ], + affinities, selectors, sourceBase, + type, ress, + p; + + ctxs = this._listAllContexts(); + for (c = 0; c < ctxs.length; c += 1) { + ctx = ctxs[c]; + posl = this.selector.getListFromContext(ctx); + posls[JSON.stringify(posl)] = posl; + } + + for (e = 0; e < envs.length; e += 1) { + env = envs[e]; + + affinities = {}; // affinity: priority modifier + affinities[env] = 1; + affinities.common = 0; + + for (poslKey in posls) { + if (posls.hasOwnProperty(poslKey)) { + posl = posls[poslKey]; + selectors = {}; // selector: priority modifier + for (p = 0; p < posl.length; p += 1) { + selectors[posl[p]] = (posl.length - p - 1) * 2; + } + sourceBase = posl.length * 2; + //console.log('-- source base ' + sourceBase); + //console.log(selectors); + //console.log(affinities); + + if (!this._appResources[env]) { + this._appResources[env] = {}; + } + this._appResources[env][poslKey] = + this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs ]); + + if (!this._mojitResources[env]) { + this._mojitResources[env] = {}; + } + if (!this._mojitResources[env][poslKey]) { + this._mojitResources[env][poslKey] = {}; + } + for (type in this._mojitRVs) { + if (this._mojitRVs.hasOwnProperty(type)) { + this._mojitResources[env][poslKey][type] = + this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); + // TODO: fire event that mojit has been resolved + } + } + } + } + } + }, + + + //==================================================================== + // PRIVATE METHODS + + + /** + * preloads metadata about resources in a package + * (but not subpackages in its node_modules/) + * + * @private + * @method _preloadPackage + * @param info {object} metadata about the package + * @return {nothing} work down via other called methods + */ + _preloadPackage: function(info) { + var dir, + pkg; + // FUTURE: use info.inherit to scope mojit dependencies + /* + console.log('--PACKAGE-- ' + info.depth + ' ' + info.pkg.name + '@' + info.pkg.version + + ' \t' + (info.pkg.yahoo && info.pkg.yahoo.mojito && info.pkg.yahoo.mojito.type) + + ' \t[' + info.parents.join(',') + ']' + // + ' \t-- ' + JSON.stringify(info.inherit) + ); + */ + pkg = { + name: info.pkg.name, + version: info.pkg.version, + depth: info.depth + }; + if (0 === info.depth) { + // the actual application is handled specially + this._preloadApp(pkg); + return; + } + if (!info.pkg.yahoo || !info.pkg.yahoo.mojito) { + return; + } + switch (info.pkg.yahoo.mojito.type) { + case 'bundle': + dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); + this._preloadDirBundle(dir, pkg); + break; + case 'mojit': + dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); + this._preloadDirMojit(dir, 'pkg', pkg); + break; + default: + Y.log('Unknown package type "' + info.pkg.yahoo.mojito.type + '"', 'warn', NAME); + break; + } + }, + + + /** + * preloads metadata about resources in the application directory + * (but not node_modules/) + * + * @private + * @method _preloadApp + * @param pkg {object} metadata (name and version) about the app's package + * @return {nothing} work down via other called methods + */ + _preloadApp: function(pkg) { + var ress, + r, + res, + list, + i; + + ress = this._findResourcesByConvention(this._config.root, 'app', pkg, 'shared'); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if ('mojit' !== res.type) { + // ignore app-level mojits found by convention, since they'll be loaded below + this.addResourceVersion(ress[r]); + } + } + + // load mojitsDirs + list = this._globList(this._config.root, this._appConfigStatic.mojitsDirs); + for (i = 0; i < list.length; i += 1) { + this._preloadDirMojits(list[i], 'app', pkg); + } + + // load mojitDirs + list = this._globList(this._config.root, this._appConfigStatic.mojitDirs || []); + for (i = 0; i < list.length; i += 1) { + this._preloadDirMojit(list[i], 'app', pkg); + } + }, + + + /** + * preloads metadata about resource in a directory + * + * @method _preloadDirBundle + * @param dir {string} directory path + * @param pkg {object} metadata (name and version) about the package + * @return {nothing} work down via other called methods + * @private + */ + _preloadDirBundle: function(dir, pkg) { + var ress, + r, + res; + // FUTURE: support configuration too + + ress = this._findResourcesByConvention(dir, 'bundle', pkg, 'shared'); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + this.addResourceVersion(res); + } + this._preloadDirMojits(libpath.join(dir, 'mojits'), 'bundle', pkg); }, /** - * Called by the ResourceStore to register a resource version. - * You most often don't want to call this directly, but instead to hook - * into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.beforeHostMethod('parseResource', this._myParseResource, this); - * ``` + * preloads a directory containing many mojits * - * @method addResourceVersion - * @param res {object} the resource version - * @return {nothing} + * @private + * @method _preloadDirMojits + * @param dir {string} directory path + * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" + * @param pkg {object} metadata (name and version) about the package + * @return {nothing} work down via other called methods */ - addResourceVersion: function(res) { - res.affinity = new Affinity(res.affinity); - if (res.mojit) { - if (!this._mojitRVs[res.mojit]) { - this._mojitRVs[res.mojit] = []; + _preloadDirMojits: function(dir, dirType, pkg) { + var i, + realDirs, + children, + childName, + childPath; + + if ('/' !== dir.charAt(0)) { + dir = libpath.join(this._config.root, dir); + } + + if (!libpath.existsSync(dir)) { + return; + } + + children = this._sortedReaddirSync(dir); + for (i = 0; i < children.length; i += 1) { + childName = children[i]; + if ('.' === childName.substring(0, 1)) { + continue; } - this._mojitRVs[res.mojit].push(res); - } else { - this._appRVs.push(res); + childPath = libpath.join(dir, childName); + this._preloadDirMojit(childPath, dirType, pkg); } }, /** - * For each possible runtime configuration (based on context), pre-calculates - * which versions of the resources will be used. - * The priority (highest to lowest): - * source, - * selector, - * affinity (env or "common"). + * preloads a directory that represents a single mojit * - * @method resolveResourceVersions - * @return {nothing} + * @private + * @method _preloadDirMojit + * @param dir {string} directory path + * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" + * @param pkg {object} metadata (name and version) about the package + * @return {nothing} work down via other called methods */ - resolveResourceVersions: function() { - var c, ctx, ctxs, - poslKey, posl, posls = {}, - e, env, envs = [ 'client', 'server' ], - affinities, selectors, sourceBase, - type, ress, - p; + _preloadDirMojit: function(dir, dirType, pkg) { + var mojitType, + packageJson, + definitionJson, + ress, + r, + res; - ctxs = this._listAllContexts(); - for (c = 0; c < ctxs.length; c += 1) { - ctx = ctxs[c]; - posl = this.selector.getListFromContext(ctx); - posls[JSON.stringify(posl)] = posl; + if ('/' !== dir.charAt(0)) { + dir = libpath.join(this._config.root, dir); } - for (e = 0; e < envs.length; e += 1) { - env = envs[e]; + if (!libpath.existsSync(dir)) { + return; + } - affinities = {}; // affinity: priority modifier - affinities[env] = 1; - affinities.common = 0; + mojitType = libpath.basename(dir); + packageJson = this.config.readConfigJSON(libpath.join(dir, 'package.json')); + if (packageJson) { + if (packageJson.name) { + mojitType = packageJson.name; + } + // FUTURE: check NPM "engine" + // TODO: register mojit's package.json as a static asset, in "static handler" plugin + } - for (poslKey in posls) { - if (posls.hasOwnProperty(poslKey)) { - posl = posls[poslKey]; - selectors = {}; // selector: priority modifier - for (p = 0; p < posl.length; p += 1) { - selectors[posl[p]] = (posl.length - p - 1) * 2; - } - sourceBase = posl.length * 2; - //console.log('-- source base ' + sourceBase); - //console.log(selectors); - //console.log(affinities); + definitionJson = this.config.readConfigYCB(libpath.join(dir, 'definition.json'), {}); + if (definitionJson.appLevel) { + mojitType = 'shared'; + } - if (!this._appResources[env]) { - this._appResources[env] = {}; - } - this._appResources[env][poslKey] = - this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs ]); + // the mojit itself is registered as an app-level resource + res = { + source: { + fs: { + fullPath: dir, + rootDir: dir, + rootType: dirType, + subDir: '.', + subDirArray: ['.'], + basename: libpath.basename(dir), + isFile: false, + ext: null + }, + pkg: pkg + }, + mojit: null, + type: 'mojit', + subtype: null, + name: mojitType, + id: 'mojit--' + mojitType, + affinity: 'common', + selector: '*' + }; + this.addResourceVersion(res); - if (!this._mojitResources[env]) { - this._mojitResources[env] = {}; - } - if (!this._mojitResources[env][poslKey]) { - this._mojitResources[env][poslKey] = {}; - } - for (type in this._mojitRVs) { - if (this._mojitRVs.hasOwnProperty(type)) { - this._mojitResources[env][poslKey][type] = - this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); - // TODO: fire event that mojit has been resolved - } - } - } + ress = this._findResourcesByConvention(dir, 'mojit', pkg, mojitType); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + // just in case, only add those resources that really do belong to us + if (res.mojit === mojitType) { + this.addResourceVersion(res); } + // FUTURE: else warn? } }, @@ -1153,7 +1272,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (!affinities.hasOwnProperty(res.affinity)) { continue; } - // TODO: conditionally skip optional affinities + // TODO: conditionally skip "-optional" affinities priority = (s * sourceBase) + selectors[res.selector] + affinities[res.affinity]; //console.log('--DEBUG-- pri=' + priority + ' --' @@ -1292,8 +1411,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** - * Finds resources based on our conventions - * -doesn't- load mojits or their contents. That's done elsewhere. + * Finds resources based on our conventions. + * -Doesn't- load mojits or their contents. That's done elsewhere. * * @private * @method _findResourcesByConvention @@ -1476,78 +1595,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }); Y.use.apply(Y, Object.keys(modules)); Y.applyConfig({ useSync: false }); - }, - - - /** - * Recursively merge one object onto another. - * From http://stackoverflow.com/questions/171251/ - * how-can-i-merge-properties-of-two-javascript-objects-dynamically/ - * 383245#383245. - * - * @method mergeRecursive - * @param dest {object} object to merge into - * @param src {object} object to merge onto "dest" - * @param matchType {boolean} controls whether a non-object in the src is - * allowed to clobber a non-object in the dest (if a different type) - * @return {object} the modified "dest" object is also returned directly - */ - mergeRecursive: function(dest, src, typeMatch) { - var p; - for (p in src) { - if (src.hasOwnProperty(p)) { - // Property in destination object set; update its value. - if (src[p] && src[p].constructor === Object) { - if (!dest[p]) { - dest[p] = {}; - } - dest[p] = this.mergeRecursive(dest[p], src[p]); - } else { - if (dest[p] && typeMatch) { - if (typeof dest[p] === typeof src[p]) { - dest[p] = src[p]; - } - } else { - dest[p] = src[p]; - } - } - } - } - return dest; - }, - - - /** - * @method cloneObj - * @param o {mixed} - * @return {mixed} deep copy of argument - */ - cloneObj: function(o) { - var newO, - i; - - if (typeof o !== 'object') { - return o; - } - if (!o) { - return o; - } - - if ('[object Array]' === Object.prototype.toString.apply(o)) { - newO = []; - for (i = 0; i < o.length; i += 1) { - newO[i] = this.cloneObj(o[i]); - } - return newO; - } - - newO = {}; - for (i in o) { - if (o.hasOwnProperty(i)) { - newO[i] = this.cloneObj(o[i]); - } - } - return newO; } From 5dac77200abc437fb729d92821fb95a4c5c15d31 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 11 Jun 2012 08:36:29 -0700 Subject: [PATCH 030/119] beginning if instance methods, starting with getMojitTypeDetails() --- source/lib/app/addons/rs/url.server.js | 11 ++ source/lib/app/addons/rs/yui.server.js | 22 +++ .../app/autoload/controller-context.common.js | 6 +- source/lib/store.server.js | 171 +++++++++++++++++- .../app/addons/rs/url-tests.server.js | 40 +++- .../app/addons/rs/yui-tests.server.js | 120 ++++++++++++ .../lib/tests/autoload/store.server-tests.js | 8 +- 7 files changed, 369 insertions(+), 9 deletions(-) diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index db11d5fb6..c28b4a504 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -27,6 +27,7 @@ YUI.add('addon-rs-url', function(Y, NAME) { this.mojitoRoot = config.mojitoRoot; this.URLpaths = {}; this.afterHostMethod('preloadResourceVersions', this.preloadResourceVersions, this); + this.onHostEvent('getMojitTypeDetails', this.getMojitTypeDetails, this); appConfig = this.rs.getStaticAppConfig(); this.config = appConfig.staticHandling || {}; @@ -83,6 +84,16 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, + getMojitTypeDetails: function(evt) { + var parts = []; + if (this.config.prefix) { + parts.push(this.config.prefix); + } + parts.push(evt.args.mojitType); + evt.mojit.assetsRoot = '/' + parts.join('/') + '/assets'; + }, + + _calcResourceURL: function(res, mojitRes) { var fs = res.source.fs, relativePath = fs.fullPath.substr(fs.rootDir.length + 1), diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index 55f61c745..d35bb1b44 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -28,6 +28,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { this.afterHostMethod('findResourceByConvention', this.findResourceByConvention, this); this.beforeHostMethod('parseResource', this.parseResource, this); this.beforeHostMethod('addResourceVersion', this.addResourceVersion, this); + this.onHostEvent('getMojitTypeDetails', this.getMojitTypeDetails, this); }, @@ -134,6 +135,27 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, + getMojitTypeDetails: function(evt) { + var dest = evt.mojit, + ress, + r, + res; + ress = this.rs.getResources(evt.args.env, evt.args.ctx, {mojit: evt.args.mojitType}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (res.type === 'binder') { + if (!dest.views[res.name]) { + dest.views[res.name] = {}; + } + dest.views[res.name]['binder-module'] = res.yui.name; + } + if (res.type === 'controller') { + dest['controller-module'] = res.yui.name; + } + } + }, + + _parseYUIModule: function(res) { var file, ctx, diff --git a/source/lib/app/autoload/controller-context.common.js b/source/lib/app/autoload/controller-context.common.js index ba63eb9c0..e8a947d09 100644 --- a/source/lib/app/autoload/controller-context.common.js +++ b/source/lib/app/autoload/controller-context.common.js @@ -40,7 +40,7 @@ YUI.add('mojito-controller-context', function(Y, NAME) { // Y.mojito.controller for legacy, multi-instance. // Y.mojito.controllers for shared instance c = this.Y.mojito.controller || - this.Y.mojito.controllers[instance.controllerModuleName]; + this.Y.mojito.controllers[instance['controller-module']]; if (!Y.Lang.isObject(c)) { error = new Error('Mojit controller prototype is not an' + @@ -78,8 +78,8 @@ YUI.add('mojito-controller-context', function(Y, NAME) { Y.Object.each(this.Y.mojito.models, function(model, modelName) { - if (!shareYUIInstance || (instance.modelYUIModuleNames && - instance.modelYUIModuleNames[modelName])) { + if (!shareYUIInstance || (instance.models && + instance.models[modelName])) { // TODO: Why? There's no particular reason to inherit here. var modelInstance = Y.mojito.util.heir(model); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index b456aa43f..8d5fe2157 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -143,6 +143,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._appResources = {}; // env: posl: array of resources this._mojitResources = {}; // env: posl: mojitType: array of resources + // Y.Plugin AOP doesn't allow afterHostMethod() callbacks to + // modify the results, so we fire an event instead. + this.publish('getMojitTypeDetails', {emitFacade: true, preventable: false}); + // We'll start with just our "config" addon. this._yuiUseSync({ 'addon-rs-config': { @@ -353,6 +357,171 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + /** + * Returns, via callback, the fully expanded mojit instance specification. + * + * @method getSpec + * @param env {string} either "client" or "server" + * @param id {string} the ID of the spec to return + * @param ctx {object} the runtime context for the spec + * @param callback {function(err,spec)} callback used to return the results (or error) + * @return {nothing} results returned via the callback parameter + */ + getSpec: function(env, id, ctx, callback) { + this.expandInstanceForEnv(env, {base: id}, ctx, function(err, obj) { + if (err) { + callback(err); + return; + } + if (env === 'client' && obj) { + delete obj.assets; + } + callback(null, obj); + }); + }, + + + /** + * Returns, via callback, the details of the mojit type. + * + * @method getType + * @param env {string} either "client" or "server" + * @param type {string} the mojit type + * @param ctx {object} the runtime context for the type + * @param callback {function(err,spec)} callback used to return the results (or error) + * @return {nothing} results returned via the callback parameter + */ + getType: function(env, type, ctx, callback) { + this.expandInstanceForEnv(env, {type: type}, ctx, function(err, obj) { + if (err) { + callback(err); + return; + } + if (env === 'client' && obj) { + delete obj.assets; + } + callback(null, obj); + }); + }, + + + /** + * This just calls expandInstanceForEnv() with `env` set to `server`. + * + * @method expandInstance + * @param instance {map} partial instance to expand + * @param ctx {object} the context + * @param cb {function(err,instance)} callback used to return the results (or error) + * @return {nothing} results returned via the callback parameter + */ + expandInstance: function(instance, ctx, cb) { + this.expandInstanceForEnv('server', instance, ctx, cb); + return; + }, + + + /** + * Returns details about a mojit type. + * + * TODO DOCS: use onHostMethod() and afterHostMethod() instead of AOP methods + * + * @method getMojitTypeDetails + * @param env {string} "client" or "server" + * @param ctx {object} the context + * @param mojitType {string} mojit type + * @param dest {object} object in which to place the results + * @return {object} returns the "dest" parameter, which has had details added to it + */ + getMojitTypeDetails: function(env, ctx, mojitType, dest) { + //logger.log('getMojitTypeDetails('+env+',ctx,'+mojitType+')'); + var ress, + r, + res, + engine, + engines = {}, // view engines + ctxKey, + module; + + if (!dest) { + dest = {}; + } + + if (!dest.models) { + dest.models = {}; + } + if (!dest.views) { + dest.views = {}; + } + + dest.definition = {}; + dest.defaults = {}; + + ress = this.getResources(env, ctx, { mojit: mojitType }); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + + if (res.type === 'config') { + if ('definition' === res.source.fs.basename) { + dest.definition = this.config.readConfigYCB(res.source.fs.fullPath, ctx); + } + if ('defaults' === res.source.fs.basename) { + dest.defaults = this.config.readConfigYCB(res.source.fs.fullPath, ctx); + } + } + + if (res.type === 'binder') { + if (!dest.views[res.name]) { + dest.views[res.name] = {}; + } + if (env === 'client') { + dest.views[res.name]['binder-path'] = res.url; + } else { + dest.views[res.name]['binder-path'] = res.source.fs.fullPath; + } + } + + if (res.type === 'controller') { + // We need the YUI Module name of the contoller so we can + // select a language for it + if (env === 'client') { + dest['controller-path'] = res.url; + } else { + dest['controller-path'] = res.source.fs.fullPath; + } + } + + if (res.type === 'model') { + dest.models[res.name] = true; + } + + if (res.type === 'view') { + if (!dest.views[res.name]) { + dest.views[res.name] = {}; + } + if (env === 'client') { + dest.views[res.name]['content-path'] = res.url; + } else { + dest.views[res.name]['content-path'] = res.source.fs.fullPath; + } + dest.views[res.name].engine = res.viewEngine; + engines[res.viewEngine] = true; + } + } + + // YUI AOP doesn't give plugins enough control, so use + // onHostMethod() and afterHostMethod(). + this.fire('getMojitTypeDetails', { + args: { + env: env, + ctx: ctx, + mojitType: mojitType + }, + mojit: dest + }); + return dest; + }, + + // TODO DOCS // TODO -- move to yui addon getYuiConfigFw: function(env, ctx) { @@ -880,7 +1049,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); return; } - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); + res.name = libpath.join(fs.subDirArray.join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); return res; } diff --git a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js index 3bef1e98a..6c4cdd11b 100644 --- a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js @@ -26,6 +26,7 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { this._mojits = {}; this._appRVs = []; this._mojitRVs = {}; + this.publish('getMojitTypeDetails', {emitFacade: true, preventable: false}); }, getStaticAppConfig: function() { @@ -67,7 +68,6 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { preloadResourceVersions: function() { // no-op - return true; }, _makeResource: function(pkg, mojit, type, name, affinity, yuiName) { @@ -278,6 +278,44 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { A.areSame('path/for/mojit--Y.common.ext/rollup.client.js', store._mojitRVs.Y[0].source.fs.rollupPath); A.areSame('/static/Y/not--yui.common.ext', store._mojitRVs.Y[1].url); A.isUndefined(store._mojitRVs.Y[1].source.fs.rollupPath); + }, + + + 'augment getMojitTypeDetails': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + + var store = new MockRS({ + root: fixtures, + appConfig: {} + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + var mojit = {}; + store.fire('getMojitTypeDetails', { + args: { + env: 'server', + ctx: {}, + mojitType: 'Foo' + }, + mojit: mojit + }); + A.areSame('/static/Foo/assets', mojit.assetsRoot); + + // honor empty prefix + store = new MockRS({ + root: fixtures, + appConfig: { staticHandling: {prefix:''} } + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + mojit = {}; + store.fire('getMojitTypeDetails', { + args: { + env: 'server', + ctx: {}, + mojitType: 'Foo' + }, + mojit: mojit + }); + A.areSame('/Foo/assets', mojit.assetsRoot); } diff --git a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js index b2a401f8e..ae311787d 100644 --- a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js @@ -21,6 +21,58 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { initializer: function(cfg) { this._config = cfg || {}; this.RVs = {}; + this._mojitResources = {}; // env: ctx: mojitType: list of resources + this._appResources = {}; // env: ctx: list of resources + this._mojits = {}; + this.publish('getMojitTypeDetails', {emitFacade: true, preventable: false}); + }, + + listAllMojits: function() { + return Object.keys(this._mojits); + }, + + getResources: function(env, ctx, filter) { + var source, + out = [], + r, + res, + k, + use; + + ctx = JSON.stringify(ctx); + if (filter.mojit) { + if (!this._mojitResources[env] || + !this._mojitResources[env][ctx] || + !this._mojitResources[env][ctx][filter.mojit]) { + return []; + } + source = this._mojitResources[env][ctx][filter.mojit]; + } else { + if (!this._appResources[env] || + !this._appResources[env][ctx]) { + return []; + } + source = this._appResources[env][ctx]; + } + // this is taken care of already, and will trip up mojit-level + // resources that are actually shared + delete filter.mojit; + for (r = 0; r < source.length; r += 1) { + res = source[r]; + use = true; + for (k in filter) { + if (filter.hasOwnProperty(k)) { + if (res[k] !== filter[k]) { + use = false; + break; + } + } + } + if (use) { + out.push(res); + } + } + return out; }, findResourceByConvention: function(source, mojitType) { @@ -33,6 +85,49 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { addResourceVersion: function(res) { this.RVs[[res.affinity, res.selector, res.id].join('/')] = res; + }, + + _makeResource: function(env, ctx, mojit, type, name, yuiName) { + if (mojit && mojit !== 'shared') { + this._mojits[mojit] = true; + } + var res = { + source: { + fs: { + fullPath: 'path/for/' + type + '--' + name + '.common.ext', + rootDir: 'path/for' + }, + pkg: { name: 'testing' } + }, + mojit: mojit, + type: type, + name: name, + id: type + '--' + name + } + if (yuiName) { + res.yui = { name: yuiName }; + } + ctx = JSON.stringify(ctx); + if (mojit) { + if (!this._mojitResources[env]) { + this._mojitResources[env] = {}; + } + if (!this._mojitResources[env][ctx]) { + this._mojitResources[env][ctx] = {}; + } + if (!this._mojitResources[env][ctx][mojit]) { + this._mojitResources[env][ctx][mojit] = []; + } + this._mojitResources[env][ctx][mojit].push(res); + } else { + if (!this._appResources[env]) { + this._appResources[env] = {}; + } + if (!this._appResources[env][ctx]) { + this._appResources[env][ctx] = []; + } + this._appResources[env][ctx].push(res); + } } }); @@ -214,6 +309,31 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { cmp(res.source, source); A.isNotUndefined(res.yui); A.areSame('X', res.yui.name); + }, + + + 'augment getMojitTypeDetails': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store._makeResource('server', {}, 'Foo', 'binder', 'index', 'FooBinderIndex'); + store._makeResource('server', {}, 'Foo', 'binder', 'list', 'FooBinderList'); + store._makeResource('server', {}, 'Foo', 'controller', 'controller', 'FooController'); + var mojit = { views: {} }; + store.fire('getMojitTypeDetails', { + args: { + env: 'server', + ctx: {}, + mojitType: 'Foo' + }, + mojit: mojit + }); + A.isNotUndefined(mojit.views.index); + A.areSame('FooBinderIndex', mojit.views.index['binder-module']); + A.isNotUndefined(mojit.views.list); + A.areSame('FooBinderList', mojit.views.list['binder-module']); + A.areSame('FooController', mojit['controller-module']); } diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 58bc3c3a8..8ef673344 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -364,7 +364,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var instance = {type:'TestMojit2'}; store.expandInstance(instance, {}, function(err, instance){ - A.isNotUndefined(instance.controller); + A.isNotUndefined(instance['controller-path']); A.areSame('/static/TestMojit2/assets', instance.assetsRoot); A.isNotUndefined(instance.yui.config.modules.test_mojit_2); }); @@ -463,7 +463,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { store.preload(); var instance = { type: 'soloMojit' }; store.expandInstance(instance, {}, function(err, instance) { - A.isNotUndefined(instance.controller); + A.isNotUndefined(instance['controller-path']); }); }, @@ -778,7 +778,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var spec = { type: 'PagedFlickr' }; var ctx = { device: 'iphone' }; store.expandInstance(spec, ctx, function(err, instance) { - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.iphone.js'), instance.controller); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.iphone.js'), instance['controller-path']); }); }, @@ -878,7 +878,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var details = {}; store.getMojitTypeDetails('server', {}, 'a', details); - A.isNotNull(details.controller.match(/a\/foo\/controller\.server\.js$/), 'controller should not be null'); + A.isNotNull(details['controller-path'].match(/a\/foo\/controller\.server\.js$/), 'controller should not be null'); }, 'find and parse resources by convention': function() { From e5734ea186ab32e8a4f142bd3a761777dd85add9 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 12 Jun 2012 13:34:49 -0700 Subject: [PATCH 031/119] a posl should only contain a selector that actually exists in the app --- source/lib/app/addons/rs/selector.server.js | 2 +- source/lib/store.server.js | 7 +++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js index f6e130dd7..63f7baebe 100644 --- a/source/lib/app/addons/rs/selector.server.js +++ b/source/lib/app/addons/rs/selector.server.js @@ -51,7 +51,7 @@ YUI.add('addon-rs-selector', function(Y, NAME) { parts = this._appConfigYCB.readNoMerge(ctx, {}); for (p = 0; p < parts.length; p += 1) { part = parts[p]; - if (part.selector) { + if (part.selector && this.rs.selectors[part.selector]) { sels.unshift(part.selector); } } diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 8d5fe2157..bbbc7bfc6 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -143,6 +143,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._appResources = {}; // env: posl: array of resources this._mojitResources = {}; // env: posl: mojitType: array of resources + // all selectors that are actually in the app + // hash: key is selector, value is just boolean true + this.selectors = {}; + // Y.Plugin AOP doesn't allow afterHostMethod() callbacks to // modify the results, so we fire an event instead. this.publish('getMojitTypeDetails', {emitFacade: true, preventable: false}); @@ -1096,6 +1100,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @return {nothing} */ addResourceVersion: function(res) { + if (res.selector) { + this.selectors[res.selector] = true; + } res.affinity = new Affinity(res.affinity); if (res.mojit) { if (!this._mojitRVs[res.mojit]) { From e7ee3e18f478fa9ebd55f85b592190508e061c46 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 12 Jun 2012 22:49:07 -0700 Subject: [PATCH 032/119] first real version of expanceInstanceForEnv(), more tests pass but more to go --- source/lib/store.server.js | 67 +++++++- .../lib/tests/autoload/store.server-tests.js | 150 ++++++------------ .../lib/tests/fixtures/gsg5/application.json | 4 + .../lib/tests/fixtures/store/application.json | 5 + 4 files changed, 124 insertions(+), 102 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index bbbc7bfc6..8458f9d74 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -424,6 +424,40 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + expandInstanceForEnv: function(env, instance, ctx, cb) { + var spec, + typeDetails, + config; + + // TODO: should this be done here, or somewhere else? + ctx.runtime = env; + + try { + spec = this._expandSpec(ctx, instance); + } catch (err) { + return cb(err); + } + spec.config = spec.config || {}; + spec.action = spec.action || 'index'; + if (!spec.instanceId) { + spec.instanceId = Y.guid(); + } + + try { + this.getMojitTypeDetails(env, ctx, spec.type, spec); + } catch (err) { + return cb(err); + } + if (spec.defaults && spec.defaults.config) { + config = this.cloneObj(spec.defaults.config); + this.mergeRecursive(config, spec.config); + spec.config = config; + } + + cb(null, spec); + }, + + /** * Returns details about a mojit type. * @@ -437,7 +471,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @return {object} returns the "dest" parameter, which has had details added to it */ getMojitTypeDetails: function(env, ctx, mojitType, dest) { - //logger.log('getMojitTypeDetails('+env+',ctx,'+mojitType+')'); + //Y.log('getMojitTypeDetails('+env+', '+JSON.stringify(ctx)+', '+mojitType+')', 'debug', NAME); var ress, r, res, @@ -450,6 +484,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { dest = {}; } + if (!dest.assets) { + dest.assets = {}; + } if (!dest.models) { dest.models = {}; } @@ -473,10 +510,19 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } } + if (res.type === 'asset') { + if (env === 'client') { + dest.assets[res.name + res.source.fs.ext] = res.url; + } else { + dest.assets[res.name + res.source.fs.ext] = res.source.fs.fullPath; + } + } + if (res.type === 'binder') { if (!dest.views[res.name]) { dest.views[res.name] = {}; } + dest.views[res.name]['binder-url'] = res.url; if (env === 'client') { dest.views[res.name]['binder-path'] = res.url; } else { @@ -1077,7 +1123,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.log('invalid view filename. skipping ' + fs.fullPath, 'warn', NAME); return; } - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); + res.name = libpath.join(fs.subDirArray.join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); return res; } @@ -1189,6 +1235,23 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // PRIVATE METHODS + // TODO DOCS + _expandSpec: function(ctx, spec) { + if (!spec.base) { + return spec; + } + // The base will need to carry its ID with it. + spec.id = spec.base; + var appConfig = this.getAppConfig(ctx); + var base = appConfig.specs[spec.base]; + if (!base) { + throw new Error('Unknown base of "' + spec.base + '"'); + } + delete spec.base; + return this.mergeRecursive(this._expandSpec(ctx, base), spec); + }, + + /** * preloads metadata about resources in a package * (but not subpackages in its node_modules/) diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 8ef673344..892ef7292 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -132,29 +132,10 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance) { A.areSame(4, Y.Object.keys(instance.models).length); - A.areSame(libpath.join(fixtures, 'models/flickr.common.js'), instance.models['flickr']); - A.areSame(libpath.join(fixtures, 'mojits/test_applevel/models/test_applevel.server.js'), instance.models['test_applevel']); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/models/test_1.server.js'), instance.models['test_1']); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/models/test_2.server.js'), instance.models['test_2']); - - A.areSame(4, Y.Object.keys(instance.modelYUIModuleNames).length); - A.isTrue(instance.modelYUIModuleNames['ModelFlickr']); - A.isTrue(instance.modelYUIModuleNames['test_applevelModel']); - A.isTrue(instance.modelYUIModuleNames['test_mojit_1_model_test_1']); - A.isTrue(instance.modelYUIModuleNames['test_mojit_1_model_test_2']); - }); - }, - - 'server mojit instance actions': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var instance = {type:'test_mojit_1'}; - store.expandInstance(instance, {}, function(err, instance) { - var actions = instance.actions.sort(); - A.areSame(2, actions.length); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/actions/test_1.server.js'), actions[0]); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/actions/test_2.server.js'), actions[1]); + A.isTrue(instance.models['flickr']); + A.isTrue(instance.models['test_applevel']); + A.isTrue(instance.models['test_1']); + A.isTrue(instance.models['test_2']); }); }, @@ -429,26 +410,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }); }, - 'server mojit view index1.mu.html is loaded correctly': function(){ - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var instance = {type:'TestMojit3'}; - store.expandInstance(instance, {device:'forotheriphone'}, function(err, instance){ - A.areSame('index1.forotheriphone.mu.html', instance.views.index1['content-path'].split('/').pop()); - }); - }, - - 'server mojit view index1.iphone.mu.html is loaded correctly': function(){ - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var instance = {type:'TestMojit3'}; - store.expandInstance(instance, {device:'otheriphone'}, function(err, instance){ - A.areSame('index1.otheriphone.mu.html', instance.views.index1['content-path'].split('/').pop()); - }); - }, - 'app-level mojits': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); @@ -582,16 +543,30 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { store.preload(); store.expandInstance(spec, {}, function(err, instance) { A.isNotUndefined(err); + A.areSame('Unknown base of "nonexistant"', err.message); A.isUndefined(instance); }); }, 'getAppConfig() returns contextualized info': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }), - config, - context = { runtime: 'server' }; - store.preload(context); - config = store.getAppConfig(null); + var context = { runtime: 'server' }, + store = new Y.mojito.ResourceStore({ root: fixtures }), + config; + store.preload(); + config = store.getAppConfig(context); + A.isObject(config); + A.areSame('testVal1-server', config.testKey1, 'testKey1 wasnt contextualized to the server'); + A.areSame('testVal2', config.testKey2, 'testKey2 gotten from the wrong context'); + A.areSame('portended', config.pathos, 'missing contextualized config'); + A.isUndefined(config.testKey4, 'testKey4 gotten from the wrong context'); + }, + + 'static context is really static': function() { + var context = { runtime: 'server' }, + store = new Y.mojito.ResourceStore({ root: fixtures, context: context }), + config; + store.preload(); + config = store.getAppConfig(); A.isObject(config); A.areSame('testVal1-server', config.testKey1, 'testKey1 wasnt contextualized to the server'); A.areSame('testVal2', config.testKey2, 'testKey2 gotten from the wrong context'); @@ -650,32 +625,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { AA.contains('soloMojit', list); }, - 'call getAllMojits()': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var mojits = store.getAllMojits('server', {}); - A.areSame(9, Object.keys(mojits).length, 'Found the wrong number of mojits'); - A.isObject(mojits.DaliProxy); - // DaliProxy has no static assets - A.isUndefined(mojits.DaliProxy.assetsRoot); - A.isObject(mojits.HTMLFrameMojit); - // HTMLFrameMojit has no static assets - A.isUndefined(mojits.HTMLFrameMojit.assetsRoot); - A.isObject(mojits.LazyLoad); - A.isObject(mojits.inlinecss); - A.areSame('/static/inlinecss/assets', mojits.inlinecss.assetsRoot, "'/static/inlinecss/assets', mojits.inlinecss.assetsRoot"); - A.isObject(mojits.rollups); - A.areSame('/static/rollups/assets', mojits.rollups.assetsRoot, "'/static/rollups/assets', mojits.rollups.assetsRoot"); - A.isObject(mojits.test_mojit_1); - A.areSame('/static/test_mojit_1/assets', mojits.test_mojit_1.assetsRoot, "'/static/test_mojit_1/assets', mojits.test_mojit_1.assetsRoot"); - A.isObject(mojits.TestMojit2); - A.areSame('/static/TestMojit2/assets', mojits.TestMojit2.assetsRoot, "'/static/TestMojit2/assets', mojits.TestMojit2.assetsRoot"); - A.isObject(mojits.TestMojit3); - A.areSame('/static/TestMojit3/assets', mojits.TestMojit3.assetsRoot, "'/static/TestMojit3/assets', mojits.TestMojit3.assetsRoot"); - A.isObject(mojits.soloMojit); - A.areSame('/static/soloMojit/assets', mojits.soloMojit.assetsRoot, "'/static/soloMojit/assets', mojits.soloMojit.assetsRoot"); - }, - 'stuff with ctx{lang:}, in language fallback': function() { var fixtures = libpath.join(__dirname, '../fixtures/gsg5'), store = new Y.mojito.ResourceStore({ root: fixtures }), @@ -843,8 +792,9 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { '_skipBadPath() does just that': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); - A.areSame(true, store._skipBadPath({ ext: '.js~' })); - A.areSame(false, store._skipBadPath({ ext: '.js' })); + A.isTrue(store._skipBadPath({ isFile: true, ext: '.js~' }), 'need to skip bad file naems'); + A.isFalse(store._skipBadPath({ isFile: false, ext: '.js~' }), 'need to not-skip bad directory names'); + A.isFalse(store._skipBadPath({ isFile: true, ext: '.js' }), 'need to not-skip good file names'); }, 'load node_modules': function() { @@ -886,8 +836,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { store = new Y.mojito.ResourceStore({ root: fixtures }); // fake out some parts of preload(), which we're trying to avoid - store._fwConfig = store._readConfigJSON(libpath.join(mojitoRoot, 'config.json')); - store._appConfigStatic = store._readAppConfigStatic(); + store._fwConfig = store.config.readConfigJSON(libpath.join(mojitoRoot, 'config.json')); + store._appConfigStatic = store.getStaticAppConfig(); var dir = libpath.join(__dirname, '../fixtures/conventions'); var pkg = { name: 'test', version: '6.6.6' }; @@ -899,7 +849,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { res = ress[r]; A.isNotUndefined(res.id, 'no resource id'); switch (res.id) { - case 'action-x': + case 'action--x': A.areSame(pkg, res.pkg); A.areSame('action', res.type); A.areSame('x', res.name); @@ -919,11 +869,11 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('x', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; - case 'action-y/z': + case 'action--y/z': A.areSame(pkg, res.pkg); A.areSame('action', res.type); A.areSame('y/z', res.name); @@ -955,7 +905,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('x', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; @@ -966,7 +916,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('y', res.name); A.areSame('y', res.shortPath); break; - case 'asset-x.css': + case 'asset--x.css': A.areSame(pkg, res.pkg); A.areSame('asset', res.type); A.areSame('css', res.assetType); @@ -986,11 +936,11 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('x', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; - case 'asset-y/z.css': + case 'asset--y/z.css': A.areSame(pkg, res.pkg); A.areSame('asset', res.type); A.areSame('css', res.assetType); @@ -1010,11 +960,11 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('z', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; - case 'binder-x': + case 'binder--x': A.areSame(pkg, res.pkg); A.areSame('binder', res.type); A.areSame('x', res.name); @@ -1034,23 +984,23 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('x', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; - case 'command-x': + case 'command--x': A.areSame(pkg, res.pkg); A.areSame('command', res.type); A.areSame('x', res.name); A.areSame('x.js', res.shortPath); break; - case 'config-config': + case 'config--config': A.areSame(pkg, res.pkg); A.areSame('config', res.type); A.areSame('config', res.name); A.areSame('config.json', res.shortPath); break; - case 'controller': + case 'controller--controller': A.areSame(pkg, res.pkg); A.areSame('controller', res.type); A.areSame('controller', res.name); @@ -1070,31 +1020,31 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('controller', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; - case 'middleware-x': + case 'middleware--x': A.areSame(pkg, res.pkg); A.areSame('middleware', res.type); A.areSame('x', res.name); A.areSame('x.js', res.shortPath); break; - case 'spec-default': + case 'spec--default': A.areSame(pkg, res.pkg); A.areSame('spec', res.type); A.areSame('default', res.name); A.areSame('testing', res.specName); A.areSame('default.json', res.shortPath); break; - case 'spec-x': + case 'spec--x': A.areSame(pkg, res.pkg); A.areSame('spec', res.type); A.areSame('x', res.name); A.areSame('testing:x', res.specName); A.areSame('x.json', res.shortPath); break; - case 'view-x': + case 'view--x': A.areSame(pkg, res.pkg); A.areSame('view', res.type); A.areSame('x', res.name); @@ -1115,7 +1065,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('x', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; @@ -1186,7 +1136,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('m', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; @@ -1210,7 +1160,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('x', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; @@ -1234,7 +1184,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame('z', res.pathParts.shortFile); break; default: - A.fail('unknown resource ' + res.fsPath); + A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; diff --git a/source/lib/tests/fixtures/gsg5/application.json b/source/lib/tests/fixtures/gsg5/application.json index a9421a3f9..2653c7486 100644 --- a/source/lib/tests/fixtures/gsg5/application.json +++ b/source/lib/tests/fixtures/gsg5/application.json @@ -50,5 +50,9 @@ "type": "FlickrDetail" } } + }, + { + "settings": [ "device:iphone" ], + "selector": "iphone" } ] diff --git a/source/lib/tests/fixtures/store/application.json b/source/lib/tests/fixtures/store/application.json index 031942067..75ccabd65 100644 --- a/source/lib/tests/fixtures/store/application.json +++ b/source/lib/tests/fixtures/store/application.json @@ -31,6 +31,11 @@ "testKey2": "testVal2-client", "testKey4": "testVal4-client" }, + { + "settings": [ "device:iphone" ], + + "selector": "iphone" + }, { "settings": [ "device:android" ], From 17ee9385735d3aebeb753fc6b0f0b270537e373e Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 08:35:46 -0700 Subject: [PATCH 033/119] fixed addon-rs-selector unit test --- .../tests/autoload/app/addons/rs/selector-tests.server.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js index ca261ab25..e0e8807d6 100644 --- a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js @@ -19,6 +19,13 @@ YUI.add('mojito-addon-rs-selector-tests', function(Y, NAME) { Y.extend(MockRS, Y.Base, { initializer: function(cfg) { this._config = cfg || {}; + this.selectors = { + 'devdroid': true, + 'droid': true, + 'shelves': true, + 'right': true, + '*': true + }; }, cloneObj: function(o) { return Y.clone(o); From 7b5e08e8357a70c66b4cb1c481c91e6c5465949e Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 10:53:50 -0700 Subject: [PATCH 034/119] missing dependency on json-stringify --- source/lib/app/autoload/resource-store-adapter.common.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/source/lib/app/autoload/resource-store-adapter.common.js b/source/lib/app/autoload/resource-store-adapter.common.js index 6207119fa..684b23b18 100644 --- a/source/lib/app/autoload/resource-store-adapter.common.js +++ b/source/lib/app/autoload/resource-store-adapter.common.js @@ -231,5 +231,6 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { }; }, '0.1.0', {requires: [ - 'mojito-util' + 'mojito-util', + 'json-stringify' ]}); From f6f0c2f1f8d5f781e16659b0d9af8059179a764f Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 11:25:11 -0700 Subject: [PATCH 035/119] moved store.getYuiConfig*() to addon-rs-yui --- source/lib/app/addons/ac/deploy.server.js | 4 +- source/lib/app/addons/rs/yui.server.js | 89 +++++++++++++++ .../autoload/resource-store-adapter.common.js | 18 ---- source/lib/app/commands/gv.js | 6 +- source/lib/app/commands/test.js | 12 +-- source/lib/index.js | 4 +- source/lib/store.server.js | 102 ++---------------- .../app/addons/ac/deploy-tests.server.js | 30 ++++-- 8 files changed, 128 insertions(+), 137 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index 792623a27..d0aaa010d 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -236,9 +236,9 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { // fw & app scripts. if (useOnDemand) { // add all framework-level and app-level code - this.addScripts('bottom', store.getYuiConfigFw('client', + this.addScripts('bottom', store.store.yui.getConfigFw('client', contextClient).modules); - this.addScripts('bottom', store.getYuiConfigApp('client', + this.addScripts('bottom', store.store.yui.getConfigApp('client', contextClient).modules); } diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index d35bb1b44..a870bc467 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -38,6 +38,95 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, + // TODO DOCS + getConfigFw: function(env, ctx) { + var r, + res, + ress, + modules = {}; + ress = this.rs.getResources(env, ctx, { mojit: 'shared' }); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (!res.yui || !res.yui.name) { + continue; + } + if ('mojito' !== res.source.pkg.name) { + continue; + } + modules[res.yui.name] = { + fullpath: ('client' === env) ? + res.url : + res.source.fs.fullPath, + requires: + (res.yui.meta && res.yui.meta.requires) || [] + }; + } + return { modules: modules }; + }, + + + // TODO DOCS + getConfigApp: function(env, ctx) { + var r, + res, + ress, + modules = {}; + ress = this.rs.getResources(env, ctx, { mojit: 'shared' }); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (!res.yui || !res.yui.name) { + continue; + } + if ('mojito' === res.source.pkg.name) { + continue; + } + modules[res.yui.name] = { + fullpath: ('client' === env) ? + res.url : + res.source.fs.fullPath, + requires: + (res.yui.meta && res.yui.meta.requires) || [] + }; + } + return { modules: modules }; + }, + + + // TODO DOCS + getConfigAllMojits: function(env, ctx) { + var m, + mojit, + mojits, + r, + res, + ress, + modules = {}; + mojits = this.rs.listAllMojits(); + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; + ress = this.rs.getResources(env, ctx, { mojit: mojit }); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (!res.yui || !res.yui.name) { + continue; + } + if (res.mojit !== mojit) { + // generally only happens if res.mojit is 'shared' + continue; + } + modules[res.yui.name] = { + fullpath: ('client' === env) ? + res.url : + res.source.fs.fullPath, + requires: + (res.yui.meta && res.yui.meta.requires) || [] + }; + } + } + return { modules: modules }; + }, + + findResourceByConvention: function(source, mojitType) { var fs = source.fs; diff --git a/source/lib/app/autoload/resource-store-adapter.common.js b/source/lib/app/autoload/resource-store-adapter.common.js index 684b23b18..bce3e64a1 100644 --- a/source/lib/app/autoload/resource-store-adapter.common.js +++ b/source/lib/app/autoload/resource-store-adapter.common.js @@ -188,24 +188,6 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { }, - getYuiConfigAllMojits: function(env, ctx) { - //logger.log('getYuiConfigAllMojits', 'warn', NAME); - return this.store.getYuiConfigAllMojits(env, ctx); - }, - - - getYuiConfigApp: function(env, ctx) { - //logger.log('getYuiConfigApp', 'warn', NAME); - return this.store.getYuiConfigApp(env, ctx); - }, - - - getYuiConfigFw: function(env, ctx) { - //logger.log('getYuiConfigFw', 'warn', NAME); - return this.store.getYuiConfigFw(env, ctx); - }, - - serializeClientStore: function(ctx, instances) { //logger.log('serializeClientStore', 'warn', NAME); return this.store.serializeClientStore(ctx, instances); diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index a2f83c7ea..1986b245d 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -110,10 +110,10 @@ run = function(params, options) { store = new ResourceStore(process.cwd()); store.preload(); if (options.framework) { - parseReqs(reqs, store.getYuiConfigFw(env, {}).modules); + parseReqs(reqs, store.yui.getConfigFw(env, {}).modules); } - parseReqs(reqs, store.getYuiConfigApp(env, {}).modules); - parseReqs(reqs, store.getYuiConfigAllMojits(env, {}).modules); + parseReqs(reqs, store.yui.getConfigApp(env, {}).modules); + parseReqs(reqs, store.yui.getConfigAllMojits(env, {}).modules); // generate graph resultsFile = libpath.join(resultsDir, 'yui.' + env + '.dot'); diff --git a/source/lib/app/commands/test.js b/source/lib/app/commands/test.js index 7afc30413..f1129d8e5 100644 --- a/source/lib/app/commands/test.js +++ b/source/lib/app/commands/test.js @@ -118,9 +118,9 @@ function collectRunResults(results) { } function configureYUI(Y, store) { - Y.applyConfig(store.getYuiConfigFw('server', {})); - Y.applyConfig(store.getYuiConfigApp('server', {})); - Y.applyConfig(store.getYuiConfigAllMojits('server', {})); + Y.applyConfig(store.yui.getConfigFw('server', {})); + Y.applyConfig(store.yui.getConfigApp('server', {})); + Y.applyConfig(store.yui.getConfigAllMojits('server', {})); } @@ -706,11 +706,11 @@ runTests = function(opts) { configureYUI(YUI, store); if (testType === 'fw') { - testConfigs = store.getYuiConfigApp('server', {}).modules; + testConfigs = store.yui.getConfigApp('server', {}).modules; } else if (testType === 'app') { testConfigs = merge( - store.getYuiConfigApp('server', {}).modules, - store.getYuiConfigAllMojits('server', {}).modules + store.yui.getConfigApp('server', {}).modules, + store.yui.getConfigAllMojits('server', {}).modules ); } } diff --git a/source/lib/index.js b/source/lib/index.js index 79cf858e4..32160766f 100644 --- a/source/lib/index.js +++ b/source/lib/index.js @@ -50,9 +50,9 @@ function configureYUI(Y, store, load) { var fw, app, module; - fw = store.getYuiConfigFw('server', {}); + fw = store.yui.getConfigFw('server', {}); Y.applyConfig(fw); - app = store.getYuiConfigApp('server', {}); + app = store.yui.getConfigApp('server', {}); Y.applyConfig(app); // also pre-load fw and app modules for (module in fw.modules) { diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 8458f9d74..fbea9f455 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -445,8 +445,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { try { this.getMojitTypeDetails(env, ctx, spec.type, spec); - } catch (err) { - return cb(err); + } catch (err2) { + return cb(err2); } if (spec.defaults && spec.defaults.config) { config = this.cloneObj(spec.defaults.config); @@ -572,98 +572,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, - // TODO DOCS - // TODO -- move to yui addon - getYuiConfigFw: function(env, ctx) { - var r, - res, - ress, - modules = {}; - ress = this.getResources(env, ctx, { mojit: 'shared' }); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if (!res.yui || !res.yui.name) { - continue; - } - if ('mojito' !== res.source.pkg.name) { - continue; - } - modules[res.yui.name] = { - fullpath: ('client' === env) ? - res.staticHandlerURL : - res.source.fs.fullPath, - requires: - (res.yui.meta && res.yui.meta.requires) || [] - }; - } - return { modules: modules }; - }, - - - // TODO DOCS - // TODO -- move to yui addon - getYuiConfigApp: function(env, ctx) { - var r, - res, - ress, - modules = {}; - ress = this.getResources(env, ctx, { mojit: 'shared' }); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if (!res.yui || !res.yui.name) { - continue; - } - if ('mojito' === res.source.pkg.name) { - continue; - } - modules[res.yui.name] = { - fullpath: ('client' === env) ? - res.staticHandlerURL : - res.source.fs.fullPath, - requires: - (res.yui.meta && res.yui.meta.requires) || [] - }; - } - return { modules: modules }; - }, - - - // TODO DOCS - // TODO -- move to yui addon - getYuiConfigAllMojits: function(env, ctx) { - var m, - mojit, - mojits, - r, - res, - ress, - modules = {}; - mojits = this.listAllMojits(); - for (m = 0; m < mojits.length; m += 1) { - mojit = mojits[m]; - ress = this.getResources(env, ctx, { mojit: mojit }); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if (!res.yui || !res.yui.name) { - continue; - } - if (res.mojit !== mojit) { - // generally only happens if res.mojit is 'shared' - continue; - } - modules[res.yui.name] = { - fullpath: ('client' === env) ? - res.staticHandlerURL : - res.source.fs.fullPath, - requires: - (res.yui.meta && res.yui.meta.requires) || [] - }; - } - } - return { modules: modules }; - }, - - /** * Returns the routes configured in the application. * @@ -1237,13 +1145,15 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // TODO DOCS _expandSpec: function(ctx, spec) { + var appConfig, + base; if (!spec.base) { return spec; } // The base will need to carry its ID with it. spec.id = spec.base; - var appConfig = this.getAppConfig(ctx); - var base = appConfig.specs[spec.base]; + appConfig = this.getAppConfig(ctx); + base = appConfig.specs[spec.base]; if (!base) { throw new Error('Unknown base of "' + spec.base + '"'); } diff --git a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js index f93e3b1eb..fb24b322b 100644 --- a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js +++ b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js @@ -53,10 +53,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { store: { getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; + }, + yui: { + getConfigFw: function() { return {}; }, + getConfigApp: function() { return {}; } } }, - getYuiConfigFw: function() { return {}; }, - getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { return 'path'; } @@ -137,10 +139,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { store: { getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; + }, + yui: { + getConfigFw: function() { return {}; }, + getConfigApp: function() { return {}; } } }, - getYuiConfigFw: function() { return {}; }, - getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { return 'path'; } @@ -229,10 +233,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { store: { getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; + }, + yui: { + getConfigFw: function() { return {}; }, + getConfigApp: function() { return {}; } } }, - getYuiConfigFw: function() { return {}; }, - getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { return 'path'; } @@ -312,10 +318,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { store: { getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; + }, + yui: { + getConfigFw: function() { return {}; }, + getConfigApp: function() { return {}; } } }, - getYuiConfigFw: function() { return {}; }, - getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { return 'path'; } @@ -395,10 +403,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { store: { getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; + }, + yui: { + getConfigFw: function() { return {}; }, + getConfigApp: function() { return {}; } } }, - getYuiConfigFw: function() { return {}; }, - getYuiConfigApp: function() { return {}; }, fileFromStaticHandlerURL: function() { return 'path'; } From 310ffd849841a9ce22cff4f0b8305d3857d45c84 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 11:27:01 -0700 Subject: [PATCH 036/119] lint --- source/lib/app/commands/start.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/app/commands/start.js b/source/lib/app/commands/start.js index 83b9a6b7d..65ebc5ff5 100644 --- a/source/lib/app/commands/start.js +++ b/source/lib/app/commands/start.js @@ -10,7 +10,7 @@ var path = require('path'), utils = require(path.join(__dirname, '../../management/utils')), - fs = require('fs'); + fs = require('fs'), Y = require('yui').YUI({useSync: true}).use('json-parse', 'json-stringify'); Y.applyConfig({useSync: false}); From df5c010248d6812fd1375b5d6802c5ab56c0a359 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 11:41:57 -0700 Subject: [PATCH 037/119] store.setLogger() is gone --- .../lib/tests/autoload/store.server-tests.js | 47 ++++++++++++++++--- 1 file changed, 40 insertions(+), 7 deletions(-) diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 984d85923..2a443c978 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -13,6 +13,32 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A = YUITest.Assert, AA = YUITest.ArrayAssert; + + function cmp(x, y, msg) { + if (Y.Lang.isArray(x)) { + A.isArray(x, msg || 'first arg should be an array'); + A.isArray(y, msg || 'second arg should be an array'); + A.areSame(x.length, y.length, msg || 'arrays are different lengths'); + for (var i = 0; i < x.length; i += 1) { + cmp(x[i], y[i], msg); + } + return; + } + if (Y.Lang.isObject(x)) { + A.isObject(x, msg || 'first arg should be an object'); + A.isObject(y, msg || 'second arg should be an object'); + A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); + for (var i in x) { + if (x.hasOwnProperty(i)) { + cmp(x[i], y[i], msg); + } + } + return; + } + A.areSame(x, y, msg || 'args should be the same'); + } + + suite.add(new YUITest.TestCase({ name: 'Store tests', @@ -503,16 +529,23 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areEqual(2, found); }, - 'multi preload, and setLogger()': function() { + 'multi preload': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); - var logsBefore, logs = 0; - store.setLogger({ log: function() { - logs++; - } }); store.preload(); - logsBefore = logs; + var pre = { + appRVs: Y.clone(store._appRVs, true), + mojitRVs: Y.clone(store._mojitRVs, true), + appResources: Y.clone(store._appResources, true), + mojitResources: Y.clone(store._mojitResources, true) + }; store.preload(); - A.areSame(logsBefore, logs); + var post = { + appRVs: Y.clone(store._appRVs, true), + mojitRVs: Y.clone(store._mojitRVs, true), + appResources: Y.clone(store._appResources, true), + mojitResources: Y.clone(store._mojitResources, true) + }; + cmp(post, pre); }, 'call getSpec()': function() { From 4300213e8854d2ef9e76a41c1400d5526a7f985a Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 11:50:52 -0700 Subject: [PATCH 038/119] store._root is spelled differently now --- source/lib/app/commands/build.js | 12 ++++++------ source/lib/app/commands/compile.js | 2 +- source/lib/app/middleware/mojito-handler-static.js | 2 +- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 9a9dcfcbf..ed51f4d80 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -91,18 +91,18 @@ exports.run = function(params, options, callback) { if (params[1] && params[1][0] === '/') { destination = libpath.join(params[1]); } else if (params[1]) { - destination = libpath.join(store._root, params[1]); + destination = libpath.join(store._config.root, params[1]); } else { - destination = libpath.join(store._root, 'artifacts/builds', type); + destination = libpath.join(store._config.root, 'artifacts/builds', type); } // Are we in a Mojito App? - utils.isMojitoApp(store._root, exports.usage, true); + utils.isMojitoApp(store._config.root, exports.usage, true); // TODO: probably should try to use store to read appConfig try { - appConfig = Y.JSON.parse(String(fs.readFileSync(libpath.join(store._root, - 'application.json')))); + appConfig = Y.JSON.parse(String(fs.readFileSync(libpath.join( + store._config.root, 'application.json')))); appConfig = appConfig[0]; // Is there a "builds" section for this "type" in the appConfig @@ -172,7 +172,7 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, tunnelPrefix = appConfig.tunnelPrefix || '/tunnel'; console.log('Building a "' + type + '" of the Mojito application at "' + - store._root + '"'); + store._config.root + '"'); console.log('...'); diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 854fb1d13..825b40d18 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -111,7 +111,7 @@ run = function(params, options, callback) { // TODO: don't assign to a parameter. options = options || {}; - utils.isMojitoApp(store._root, exports.usage, true); + utils.isMojitoApp(store._config.root, exports.usage, true); if (options.context) { // TODO: parseURL. diff --git a/source/lib/app/middleware/mojito-handler-static.js b/source/lib/app/middleware/mojito-handler-static.js index 78a9225b2..6dc3a1a6e 100644 --- a/source/lib/app/middleware/mojito-handler-static.js +++ b/source/lib/app/middleware/mojito-handler-static.js @@ -200,7 +200,7 @@ function staticProvider(store, globalLogger) { filename = store.fileFromStaticHandlerURL(path); // TODO: [Issue 80] remove this for performance if ((!filename) && (path === '/favicon.ico')) { - filename = pa.join(store._root, 'assets', path); + filename = pa.join(store._config.root, 'assets', path); } if (!filename) { From 9a1f88951925f59329c6243fcd0348df6fe7e290 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 11:52:33 -0700 Subject: [PATCH 039/119] done --- source/lib/app/autoload/action-context.common.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/lib/app/autoload/action-context.common.js b/source/lib/app/autoload/action-context.common.js index ecfc86672..0d6103538 100644 --- a/source/lib/app/autoload/action-context.common.js +++ b/source/lib/app/autoload/action-context.common.js @@ -274,8 +274,6 @@ YUI.add('mojito-action-context', function(Y, NAME) { self._dispatch.apply(self, arguments); }; - // TODO: should rework to be 'getAppConfig()' and 'getAppRoutes()' and - // not property access through a hash. this.app = { config: store.getAppConfig(this.context), routes: store.getRoutes(this.context) From eaf657afaf16df027623174b75374e6d932ccc6c Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 12:05:02 -0700 Subject: [PATCH 040/119] fixed up call to store.getAppConfig() --- .../app/autoload/resource-store-adapter-tests.common.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js index 070328421..d6fa6dc4e 100644 --- a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js +++ b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js @@ -34,7 +34,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { resourceStore.preload(); store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var config = store.getAppConfig(null, 'application'); + var config = store.getAppConfig(null); A.isTrue(config.testKey1 === 'testVal1'); }, From 74a3cf72c6effbef04783dbefac9fa00cf45a267 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 13 Jun 2012 13:28:06 -0700 Subject: [PATCH 041/119] store.getAllMojits() no longer exists --- source/lib/app/commands/compile.js | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 825b40d18..7b71a8752 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -506,7 +506,7 @@ compile.views = function(context, options, callback) { ' views...'); // Get all the Mojits - mojits = store.getAllMojits('server', context); + mojits = getAllMojits(store, 'server', context); if (options.mojit && !mojits[options.mojit]) { callback('Unknown "' + options.mojit + '"'); @@ -630,7 +630,7 @@ compile.json = function(context, options, callback) { store.preload(); // Get all the Mojits - mojits = store.getAllMojits('server', context); + mojits = getAllMojits(store, 'server', context); mojitNames = Object.keys(mojits); total = mojitNames.length; @@ -944,6 +944,22 @@ removeFile = function(file) { }; +// Returns details about all the mojits in the application. +function getAllMojits(store, env, ctx) +{ + var details = {}, + mojits, + m, + mojit; + mojits = store.listAllMojits(); + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; + details[mojit] = store.getMojitTypeDetails(env, ctx, mojit); + } + return details; +} + + /* * Used for writing out YUI modules containing cached content */ From a49fb86416f661ab8626160edff6a31f8b17c7df Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 14 Jun 2012 10:32:15 -0700 Subject: [PATCH 042/119] store._staticURLs and store.fileFromStaticHandlerURL() now part of addon-rs-url --- source/lib/app/addons/ac/deploy.server.js | 13 ++-- source/lib/app/addons/rs/url.server.js | 12 ++++ .../autoload/resource-store-adapter.common.js | 7 --- source/lib/app/commands/build.js | 8 ++- source/lib/app/commands/compile.js | 8 ++- .../app/middleware/mojito-handler-static.js | 2 +- .../app/addons/ac/deploy-tests.server.js | 40 ++++++++----- .../app/addons/rs/url-tests.server.js | 52 +++++++++------- .../resource-store-adapter-tests.common.js | 59 ------------------- .../lib/tests/autoload/store.server-tests.js | 17 ++---- 10 files changed, 89 insertions(+), 129 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index d0aaa010d..fd07b5092 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -205,7 +205,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { path = binder.needs[module]; // Anything we don't know about we'll assume is // a YUI library module. - if (!store.fileFromStaticHandlerURL(path)) { + if (!store.store.yui.getPathForURL(path)) { yuiModules.push(module); yuiJsUrlContains[module] = true; } @@ -337,6 +337,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { */ getScripts: function(embed) { var i, + path, x, assets = {}, blob = { @@ -348,16 +349,14 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { // Walk over the scripts and check what we can do for (i in this.scripts) { if (this.scripts.hasOwnProperty(i)) { - if (embed && this.rs._staticURLs[i]) { - //blob.content+= fs.readFileSync(this.rs._staticURLs[i], - // 'utf8'); - //delete this.scripts[i]; + path = this.rs.store.url.getPathForURL[i]; + if (embed && path) { this.scripts[i] = { type: 'blob', position: 'bottom', content: '' + minify(fs.readFileSync(path, 'utf8')) + + '' }; } else { this.scripts[i] = { diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index c28b4a504..236b321d7 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -47,6 +47,18 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, + // TODO DOCS + getPathForURL: function(url) { + return this.URLpaths[url]; + }, + + + // TODO DOCS + getURLPaths: function() { + return Y.clone(this.URLpaths, true); + }, + + preloadResourceVersions: function() { var mojits = this.rs.listAllMojits(), m, diff --git a/source/lib/app/autoload/resource-store-adapter.common.js b/source/lib/app/autoload/resource-store-adapter.common.js index bce3e64a1..553888c3d 100644 --- a/source/lib/app/autoload/resource-store-adapter.common.js +++ b/source/lib/app/autoload/resource-store-adapter.common.js @@ -39,7 +39,6 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { this.store = resourceStore; this._root = this.store._config.root; - this._staticURLs = this.store._staticURLs; return this; }, @@ -200,12 +199,6 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { }, - fileFromStaticHandlerURL: function(url) { - //logger.log('fileFromStaticHandlerURL', 'warn', NAME); - return this.store.fileFromStaticHandlerURL(url); - }, - - getRoutes: function(ctx) { //logger.log('getRoutes', 'warn', NAME); return this.store.getRoutes(ctx); diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index ed51f4d80..6feb62671 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -148,6 +148,7 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, indexJs = "module.exports = require('express')." + "createServer(require('express')['static'](__dirname));", url, + storeURLs, urls = {}, // from: to app, context = '', @@ -180,9 +181,10 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, manifest += 'CACHE:\n'; // Copy all the files into the destination directory - for (url in store._staticURLs) { - if (store._staticURLs.hasOwnProperty(url)) { - from = store._staticURLs[url]; // filesystem path + storeURLs = store.url.getURLPaths(); + for (url in storeURLs) { + if (storeURLs.hasOwnProperty(url)) { + from = storeURLs[url]; // filesystem path to = libpath.join(destination, url); extension = from.split('.').pop(); diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 7b71a8752..54fcba093 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -231,6 +231,7 @@ compile.inlinecss = function(context, options, callback) { shortDest, i, url, + storeURLs, fs2url = {}, srcKey, srcDir, @@ -245,9 +246,10 @@ compile.inlinecss = function(context, options, callback) { mojitName = inline.mojitName; // need a reverse mapping - for (url in store._staticURLs) { - if (store._staticURLs.hasOwnProperty(url)) { - fs2url[store._staticURLs[url]] = url; + storeURLs = store.url.getURLPaths(); + for (url in storeURLs) { + if (storeURLs.hasOwnProperty(url)) { + fs2url[storeURLs[url]] = url; } } diff --git a/source/lib/app/middleware/mojito-handler-static.js b/source/lib/app/middleware/mojito-handler-static.js index 6dc3a1a6e..875b78ef2 100644 --- a/source/lib/app/middleware/mojito-handler-static.js +++ b/source/lib/app/middleware/mojito-handler-static.js @@ -197,7 +197,7 @@ function staticProvider(store, globalLogger) { // Use the resource store as a URI "rewriter" here. // /favicon.ico is sent to ./my_app_folder/assets/favicon.ico - filename = store.fileFromStaticHandlerURL(path); + filename = store.yui.getPathForURL(path); // TODO: [Issue 80] remove this for performance if ((!filename) && (path === '/favicon.ico')) { filename = pa.join(store._config.root, 'assets', path); diff --git a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js index fb24b322b..7ae141f55 100644 --- a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js +++ b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js @@ -57,10 +57,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } + }, + url: { + getPathForURL: function() { + return 'path'; + } } - }, - fileFromStaticHandlerURL: function() { - return 'path'; } }); @@ -143,10 +145,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } + }, + url: { + getPathForURL: function() { + return 'path'; + } } - }, - fileFromStaticHandlerURL: function() { - return 'path'; } }); @@ -237,10 +241,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } + }, + url: { + getPathForURL: function() { + return 'path'; + } } - }, - fileFromStaticHandlerURL: function() { - return 'path'; } }); @@ -322,10 +328,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } + }, + url: { + getPathForURL: function() { + return 'path'; + } } - }, - fileFromStaticHandlerURL: function() { - return 'path'; } }); @@ -407,10 +415,12 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } + }, + url: { + getPathForURL: function() { + return 'path'; + } } - }, - fileFromStaticHandlerURL: function() { - return 'path'; } }); diff --git a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js index 6c4cdd11b..4214d5919 100644 --- a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js @@ -131,28 +131,6 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { } - function makeSource(dir, dirType, subdir, file, isFile) { - var source = { - fs: { - fullPath: libpath.join(dir, subdir, file), - rootDir: dir, - rootType: dirType, - subDir: subdir, - subDirArray: subdir.split('/'), - isFile: isFile, - ext: libpath.extname(file) - }, - pkg: { - name: 'unittest', - version: '999.666.999', - depth: 999 - } - }; - source.fs.basename = libpath.basename(file, source.fs.ext); - return source; - } - - suite.add(new YUITest.TestCase({ name: 'url rs addon tests', @@ -316,6 +294,36 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { mojit: mojit }); A.areSame('/Foo/assets', mojit.assetsRoot); + }, + + + 'resource URLs': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ + root: fixtures, + appConfig: { staticHandling: {} } + }); + store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + store._makeResource('mojito', 'shared', 'x', 'y', 'common'); + store._makeResource('orange', 'shared', 'x', 'y', 'common'); + store._makeResource('orange', null, 'mojit', 'X', 'common'); + store._makeResource('orange', 'X', 'x', 'y', 'common'); + store._makeResource('orange', null, 'mojit', 'Y', 'common'); + store._makeResource('orange', 'Y', 'x', 'y', 'common'); + store.preloadResourceVersions(); + A.areSame('path/for/x--y.common.ext', store.url.getPathForURL('/static/X/x--y.common.ext')); + A.areSame('path/for/x--y.common.ext', store.url.getPathForURL('/static/Y/x--y.common.ext')); + A.areSame('path/for/x--y.common.ext', store.url.getPathForURL('/static/mojito/x--y.common.ext')); + A.areSame('path/for/x--y.common.ext', store.url.getPathForURL('/static/store/x--y.common.ext')); + var have = store.url.getURLPaths(); + var want = { + '/static/X/x--y.common.ext': 'path/for/x--y.common.ext', + '/static/Y/x--y.common.ext': 'path/for/x--y.common.ext', + '/static/mojito/x--y.common.ext': 'path/for/x--y.common.ext', + '/static/store/x--y.common.ext': 'path/for/x--y.common.ext' + }; + cmp(have, want); } diff --git a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js index d6fa6dc4e..7f93d45b0 100644 --- a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js +++ b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js @@ -215,65 +215,6 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }); }, - 'server mojit is NOT loaded becuase of pacakge mojito version miss-match': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - A.isTrue(typeof store._staticURLs['/static/test_mojit_4/package.json'] === 'undefined'); - A.isTrue(typeof store._staticURLs['/static/TestMojit4/package.json'] === 'undefined'); - }, - - 'server mojit is loaded becuase of pacakge mojito version match': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit2'}; - store.expandInstance(instance, {}, function(err, instance){ - A.areSame('/static/TestMojit2/assets', instance.assetsRoot); - }); - }, - - 'server a mojits package.json file is NOT publicly accessible': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - A.isTrue(typeof store._staticURLs['/static/TestMojit2/package.json'] === 'undefined'); - }, - - 'server a mojits package.json file is publicly accessible': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - A.isTrue(typeof store._staticURLs['/static/TestMojit3/package.json'] === 'string'); - }, - - 'server a mojit is NOT loaded because it has a package.json file with no mojito config': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - A.isTrue(typeof store._staticURLs['/static/TestMojit5/package.json'] === 'undefined'); - }, - 'server mojit view index.mu.html is loaded correctly': function(){ var resourceStore, store; diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 2a443c978..e452dcd34 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -381,8 +381,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - A.isTrue(typeof store._staticURLs['/static/test_mojit_4/package.json'] === 'undefined'); - A.isTrue(typeof store._staticURLs['/static/TestMojit4/package.json'] === 'undefined'); + A.isTrue(typeof store.url.getPathForURL('/static/test_mojit_4/package.json') === 'undefined'); + A.isTrue(typeof store.url.getPathForURL('/static/TestMojit4/package.json') === 'undefined'); }, 'server mojit is loaded because of package mojito version match': function(){ @@ -399,21 +399,21 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - A.isTrue(typeof store._staticURLs['/static/TestMojit2/package.json'] === 'undefined'); + A.isTrue(typeof store.url.getPathForURL('/static/TestMojit2/package.json') === 'undefined'); }, 'server a mojits package.json file is publicly accessible': function(){ var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - A.isTrue(typeof store._staticURLs['/static/TestMojit3/package.json'] === 'string'); + A.isTrue(typeof store.url.getPathForURL('/static/TestMojit3/package.json') === 'string'); }, 'server a mojit is NOT loaded because it has a package.json file with no mojito config': function(){ var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - A.isTrue(typeof store._staticURLs['/static/TestMojit5/package.json'] === 'undefined'); + A.isTrue(typeof store.url.getPathForURL('/static/TestMojit5/package.json') === 'undefined'); }, 'server mojit view index.mu.html is loaded correctly': function(){ @@ -616,13 +616,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isObject(routes.flickr_base, 'missing route flickr_base'); }, - 'call fileFromStaticHandlerURL()': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var fullpath = store.fileFromStaticHandlerURL('/static/TestMojit2/controller.server.js'); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_2/controller.server.js'), fullpath); - }, - 'call serializeClientStore()': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); From 17e26172ca910d2b399249532cc954fc06358cff Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 15 Jun 2012 11:46:48 -0700 Subject: [PATCH 043/119] more stubs for YUI when parsing a javascript file --- source/lib/app/addons/rs/yui.server.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index a870bc467..e13dde9be 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -257,6 +257,9 @@ YUI.add('addon-rs-yui', function(Y, NAME) { window: {}, document: {}, YUI: { + ENV: {}, + config: {}, + use: function() {}, add: function(name, fn, version, meta) { yui.name = name; yui.version = version; From fc23793979e0c4712b33c2541b6c9f32cd025ced Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 15 Jun 2012 12:26:27 -0700 Subject: [PATCH 044/119] specs --- source/lib/store.server.js | 48 ++++++++++++++++++++++++++++++++++---- 1 file changed, 44 insertions(+), 4 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index fbea9f455..4211e9873 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -78,6 +78,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { 'commands': 'command', 'middleware': 'middleware', 'models': 'model', + 'specs': 'spec', 'views': 'view' }, CONVENTION_SUBDIR_TYPE_IS_JS = { @@ -433,7 +434,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { ctx.runtime = env; try { - spec = this._expandSpec(ctx, instance); + spec = this._expandSpec(env, ctx, instance); } catch (err) { return cb(err); } @@ -839,6 +840,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (CONVENTION_SUBDIR_TYPE_IS_JS[type] && '.js' !== fs.ext) { return false; } + if ('spec' === type && '.json' !== fs.ext) { + return false; + } return { type: type, skipSubdirParts: 1 @@ -1012,6 +1016,24 @@ YUI.add('mojito-resource-store', function(Y, NAME) { return res; } + // special case: spec + if ('spec' === type) { + res = { + source: source, + mojit: mojitType, + type: 'spec', + affinity: 'common', + selector: '*' + }; + if (baseParts.length !== 1) { + Y.log('invalid spec filename. skipping ' + source.fs.fullPath, 'warn', NAME); + return; + } + res.name = libpath.join(source.fs.subDir, baseParts.join('.')); + res.id = [res.type, res.subtype, res.name].join('-'); + return res; + } + // special case: view if ('view' === type) { res = { @@ -1144,21 +1166,39 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // TODO DOCS - _expandSpec: function(ctx, spec) { + _expandSpec: function(env, ctx, spec) { var appConfig, - base; + base, + idParts, + mojitType, + specName, + ress; + if (!spec.base) { return spec; } + // The base will need to carry its ID with it. spec.id = spec.base; appConfig = this.getAppConfig(ctx); base = appConfig.specs[spec.base]; + + if (!base) { + // look in resources + idParts = spec.base.split(':'); + mojitType = idParts.shift(); + specName = idParts.join(':'); + ress = this.getResources(env, ctx, {type: 'spec', mojit: mojitType, name: specName}); + if (1 === ress.length) { + base = this.config.readConfigYCB(ress[0].source.fs.fullPath, ctx); + } + } if (!base) { throw new Error('Unknown base of "' + spec.base + '"'); } + delete spec.base; - return this.mergeRecursive(this._expandSpec(ctx, base), spec); + return this.mergeRecursive(this._expandSpec(env, ctx, base), spec); }, From acc7bc2fc60c4dac62f3cbc170de3e0863a46c31 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 26 Jun 2012 14:24:42 -0700 Subject: [PATCH 045/119] yui RS addon calculates yui module dependencies --- source/lib/app/addons/rs/yui.server.js | 258 ++++++- source/lib/app/autoload/dispatch.common.js | 4 +- source/lib/store.server.js | 40 +- .../app/addons/rs/yui-tests.server.js | 369 +++++++++- .../lib/tests/autoload/store.server-tests.js | 679 +++--------------- 5 files changed, 762 insertions(+), 588 deletions(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index e13dde9be..2122e5693 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -29,6 +29,17 @@ YUI.add('addon-rs-yui', function(Y, NAME) { this.beforeHostMethod('parseResource', this.parseResource, this); this.beforeHostMethod('addResourceVersion', this.addResourceVersion, this); this.onHostEvent('getMojitTypeDetails', this.getMojitTypeDetails, this); + this.onHostEvent('mojitResourcesResolved', this.mojitResourcesResolved, this); + this.yuiConfig = this.rs.getStaticAppConfig().yui; + + this.modules = {}; // env: poslKey: module: details + this.sortedModules = {}; // env: poslKey: lang: module: details + + this.usePrecomputed = -1 !== this.yuiConfig.dependencyCalculations.indexOf('precomputed'); + this.useOnDemand = -1 !== this.yuiConfig.dependencyCalculations.indexOf('ondemand'); + if (!this.usePrecomputed) { + this.useOnDemand = true; + } }, @@ -226,10 +237,29 @@ YUI.add('addon-rs-yui', function(Y, NAME) { getMojitTypeDetails: function(evt) { var dest = evt.mojit, + env = evt.args.env, + ctx = evt.args.ctx, + posl = evt.args.posl, + poslKey = JSON.stringify(posl), + mojitType = evt.args.mojitType, ress, r, - res; - ress = this.rs.getResources(evt.args.env, evt.args.ctx, {mojit: evt.args.mojitType}); + res, + sorted; + //console.log('--------------------------------- getMojitTypeDetails -- ' + [env, ctx.lang, poslKey, mojitType].join(',')); + + if (!dest.yui) { + dest.yui = { config: {} }; + } + if (!dest.yui.config) { + dest.yui.config = { module: {} }; + } + + if (this.modules[env] && this.modules[env][poslKey]) { + dest.yui.config.modules = this.modules[env][poslKey][mojitType]; + } + + ress = this.rs.getResources(evt.args.env, evt.args.ctx, {mojit: mojitType}); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (res.type === 'binder') { @@ -237,14 +267,238 @@ YUI.add('addon-rs-yui', function(Y, NAME) { dest.views[res.name] = {}; } dest.views[res.name]['binder-module'] = res.yui.name; + sorted = this._getYUISorted('client', poslKey, ctx.lang, res.yui.name); + if (sorted && sorted.paths) { + dest.views[res.name]['binder-yui-sorted'] = this.rs.cloneObj(sorted.paths); + } } if (res.type === 'controller') { dest['controller-module'] = res.yui.name; + sorted = this._getYUISorted(env, poslKey, ctx.lang, res.yui.name); + if (sorted && sorted.sorted) { + dest.yui.sorted = sorted.sorted.slice(); + } + if (this.usePrecomputed && sorted && sorted.paths) { + dest.yui.sortedPaths = this.rs.cloneObj(sorted.paths); + } + } + } + }, + + + mojitResourcesResolved: function(evt) { + var env = evt.env, + posl = evt.posl, + poslKey = JSON.stringify(posl), + mojit = evt.mojit, + ress = evt.ress, + r, + res, + langs = {}, + l, + ll, + langName, + langNames, + viewEngineRequired = {}, + modules = {}, + binders = {}, + controller, + controllerRequired = {}, + required, + sorted, + binderName, + binder; + //console.log('--------------------------------- mojitResourcesResolved -- ' + [env, poslKey, mojit].join(',')); + + if ('shared' === mojit) { + return; + } + + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if ('addon' === res.type && 'view-engines' === res.subtype) { + viewEngineRequired[res.yui.name] = true; + } + if ('yui-lang' === res.type) { + langs[res.name] = res; + } + if (res.yui && res.yui.name) { + modules[res.yui.name] = { + requires: res.yui.meta.requires, + fullpath: (('client' === env) ? res.url : res.source.fs.fullPath) + } + if (res.mojit === mojit && res.type !== 'yui-lang') { + controllerRequired[res.yui.name] = true; + } + if ('binder' === res.type) { + binders[res.name] = res; + } + if ('controller' === res.type) { + controller = res; + } + } + } + if (modules['inlinecss/' + mojit]) { + // TODO: does this polute something? need to make a copy somewhere? + modules[controller.yui.module].requires.push('inlinecss/' + mojit); + } + + if (!this.modules[env]) { + this.modules[env] = {}; + } + if (!this.modules[env][poslKey]) { + this.modules[env][poslKey] = {}; + } + this.modules[env][poslKey][mojit] = this.rs.cloneObj(modules); + + // we always want to do calculations for no-lang + if (!langs['']) { + langs[''] = undefined; + } + langNames = Object.keys(langs); + for (l = 0; l < langNames.length; l += 1) { + langName = langNames[l]; + lang = langs[langName]; + + if (controller) { + required = Y.clone(controllerRequired, true); + required['mojito-dispatcher'] = true; + required[controller.yui.name] = true; + if (lang && lang.yui) { + required[lang.yui.name] = true; + } + else { + for (ll in langs) { + if (langs.hasOwnProperty(ll) && ll !== '') { + lang = langs[ll]; + required[lang.yui.name] = true; + } + } + } + // we don't know which views will be used, so we need all view engines + required = Y.merge(required, viewEngineRequired); + sorted = this._sortYUIModules(langName, env, mojit, modules, required); + this._setYUISorted(env, poslKey, langName, controller.yui.name, sorted); + } + + if ('client' === env) { + for (binderName in binders) { + if (binders.hasOwnProperty(binderName)) { + binder = binders[binderName]; + required = { 'mojito-client': true }; + required[binder.yui.name] = true; + + // view engines are needed to support mojitProxy.render() + required = Y.merge(required, viewEngineRequired); + + sorted = this._sortYUIModules(langName, env, mojit, modules, required); + this._setYUISorted(env, poslKey, langName, binder.yui.name, sorted); + } + } + } + } // for each lang + }, + + + // TODO DOCS + _sortYUIModules: function(lang, env, mojit, modules, required) { + var Y, + loader, + m, + module, + info, + sortedPaths = {}; + + // We don't actually need the full list, just the required modules. + // YUI.Loader() will do the rest at runtime. + if (!this.usePrecomputed) { + for (module in required) { + if (required.hasOwnProperty(module)) { + sortedPaths[module] = modules[module].fullpath; + } + } + return { + sorted: Object.keys(sortedPaths), + paths: sortedPaths + }; + } + + Y = YUI({ useSync: true }).use('loader-base'); + Y.applyConfig({ useSync: false }); + + // We need to clear YUI's cached dependencies, since there's no + // guarantee that the previously calculated dependencies have been done + // using the same context as this calculation. + delete YUI.Env._renderedMods; + + // Use ignoreRegistered here instead of the old `delete YUI.Env._renderedMods` hack + loader = new Y.Loader({ lang: lang, ignoreRegistered: true }); + // Only override the default if it's required + if (this.yuiConfig && this.yuiConfig.base) { + loader.base = this.yuiConfig.base; + } + + loader.addGroup({modules: modules}, mojit); + loader.calculate({required: required}); + + for (m = 0; m < loader.sorted.length; m += 1) { + module = loader.sorted[m]; + info = loader.moduleInfo[module]; + if (info) { + // modules with "nodejs" in their name are tweaks on other modules + if ('client' === env && module.indexOf('nodejs') !== -1) { + continue; + } + sortedPaths[module] = info.fullpath || loader._url(info.path); } } + return { + sorted: loader.sorted, + paths: sortedPaths + }; }, + // TODO DOCS + _setYUISorted: function(env, poslKey, lang, module, sorted) { + if (!this.sortedModules[env]) { + this.sortedModules[env] = {}; + } + if (!this.sortedModules[env][poslKey]) { + this.sortedModules[env][poslKey] = {}; + } + if (!this.sortedModules[env][poslKey][lang]) { + this.sortedModules[env][poslKey][lang] = {}; + } + this.sortedModules[env][poslKey][lang][module] = sorted; + }, + + + // TODO DOCS + _getYUISorted: function(env, poslKey, lang, module) { + lang = lang || ''; + var parts = lang.split('-'), + p, + test; + if (!this.sortedModules[env]) { + return; + } + if (!this.sortedModules[env][poslKey]) { + return; + } + for (p = parts.length; p > 0; p -= 1) { + test = parts.slice(0, p).join('-'); + if (this.sortedModules[env][poslKey][test] && + this.sortedModules[env][poslKey][test][module]) { + return this.sortedModules[env][poslKey][test][module]; + } + } + // fall back to "default language" + return this.sortedModules[env][poslKey][''][module]; + }, + + + // TODO DOCS _parseYUIModule: function(res) { var file, ctx, diff --git a/source/lib/app/autoload/dispatch.common.js b/source/lib/app/autoload/dispatch.common.js index 655b32ed1..bf077cd4f 100644 --- a/source/lib/app/autoload/dispatch.common.js +++ b/source/lib/app/autoload/dispatch.common.js @@ -216,9 +216,7 @@ YUI.add('mojito-dispatcher', function(Y, NAME) { fixupInstanceLang(command.instance.type, command.context.lang, instance); - moduleList = (usePrecomputed ? - instance.yui.sorted : - instance.yui.requires); + moduleList = instance.yui.sorted; // gotta copy this or else it pollutes the client runtime mojitYuiModules = Y.mojito.util.copy(moduleList); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 4211e9873..4ac383ca4 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -89,6 +89,19 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // which addon subtypes are app-level ADDON_SUBTYPES_APPLEVEL = { 'rs': true + }, + DEFAULT_AFFINITIES = { + 'action': 'server', + 'addon': 'server', + 'archetype': 'server', + 'asset': 'common', + 'binder': 'client', + 'command': 'server', + 'controller': 'server', + 'middleware': 'server', + 'model': 'server', + 'spec': 'common', + 'view': 'common' }; @@ -151,6 +164,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // Y.Plugin AOP doesn't allow afterHostMethod() callbacks to // modify the results, so we fire an event instead. this.publish('getMojitTypeDetails', {emitFacade: true, preventable: false}); + this.publish('mojitResourcesResolved', {emitFacade: true, preventable: false}); // We'll start with just our "config" addon. this._yuiUseSync({ @@ -478,6 +492,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { res, engine, engines = {}, // view engines + posl = this.selector.getListFromContext(ctx), ctxKey, module; @@ -565,6 +580,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { args: { env: env, ctx: ctx, + posl: posl, mojitType: mojitType }, mojit: dest @@ -955,7 +971,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { type: type, subtype: subtype, name: fs.basename, - affinity: 'server', + affinity: DEFAULT_AFFINITIES[type], selector: '*' }; res.id = [res.type, res.subtype, res.name].join('-'); @@ -972,7 +988,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { mojit: mojitType, type: type, subtype: subtype, - affinity: 'server', + affinity: DEFAULT_AFFINITIES[type], selector: '*' }; if (baseParts.length >= 3) { @@ -985,7 +1001,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); return; } - res.name = libpath.join(fs.subDirArray.slice(1).join('/'), baseParts.join('.')); + res.name = libpath.join(fs.subDirArray.join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); // special case if ('addon' === type && ADDON_SUBTYPES_APPLEVEL[res.subtype]) { @@ -1001,7 +1017,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { mojit: mojitType, type: type, subtype: subtype, - affinity: 'common', + affinity: DEFAULT_AFFINITIES[type], selector: '*' }; if (baseParts.length >= 2) { @@ -1022,7 +1038,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { source: source, mojit: mojitType, type: 'spec', - affinity: 'common', + affinity: DEFAULT_AFFINITIES[type], selector: '*' }; if (baseParts.length !== 1) { @@ -1043,7 +1059,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { subtype: subtype, viewOutputFormat: fs.ext.substr(1), viewEngine: baseParts.pop(), - affinity: 'common', + affinity: DEFAULT_AFFINITIES[type], selector: '*' }; if (baseParts.length >= 2) { @@ -1150,9 +1166,15 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } for (type in this._mojitRVs) { if (this._mojitRVs.hasOwnProperty(type)) { - this._mojitResources[env][poslKey][type] = - this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); - // TODO: fire event that mojit has been resolved + ress = this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); + this._mojitResources[env][poslKey][type] = ress; + // TODO DOCS: document event + this.fire('mojitResourcesResolved', { + env: env, + posl: posl, + mojit: type, + ress: ress + }); } } } diff --git a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js index ae311787d..5af979ce3 100644 --- a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js @@ -8,7 +8,8 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { var suite = new YUITest.TestSuite(NAME), libpath = require('path'), mojitoRoot = libpath.join(__dirname, '../../../../../'), - A = YUITest.Assert; + A = YUITest.Assert, + AA = YUITest.ArrayAssert; function MockRS(config) { @@ -25,12 +26,21 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { this._appResources = {}; // env: ctx: list of resources this._mojits = {}; this.publish('getMojitTypeDetails', {emitFacade: true, preventable: false}); + this._appConfig = { + yui: { + dependencyCalculations: 'precomputed' + } + }; }, listAllMojits: function() { return Object.keys(this._mojits); }, + getStaticAppConfig: function() { + return Y.clone(this._appConfig, true); + }, + getResources: function(env, ctx, filter) { var source, out = [], @@ -334,6 +344,356 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { A.isNotUndefined(mojit.views.list); A.areSame('FooBinderList', mojit.views.list['binder-module']); A.areSame('FooController', mojit['controller-module']); + }, + + + 'find and parse resources by convention': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/conventions'), + store = new Y.mojito.ResourceStore({ root: fixtures }); + + // fake out some parts of preload(), which we're trying to avoid + store._fwConfig = store.config.readConfigJSON(libpath.join(mojitoRoot, 'config.json')); + store._appConfigStatic = store.getStaticAppConfig(); + store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var pkg = { name: 'test', version: '6.6.6' }; + var mojitType = 'testing'; + var ress = store._findResourcesByConvention(fixtures, 'app', pkg, mojitType) + + var r, res; + for (r = 0; r < ress.length; r++) { + res = ress[r]; + A.isNotUndefined(res.id, 'no resource id'); + switch (res.id) { + case 'action--x': + case 'action--y/z': + case 'addon-a-x': + case 'archetype-x-y': + case 'asset-css-x': + case 'asset-css-y/z': + case 'binder--x': + case 'command--x': + case 'config--config': + case 'controller--controller': + case 'middleware--x': + case 'spec--default': + case 'spec--x': + case 'view--x': + break; + case 'yui-lang--': + A.areSame(pkg, res.source.pkg); + A.areSame('yui-lang', res.type); + A.areSame('', res.name); + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.', res.source.fs.subDir); + A.areSame('testing', res.source.fs.basename); + A.areSame('.js', res.source.fs.ext); + break; + case 'yui-lang--de': + A.areSame(pkg, res.source.pkg); + A.areSame('yui-lang', res.type); + A.areSame('de', res.name); + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.', res.source.fs.subDir); + A.areSame('testing_de', res.source.fs.basename); + A.areSame('.js', res.source.fs.ext); + break; + case 'yui-lang--en': + A.areSame(pkg, res.source.pkg); + A.areSame('yui-lang', res.type); + A.areSame('en', res.name); + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.', res.source.fs.subDir); + A.areSame('testing_en', res.source.fs.basename); + A.areSame('.js', res.source.fs.ext); + break; + case 'yui-lang--en-US': + A.areSame(pkg, res.source.pkg); + A.areSame('yui-lang', res.type); + A.areSame('en-US', res.name); + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.', res.source.fs.subDir); + A.areSame('testing_en-US', res.source.fs.basename); + A.areSame('.js', res.source.fs.ext); + break; + case 'yui-module--m': + A.areSame(pkg, res.source.pkg); + A.areSame('yui-module', res.type); + A.areSame('m', res.name); + A.areSame('m', res.yui.name); + switch (res.source.fs.basename) { + case 'm.common': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + break; + case 'm.common.iphone': + A.areSame('iphone', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + break; + default: + A.fail('unknown resource ' + res.source.fs.fullPath); + break; + } + break; + case 'yui-module--x': + A.areSame(pkg, res.source.pkg); + A.areSame('yui-module', res.type); + A.areSame('x', res.name); + A.areSame('x', res.yui.name); + switch (res.source.fs.basename) { + case 'x.common': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + break; + case 'x.common.iphone': + A.areSame('iphone', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + break; + default: + A.fail('unknown resource ' + res.source.fs.fullPath); + break; + } + break; + case 'yui-module--z': + A.areSame(pkg, res.source.pkg); + A.areSame('yui-module', res.type); + A.areSame('z', res.name); + A.areSame('z', res.yui.name); + A.areSame('y', res.source.fs.subDir); + switch (res.source.fs.basename) { + case 'z.common': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + break; + case 'z.common.android': + A.areSame('android', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + break; + default: + A.fail('unknown resource ' + res.source.fs.fullPath); + break; + } + break; + + default: + A.fail('unknown resource ' + res.id); + break; + } + } + A.areSame(31, ress.length, 'wrong number of resources'); + }, + + + 'server mojit instance yui': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new Y.mojito.ResourceStore({ root: fixtures }); + store.preload(); + + var instance = {type:'TestMojit2'}; + store.expandInstance(instance, {}, function(err, instance) { + A.isNotUndefined(instance.yui); + + A.isNotUndefined(instance.yui.config); + A.isNotUndefined(instance.yui.config.modules); + A.isNotUndefined(instance.yui.config.modules['test_mojit_2']); + A.areSame(libpath.join(fixtures, 'mojits/test_mojit_2/controller.server.js'), instance.yui.config.modules['test_mojit_2'].fullpath); + A.isNotUndefined(instance.yui.config.modules['mojito-mu']); + A.areSame(libpath.join(mojitoRoot, 'app/addons/view-engines/mu.server.js'), instance.yui.config.modules['mojito-mu'].fullpath); + + A.isArray(instance.yui.sorted); + AA.contains('test_mojit_2', instance.yui.sorted); + AA.doesNotContain('test_applevelModel', instance.yui.sorted); + AA.doesNotContain('ModelFlickr', instance.yui.sorted); + AA.contains('mojito-mu', instance.yui.sorted); + AA.contains('mojito', instance.yui.sorted); + + A.isObject(instance.yui.sortedPaths); + A.areSame(libpath.join(fixtures, 'mojits/test_mojit_2/controller.server.js'), instance.yui.sortedPaths['test_mojit_2']); + A.isUndefined(instance.yui.sortedPaths['test_applevelModel']); + A.isUndefined(instance.yui.sortedPaths['ModelFlickr']); + A.areSame(libpath.join(mojitoRoot, 'app/addons/view-engines/mu.server.js'), instance.yui.sortedPaths['mojito-mu']); + A.areSame(libpath.join(mojitoRoot, 'app/autoload/mojito.common.js'), instance.yui.sortedPaths['mojito']); + }); + }, + + + 'server mojit instance yui - precomputed': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/precomputed'); + var store = new Y.mojito.ResourceStore({ root: fixtures }); + store.preload(); + + var instance = { type:'PagedFlickr' }; + store.expandInstance(instance, {}, function(err, instance) { + A.isNotUndefined(instance.yui); + + A.isArray(instance.yui.sorted); + AA.contains('intl', instance.yui.sorted); + AA.contains('datatype-date-format', instance.yui.sorted); + AA.contains('mojito', instance.yui.sorted); + AA.contains('mojito-util', instance.yui.sorted); + AA.contains('mojito-intl-addon', instance.yui.sorted); + AA.contains('lang/PagedFlickr_de', instance.yui.sorted); + AA.contains('lang/PagedFlickr_en', instance.yui.sorted); + AA.contains('lang/PagedFlickr_en-US', instance.yui.sorted); + + A.isObject(instance.yui.sortedPaths); + A.isNotUndefined(instance.yui.sortedPaths['intl']); + A.isNotUndefined(instance.yui.sortedPaths['datatype-date-format']); + A.isNotUndefined(instance.yui.sortedPaths['mojito']); + A.isNotUndefined(instance.yui.sortedPaths['mojito-util']); + A.isNotUndefined(instance.yui.sortedPaths['mojito-intl-addon']); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.js'), instance.yui.sortedPaths['PagedFlickr']); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_de.js'), instance.yui.sortedPaths['lang/PagedFlickr_de']); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_en.js'), instance.yui.sortedPaths['lang/PagedFlickr_en']); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_en-US.js'), instance.yui.sortedPaths['lang/PagedFlickr_en-US']); + + // the particular datatype-date-format for no-lang is up to YUI, + // so this test is a little fragile + AA.contains('lang/datatype-date-format_en', instance.yui.sorted); + A.isNotUndefined(instance.yui.sortedPaths['lang/datatype-date-format_en']); + }); + }, + + + 'server mojit instance yui - ondemand': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/ondemand'); + var store = new Y.mojito.ResourceStore({ root: fixtures }); + store.preload(); + + var instance = { type:'PagedFlickr' }; + store.expandInstance(instance, {}, function(err, instance) { + A.isNotUndefined(instance.yui); + + A.isArray(instance.yui.sorted); + AA.contains('mojito-dispatcher', instance.yui.sorted); + AA.contains('mojito-mu', instance.yui.sorted); + AA.contains('PagedFlickr', instance.yui.sorted); + AA.contains('lang/PagedFlickr_de', instance.yui.sorted); + AA.contains('lang/PagedFlickr_en', instance.yui.sorted); + AA.contains('lang/PagedFlickr_en-US', instance.yui.sorted); + + A.isUndefined(instance.yui.sortedPaths); + }); + }, + + + 'server mojit instance yui - precomputed+ondemand': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/precomputed-ondemand'); + var store = new Y.mojito.ResourceStore({ root: fixtures }); + store.preload(); + + var instance = { type:'PagedFlickr' }; + store.expandInstance(instance, {}, function(err, instance) { + A.isNotUndefined(instance.yui); + + A.isArray(instance.yui.sorted); + AA.contains('intl', instance.yui.sorted, 'contains intl'); + AA.contains('datatype-date-format', instance.yui.sorted, 'contains datatype-date-format'); + AA.contains('mojito', instance.yui.sorted, 'contains mojito'); + AA.contains('mojito-util', instance.yui.sorted, 'contains mojito-util'); + AA.contains('mojito-intl-addon', instance.yui.sorted, 'contains mojito-intl-addon'); + AA.contains('lang/PagedFlickr_de', instance.yui.sorted); + AA.contains('lang/PagedFlickr_en', instance.yui.sorted); + AA.contains('lang/PagedFlickr_en-US', instance.yui.sorted); + AA.doesNotContain('lang/datatype-date-format_de', instance.yui.sorted, 'does not contain datatype-date-format_de'); + AA.contains('lang/datatype-date-format_en', instance.yui.sorted, 'contains datatype-date-format_en'); + AA.doesNotContain('lang/datatype-date-format_en-US', instance.yui.sorted, 'does not contain datatype-date-format_en-US'); + + A.isObject(instance.yui.sortedPaths); + A.isNotUndefined(instance.yui.sortedPaths['intl']); + A.isNotUndefined(instance.yui.sortedPaths['datatype-date-format']); + A.isNotUndefined(instance.yui.sortedPaths['mojito']); + A.isNotUndefined(instance.yui.sortedPaths['mojito-util']); + A.isNotUndefined(instance.yui.sortedPaths['mojito-intl-addon']); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.js'), instance.yui.sortedPaths['PagedFlickr']); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_de.js'), instance.yui.sortedPaths['lang/PagedFlickr_de']); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_en.js'), instance.yui.sortedPaths['lang/PagedFlickr_en']); + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_en-US.js'), instance.yui.sortedPaths['lang/PagedFlickr_en-US']); + A.isUndefined(instance.yui.sortedPaths['lang/datatype-date-format_de']); + A.isNotUndefined(instance.yui.sortedPaths['lang/datatype-date-format_en']); + A.isUndefined(instance.yui.sortedPaths['lang/datatype-date-format_en-US']); + }); + }, + + + 'stuff with ctx{lang:}, in language fallback': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/gsg5'), + store = new Y.mojito.ResourceStore({ root: fixtures }), + ctx, spec; + store.preload(); + + // first test + ctx = { lang: 'en-US' }; + spec = { type: 'PagedFlickr' }; + store.expandInstance(spec, ctx, function(err, instance) { + A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:en-US}'); + A.isUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en'], 'en is not undefined {lang:en-US}'); + + // second test + ctx = { lang: 'en' }; + spec = { type: 'PagedFlickr' }; + store.expandInstance(spec, ctx, function(err, instance) { + A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en'], 'en is undefined {lang-en}'); + A.isUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is not undefined {lang:en}'); + + // third test + ctx = { lang: 'de-US' }; + spec = { type: 'PagedFlickr' }; + store.expandInstance(spec, ctx, function(err, instance) { + A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_de'], 'de is undefined {lang:de-US}'); + A.isUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is not undefined {lang:de-US}'); + + // fourth test + ctx = { lang: 'xy-ZU' }; + spec = { type: 'PagedFlickr' }; + store.expandInstance(spec, ctx, function(err, instance) { + A.isTrue( + Boolean(instance.yui.sortedPaths['lang/PagedFlickr_de']), + 'de is undefined {lang:xy-ZU}' + ); + A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:xy-ZU}'); + + // fifth test + ctx = {}; + spec = { type: 'PagedFlickr' }; + store.expandInstance(spec, ctx, function(err, instance) { + A.isTrue( + Boolean(instance.yui.sortedPaths['lang/PagedFlickr_de']), + 'de is undefined {}' + ); + A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {}'); + }); + }); + }); + }); + }); + }, + + + 'appConfig yui.base': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/gsg5-appConfig'), + store = new Y.mojito.ResourceStore({ root: fixtures }); + store.preload(); + var spec = { type: 'PagedFlickr' }; + store.expandInstance(spec, {}, function(err, instance) { + A.areSame('/foo/', instance.yui.sortedPaths['oop'].substr(0, 5)); + A.areSame('/foo/', instance.yui.sortedPaths['intl'].substr(0, 5)); + A.areSame('/foo/', instance.yui.sortedPaths['jsonp'].substr(0, 5)); + A.areSame('/foo/', instance.yui.sortedPaths['yql'].substr(0, 5)); + A.areSame('/foo/', instance.yui.sortedPaths['querystring-parse'].substr(0, 5)); + A.areSame('/foo/', instance.yui.sortedPaths['querystring-stringify'].substr(0, 5)); + A.areSame('/foo/', instance.yui.sortedPaths['json-stringify'].substr(0, 5)); + }); } @@ -341,4 +701,9 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { YUITest.TestRunner.add(suite); -}, '0.0.1', {requires: ['base', 'oop', 'addon-rs-yui']}); +}, '0.0.1', {requires: [ + 'base', + 'oop', + 'mojito-resource-store', + 'addon-rs-yui' +]}); diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index e452dcd34..a82b5f606 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -165,200 +165,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }); }, - 'server mojit instance yui': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var instance = {type:'TestMojit2'}; - store.expandInstance(instance, {}, function(err, instance) { - A.isNotUndefined(instance.yui); - - A.isNotUndefined(instance.yui.config); - A.isNotUndefined(instance.yui.config.modules); - A.isNotUndefined(instance.yui.config.modules['test_mojit_2']); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_2/controller.server.js'), instance.yui.config.modules['test_mojit_2'].fullpath); - A.isNotUndefined(instance.yui.config.modules['mojito-mu']); - A.areSame(libpath.join(mojitoRoot, 'app/addons/view-engines/mu.server.js'), instance.yui.config.modules['mojito-mu'].fullpath); - - A.isObject(instance.yui.langs); - A.areSame(2, Y.Object.keys(instance.yui.langs).length, 'wrong number of langs'); - A.areSame('lang/test_mojit_2_en-US', instance.yui.langs['en-US']); - A.areSame('lang/test_mojit_2_de', instance.yui.langs['de']); - - A.isArray(instance.yui.requires); - AA.contains('test_mojit_2', instance.yui.requires); - AA.doesNotContain('test_applevelModel', instance.yui.requires); - AA.doesNotContain('ModelFlickr', instance.yui.requires); - AA.contains('mojito-mu', instance.yui.requires); - AA.contains('mojito', instance.yui.requires); - - A.isArray(instance.yui.sorted); - AA.contains('test_mojit_2', instance.yui.sorted); - AA.doesNotContain('test_applevelModel', instance.yui.sorted); - AA.doesNotContain('ModelFlickr', instance.yui.sorted); - AA.contains('mojito-mu', instance.yui.sorted); - AA.contains('mojito', instance.yui.sorted); - - A.isObject(instance.yui.sortedPaths); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_2/controller.server.js'), instance.yui.sortedPaths['test_mojit_2']); - A.isUndefined(instance.yui.sortedPaths['test_applevelModel']); - A.isUndefined(instance.yui.sortedPaths['ModelFlickr']); - A.areSame(libpath.join(mojitoRoot, 'app/addons/view-engines/mu.server.js'), instance.yui.sortedPaths['mojito-mu']); - A.areSame(libpath.join(mojitoRoot, 'app/autoload/mojito.common.js'), instance.yui.sortedPaths['mojito']); - }); - - }, - - 'server mojit instance yui - precomputed': function() { - var fixtures = libpath.join(__dirname, '../fixtures/precomputed'); - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var instance = { type:'PagedFlickr' }; - store.expandInstance(instance, {}, function(err, instance) { - A.isNotUndefined(instance.yui); - - A.isArray(instance.yui.requires); - AA.contains('mojito', instance.yui.requires); - AA.contains('mojito-dispatcher', instance.yui.requires); - AA.contains('mojito-mu', instance.yui.requires); - AA.contains('PagedFlickr', instance.yui.requires); - - A.isArray(instance.yui.sorted); - AA.contains('intl', instance.yui.sorted); - AA.contains('datatype-date-format', instance.yui.sorted); - AA.contains('mojito', instance.yui.sorted); - AA.contains('mojito-util', instance.yui.sorted); - AA.contains('mojito-intl-addon', instance.yui.sorted); - AA.contains('lang/PagedFlickr_de', instance.yui.sorted); - AA.contains('lang/PagedFlickr_en', instance.yui.sorted); - AA.contains('lang/PagedFlickr_en-US', instance.yui.sorted); - AA.contains('lang/datatype-date-format_de', instance.yui.sorted); - AA.contains('lang/datatype-date-format_en', instance.yui.sorted); - AA.contains('lang/datatype-date-format_en-US', instance.yui.sorted); - - A.isObject(instance.yui.sortedPaths); - A.isNotUndefined(instance.yui.sortedPaths['intl']); - A.isNotUndefined(instance.yui.sortedPaths['datatype-date-format']); - A.isNotUndefined(instance.yui.sortedPaths['mojito']); - A.isNotUndefined(instance.yui.sortedPaths['mojito-util']); - A.isNotUndefined(instance.yui.sortedPaths['mojito-intl-addon']); - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.js'), instance.yui.sortedPaths['PagedFlickr']); - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_de.js'), instance.yui.sortedPaths['lang/PagedFlickr_de']); - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_en.js'), instance.yui.sortedPaths['lang/PagedFlickr_en']); - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_en-US.js'), instance.yui.sortedPaths['lang/PagedFlickr_en-US']); - A.isNotUndefined(instance.yui.sortedPaths['lang/datatype-date-format_de']); - A.isNotUndefined(instance.yui.sortedPaths['lang/datatype-date-format_en']); - A.isNotUndefined(instance.yui.sortedPaths['lang/datatype-date-format_en-US']); - - A.isObject(instance.yui.langs); - A.areSame('lang/PagedFlickr_de', instance.yui.langs['de']); - A.areSame('lang/PagedFlickr_en', instance.yui.langs['en']); - A.areSame('lang/PagedFlickr_en-US', instance.yui.langs['en-US']); - }); - - }, - - 'server mojit instance yui - ondemand': function() { - var fixtures = libpath.join(__dirname, '../fixtures/ondemand'); - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var instance = { type:'PagedFlickr' }; - store.expandInstance(instance, {}, function(err, instance) { - A.isNotUndefined(instance.yui); - - A.isArray(instance.yui.requires); - AA.contains('mojito', instance.yui.requires); - AA.contains('mojito-dispatcher', instance.yui.requires); - AA.contains('mojito-controller-context', instance.yui.requires); - AA.contains('mojito-action-context', instance.yui.requires); - AA.contains('mojito-output-adapter-addon', instance.yui.requires); - AA.contains('mojito-deploy-addon', instance.yui.requires); - AA.contains('mojito-partial-addon', instance.yui.requires); - AA.contains('mojito-url-addon', instance.yui.requires); - AA.contains('mojito-mu', instance.yui.requires); - AA.contains('mojito-util', instance.yui.requires); - AA.contains('mojito-view-renderer', instance.yui.requires); - AA.contains('ModelFlickr', instance.yui.requires); - AA.contains('PagedFlickr', instance.yui.requires); - AA.contains('lang/PagedFlickr_de', instance.yui.requires); - AA.contains('lang/PagedFlickr_en', instance.yui.requires); - AA.contains('lang/PagedFlickr_en-US', instance.yui.requires); - - A.isUndefined(instance.yui.sorted); - A.isUndefined(instance.yui.sortedPaths); - - A.isObject(instance.yui.langs); - A.areSame('lang/PagedFlickr_de', instance.yui.langs['de']); - A.areSame('lang/PagedFlickr_en', instance.yui.langs['en']); - A.areSame('lang/PagedFlickr_en-US', instance.yui.langs['en-US']); - }); - - }, - - 'server mojit instance yui - precomputed+ondemand': function() { - var fixtures = libpath.join(__dirname, '../fixtures/precomputed-ondemand'); - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var instance = { type:'PagedFlickr' }; - store.expandInstance(instance, {}, function(err, instance) { - A.isNotUndefined(instance.yui); - - A.isArray(instance.yui.requires); - AA.contains('mojito', instance.yui.requires); - AA.contains('mojito-dispatcher', instance.yui.requires); - AA.contains('mojito-controller-context', instance.yui.requires); - AA.contains('mojito-action-context', instance.yui.requires); - AA.contains('mojito-output-adapter-addon', instance.yui.requires); - AA.contains('mojito-deploy-addon', instance.yui.requires); - AA.contains('mojito-partial-addon', instance.yui.requires); - AA.contains('mojito-url-addon', instance.yui.requires); - AA.contains('mojito-mu', instance.yui.requires); - AA.contains('mojito-util', instance.yui.requires); - AA.contains('mojito-view-renderer', instance.yui.requires); - AA.contains('ModelFlickr', instance.yui.requires); - AA.contains('PagedFlickr', instance.yui.requires); - AA.contains('lang/PagedFlickr_de', instance.yui.requires); - AA.contains('lang/PagedFlickr_en', instance.yui.requires); - AA.contains('lang/PagedFlickr_en-US', instance.yui.requires); - - A.isArray(instance.yui.sorted); - AA.doesNotContain('intl', instance.yui.sorted); - AA.doesNotContain('datatype-date-format', instance.yui.sorted); - AA.contains('mojito', instance.yui.sorted); - AA.doesNotContain('mojito-util', instance.yui.sorted); - AA.doesNotContain('mojito-intl-addon', instance.yui.sorted); - AA.contains('lang/PagedFlickr_de', instance.yui.sorted); - AA.contains('lang/PagedFlickr_en', instance.yui.sorted); - AA.contains('lang/PagedFlickr_en-US', instance.yui.sorted); - AA.doesNotContain('lang/datatype-date-format_de', instance.yui.sorted); - AA.doesNotContain('lang/datatype-date-format_en', instance.yui.sorted); - AA.doesNotContain('lang/datatype-date-format_en-US', instance.yui.sorted); - - A.isObject(instance.yui.sortedPaths); - A.isUndefined(instance.yui.sortedPaths['intl']); - A.isUndefined(instance.yui.sortedPaths['datatype-date-format']); - A.isNotUndefined(instance.yui.sortedPaths['mojito']); - A.isUndefined(instance.yui.sortedPaths['mojito-util']); - A.isUndefined(instance.yui.sortedPaths['mojito-intl-addon']); - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.js'), instance.yui.sortedPaths['PagedFlickr']); - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_de.js'), instance.yui.sortedPaths['lang/PagedFlickr_de']); - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_en.js'), instance.yui.sortedPaths['lang/PagedFlickr_en']); - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/lang/PagedFlickr_en-US.js'), instance.yui.sortedPaths['lang/PagedFlickr_en-US']); - A.isUndefined(instance.yui.sortedPaths['lang/datatype-date-format_de']); - A.isUndefined(instance.yui.sortedPaths['lang/datatype-date-format_en']); - A.isUndefined(instance.yui.sortedPaths['lang/datatype-date-format_en-US']); - - A.isObject(instance.yui.langs); - A.areSame('lang/PagedFlickr_de', instance.yui.langs['de']); - A.areSame('lang/PagedFlickr_en', instance.yui.langs['en']); - A.areSame('lang/PagedFlickr_en-US', instance.yui.langs['en-US']); - }); - - }, - 'dynamic handling of mojit definition.json': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); @@ -465,70 +271,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }); }, - 'rollups mojits': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var rollups = store.getRollupsMojits('client', {}); - // We'll just check test_mojit_1, so that this test doesn't break as others add mojits to the fixtures - var rollup = rollups['rollups']; - A.isNotUndefined(rollup); - A.areEqual(rollup.dest, libpath.join(fixtures,'mojits/rollups/rollup.client.js'), 'wrong dest'); - A.areEqual(3, rollup.srcs.length, 'wrong number of sources'); - rollup.srcs.sort(); - A.areEqual(rollup.srcs[0], libpath.join(fixtures,'mojits/rollups/binders/index.js')); - A.areEqual(rollup.srcs[1], libpath.join(fixtures,'mojits/rollups/controller.common.js')); - A.areEqual(rollup.srcs[2], libpath.join(fixtures,'mojits/rollups/models/model.client.js')); - }, - - 'rollups app': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var rollup = store.getRollupsApp('client', {}); - A.isNotUndefined(rollup); - A.areEqual(rollup.dest, libpath.join(fixtures,'rollup.client.js')); - // Hmmm... since the rollups.src list contains a great deal of the - // Mojito framework YUI modules, and since those change often, it's - // a bit fragile to do a hard test for specific values. - // So, instead, we'll just test for a few things. - AA.contains(libpath.join(fixtures, 'models/flickr.common.js'), rollup.srcs); - AA.contains(libpath.join(mojitoRoot, 'app/autoload/mojito.common.js'), rollup.srcs); - AA.contains(libpath.join(mojitoRoot, 'app/autoload/mojito-client.client.js'), rollup.srcs); - }, - - 'inline css mojits': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - var inlines = store.getInlineCssMojits('client', {}); - // We'll just check test_mojit_1, so that this test doesn't break as others add mojits to the fixtures - var found = 0; - Y.Array.each(inlines, function(inline) { - if (inline.mojitName !== 'inlinecss') { - return; - } - ++found; - if (inline.context.device && 'iphone' === inline.context.device) { - A.areEqual(inline.dest, libpath.join(fixtures,'mojits/inlinecss/autoload/compiled/inlinecss.iphone.common.js')); - A.areEqual(inline.yuiModuleName, 'inlinecss/inlinecss'); - A.areEqual(3, Object.keys(inline.srcs).length); - A.areEqual(inline.srcs['/static/inlinecss/assets/foo.css'], libpath.join(fixtures,'mojits/inlinecss/assets/foo.css')); - A.areEqual(inline.srcs['/static/inlinecss/assets/bar.iphone.css'], libpath.join(fixtures,'mojits/inlinecss/assets/bar.iphone.css')); - A.areEqual(inline.srcs['/static/inlinecss/assets/deeper/foo.css'], libpath.join(fixtures,'mojits/inlinecss/assets/deeper/foo.css')); - } - else { - A.areEqual(inline.dest, libpath.join(fixtures,'mojits/inlinecss/autoload/compiled/inlinecss.common.js')); - A.areEqual(inline.yuiModuleName, 'inlinecss/inlinecss'); - A.areEqual(3, Object.keys(inline.srcs).length); - A.areEqual(inline.srcs['/static/inlinecss/assets/foo.css'], libpath.join(fixtures,'mojits/inlinecss/assets/foo.css')); - A.areEqual(inline.srcs['/static/inlinecss/assets/bar.css'], libpath.join(fixtures,'mojits/inlinecss/assets/bar.css')); - A.areEqual(inline.srcs['/static/inlinecss/assets/deeper/foo.css'], libpath.join(fixtures,'mojits/inlinecss/assets/deeper/foo.css')); - } - }); - A.areEqual(2, found); - }, - 'multi preload': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); @@ -651,59 +393,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { AA.contains('soloMojit', list); }, - 'stuff with ctx{lang:}, in language fallback': function() { - var fixtures = libpath.join(__dirname, '../fixtures/gsg5'), - store = new Y.mojito.ResourceStore({ root: fixtures }), - ctx, spec; - store.preload(); - - // first test - ctx = { lang: 'en-US' }; - spec = { type: 'PagedFlickr' }; - store.expandInstance(spec, ctx, function(err, instance) { - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:en-US}'); - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en'], 'en is undefined {lang:en-US}'); - - // second test - ctx = { lang: 'en' }; - spec = { type: 'PagedFlickr' }; - store.expandInstance(spec, ctx, function(err, instance) { - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en'], 'en is undefined {lang-en}'); - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:en}'); - - // third test - ctx = { lang: 'de-US' }; - spec = { type: 'PagedFlickr' }; - store.expandInstance(spec, ctx, function(err, instance) { - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_de'], 'de is undefined {lang:de-US}'); - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:de-US}'); - - // fourth test - ctx = { lang: 'xy-ZU' }; - spec = { type: 'PagedFlickr' }; - store.expandInstance(spec, ctx, function(err, instance) { - A.isTrue( - Boolean(instance.yui.sortedPaths['lang/PagedFlickr_de']), - 'de is undefined {lang:xy-ZU}' - ); - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:xy-ZU}'); - - // fifth test - ctx = {}; - spec = { type: 'PagedFlickr' }; - store.expandInstance(spec, ctx, function(err, instance) { - A.isTrue( - Boolean(instance.yui.sortedPaths['lang/PagedFlickr_de']), - 'de is undefined {}' - ); - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {}'); - }); - }); - }); - }); - }); - }, - 'bad files': function() { var fixtures = libpath.join(__dirname, '../fixtures/badfiles'), store = new Y.mojito.ResourceStore({ root: fixtures }); @@ -783,22 +472,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.skip(); }, - 'appConfig yui.base': function() { - var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'), - store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var spec = { type: 'PagedFlickr' }; - store.expandInstance(spec, {}, function(err, instance) { - A.areSame('/foo/', instance.yui.sortedPaths['oop'].substr(0, 5)); - A.areSame('/foo/', instance.yui.sortedPaths['intl'].substr(0, 5)); - A.areSame('/foo/', instance.yui.sortedPaths['jsonp'].substr(0, 5)); - A.areSame('/foo/', instance.yui.sortedPaths['yql'].substr(0, 5)); - A.areSame('/foo/', instance.yui.sortedPaths['querystring-parse'].substr(0, 5)); - A.areSame('/foo/', instance.yui.sortedPaths['querystring-stringify'].substr(0, 5)); - A.areSame('/foo/', instance.yui.sortedPaths['json-stringify'].substr(0, 5)); - }); - }, - 'sortedReaddirSync() sorts the result of fs.readdirSync()': function() { var mockfs = Mock(); @@ -821,6 +494,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isTrue(store._skipBadPath({ isFile: true, ext: '.js~' }), 'need to skip bad file naems'); A.isFalse(store._skipBadPath({ isFile: false, ext: '.js~' }), 'need to not-skip bad directory names'); A.isFalse(store._skipBadPath({ isFile: true, ext: '.js' }), 'need to not-skip good file names'); + }, 'load node_modules': function() { @@ -828,6 +502,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); + // TODO: _mojitMeta is gone. query for resources directly instead if (!store._mojitMeta.server.a && !store._mojitMeta.server.aa && !store._mojitMeta.server.ba) { // This happens when mojito is installed via npm, since npm // won't install the node_modules/ directories in @@ -868,7 +543,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var dir = libpath.join(__dirname, '../fixtures/conventions'); var pkg = { name: 'test', version: '6.6.6' }; var mojitType = 'testing'; - var ress = store._findResourcesByConvention(dir, pkg, mojitType) + var ress = store._findResourcesByConvention(dir, 'app', pkg, mojitType) var r, res; for (r = 0; r < ress.length; r++) { @@ -876,23 +551,21 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isNotUndefined(res.id, 'no resource id'); switch (res.id) { case 'action--x': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('action', res.type); A.areSame('x', res.name); - A.areSame('action-x', res.yuiModuleName); - switch (res.shortPath) { - case 'x.common.js': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + switch (res.source.fs.basename) { + case 'x.common': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + A.areSame('x', res.name); break; - case 'x.common.iphone.js': - A.areSame('device=iphone', res.pathParts.contextKey); - A.areSame('iphone', res.pathParts.contextParts.device); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + case 'x.common.iphone': + A.areSame('iphone', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + A.areSame('x', res.name); break; default: A.fail('unknown resource ' + res.source.fs.fullPath); @@ -900,35 +573,31 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { } break; case 'action--y/z': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('action', res.type); A.areSame('y/z', res.name); - A.areSame('y/z.common.js', res.shortPath); - A.areSame('action-y-z', res.yuiModuleName); - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('z', res.pathParts.shortFile); + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + A.areSame('z.common', res.source.fs.basename); break; case 'addon-a-x': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('addon', res.type); - A.areSame('a', res.addonType); + A.areSame('a', res.subtype); A.areSame('x', res.name); - A.areSame('addon-a-x', res.yuiModuleName); - switch (res.shortPath) { - case 'x.common.js': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + switch (res.source.fs.basename) { + case 'x.common': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + A.areSame('x', res.name); break; - case 'x.common.iphone.js': - A.areSame('device=iphone', res.pathParts.contextKey); - A.areSame('iphone', res.pathParts.contextParts.device); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + case 'x.common.iphone': + A.areSame('iphone', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); + A.areSame('x', res.name); break; default: A.fail('unknown resource ' + res.source.fs.fullPath); @@ -936,54 +605,48 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { } break; case 'archetype-x-y': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('archetype', res.type); A.areSame('x', res.subtype); A.areSame('y', res.name); - A.areSame('y', res.shortPath); + A.areSame('y', res.source.fs.basename); break; - case 'asset--x.css': - A.areSame(pkg, res.pkg); + case 'asset-css-x': + A.areSame(pkg, res.source.pkg); A.areSame('asset', res.type); - A.areSame('css', res.assetType); - A.areSame('x.css', res.name); - switch (res.shortPath) { - case 'x.css': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.css', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + A.areSame('css', res.subtype); + A.areSame('x', res.name); + switch (res.source.fs.basename) { + case 'x': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.css', res.source.fs.ext); break; - case 'x.iphone.css': - A.areSame('device=iphone', res.pathParts.contextKey); - A.areSame('iphone', res.pathParts.contextParts.device); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.css', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + case 'x.iphone': + A.areSame('iphone', res.selector); + A.areSame('common', res.affinity); + A.areSame('.css', res.source.fs.ext); break; default: A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; - case 'asset--y/z.css': - A.areSame(pkg, res.pkg); + case 'asset-css-y/z': + A.areSame(pkg, res.source.pkg); A.areSame('asset', res.type); - A.areSame('css', res.assetType); - A.areSame('y/z.css', res.name); - switch (res.shortPath) { - case 'y/z.css': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.css', res.pathParts.ext); - A.areSame('z', res.pathParts.shortFile); + A.areSame('css', res.subtype); + A.areSame('y/z', res.name); + switch (res.source.fs.basename) { + case 'z': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.css', res.source.fs.ext); break; - case 'y/z.android.css': - A.areSame('device=android', res.pathParts.contextKey); - A.areSame('android', res.pathParts.contextParts.device); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.css', res.pathParts.ext); - A.areSame('z', res.pathParts.shortFile); + case 'z.android': + A.areSame('android', res.selector); + A.areSame('common', res.affinity); + A.areSame('.css', res.source.fs.ext); break; default: A.fail('unknown resource ' + res.source.fs.fullPath); @@ -991,23 +654,19 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { } break; case 'binder--x': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('binder', res.type); A.areSame('x', res.name); - A.areSame('x', res.yuiModuleName); - switch (res.shortPath) { - case 'x.js': - A.areSame('*', res.pathParts.contextKey); - A.areSame('client', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + switch (res.source.fs.basename) { + case 'x': + A.areSame('*', res.selector); + A.areSame('client', res.affinity); + A.areSame('.js', res.source.fs.ext); break; - case 'x.iphone.js': - A.areSame('device=iphone', res.pathParts.contextKey); - A.areSame('iphone', res.pathParts.contextParts.device); - A.areSame('client', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + case 'x.iphone': + A.areSame('iphone', res.selector); + A.areSame('client', res.affinity); + A.areSame('.js', res.source.fs.ext); break; default: A.fail('unknown resource ' + res.source.fs.fullPath); @@ -1015,35 +674,32 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { } break; case 'command--x': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('command', res.type); A.areSame('x', res.name); - A.areSame('x.js', res.shortPath); + A.areSame('x', res.source.fs.basename); break; case 'config--config': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('config', res.type); A.areSame('config', res.name); - A.areSame('config.json', res.shortPath); + A.areSame('config', res.source.fs.basename); + A.areSame('.json', res.source.fs.ext); break; case 'controller--controller': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('controller', res.type); A.areSame('controller', res.name); - A.areSame('controller', res.yuiModuleName); - switch (res.shortPath) { - case 'controller.common.js': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('controller', res.pathParts.shortFile); + switch (res.source.fs.basename) { + case 'controller.common': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.js', res.source.fs.ext); break; - case 'controller.server.iphone.js': - A.areSame('device=iphone', res.pathParts.contextKey); - A.areSame('iphone', res.pathParts.contextParts.device); - A.areSame('server', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('controller', res.pathParts.shortFile); + case 'controller.server.iphone': + A.areSame('iphone', res.selector); + A.areSame('server', res.affinity); + A.areSame('.js', res.source.fs.ext); break; default: A.fail('unknown resource ' + res.source.fs.fullPath); @@ -1051,176 +707,55 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { } break; case 'middleware--x': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('middleware', res.type); A.areSame('x', res.name); - A.areSame('x.js', res.shortPath); + A.areSame('x', res.source.fs.basename); + A.areSame('.js', res.source.fs.ext); break; case 'spec--default': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('spec', res.type); A.areSame('default', res.name); - A.areSame('testing', res.specName); - A.areSame('default.json', res.shortPath); + A.areSame('default', res.source.fs.basename); + A.areSame('.json', res.source.fs.ext); break; case 'spec--x': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('spec', res.type); + A.areSame('testing', res.mojit); A.areSame('x', res.name); - A.areSame('testing:x', res.specName); - A.areSame('x.json', res.shortPath); + A.areSame('x', res.source.fs.basename); + A.areSame('.json', res.source.fs.ext); break; case 'view--x': - A.areSame(pkg, res.pkg); + A.areSame(pkg, res.source.pkg); A.areSame('view', res.type); A.areSame('x', res.name); A.areSame('html', res.viewOutputFormat); A.areSame('mu', res.viewEngine); - switch (res.shortPath) { - case 'x.mu.html': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.html', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + switch (res.source.fs.basename) { + case 'x.mu': + A.areSame('*', res.selector); + A.areSame('common', res.affinity); + A.areSame('.html', res.source.fs.ext); break; - case 'x.iphone.mu.html': - A.areSame('device=iphone', res.pathParts.contextKey); - A.areSame('iphone', res.pathParts.contextParts.device); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.html', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); + case 'x.iphone.mu': + A.areSame('iphone', res.selector); + A.areSame('common', res.affinity); + A.areSame('.html', res.source.fs.ext); break; default: A.fail('unknown resource ' + res.source.fs.fullPath); break; } break; - case 'yui-lang-': - A.areSame(pkg, res.pkg); - A.areSame('yui-lang', res.type); - A.areSame('', res.name); - A.areSame('', res.langCode); - A.areSame('testing.js', res.shortPath); - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('testing', res.pathParts.shortFile); - break; - case 'yui-lang-de': - A.areSame(pkg, res.pkg); - A.areSame('yui-lang', res.type); - A.areSame('de', res.name); - A.areSame('de', res.langCode); - A.areSame('testing_de.js', res.shortPath); - A.areSame('lang=de', res.pathParts.contextKey); - A.areSame('de', res.pathParts.contextParts.lang); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('testing_de', res.pathParts.shortFile); - break; - case 'yui-lang-en': - A.areSame(pkg, res.pkg); - A.areSame('yui-lang', res.type); - A.areSame('en', res.name); - A.areSame('en', res.langCode); - A.areSame('testing_en.js', res.shortPath); - A.areSame('lang=en', res.pathParts.contextKey); - A.areSame('en', res.pathParts.contextParts.lang); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('testing_en', res.pathParts.shortFile); - break; - case 'yui-lang-en-US': - A.areSame(pkg, res.pkg); - A.areSame('yui-lang', res.type); - A.areSame('en-US', res.name); - A.areSame('en-US', res.langCode); - A.areSame('testing_en-US.js', res.shortPath); - A.areSame('lang=en-US', res.pathParts.contextKey); - A.areSame('en-US', res.pathParts.contextParts.lang); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('testing_en-US', res.pathParts.shortFile); - break; - case 'yui-module-m': - A.areSame(pkg, res.pkg); - A.areSame('yui-module', res.type); - A.areSame('m', res.name); - A.areSame('m', res.yuiModuleName); - switch (res.shortPath) { - case 'm.common.js': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('m', res.pathParts.shortFile); - break; - case 'm.common.iphone.js': - A.areSame('device=iphone', res.pathParts.contextKey); - A.areSame('iphone', res.pathParts.contextParts.device); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('m', res.pathParts.shortFile); - break; - default: - A.fail('unknown resource ' + res.source.fs.fullPath); - break; - } - break; - case 'yui-module-x': - A.areSame(pkg, res.pkg); - A.areSame('yui-module', res.type); - A.areSame('x', res.name); - A.areSame('x', res.yuiModuleName); - switch (res.shortPath) { - case 'x.common.js': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); - break; - case 'x.common.iphone.js': - A.areSame('device=iphone', res.pathParts.contextKey); - A.areSame('iphone', res.pathParts.contextParts.device); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('x', res.pathParts.shortFile); - break; - default: - A.fail('unknown resource ' + res.source.fs.fullPath); - break; - } - break; - case 'yui-module-z': - A.areSame(pkg, res.pkg); - A.areSame('yui-module', res.type); - A.areSame('z', res.name); - A.areSame('z', res.yuiModuleName); - switch (res.shortPath) { - case 'y/z.common.js': - A.areSame('*', res.pathParts.contextKey); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('z', res.pathParts.shortFile); - break; - case 'y/z.common.android.js': - A.areSame('device=android', res.pathParts.contextKey); - A.areSame('android', res.pathParts.contextParts.device); - A.areSame('common', res.pathParts.affinity.affinity); - A.areSame('.js', res.pathParts.ext); - A.areSame('z', res.pathParts.shortFile); - break; - default: - A.fail('unknown resource ' + res.source.fs.fullPath); - break; - } - break; - default: A.fail('unknown resource ' + res.id); break; } } - A.areSame(31, ress.length, 'wrong number of resources'); + A.areSame(21, ress.length, 'wrong number of resources'); } })); From 33d984bf3242aca3464b13a0372d8e3e6b57114f Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 26 Jun 2012 15:08:26 -0700 Subject: [PATCH 046/119] updated resource-store-adapter tests. fixed some RS regressions --- source/lib/store.server.js | 5 ++- .../resource-store-adapter-tests.common.js | 39 +++---------------- 2 files changed, 10 insertions(+), 34 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 4ac383ca4..86596cac1 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -458,6 +458,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { spec.instanceId = Y.guid(); } + spec.appConfig = this.getAppConfig(ctx); + delete spec.appConfig.specs; + try { this.getMojitTypeDetails(env, ctx, spec.type, spec); } catch (err2) { @@ -1209,7 +1212,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // look in resources idParts = spec.base.split(':'); mojitType = idParts.shift(); - specName = idParts.join(':'); + specName = idParts.join(':') || 'default'; ress = this.getResources(env, ctx, {type: 'spec', mojit: mojitType, name: specName}); if (1 === ress.length) { base = this.config.readConfigYCB(ress[0].source.fs.fullPath, ctx); diff --git a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js index 7f93d45b0..551c32423 100644 --- a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js +++ b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js @@ -144,7 +144,8 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance){ - A.isTrue(instance.actions.length === 2); + A.isNotUndefined(instance.yui.config.modules['test_mojit_1_actions_test_1']); + A.isNotUndefined(instance.yui.config.modules['test_mojit_1_actions_test_2']); }); }, @@ -162,9 +163,9 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { testKey3: 'other' } }; - store.expandInstance(command, {}, function(err, instance){ - A.isTrue(instance.appConfig.testKey2 === 'testVal2'); - A.isTrue(instance.appConfig.testKey3 === 'other'); + store.expandInstance(command, {}, function(err, instance) { + A.areSame('testVal2', instance.appConfig.testKey2); + A.areSame('other', instance.appConfig.testKey3); }); }, @@ -209,7 +210,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'TestMojit2'}; store.expandInstance(instance, {}, function(err, instance){ - A.isNotUndefined(instance.controller); + A.isNotUndefined(instance['controller-path']); A.areSame('/static/TestMojit2/assets', instance.assetsRoot); A.isNotUndefined(instance.yui.config.modules.test_mojit_2); }); @@ -243,34 +244,6 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }); }, - 'server mojit view index1.mu.html is loaded correctly': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit3', action: 'index1'}; - store.expandInstance(instance, {device:'forotheriphone'}, function(err, instance){ - A.areSame('index1.forotheriphone.mu.html', instance.views.index1['content-path'].split('/').pop()); - }); - }, - - 'server mojit view index1.iphone.mu.html is loaded correctly': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); - var instance = {type:'TestMojit3', action: 'index1'}; - store.expandInstance(instance, {device:'otheriphone'}, function(err, instance){ - A.areSame('index1.otheriphone.mu.html', instance.views.index1['content-path'].split('/').pop()); - }); - }, - 'test getSpec() from specs dir': function(){ var resourceStore, store; From 6332a05774452a6017077de632d62d6c709329a9 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 26 Jun 2012 17:19:40 -0700 Subject: [PATCH 047/119] implemented deferAllOptionalAutoloads --- source/lib/store.server.js | 9 +++++++-- source/lib/tests/autoload/store.server-tests.js | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 86596cac1..b8bd8f192 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1095,10 +1095,16 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @return {nothing} */ addResourceVersion: function(res) { + res.affinity = new Affinity(res.affinity); + + if (this._appConfigStatic.deferAllOptionalAutoloads && + 'optional' === res.affinity.type) { + return; + } + if (res.selector) { this.selectors[res.selector] = true; } - res.affinity = new Affinity(res.affinity); if (res.mojit) { if (!this._mojitRVs[res.mojit]) { this._mojitRVs[res.mojit] = []; @@ -1486,7 +1492,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (!affinities.hasOwnProperty(res.affinity)) { continue; } - // TODO: conditionally skip "-optional" affinities priority = (s * sourceBase) + selectors[res.selector] + affinities[res.affinity]; //console.log('--DEBUG-- pri=' + priority + ' --' diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index a82b5f606..3e6989ccd 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -411,7 +411,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var spec = { type: 'PagedFlickr' }; - store.expandInstance(spec, {}, function(err, instance) { + store.expandInstanceForEnv('client', spec, {}, function(err, instance) { A.isUndefined(instance.views.index['binder-yui-sorted']['breg'], 'breg'); A.isUndefined(instance.views.index['binder-yui-sorted']['dali-bean'], 'dali-bean'); A.isUndefined(instance.views.index['binder-yui-sorted']['dali-transport-base'], 'dali-transport-base'); From fb31cc0b5c42d6fd36eae18ad7f45123fe1f3962 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 26 Jun 2012 17:24:27 -0700 Subject: [PATCH 048/119] lint --- source/lib/app/addons/rs/yui.server.js | 8 +++---- source/lib/app/commands/build.js | 4 +++- source/lib/app/commands/compile.js | 33 +++++++++++++------------- source/lib/app/commands/jslint.js | 2 +- source/lib/store.server.js | 2 +- 5 files changed, 25 insertions(+), 24 deletions(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index 2122e5693..81c643609 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -297,6 +297,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { langs = {}, l, ll, + lang, langName, langNames, viewEngineRequired = {}, @@ -326,7 +327,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { modules[res.yui.name] = { requires: res.yui.meta.requires, fullpath: (('client' === env) ? res.url : res.source.fs.fullPath) - } + }; if (res.mojit === mojit && res.type !== 'yui-lang') { controllerRequired[res.yui.name] = true; } @@ -366,8 +367,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { required[controller.yui.name] = true; if (lang && lang.yui) { required[lang.yui.name] = true; - } - else { + } else { for (ll in langs) { if (langs.hasOwnProperty(ll) && ll !== '') { lang = langs[ll]; @@ -489,7 +489,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { for (p = parts.length; p > 0; p -= 1) { test = parts.slice(0, p).join('-'); if (this.sortedModules[env][poslKey][test] && - this.sortedModules[env][poslKey][test][module]) { + this.sortedModules[env][poslKey][test][module]) { return this.sortedModules[env][poslKey][test][module]; } } diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 6feb62671..ca4637e47 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -102,7 +102,9 @@ exports.run = function(params, options, callback) { // TODO: probably should try to use store to read appConfig try { appConfig = Y.JSON.parse(String(fs.readFileSync(libpath.join( - store._config.root, 'application.json')))); + store._config.root, + 'application.json' + )))); appConfig = appConfig[0]; // Is there a "builds" section for this "type" in the appConfig diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 02edbef67..f3557ca1c 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -109,6 +109,21 @@ options = [ ]; +// Returns details about all the mojits in the application. +function getAllMojits(store, env, ctx) { + var details = {}, + mojits, + m, + mojit; + mojits = store.listAllMojits(); + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; + details[mojit] = store.getMojitTypeDetails(env, ctx, mojit); + } + return details; +} + + run = function(params, options, callback) { var store = new ResourceStore(process.cwd()), displayResults, @@ -437,7 +452,7 @@ compile.rollups = function(context, options, callback) { rollupBody = ''; for (i = 0; i < rollup.srcs.length; i += 1) { src = rollup.srcs[i]; - if (!options['core'] || src.match(/\/mojito\//)) { + if (!options.core || src.match(/\/mojito\//)) { rollupBody += fs.readFileSync(src, 'utf-8'); } } @@ -955,22 +970,6 @@ removeFile = function(file) { }; -// Returns details about all the mojits in the application. -function getAllMojits(store, env, ctx) -{ - var details = {}, - mojits, - m, - mojit; - mojits = store.listAllMojits(); - for (m = 0; m < mojits.length; m += 1) { - mojit = mojits[m]; - details[mojit] = store.getMojitTypeDetails(env, ctx, mojit); - } - return details; -} - - /* * Used for writing out YUI modules containing cached content */ diff --git a/source/lib/app/commands/jslint.js b/source/lib/app/commands/jslint.js index 2d10560c5..336859880 100644 --- a/source/lib/app/commands/jslint.js +++ b/source/lib/app/commands/jslint.js @@ -364,7 +364,7 @@ function processFiles(inDir, outDir, excludeMatcher) { if (!outDir) { out = new OutputStdout(relname); } else { - out = new OutputFile(path.join(outDir, outname)) + out = new OutputFile(path.join(outDir, outname)); } errors = lintOneFile(f, out); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index b8bd8f192..c4491f6c9 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1098,7 +1098,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { res.affinity = new Affinity(res.affinity); if (this._appConfigStatic.deferAllOptionalAutoloads && - 'optional' === res.affinity.type) { + 'optional' === res.affinity.type) { return; } From e729db89ba301be1d89e0372a382e672fb7d241c Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 27 Jun 2012 13:41:26 -0700 Subject: [PATCH 049/119] expandInstanceForEnv() caching --- source/lib/store.server.js | 14 +++++++++++++- source/lib/tests/autoload/store.server-tests.js | 3 ++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index c4491f6c9..027565686 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -156,6 +156,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._mojitRVs = {}; // mojitType: array of resource versions this._appResources = {}; // env: posl: array of resources this._mojitResources = {}; // env: posl: mojitType: array of resources + this._expandInstanceCache = { // env: cacheKey: instance + client: {}, + server: {} + }; // all selectors that are actually in the app // hash: key is selector, value is just boolean true @@ -440,10 +444,17 @@ YUI.add('mojito-resource-store', function(Y, NAME) { expandInstanceForEnv: function(env, instance, ctx, cb) { - var spec, + var cacheKey = Y.JSON.stringify(instance) + Y.JSON.stringify(ctx), + cacheValue = this._expandInstanceCache[env][cacheKey], + spec, typeDetails, config; + if (cacheValue) { + cb(null, this.cloneObj(cacheValue)); + return; + } + // TODO: should this be done here, or somewhere else? ctx.runtime = env; @@ -472,6 +483,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { spec.config = config; } + this._expandInstanceCache[env][cacheKey] = this.cloneObj(spec); cb(null, spec); }, diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 3e6989ccd..819640f8f 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -265,7 +265,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { store.preload(); var instance = 'foo'; var context = {}; - store._expandInstanceCache.server[Y.JSON.stringify(instance)+Y.JSON.stringify(context)] = 'bar'; + var key = Y.JSON.stringify(instance) + Y.JSON.stringify(context); + store._expandInstanceCache.server[key] = 'bar'; store.expandInstance(instance, context, function(err, instance) { A.areEqual('bar', instance); }); From 3fa37b3ec6e717c6ea5d48373208e3e40f5e4912 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 27 Jun 2012 14:28:22 -0700 Subject: [PATCH 050/119] allow the RS to have a configurable mojitoRoot, mainly to get coverage unit tests working --- source/lib/store.server.js | 12 +++++++----- .../tests/autoload/app/addons/rs/yui-tests.server.js | 4 ++-- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 027565686..4039b8196 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -149,6 +149,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._config = cfg; this._config.context = this._config.context || {}; this._config.appConfig = this._config.appConfig || {}; + this._config.mojitoRoot = this._config.mojitoRoot || mojitoRoot; this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: context: YCB config object @@ -176,9 +177,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { fullpath: libpath.join(__dirname, 'app/addons/rs/config.server.js') } }); - this.plug(Y.mojito.addons.rs.config, { appRoot: this._config.root, mojitoRoot: mojitoRoot }); + this.plug(Y.mojito.addons.rs.config, { appRoot: this._config.root, mojitoRoot: this._config.mojitoRoot }); - this._fwConfig = this.config.readConfigJSON(libpath.join(mojitoRoot, 'config.json')); + this._fwConfig = this.config.readConfigJSON(libpath.join(this._config.mojitoRoot, 'config.json')); this._appConfigStatic = this.getAppConfig({}); }, destructor: function() {}, @@ -759,7 +760,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._yuiUseSync(modules); Y.Object.each(Y.mojito.addons.rs, function(fn, name) { - this.plug(fn, { appRoot: this._config.root, mojitoRoot: mojitoRoot }); + this.plug(fn, { appRoot: this._config.root, mojitoRoot: this._config.mojitoRoot }); }, this); }, @@ -803,7 +804,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // application. (they -should- have but might not have.) // FUTURE: instead walk -all- global packages? if (!walkedMojito) { - dir = libpath.join(mojitoRoot, '..'); + dir = libpath.join(this._config.mojitoRoot, '..'); info = { depth: 999, parents: [], @@ -813,7 +814,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // special case for weird packaging situations if (!Object.keys(info.pkg).length) { - info.dir = mojitoRoot; + info.dir = this._config.mojitoRoot; info.pkg = { name: 'mojito', version: '0.666.666', @@ -1837,5 +1838,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, '0.0.1', { requires: [ 'base', + 'json-stringify', 'oop' ]}); diff --git a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js index 5af979ce3..fbf1e2f9f 100644 --- a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js @@ -315,7 +315,7 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { selector: '*' }; store.addResourceVersion(res); - res = store.RVs['common/*/controller--controller']; + res = store.RVs['common/*' + '/controller--controller']; cmp(res.source, source); A.isNotUndefined(res.yui); A.areSame('X', res.yui.name); @@ -496,7 +496,7 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { 'server mojit instance yui': function() { var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); - var store = new Y.mojito.ResourceStore({ root: fixtures }); + var store = new Y.mojito.ResourceStore({ root: fixtures, mojitoRoot: mojitoRoot }); store.preload(); var instance = {type:'TestMojit2'}; From b2ac142aca75cb569acdef0da91bcc5a62d73b89 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 28 Jun 2012 10:59:06 -0700 Subject: [PATCH 051/119] reimplemented mojit package.json check. reimplemented publishing of package.json --- source/lib/app/addons/rs/url.server.js | 30 ++++++++++++++++++- source/lib/store.server.js | 19 +++++++++--- .../lib/tests/autoload/store.server-tests.js | 27 +++++------------ .../store/mojits/test_mojit_2/package.json | 6 ++-- .../store/mojits/test_mojit_3/package.json | 6 ++-- .../store/mojits/test_mojit_4/package.json | 6 ++-- source/package.json | 1 + 7 files changed, 64 insertions(+), 31 deletions(-) diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 236b321d7..13adff77f 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -65,9 +65,11 @@ YUI.add('addon-rs-url', function(Y, NAME) { mojit, mojitRes, mojitControllerRess, + packageJson, ress, r, - res; + res, + skip; mojits.push('shared'); for (m = 0; m < mojits.length; m += 1) { @@ -87,9 +89,35 @@ YUI.add('addon-rs-url', function(Y, NAME) { } } + if (mojitRes) { + packageJson = libpath.join(mojitRes.source.fs.fullPath, 'package.json'); + packageJson = this.rs.config.readConfigJSON(packageJson); + } + ress = this.rs.getResourceVersions({mojit: mojit}); for (r = 0; r < ress.length; r += 1) { res = ress[r]; + + skip = false; + if ('config' === res.type) { + skip = true; + } + + // This is mainly used during `mojito build html5app`. + // In that situation, the user mainly doesn't want to + // publish each mojit's package.json. However, Livestand + // did need to, so this feature allowed them to opt-in. + if ('config--package' === res.id && + 'public' === (packageJson.yahoo && + packageJson.yahoo.mojito && + packageJson.yahoo.mojito.package)) { + skip = false; + } + + if (skip) { + continue; + } + this._calcResourceURL(res, mojitRes); } } diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 4039b8196..bd58bbec5 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -65,11 +65,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { var libfs = require('fs'), libglob = require('./glob'), libpath = require('path'), + libsemver = require('semver'), libwalker = require('./package-walker.server'), isNotAlphaNum = /[^a-zA-Z0-9]/, mojitoRoot = __dirname, + mojitoVersion = '0.666.666', // special case for weird packaging situations CONVENTION_SUBDIR_TYPES = { // subdir: resource type @@ -812,12 +814,14 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }; info.pkg = this.config.readConfigJSON(libpath.join(dir, 'package.json')); - // special case for weird packaging situations - if (!Object.keys(info.pkg).length) { + if (Object.keys(info.pkg).length) { + mojitoVersion = info.pkg.version; + } else { + // special case for weird packaging situations info.dir = this._config.mojitoRoot; info.pkg = { name: 'mojito', - version: '0.666.666', + version: mojitoVersion, yahoo: { mojito: { type: 'bundle', @@ -1427,7 +1431,14 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (packageJson.name) { mojitType = packageJson.name; } - // FUTURE: check NPM "engine" + + if (packageJson.engines && packageJson.engines.mojito) { + if(! libsemver.satisfies(mojitoVersion, packageJson.engines.mojito)) { + Y.log('skipping mojit because of version check ' + dir, 'warn', NAME); + return; + } + } + // TODO: register mojit's package.json as a static asset, in "static handler" plugin } diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 819640f8f..4467fd0c1 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -183,7 +183,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }); }, - 'server mojit is NOT loaded because of package mojito version miss-match': function(){ + 'server mojit is NOT loaded because of package mojito version mismatch': function(){ var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); @@ -201,28 +201,16 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }); }, - 'server a mojits package.json file is NOT publicly accessible': function(){ + 'server a mojits package.json file is available as appropriate': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - A.isTrue(typeof store.url.getPathForURL('/static/TestMojit2/package.json') === 'undefined'); + A.isUndefined(store.url.getPathForURL('/static/TestMojit2/package.json')); + A.isNotUndefined(store.url.getPathForURL('/static/TestMojit3/package.json')); + A.isUndefined(store.url.getPathForURL('/static/TestMojit5/package.json')); }, - 'server a mojits package.json file is publicly accessible': function(){ - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - A.isTrue(typeof store.url.getPathForURL('/static/TestMojit3/package.json') === 'string'); - }, - - 'server a mojit is NOT loaded because it has a package.json file with no mojito config': function(){ - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - - A.isTrue(typeof store.url.getPathForURL('/static/TestMojit5/package.json') === 'undefined'); - }, - - 'server mojit view index.mu.html is loaded correctly': function(){ + 'server mojit view index.mu.html is loaded correctly': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); @@ -382,7 +370,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); var list = store.listAllMojits('server'); - A.areSame(9, list.length, 'found the wrong number of mojits'); + A.areSame(10, list.length, 'found the wrong number of mojits'); AA.contains('DaliProxy', list); AA.contains('HTMLFrameMojit', list); AA.contains('LazyLoad', list); @@ -391,6 +379,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { AA.contains('test_mojit_1', list); AA.contains('TestMojit2', list); AA.contains('TestMojit3', list); + AA.contains('TestMojit5', list); AA.contains('soloMojit', list); }, diff --git a/source/lib/tests/fixtures/store/mojits/test_mojit_2/package.json b/source/lib/tests/fixtures/store/mojits/test_mojit_2/package.json index 5811cd49b..65f94715a 100644 --- a/source/lib/tests/fixtures/store/mojits/test_mojit_2/package.json +++ b/source/lib/tests/fixtures/store/mojits/test_mojit_2/package.json @@ -1,8 +1,8 @@ { "name": "TestMojit2", - "config": { + "yahoo": { "mojito": { - "version": "*" + "version": "0.3.x" } } -} \ No newline at end of file +} diff --git a/source/lib/tests/fixtures/store/mojits/test_mojit_3/package.json b/source/lib/tests/fixtures/store/mojits/test_mojit_3/package.json index ed1f90df3..c98083c6f 100644 --- a/source/lib/tests/fixtures/store/mojits/test_mojit_3/package.json +++ b/source/lib/tests/fixtures/store/mojits/test_mojit_3/package.json @@ -1,9 +1,11 @@ { "name": "TestMojit3", - "config": { + "yahoo": { "mojito": { - "version": "0.1.0", "package": "public" } + }, + "engines": { + "mojito": ">0.1.0" } } diff --git a/source/lib/tests/fixtures/store/mojits/test_mojit_4/package.json b/source/lib/tests/fixtures/store/mojits/test_mojit_4/package.json index 762dbdcfe..b2c6da8cc 100644 --- a/source/lib/tests/fixtures/store/mojits/test_mojit_4/package.json +++ b/source/lib/tests/fixtures/store/mojits/test_mojit_4/package.json @@ -1,9 +1,11 @@ { "name": "TestMojit4", - "config": { + "yahoo": { "mojito": { - "version": "1.0.0", "package": "public" } + }, + "engines": { + "mojito": "<0.1.0" } } diff --git a/source/package.json b/source/package.json index 9300bc71e..d4f7d3ea7 100644 --- a/source/package.json +++ b/source/package.json @@ -19,6 +19,7 @@ "connect": "1.8.2", "express": "2.5.2", "mime": "1.2.4", + "semver": "1.0.14", "yui": "~3.5.1", "yuitest": "~0.7.4" }, From a88b834d528a3edc68355123e9227a248de1e44d Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 28 Jun 2012 11:55:48 -0700 Subject: [PATCH 052/119] fixed a couple of low-hanging unit tests --- source/lib/store.server.js | 97 +++++++++++-------- .../lib/tests/autoload/store.server-tests.js | 22 ++--- 2 files changed, 64 insertions(+), 55 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index bd58bbec5..02b1c2bfa 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -62,11 +62,7 @@ */ YUI.add('mojito-resource-store', function(Y, NAME) { - var libfs = require('fs'), - libglob = require('./glob'), - libpath = require('path'), - libsemver = require('semver'), - libwalker = require('./package-walker.server'), + var libs = {}; isNotAlphaNum = /[^a-zA-Z0-9]/, @@ -106,6 +102,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { 'view': 'common' }; + libs.fs = require('fs'); + libs.glob = require('./glob'); + libs.path = require('path'); + libs.semver = require('semver'); + libs.walker = require('./package-walker.server'); // The Affinity object is to manage the use of the affinity string in @@ -148,6 +149,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.extend(ResourceStore, Y.Base, { initializer: function(cfg) { + var i; + this._config = cfg; this._config.context = this._config.context || {}; this._config.appConfig = this._config.appConfig || {}; @@ -155,6 +158,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: context: YCB config object + this._libs = {}; + for (i in libs) { + if (libs.hasOwnProperty(i)) { + this._libs[i] = libs[i]; + } + } + this._appRVs = []; // array of resource versions this._mojitRVs = {}; // mojitType: array of resource versions this._appResources = {}; // env: posl: array of resources @@ -176,12 +186,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // We'll start with just our "config" addon. this._yuiUseSync({ 'addon-rs-config': { - fullpath: libpath.join(__dirname, 'app/addons/rs/config.server.js') + fullpath: this._libs.path.join(__dirname, 'app/addons/rs/config.server.js') } }); this.plug(Y.mojito.addons.rs.config, { appRoot: this._config.root, mojitoRoot: this._config.mojitoRoot }); - this._fwConfig = this.config.readConfigJSON(libpath.join(this._config.mojitoRoot, 'config.json')); + this._fwConfig = this.config.readConfigJSON(this._libs.path.join(this._config.mojitoRoot, 'config.json')); this._appConfigStatic = this.getAppConfig({}); }, destructor: function() {}, @@ -239,7 +249,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { appConfig = this.cloneObj(this._fwConfig.appConfigBase); // apply the read values from the file - ycb = this.config.readConfigYCB(libpath.join(this._config.root, 'application.json'), ctx); + ycb = this.config.readConfigYCB(this._libs.path.join(this._config.root, 'application.json'), ctx); this.mergeRecursive(appConfig, ycb); // apply the passed-in overrides @@ -629,7 +639,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { path = routesFiles[p]; // relative paths are relative to the application if ('/' !== path.charAt(1)) { - path = libpath.join(this._config.root, path); + path = this._libs.path.join(this._config.root, path); } fixedPaths[path] = true; } @@ -791,7 +801,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._appRVs = []; this._mojitRVs = {}; - walker = new libwalker.BreadthFirst(this._config.root); + walker = new this._libs.walker.BreadthFirst(this._config.root); walker.walk(function(err, info) { if (err) { throw err; @@ -806,13 +816,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // application. (they -should- have but might not have.) // FUTURE: instead walk -all- global packages? if (!walkedMojito) { - dir = libpath.join(this._config.mojitoRoot, '..'); + dir = this._libs.path.join(this._config.mojitoRoot, '..'); info = { depth: 999, parents: [], dir: dir }; - info.pkg = this.config.readConfigJSON(libpath.join(dir, 'package.json')); + info.pkg = this.config.readConfigJSON(this._libs.path.join(dir, 'package.json')); if (Object.keys(info.pkg).length) { mojitoVersion = info.pkg.version; @@ -1021,7 +1031,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); return; } - res.name = libpath.join(fs.subDirArray.join('/'), baseParts.join('.')); + res.name = this._libs.path.join(fs.subDirArray.join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); // special case if ('addon' === type && ADDON_SUBTYPES_APPLEVEL[res.subtype]) { @@ -1047,7 +1057,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.log('invalid ' + type + ' filename. skipping ' + fs.fullPath, 'warn', NAME); return; } - res.name = libpath.join(fs.subDirArray.join('/'), baseParts.join('.')); + res.name = this._libs.path.join(fs.subDirArray.join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); return res; } @@ -1065,7 +1075,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.log('invalid spec filename. skipping ' + source.fs.fullPath, 'warn', NAME); return; } - res.name = libpath.join(source.fs.subDir, baseParts.join('.')); + res.name = this._libs.path.join(source.fs.subDir, baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); return res; } @@ -1089,7 +1099,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { Y.log('invalid view filename. skipping ' + fs.fullPath, 'warn', NAME); return; } - res.name = libpath.join(fs.subDirArray.join('/'), baseParts.join('.')); + res.name = this._libs.path.join(fs.subDirArray.join('/'), baseParts.join('.')); res.id = [res.type, res.subtype, res.name].join('-'); return res; } @@ -1213,6 +1223,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // PRIVATE METHODS + // used for unit testing + // TODO DOCS + _mockLib: function(name, lib) { + this._libs[name] = lib; + }, + + // TODO DOCS _expandSpec: function(env, ctx, spec) { var appConfig, @@ -1285,11 +1302,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } switch (info.pkg.yahoo.mojito.type) { case 'bundle': - dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); + dir = this._libs.path.join(info.dir, info.pkg.yahoo.mojito.location); this._preloadDirBundle(dir, pkg); break; case 'mojit': - dir = libpath.join(info.dir, info.pkg.yahoo.mojito.location); + dir = this._libs.path.join(info.dir, info.pkg.yahoo.mojito.location); this._preloadDirMojit(dir, 'pkg', pkg); break; default: @@ -1358,7 +1375,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { res = ress[r]; this.addResourceVersion(res); } - this._preloadDirMojits(libpath.join(dir, 'mojits'), 'bundle', pkg); + this._preloadDirMojits(this._libs.path.join(dir, 'mojits'), 'bundle', pkg); }, @@ -1380,10 +1397,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { childPath; if ('/' !== dir.charAt(0)) { - dir = libpath.join(this._config.root, dir); + dir = this._libs.path.join(this._config.root, dir); } - if (!libpath.existsSync(dir)) { + if (!this._libs.path.existsSync(dir)) { return; } @@ -1393,7 +1410,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if ('.' === childName.substring(0, 1)) { continue; } - childPath = libpath.join(dir, childName); + childPath = this._libs.path.join(dir, childName); this._preloadDirMojit(childPath, dirType, pkg); } }, @@ -1418,22 +1435,22 @@ YUI.add('mojito-resource-store', function(Y, NAME) { res; if ('/' !== dir.charAt(0)) { - dir = libpath.join(this._config.root, dir); + dir = this._libs.path.join(this._config.root, dir); } - if (!libpath.existsSync(dir)) { + if (!this._libs.path.existsSync(dir)) { return; } - mojitType = libpath.basename(dir); - packageJson = this.config.readConfigJSON(libpath.join(dir, 'package.json')); + mojitType = this._libs.path.basename(dir); + packageJson = this.config.readConfigJSON(this._libs.path.join(dir, 'package.json')); if (packageJson) { if (packageJson.name) { mojitType = packageJson.name; } if (packageJson.engines && packageJson.engines.mojito) { - if(! libsemver.satisfies(mojitoVersion, packageJson.engines.mojito)) { + if(! this._libs.semver.satisfies(mojitoVersion, packageJson.engines.mojito)) { Y.log('skipping mojit because of version check ' + dir, 'warn', NAME); return; } @@ -1442,7 +1459,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // TODO: register mojit's package.json as a static asset, in "static handler" plugin } - definitionJson = this.config.readConfigYCB(libpath.join(dir, 'definition.json'), {}); + definitionJson = this.config.readConfigYCB(this._libs.path.join(dir, 'definition.json'), {}); if (definitionJson.appLevel) { mojitType = 'shared'; } @@ -1456,7 +1473,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { rootType: dirType, subDir: '.', subDirArray: ['.'], - basename: libpath.basename(dir), + basename: this._libs.path.basename(dir), isFile: false, ext: null }, @@ -1685,17 +1702,17 @@ YUI.add('mojito-resource-store', function(Y, NAME) { source = { fs: { - fullPath: libpath.join(dir, subdir, file), + fullPath: me._libs.path.join(dir, subdir, file), rootDir: dir, rootType: dirType, subDir: subdir, subDirArray: subdir.split('/'), isFile: isFile, - ext: libpath.extname(file) + ext: me._libs.path.extname(file) }, pkg: pkg }; - source.fs.basename = libpath.basename(file, source.fs.ext); + source.fs.basename = me._libs.path.basename(file, source.fs.ext); if (me._skipBadPath(source.fs)) { return false; @@ -1748,7 +1765,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @return {array} files in the directory */ _sortedReaddirSync: function(path) { - var out = libfs.readdirSync(path); + var out = this._libs.fs.readdirSync(path); return out.sort(); }, @@ -1774,8 +1791,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { childStat; subdir = _subdir || '.'; - fulldir = libpath.join(dir, subdir); - if (!libpath.existsSync(fulldir)) { + fulldir = this._libs.path.join(dir, subdir); + if (!this._libs.path.existsSync(fulldir)) { return; } @@ -1788,9 +1805,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if ('node_modules' === childName) { continue; } - childPath = libpath.join(subdir, childName); - childFullPath = libpath.join(dir, childPath); - childStat = libfs.statSync(childFullPath); + childPath = this._libs.path.join(subdir, childName); + childFullPath = this._libs.path.join(dir, childPath); + childStat = this._libs.fs.statSync(childFullPath); if (childStat.isFile()) { cb(null, subdir, childName, true); } else if (childStat.isDirectory()) { @@ -1816,9 +1833,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { for (i = 0; i < list.length; i += 1) { glob = list[i]; if ('/' !== glob.charAt(0)) { - glob = libpath.join(prefix, glob); + glob = this._libs.path.join(prefix, glob); } - libglob.globSync(glob, {}, found); + this._libs.glob.globSync(glob, {}, found); } return found; }, diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 4467fd0c1..1d1c04a25 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -126,26 +126,24 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { store.preload(); var instance = {type:'test_mojit_1'}; - store.expandInstance(instance, {}, function(err, instance) { + store.expandInstanceForEnv('client', instance, {}, function(err, instance) { A.areSame(3, Y.Object.keys(instance.views).length); A.isObject(instance.views['test_1']); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/views/test_1.mu.html'), instance.views['test_1']['content-path']); + A.areSame('/static/test_mojit_1/views/test_1.mu.html', instance.views['test_1']['content-path']); A.areSame('mu', instance.views['test_1']['engine']); - A.areSame('/static/test_mojit_1/binders/test_1.js', instance.views['test_1']['binder-url']); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/binders/test_1.js'), instance.views['test_1']['binder-path']); + A.areSame('/static/test_mojit_1/binders/test_1.js', instance.views['test_1']['binder-path']); A.areSame('test_mojit_1Bindertest_1', instance.views['test_1']['binder-module']); A.isNotUndefined(instance.views['test_1']['binder-yui-sorted']['mojito-client']); A.isObject(instance.views['test_2']); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/views/test_2.mu.html'), instance.views['test_2']['content-path']); + A.areSame('/static/test_mojit_1/views/test_2.mu.html', instance.views['test_2']['content-path']); A.areSame('mu', instance.views['test_2']['engine']); A.isObject(instance.views['subdir/test_1']); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/views/subdir/test_1.mu.html'), instance.views['subdir/test_1']['content-path']); + A.areSame('/static/test_mojit_1/views/subdir/test_1.mu.html', instance.views['subdir/test_1']['content-path']); A.areSame('mu', instance.views['subdir/test_1']['engine']); - A.areSame('/static/test_mojit_1/binders/subdir/test_1.js', instance.views['subdir/test_1']['binder-url']); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/binders/subdir/test_1.js'), instance.views['subdir/test_1']['binder-path']); + A.areSame('/static/test_mojit_1/binders/subdir/test_1.js', instance.views['subdir/test_1']['binder-path']); A.areSame('test_mojit_1Bindersubdir/test_1', instance.views['subdir/test_1']['binder-module']); A.isNotUndefined(instance.views['subdir/test_1']['binder-yui-sorted']['mojito-client']); }); @@ -165,12 +163,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }); }, - 'dynamic handling of mojit definition.json': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - A.areSame(libpath.join(fixtures, 'mojits/test_mojit_1/definition.json'), store._dynamicURLs['/static/test_mojit_1/definition.json']); - }, - 'server mojit type name can come from package.json': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); @@ -471,8 +463,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { returns: ['d', 'c', 'a', 'b'] }); -// TODO -- store._libs = { fs: mockfs } var store = new Y.mojito.ResourceStore({ root: fixtures }); + store._mockLib('fs', mockfs); var files = store._sortedReaddirSync('dir'); AA.itemsAreSame(['a', 'b', 'c', 'd'], files); From 4d69bfaa87fa1e6318f2fe927cef39e185305c95 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 28 Jun 2012 13:56:35 -0700 Subject: [PATCH 053/119] implemented serializeClientStore() --- source/lib/app/addons/ac/deploy.server.js | 4 +-- .../autoload/resource-store-adapter.common.js | 4 +-- source/lib/store.server.js | 27 +++++++++++++++++++ .../lib/tests/autoload/store.server-tests.js | 2 +- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index 41d376c2e..6390f533c 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -97,7 +97,6 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { binder, i, id, - instances = {}, clientConfig = {}, clientConfigEscaped, clientConfigStr, @@ -137,8 +136,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { yuiConfig.loaderPath = appConfigClient.yui.loader; } - clientConfig.store = store.serializeClientStore(contextClient, - instances); + clientConfig.store = store.serializeClientStore(contextClient); usePrecomputed = appConfigServer.yui && appConfigServer.yui.dependencyCalculations && (-1 !== diff --git a/source/lib/app/autoload/resource-store-adapter.common.js b/source/lib/app/autoload/resource-store-adapter.common.js index 553888c3d..d6c6a80e3 100644 --- a/source/lib/app/autoload/resource-store-adapter.common.js +++ b/source/lib/app/autoload/resource-store-adapter.common.js @@ -187,9 +187,9 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { }, - serializeClientStore: function(ctx, instances) { + serializeClientStore: function(ctx) { //logger.log('serializeClientStore', 'warn', NAME); - return this.store.serializeClientStore(ctx, instances); + return this.store.serializeClientStore(ctx); }, diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 02b1c2bfa..508893c15 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1219,6 +1219,33 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + /** + * Returns a serializable object used to initialized Mojito on the client. + * + * FUTURE: [issue 105] cache the output of this function + * cache key: all of ctx + * + * @method serializeClientStore + * @param ctx {object} the context + * @return {object} object that should be serialized and used to initialize MojitoClient + */ + serializeClientStore: function(ctx) { + var out = {}; + out.specs = {}; + out.mojits = {}; + + out.appConfig = this.getAppConfig(ctx); + delete out.appConfig.mojitsDirs; + delete out.appConfig.mojitDirs; + delete out.appConfig.routesFiles; + delete out.appConfig.specs; + + out.routes = this.getRoutes(ctx); + + return out; + }, + + //==================================================================== // PRIVATE METHODS diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 1d1c04a25..57468d84f 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -342,7 +342,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'call serializeClientStore()': function() { var store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - var client = store.serializeClientStore({}, []); + var client = store.serializeClientStore({}); A.isObject(client, 'config is missing'); A.isObject(client.appConfig, 'missing appConfig'); A.areSame('/tunnel', client.appConfig.tunnelPrefix); From e33d11012612a82902e192c390c77d7f00fddb08 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 28 Jun 2012 14:07:40 -0700 Subject: [PATCH 054/119] small memory optimization --- source/lib/store.server.js | 1 + 1 file changed, 1 insertion(+) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 508893c15..9280e0991 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1168,6 +1168,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { posl = this.selector.getListFromContext(ctx); posls[JSON.stringify(posl)] = posl; } + ctxs = []; // free a bunch of memory for (e = 0; e < envs.length; e += 1) { env = envs[e]; From 4a26c6eaaa7005ab02a11f8bdec36b6ca3bac07f Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 28 Jun 2012 15:46:22 -0700 Subject: [PATCH 055/119] fixed addon-rs-url tests --- source/lib/tests/autoload/app/addons/rs/url-tests.server.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js index 4214d5919..6a1b01216 100644 --- a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js @@ -27,6 +27,9 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { this._appRVs = []; this._mojitRVs = {}; this.publish('getMojitTypeDetails', {emitFacade: true, preventable: false}); + this.config = { + readConfigJSON: function() { return {} } + }; }, getStaticAppConfig: function() { From b49e50159ab1c9d4ba19530d507f1e699b8902a9 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 28 Jun 2012 16:02:59 -0700 Subject: [PATCH 056/119] optimized RS unit tests --- .../resource-store-adapter-tests.common.js | 130 ++--------- .../lib/tests/autoload/store.server-tests.js | 217 ++++++++---------- 2 files changed, 112 insertions(+), 235 deletions(-) diff --git a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js index 551c32423..4e1efe2bb 100644 --- a/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js +++ b/source/lib/tests/autoload/app/autoload/resource-store-adapter-tests.common.js @@ -8,6 +8,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { var suite = new YUITest.TestSuite(NAME), path = require('path'), fixtures = path.join(__dirname, '../../../fixtures/store'), + resourceStore, dummyLog = {log: function() {}}, A = YUITest.Assert; @@ -15,37 +16,24 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { name: 'Resource Store Adapter Tests', - 'pre load': function() { - var resourceStore, - store; - + init: function() { resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); resourceStore.preload(); + }, - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + 'pre load': function() { + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); A.isTrue(store.getAppPath() === fixtures); }, 'server app config value': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var config = store.getAppConfig(null); A.isTrue(config.testKey1 === 'testVal1'); }, 'server mojit config value': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {base:'test1'}; store.expandInstance(instance, {}, function(err, instance){ A.isTrue(instance.id === 'test1'); @@ -55,13 +43,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config value via type': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance){ A.isTrue(instance.type === 'test_mojit_1'); @@ -71,13 +53,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config value via type & overrride': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = { type:'test_mojit_1', config:{testKey4: 'other'} @@ -90,13 +66,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config assets': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance){ A.isTrue(instance.assets['css/main.css'] !== undefined); @@ -105,13 +75,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config views': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance){ A.isTrue(instance.views['test_1'] !== undefined); @@ -120,13 +84,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config models': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance){ A.isTrue(instance.models['test_1'] !== undefined); @@ -135,13 +93,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config actions': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance){ A.isNotUndefined(instance.yui.config.modules['test_mojit_1_actions_test_1']); @@ -150,13 +102,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit config appConfig': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var command = { type:'test_mojit_1', appConfig:{ @@ -182,13 +128,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit instance definition override': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var command = { type:'test_mojit_1', models: { @@ -201,13 +141,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit type name can come from package.json': function() { - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'TestMojit2'}; store.expandInstance(instance, {}, function(err, instance){ A.isNotUndefined(instance['controller-path']); @@ -217,13 +151,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit view index.mu.html is loaded correctly': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'TestMojit3'}; store.expandInstance(instance, {}, function(err, instance){ A.areSame('index.mu.html', instance.views.index['content-path'].split('/').pop()); @@ -231,13 +159,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'server mojit view index.iphone.mu.html is loaded correctly': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); var instance = {type:'TestMojit3'}; store.expandInstance(instance, {device:'iphone'}, function(err, instance){ A.areSame('index.iphone.mu.html', instance.views.index['content-path'].split('/').pop()); @@ -245,13 +167,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'test getSpec() from specs dir': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); store.getSpec('server', 'TestMojit2', {}, function(err, instance){ A.isTrue(instance.type === 'TestMojit2'); A.isTrue(instance.config.testKey1 === 'testVal1'); @@ -259,13 +175,7 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) { }, 'test getType()': function(){ - var resourceStore, - store; - - resourceStore = new Y.mojito.ResourceStore({ root: fixtures }); - resourceStore.preload(); - - store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); + var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog); store.getType('server', 'test_mojit_1', {}, function(err, instance){ A.isTrue(instance.type === 'test_mojit_1'); A.isTrue(instance.config.testKey4 === 'testVal4'); diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 57468d84f..63b9073ce 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -7,8 +7,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var suite = new YUITest.TestSuite(NAME), libpath = require('path'), - fixtures = libpath.join(__dirname, '../fixtures/store'), mojitoRoot = libpath.join(__dirname, '../..'), + store, Mock = YUITest.Mock, A = YUITest.Assert, AA = YUITest.ArrayAssert; @@ -41,37 +41,26 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { suite.add(new YUITest.TestCase({ - name: 'Store tests', + name: 'Store tests -- preload fixture "store"', - 'pre load': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); + init: function() { + var fixtures = libpath.join(__dirname, '../fixtures/store'); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - - //Y.log(Y.JSON.stringify(store,null,4)); - A.isTrue(store._config.root === fixtures); }, - 'pre load no application.json file': function() { - var fixtures = libpath.join(__dirname, '../fixtures/store_no_app_config'), - store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - + 'pre load': function() { + var fixtures = libpath.join(__dirname, '../fixtures/store'); //Y.log(Y.JSON.stringify(store,null,4)); A.isTrue(store._config.root === fixtures); }, 'server app config value': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var config = store.getAppConfig(null); A.isTrue(config.testKey1 === 'testVal1'); }, 'server mojit config value': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = {base:'test1'}; store.expandInstance(instance, {}, function(err, instance){ A.isTrue(instance.id === 'test1', 'wrong ID'); @@ -81,9 +70,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit config value via type': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance){ A.isTrue(instance.type === 'test_mojit_1', 'wrong ID'); @@ -93,9 +79,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit config value via type & overrride': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = { type:'test_mojit_1', config:{testKey4: 'other'} @@ -108,9 +91,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance assets': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - + var fixtures = libpath.join(__dirname, '../fixtures/store'); var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance) { A.areSame('/static/test_mojit_1/assets', instance.assetsRoot); @@ -122,9 +103,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance views & binders': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = {type:'test_mojit_1'}; store.expandInstanceForEnv('client', instance, {}, function(err, instance) { A.areSame(3, Y.Object.keys(instance.views).length); @@ -150,9 +128,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit instance models': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = {type:'test_mojit_1'}; store.expandInstance(instance, {}, function(err, instance) { A.areSame(4, Y.Object.keys(instance.models).length); @@ -164,9 +139,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit type name can come from package.json': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = {type:'TestMojit2'}; store.expandInstance(instance, {}, function(err, instance){ A.isNotUndefined(instance['controller-path']); @@ -176,17 +148,11 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit is NOT loaded because of package mojito version mismatch': function(){ - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - A.isTrue(typeof store.url.getPathForURL('/static/test_mojit_4/package.json') === 'undefined'); A.isTrue(typeof store.url.getPathForURL('/static/TestMojit4/package.json') === 'undefined'); }, 'server mojit is loaded because of package mojito version match': function(){ - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = {type:'TestMojit2'}; store.expandInstance(instance, {}, function(err, instance){ A.areSame('/static/TestMojit2/assets', instance.assetsRoot); @@ -194,18 +160,12 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server a mojits package.json file is available as appropriate': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - A.isUndefined(store.url.getPathForURL('/static/TestMojit2/package.json')); A.isNotUndefined(store.url.getPathForURL('/static/TestMojit3/package.json')); A.isUndefined(store.url.getPathForURL('/static/TestMojit5/package.json')); }, 'server mojit view index.mu.html is loaded correctly': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = {type:'TestMojit3'}; store.expandInstance(instance, {}, function(err, instance){ A.areSame('index.mu.html', instance.views.index['content-path'].split('/').pop()); @@ -213,9 +173,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit view index.iphone.mu.html is loaded correctly': function(){ - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); - var instance = {type:'TestMojit3'}; store.expandInstance(instance, {device:'iphone'}, function(err, instance){ A.areSame('index.iphone.mu.html', instance.views.index['content-path'].split('/').pop()); @@ -223,8 +180,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'app-level mojits': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); var instance = { type: 'test_mojit_1' }; store.expandInstance(instance, {}, function(err, instance) { A.isNotUndefined(instance.models.test_applevel); @@ -232,8 +187,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'mojitDirs setting': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); var instance = { type: 'soloMojit' }; store.expandInstance(instance, {}, function(err, instance) { A.isNotUndefined(instance['controller-path']); @@ -241,8 +194,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'expandInstance caching': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); var instance = 'foo'; var context = {}; var key = Y.JSON.stringify(instance) + Y.JSON.stringify(context); @@ -253,8 +204,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'multi preload': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); var pre = { appRVs: Y.clone(store._appRVs, true), mojitRVs: Y.clone(store._mojitRVs, true), @@ -272,8 +221,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call getSpec()': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); store.getSpec('server', 'test1', {}, function(err, instance) { A.areSame('test_mojit_1', instance.type); A.areSame('test1', instance.id); @@ -283,8 +230,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call getType()': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); store.getType('server', 'test_mojit_1', {}, function(err, instance) { A.areSame('test_mojit_1', instance.type); A.isUndefined(instance.id); @@ -294,9 +239,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'instance with base pointing to non-existant spec': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }), - spec = { base: 'nonexistant' }; - store.preload(); + var spec = { base: 'nonexistant' }; store.expandInstance(spec, {}, function(err, instance) { A.isNotUndefined(err); A.areSame('Unknown base of "nonexistant"', err.message); @@ -306,9 +249,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { 'getAppConfig() returns contextualized info': function() { var context = { runtime: 'server' }, - store = new Y.mojito.ResourceStore({ root: fixtures }), config; - store.preload(); config = store.getAppConfig(context); A.isObject(config); A.areSame('testVal1-server', config.testKey1, 'testKey1 wasnt contextualized to the server'); @@ -317,22 +258,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isUndefined(config.testKey4, 'testKey4 gotten from the wrong context'); }, - 'static context is really static': function() { - var context = { runtime: 'server' }, - store = new Y.mojito.ResourceStore({ root: fixtures, context: context }), - config; - store.preload(); - config = store.getAppConfig(); - A.isObject(config); - A.areSame('testVal1-server', config.testKey1, 'testKey1 wasnt contextualized to the server'); - A.areSame('testVal2', config.testKey2, 'testKey2 gotten from the wrong context'); - A.areSame('portended', config.pathos, 'missing contextualized config'); - A.isUndefined(config.testKey4, 'testKey4 gotten from the wrong context'); - }, - 'call getRoutes()': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); var routes = store.getRoutes({}); A.isObject(routes, 'no routes at all'); A.isObject(routes.flickr_by_page, 'missing route flickr_by_page'); @@ -340,8 +266,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call serializeClientStore()': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); var client = store.serializeClientStore({}); A.isObject(client, 'config is missing'); A.isObject(client.appConfig, 'missing appConfig'); @@ -359,8 +283,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'call listAllMojits()': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); var list = store.listAllMojits('server'); A.areSame(10, list.length, 'found the wrong number of mojits'); AA.contains('DaliProxy', list); @@ -375,23 +297,60 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { AA.contains('soloMojit', list); }, - 'bad files': function() { - var fixtures = libpath.join(__dirname, '../fixtures/badfiles'), - store = new Y.mojito.ResourceStore({ root: fixtures }); + 'app with rollups': function() { + var spec = { type: 'rollups' }; + store.expandInstanceForEnv('client', spec, {}, function(err, instance) { + A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollups']); + A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollupsBinderIndex']); + A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollupsModelClient']); + }); + } + + })); + + + suite.add(new YUITest.TestCase({ + + name: 'Store tests -- preload fixture "gsg5"', + + init: function() { + var fixtures = libpath.join(__dirname, '../fixtures/gsg5'); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - var spec = { type: 'M' }; - store.expandInstance(spec, {}, function(err, instance) { - A.isUndefined(instance.yui.sortedPaths['addon-ac-not']); - A.isUndefined(instance.yui.sortedPaths['MAutoloadNot']); - A.isUndefined(instance.yui.sortedPaths['MModelNot']); - A.isUndefined(instance.views['not']['binder-url']); + }, + + 'controller with selector': function() { + var fixtures = libpath.join(__dirname, '../fixtures/gsg5'); + var spec = { type: 'PagedFlickr' }; + var ctx = { device: 'iphone' }; + store.expandInstance(spec, ctx, function(err, instance) { + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.iphone.js'), instance['controller-path']); }); }, - 'appConfig deferAllOptionalAutoloads': function() { - var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'), - store = new Y.mojito.ResourceStore({ root: fixtures }); + 'binder with selector': function() { + var fixtures = libpath.join(__dirname, '../fixtures/gsg5'); + var spec = { type: 'PagedFlickr' }; + var ctx = { device: 'iphone' }; + store.expandInstance(spec, ctx, function(err, instance) { + A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/views/index.iphone.mu.html'), instance.views.index['content-path']); + }); + }, + + })); + + + suite.add(new YUITest.TestCase({ + + name: 'Store tests -- preload fixture "gsg5-appConfig"', + + init: function() { + var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'); + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); + }, + + 'appConfig deferAllOptionalAutoloads': function() { var spec = { type: 'PagedFlickr' }; store.expandInstanceForEnv('client', spec, {}, function(err, instance) { A.isUndefined(instance.views.index['binder-yui-sorted']['breg'], 'breg'); @@ -408,45 +367,52 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'appConfig staticHandling.prefix': function() { - var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'), - store = new Y.mojito.ResourceStore({ root: fixtures }); - store.preload(); var spec = { type: 'PagedFlickr' }; store.expandInstance(spec, {}, function(err, instance) { A.areSame('/PagedFlickr/assets', instance.assetsRoot); }); }, - 'controller with selector': function() { - var fixtures = libpath.join(__dirname, '../fixtures/gsg5'), - store = new Y.mojito.ResourceStore({ root: fixtures }); + })); + + + suite.add(new YUITest.TestCase({ + + name: 'Store tests -- misc', + + 'static context is really static': function() { + var fixtures = libpath.join(__dirname, '../fixtures/store'), + context = { runtime: 'server' }, + store = new Y.mojito.ResourceStore({ root: fixtures, context: context }), + config; store.preload(); - var spec = { type: 'PagedFlickr' }; - var ctx = { device: 'iphone' }; - store.expandInstance(spec, ctx, function(err, instance) { - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.iphone.js'), instance['controller-path']); - }); + config = store.getAppConfig(); + A.isObject(config); + A.areSame('testVal1-server', config.testKey1, 'testKey1 wasnt contextualized to the server'); + A.areSame('testVal2', config.testKey2, 'testKey2 gotten from the wrong context'); + A.areSame('portended', config.pathos, 'missing contextualized config'); + A.isUndefined(config.testKey4, 'testKey4 gotten from the wrong context'); }, - 'binder with selector': function() { - var fixtures = libpath.join(__dirname, '../fixtures/gsg5'), + 'pre load no application.json file': function() { + var fixtures = libpath.join(__dirname, '../fixtures/store_no_app_config'), store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - var spec = { type: 'PagedFlickr' }; - var ctx = { device: 'iphone' }; - store.expandInstance(spec, ctx, function(err, instance) { - A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/views/index.iphone.mu.html'), instance.views.index['content-path']); - }); + + //Y.log(Y.JSON.stringify(store,null,4)); + A.isTrue(store._config.root === fixtures); }, - 'app with rollups': function() { - var store = new Y.mojito.ResourceStore({ root: fixtures }); + 'bad files': function() { + var fixtures = libpath.join(__dirname, '../fixtures/badfiles'), + store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - var spec = { type: 'rollups' }; - store.expandInstanceForEnv('client', spec, {}, function(err, instance) { - A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollups']); - A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollupsBinderIndex']); - A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollupsModelClient']); + var spec = { type: 'M' }; + store.expandInstance(spec, {}, function(err, instance) { + A.isUndefined(instance.yui.sortedPaths['addon-ac-not']); + A.isUndefined(instance.yui.sortedPaths['MAutoloadNot']); + A.isUndefined(instance.yui.sortedPaths['MModelNot']); + A.isUndefined(instance.views['not']['binder-url']); }); }, @@ -455,6 +421,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'sortedReaddirSync() sorts the result of fs.readdirSync()': function() { + var fixtures = libpath.join(__dirname, '../fixtures/store'); var mockfs = Mock(); Mock.expect(mockfs, { @@ -472,11 +439,11 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, '_skipBadPath() does just that': function() { + var fixtures = libpath.join(__dirname, '../fixtures/store'); var store = new Y.mojito.ResourceStore({ root: fixtures }); A.isTrue(store._skipBadPath({ isFile: true, ext: '.js~' }), 'need to skip bad file naems'); A.isFalse(store._skipBadPath({ isFile: false, ext: '.js~' }), 'need to not-skip bad directory names'); A.isFalse(store._skipBadPath({ isFile: true, ext: '.js' }), 'need to not-skip good file names'); - }, 'load node_modules': function() { From dc4ade41c5649eedf11dc3a40be327e8b44de02f Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 28 Jun 2012 16:40:35 -0700 Subject: [PATCH 057/119] last RS unit test now passes --- source/lib/store.server.js | 6 +++- .../lib/tests/autoload/store.server-tests.js | 31 +++++++++---------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 9280e0991..fb4e18f2b 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1470,7 +1470,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { return; } - mojitType = this._libs.path.basename(dir); + if ('pkg' === dirType) { + mojitType = pkg.name; + } else { + mojitType = this._libs.path.basename(dir); + } packageJson = this.config.readConfigJSON(this._libs.path.join(dir, 'package.json')); if (packageJson) { if (packageJson.name) { diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 63b9073ce..4feb977e0 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -451,8 +451,7 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { store = new Y.mojito.ResourceStore({ root: fixtures }); store.preload(); - // TODO: _mojitMeta is gone. query for resources directly instead - if (!store._mojitMeta.server.a && !store._mojitMeta.server.aa && !store._mojitMeta.server.ba) { + if (!store._mojitRVs.a && !store._mojitRVs.aa && !store._mojitRVs.ba) { // This happens when mojito is installed via npm, since npm // won't install the node_modules/ directories in // tests/fixtures/packages. @@ -460,24 +459,24 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { return; } - var m, mojits = ['a', 'aa', 'ba']; - var mojitType, mojitMeta; + var m, mojitType, mojits = ['a', 'aa', 'ba']; + var r, res, ress, found; for (m = 0; m < mojits.length; m += 1) { mojitType = mojits[m]; - mojitMeta = store._mojitMeta.server[mojitType]; - A.isNotUndefined(mojitMeta, 'mojitMeta should be defined'); - mojitMeta = mojitMeta['*']; - A.isNotUndefined(mojitMeta['yui-module-b'], 'yui-module-b should be defined'); - A.isNotUndefined(mojitMeta['yui-module-ab'], 'yui-module-ab should be defined'); - A.isNotUndefined(mojitMeta['yui-module-bb'], 'yui-module-bb should be defined'); - A.isNotUndefined(mojitMeta['yui-module-cb'], 'yui-module-cb should be defined'); - // tests that yahoo.mojito.location in package.json works - // (which mojito package itself uses) - A.isNotUndefined(mojitMeta['addon-ac-assets'], 'addon-ac-assets should be defined'); + + ress = store.getResources('server', {}, {mojit: mojitType}); + found = 0; + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (res.id === 'yui-module--b') { found += 1; } + if (res.id === 'yui-module--ab') { found += 1; } + if (res.id === 'yui-module--bb') { found += 1; } + if (res.id === 'yui-module--cb') { found += 1; } + } + A.areSame(4, found, 'some child node_modules not loaded'); } - var details = {}; - store.getMojitTypeDetails('server', {}, 'a', details); + var details = store.getMojitTypeDetails('server', {}, 'a'); A.isNotNull(details['controller-path'].match(/a\/foo\/controller\.server\.js$/), 'controller should not be null'); }, From dd22147078c7276fc337293c62f0bae484097fa4 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 28 Jun 2012 16:54:59 -0700 Subject: [PATCH 058/119] delint --- source/lib/app/addons/rs/url.server.js | 4 ++-- source/lib/store.server.js | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 13adff77f..66e54ab3e 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -110,8 +110,8 @@ YUI.add('addon-rs-url', function(Y, NAME) { if ('config--package' === res.id && 'public' === (packageJson.yahoo && packageJson.yahoo.mojito && - packageJson.yahoo.mojito.package)) { - skip = false; + packageJson.yahoo.mojito['package'])) { + skip = false; } if (skip) { diff --git a/source/lib/store.server.js b/source/lib/store.server.js index fb4e18f2b..5742972ab 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -62,7 +62,7 @@ */ YUI.add('mojito-resource-store', function(Y, NAME) { - var libs = {}; + var libs = {}, isNotAlphaNum = /[^a-zA-Z0-9]/, @@ -1482,7 +1482,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } if (packageJson.engines && packageJson.engines.mojito) { - if(! this._libs.semver.satisfies(mojitoVersion, packageJson.engines.mojito)) { + if (!this._libs.semver.satisfies(mojitoVersion, packageJson.engines.mojito)) { Y.log('skipping mojit because of version check ' + dir, 'warn', NAME); return; } From 47485cb60371c350104f066cb2aff76fa858c6f1 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 1 Jul 2012 12:47:09 -0700 Subject: [PATCH 059/119] guard against missing controller --- source/lib/app/addons/rs/yui.server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index 81c643609..0fcfe9907 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -339,7 +339,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { } } } - if (modules['inlinecss/' + mojit]) { + if (controller && modules['inlinecss/' + mojit]) { // TODO: does this polute something? need to make a copy somewhere? modules[controller.yui.module].requires.push('inlinecss/' + mojit); } From e4c1cee5f5152c1a2dd3e4035a730b5489f41edb Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 1 Jul 2012 12:48:14 -0700 Subject: [PATCH 060/119] works with new resource store --- source/lib/app/commands/gv.js | 98 +++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 21 deletions(-) diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index 1986b245d..90eb6c8fb 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -20,33 +20,45 @@ var run, libpath = require('path'), libfs = require('fs'), libutils = require(libpath.join(__dirname, '../../management/utils')), + YUI = require('yui').YUI, MODE_ALL = parseInt('777', 8), - ResourceStore = require(libpath.join(__dirname, '../../store.server.js')), - artifactsDir = 'artifacts', resultsDir = 'artifacts/gv'; -function parseReqs(dest, modules) { - var module, - info; - - for (module in modules) { - if (modules.hasOwnProperty(module)) { - info = modules[module]; - dest[module] = info.requires || []; +function parseReqs(dest, ress, options) { + var r, res, ress; + var src; + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (!res.yui || !res.yui.name) { + continue; + } + if (('mojito' === res.source.pkg.name) && (!options.framework)) { + continue; + } + src = 'package ' + res.source.pkg.name; + if (res.mojit && 'shared' !== res.mojit) { + src = 'mojit ' + res.mojit; } + if (!dest[src]) { + dest[src] = {}; + } + dest[src][res.yui.name] = res.yui.meta.requires || []; } } function makeDepGraph(reqs, destFile) { var graph, + src, mod, i, req, + cluster = 0, + edges = '', graphAttrs; graph = 'digraph yui {\n'; @@ -54,16 +66,38 @@ function makeDepGraph(reqs, destFile) { graph += ' fontsize=11;\n'; graph += ' node [shape=Mrecord,fontsize=11];\n'; graph += ' edge [color=grey33,arrowsize=0.5,fontsize=8];\n'; - - for (mod in reqs) { - if (reqs.hasOwnProperty(mod)) { - for (i = 0; i < reqs[mod].length; i += 1) { - req = reqs[mod][i]; - graph += ' "' + mod + '" -> "' + req + '";\n'; + graph += '\n'; + + for (src in reqs) { + if (reqs.hasOwnProperty(src)) { + cluster++ + graph += ' subgraph cluster' + cluster + ' {\n'; + graph += ' label="' + src + '";\n'; + graph += ' style="filled";\n'; + graph += ' color="lightgrey";\n'; + graph += ' node [style="filled",color="white"];\n'; + for (mod in reqs[src]) { + if (reqs[src].hasOwnProperty(mod)) { + graph += ' "' + mod + '";\n'; + } + } + graph += ' };\n'; + + for (mod in reqs[src]) { + if (reqs[src].hasOwnProperty(mod)) { + for (i = 0; i < reqs[src][mod].length; i += 1) { + req = reqs[src][mod][i]; + edges += ' "' + mod + '" -> "' + req + '";\n'; + } + } } } } + graph += '\n'; + graph += edges; + graph += '\n'; + graphAttrs = [ 'remincross=true', // 'rankdir=LR', @@ -88,6 +122,8 @@ function makeDepGraph(reqs, destFile) { run = function(params, options) { var env, store, reqs = {}, + Y, + m, mojit, mojits, resultsFile; options = options || {}; @@ -106,14 +142,34 @@ run = function(params, options) { libfs.mkdirSync(resultsDir, MODE_ALL); } + Y = YUI(); + Y.applyConfig({ + useSync: true, + modules: { + 'mojito-resource-store': { + fullpath: libpath.join(__dirname, '../../store.server.js') + } + } + }); + Y.use('mojito-resource-store'); + // load details - store = new ResourceStore(process.cwd()); + store = new Y.mojito.ResourceStore({ + root: process.cwd(), + context: {} + }); store.preload(); - if (options.framework) { - parseReqs(reqs, store.yui.getConfigFw(env, {}).modules); + + ress = store.getResources(env, {}, {}); + parseReqs(reqs, ress, options); + + mojits = store.listAllMojits(); + mojits.push('shared'); + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; + ress = store.getResources(env, {}, { mojit: mojit }); + parseReqs(reqs, ress, options); } - parseReqs(reqs, store.yui.getConfigApp(env, {}).modules); - parseReqs(reqs, store.yui.getConfigAllMojits(env, {}).modules); // generate graph resultsFile = libpath.join(resultsDir, 'yui.' + env + '.dot'); From bd15400fda3691837157bedc0abaac2c49f0aaad Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 1 Jul 2012 20:28:13 -0700 Subject: [PATCH 061/119] preliminaries of `mojito build html5app` --- source/lib/app/addons/rs/url.server.js | 35 ++++++-- source/lib/app/commands/build.js | 87 ++++++++++++------- .../app/middleware/mojito-handler-static.js | 4 +- 3 files changed, 87 insertions(+), 39 deletions(-) diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 66e54ab3e..d1537ce19 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -59,6 +59,19 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, + // utility for `build html5app` command + // TODO DOCS + getSpecURL: function(id) { + var urlParts = []; + if (this.config.prefix) { + urlParts.push(this.config.prefix); + } + urlParts.push(id); + urlParts.push('specs/default.json'); + return '/' + urlParts.join('/'); + }, + + preloadResourceVersions: function() { var mojits = this.rs.listAllMojits(), m, @@ -75,6 +88,9 @@ YUI.add('addon-rs-url', function(Y, NAME) { for (m = 0; m < mojits.length; m += 1) { mojit = mojits[m]; mojitRes = this.rs.getResourceVersions({id: 'mojit--' + mojit})[0]; + if (mojitRes) { + this._calcResourceURL(mojitRes, mojitRes); + } // Server-only framework mojits like DaliProxy and HTMLFrameMojit // should never have URLs associated with them. This never used @@ -125,12 +141,8 @@ YUI.add('addon-rs-url', function(Y, NAME) { getMojitTypeDetails: function(evt) { - var parts = []; - if (this.config.prefix) { - parts.push(this.config.prefix); - } - parts.push(evt.args.mojitType); - evt.mojit.assetsRoot = '/' + parts.join('/') + '/assets'; + var ress = this.rs.getResources(evt.args.env, evt.args.ctx, {type: 'mojit', name: evt.args.mojitType}); + evt.mojit.assetRoot = ress[0].url + '/assets'; }, @@ -159,7 +171,11 @@ YUI.add('addon-rs-url', function(Y, NAME) { rollupFsPath = libpath.join(this.appRoot, 'rollup.client.js'); } } else { - urlParts.push(res.mojit); + if ('mojit' === res.type) { + urlParts.push(res.name); + } else { + urlParts.push(res.mojit); + } if (res.yui && res.yui.name) { rollupParts.push(res.mojit); rollupParts.push('rollup.client.js'); @@ -167,6 +183,11 @@ YUI.add('addon-rs-url', function(Y, NAME) { } } + if ('mojit' === res.type) { + res.url = '/' + urlParts.join('/'); + return; + } + urlParts.push(relativePath); if (rollupFsPath && (this.assumeRollups || libpath.existsSync(rollupFsPath))) { diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index ca4637e47..790dc40b9 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -12,14 +12,12 @@ var libpath = require('path'), utils = require(libpath.join(__dirname, '../../management/utils')), fs = require('fs'), libqs = require('querystring'), - ResourceStore = require(libpath.join(__dirname, '../..', - 'store.server.js') - ), MODE_755 = parseInt('755', 8), mkdirP, rmdirR, writeWebPagesToFiles, - Y = require('yui').YUI({useSync: true}).use('json-parse', 'json-stringify'); + YUI = require('yui').YUI, + Y = YUI({useSync: true}).use('json-parse', 'json-stringify'); Y.applyConfig({useSync: false}); @@ -67,13 +65,28 @@ exports.options = [ * @param {Function} callback Function to invoke on commmand completion. */ exports.run = function(params, options, callback) { - - var store = new ResourceStore(process.cwd()), + var store, type = 'html5app', + cwd = process.cwd(), destination, appConfig, config = {}; + Y.applyConfig({ + useSync: true, + modules: { + 'mojito-resource-store': { + fullpath: libpath.join(__dirname, '../../store.server.js') + } + } + }); + Y.use('mojito-resource-store'); + Y.applyConfig({useSync: false}); + store = new Y.mojito.ResourceStore({ + root: cwd, + context: {} + }); + if (!params[0]) { params[0] = ''; } @@ -91,29 +104,15 @@ exports.run = function(params, options, callback) { if (params[1] && params[1][0] === '/') { destination = libpath.join(params[1]); } else if (params[1]) { - destination = libpath.join(store._config.root, params[1]); + destination = libpath.join(cwd, params[1]); } else { - destination = libpath.join(store._config.root, 'artifacts/builds', type); + destination = libpath.join(cwd, 'artifacts/builds', type); } // Are we in a Mojito App? - utils.isMojitoApp(store._config.root, exports.usage, true); - - // TODO: probably should try to use store to read appConfig - try { - appConfig = Y.JSON.parse(String(fs.readFileSync(libpath.join( - store._config.root, - 'application.json' - )))); - appConfig = appConfig[0]; - - // Is there a "builds" section for this "type" in the appConfig - if (appConfig.builds && appConfig.builds[type]) { - config = appConfig.builds[type]; - } - } catch (err) { - // there is no application.json, but that is okay - } + utils.isMojitoApp(cwd, exports.usage, true); + + appConfig = store.getStaticAppConfig(); if (options.replace) { try { @@ -154,8 +153,13 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, urls = {}, // from: to app, context = '', + contextObj = libqs.parse(cmdOptions.context), appConfig, - tunnelPrefix; + tunnelPrefix, + dynamicURLs = {}, + mr, mojitRes, mojitRess, + sr, specRes, specRess, + id; if (cmdOptions.context) { context = '?' + cmdOptions.context; @@ -171,7 +175,7 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, store.preload(); - appConfig = store.getAppConfig(libqs.parse(cmdOptions.context)); + appConfig = store.getAppConfig(contextObj); tunnelPrefix = appConfig.tunnelPrefix || '/tunnel'; console.log('Building a "' + type + '" of the Mojito application at "' + @@ -194,7 +198,7 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, manifest += url + '\n'; - if (serverFiles[extension] !== undefined) { + if (serverFiles[extension]) { urls[url + context] = url; mkdirP(libpath.dirname(libpath.join(destination, url)), MODE_755); @@ -204,9 +208,32 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, } } + mojitRess = store.getResources('client', contextObj, {type: 'mojit'}); + for (mr = 0; mr < mojitRess.length; mr += 1) { + mojitRes = mojitRess[mr]; + if (!mojitRes.url) { + continue; + } + url = mojitRes.url + '/definition.json'; + dynamicURLs[url] = true; + + specRess = store.getResources('client', contextObj, {type: 'spec', mojit: mojitRes.name}); + for (sr = 0; sr < specRess.length; sr += 1) { + specRes = specRess[sr]; + dynamicURLs[specRes.url] = true; + } + } + + for (id in appConfig.specs) { + if (appConfig.specs.hasOwnProperty(id)) { + url = store.url.getSpecURL(id); + dynamicURLs[url] = true; + } + } + // Get all the dynamic URLs we have to call via the "tunnel" - for (url in store._dynamicURLs) { - if (store._dynamicURLs.hasOwnProperty(url)) { + for (url in dynamicURLs) { + if (dynamicURLs.hasOwnProperty(url)) { urls[tunnelPrefix + url + context] = url; mkdirP(libpath.dirname(libpath.join(destination, url)), MODE_755); } diff --git a/source/lib/app/middleware/mojito-handler-static.js b/source/lib/app/middleware/mojito-handler-static.js index 875b78ef2..65e87ffd7 100644 --- a/source/lib/app/middleware/mojito-handler-static.js +++ b/source/lib/app/middleware/mojito-handler-static.js @@ -164,7 +164,7 @@ function clearCache(key) { */ function staticProvider(store, globalLogger) { logger = globalLogger; - var appConfig = store.getAppConfig(null), + var appConfig = store.getStaticAppConfig(), options = appConfig.staticHandling || {}, cache = options.cache, maxAge = options.maxAge; @@ -197,7 +197,7 @@ function staticProvider(store, globalLogger) { // Use the resource store as a URI "rewriter" here. // /favicon.ico is sent to ./my_app_folder/assets/favicon.ico - filename = store.yui.getPathForURL(path); + filename = store.url.getPathForURL(path); // TODO: [Issue 80] remove this for performance if ((!filename) && (path === '/favicon.ico')) { filename = pa.join(store._config.root, 'assets', path); From 923980b3256ca53ca59563bf3db849009c063829 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 1 Jul 2012 21:04:24 -0700 Subject: [PATCH 062/119] no longer need to fixup instance langs --- source/lib/app/autoload/dispatch.common.js | 66 ---------------------- 1 file changed, 66 deletions(-) diff --git a/source/lib/app/autoload/dispatch.common.js b/source/lib/app/autoload/dispatch.common.js index bf077cd4f..fdd0ef11c 100644 --- a/source/lib/app/autoload/dispatch.common.js +++ b/source/lib/app/autoload/dispatch.common.js @@ -32,70 +32,6 @@ YUI.add('mojito-dispatcher', function(Y, NAME) { useOnDemand, appShareYUIInstance; - - /** - * Modifies the YUI modules in the instance to point to the correct - * language. - * - * @method fixupInstanceLang - * @param {string} lang target language. - * @param {Object} instance mojit instance (results of expandInstance()). - * @private - */ - function fixupInstanceLang(type, lang, instance) { - var fixedSorted = [], - fixedSortedPaths = {}, - bestLang = Y.Intl.lookupBestLang(lang, - Y.Object.keys(instance.yui.langs)), - suffix = (bestLang) ? '_' + bestLang : '', - OK = {}, - fixedMod, - fixedPath; - - // hard fallbacks if no "root" bundle - if (!bestLang && !instance.yui.langs['']) { - if (instance.yui.langs.en) { - bestLang = 'en'; - suffix = '_en'; - } - if (!bestLang && instance.yui.langs['en-US']) { - bestLang = 'en-US'; - suffix = '_en-US'; - } - } - - OK['lang/' + type + suffix] = true; - if (suffix) { - OK['lang/datatype-date-format' + suffix] = true; - } else { - // The "root" (no lang) version doesn't contain aggregates like %x. - OK['lang/datatype-date-format_en'] = true; - } - - Y.Array.each(instance.yui.sorted, function(mod) { - if ('lang/' === mod.substring(0, 5)) { - if (OK[mod]) { - fixedSorted.push(mod); - } - } else { - fixedSorted.push(mod); - } - }); - Y.Object.each(instance.yui.sortedPaths, function(path, mod) { - if ('lang/' === mod.substring(0, 5)) { - if (OK[mod]) { - fixedSortedPaths[mod] = path; - } - } else { - fixedSortedPaths[mod] = path; - } - }); - - instance.yui.sorted = fixedSorted; - instance.yui.sortedPaths = fixedSortedPaths; - } - - /* Optimization methods: ============ 1). YUI({bootstrap:false}).use('*') @@ -214,8 +150,6 @@ YUI.add('mojito-dispatcher', function(Y, NAME) { var moduleList, mojitYuiModules; - fixupInstanceLang(command.instance.type, command.context.lang, instance); - moduleList = instance.yui.sorted; // gotta copy this or else it pollutes the client runtime mojitYuiModules = Y.mojito.util.copy(moduleList); From e0b50c67fd16775e82167c0ecd8362b761128897 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 2 Jul 2012 10:49:56 -0700 Subject: [PATCH 063/119] the binders. working. --- source/lib/app/addons/ac/deploy.server.js | 10 +++++----- source/lib/store.server.js | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index 6390f533c..3db80e32a 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -206,7 +206,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { path = binder.needs[module]; // Anything we don't know about we'll assume is // a YUI library module. - if (!store.store.yui.getPathForURL(path)) { + if (!store.store.url.getPathForURL(path)) { yuiModules.push(module); yuiJsUrlContains[module] = true; } @@ -237,10 +237,10 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { // fw & app scripts. if (useOnDemand) { // add all framework-level and app-level code - this.addScripts('bottom', store.store.yui.getConfigFw('client', - contextClient).modules); - this.addScripts('bottom', store.store.yui.getConfigApp('client', - contextClient).modules); + this.addScripts('bottom', store.store.config.getConfigFw( + 'client', contextClient).modules); + this.addScripts('bottom', store.store.config.getConfigApp( + 'client', contextClient).modules); } // add binders' dependencies diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 5742972ab..76f25e516 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -93,7 +93,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { 'addon': 'server', 'archetype': 'server', 'asset': 'common', - 'binder': 'client', + 'binder': 'common', // need to be common so that binders meta-bubble 'command': 'server', 'controller': 'server', 'middleware': 'server', From bbc3c8b377744533a7576506227eb1099aff5249 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 2 Jul 2012 13:08:00 -0700 Subject: [PATCH 064/119] fixed builds.html5app.forceRelativePaths --- source/lib/app/commands/build.js | 26 +++++++++++++++++++++----- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 790dc40b9..5ec445962 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -17,7 +17,7 @@ var libpath = require('path'), rmdirR, writeWebPagesToFiles, YUI = require('yui').YUI, - Y = YUI({useSync: true}).use('json-parse', 'json-stringify'); + Y = YUI({useSync: true}).use('json-parse', 'json-stringify', 'escape'); Y.applyConfig({useSync: false}); @@ -113,6 +113,10 @@ exports.run = function(params, options, callback) { utils.isMojitoApp(cwd, exports.usage, true); appConfig = store.getStaticAppConfig(); + // Is there a "builds" section for this "type" in the appConfig + if (appConfig.builds && appConfig.builds[type]) { + config = appConfig.builds[type]; + } if (options.replace) { try { @@ -377,6 +381,16 @@ function attachManifest(root, relativePath, content, force) { } +function unEscape(string) { + return string.replace(/(&[^;]+;)/g, function(all, ent) { + if ('&#x' === ent.substr(0,3)) { + return String.fromCharCode(parseInt(ent.substring(3, ent.length-1), 16)) + } + return ent; + }); +} + + /** * Changes server-relative paths to file-relative paths. * @@ -397,11 +411,13 @@ function forceRelativePaths(root, relativePath, content, force) { content = content.replace(/(src|href)="([^"]+)"/g, function(all, name, val) { - var fixed = val; - if ('/' === val.charAt(0)) { - fixed = libpath.join(pathTo(libpath.dirname(val), dirname), - libpath.basename(val)); + // FUTURE: once the "/" aren't escaped, we can do this easier + var fixed = unEscape(val); + if ('/' === fixed.charAt(0)) { + fixed = libpath.join(pathTo(libpath.dirname(fixed), dirname), + libpath.basename(fixed)); } + fixed = Y.Escape.html(fixed); return name + '="' + fixed + '"'; }); } From 56d9567fee7dde248c8875779e77ef2ee78ab3a6 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 2 Jul 2012 15:37:36 -0700 Subject: [PATCH 065/119] more client-side fixes --- .../lib/app/autoload/resource-store-adapter.common.js | 4 +--- source/lib/app/commands/build.js | 10 ++++++++-- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/source/lib/app/autoload/resource-store-adapter.common.js b/source/lib/app/autoload/resource-store-adapter.common.js index d6c6a80e3..0757e111a 100644 --- a/source/lib/app/autoload/resource-store-adapter.common.js +++ b/source/lib/app/autoload/resource-store-adapter.common.js @@ -38,8 +38,6 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { this.ENV = env; this.store = resourceStore; - this._root = this.store._config.root; - return this; }, @@ -122,7 +120,7 @@ YUI.add('mojito-resource-store-adapter', function(Y, NAME) { getAppPath: function() { - return this._root; + return this.store._config.root; }, diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 5ec445962..c2a8f59fd 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -381,13 +381,19 @@ function attachManifest(root, relativePath, content, force) { } -function unEscape(string) { - return string.replace(/(&[^;]+;)/g, function(all, ent) { +function unEscape(txt) { + txt = txt.replace(/(&[^;]+;)/g, function(all, ent) { if ('&#x' === ent.substr(0,3)) { return String.fromCharCode(parseInt(ent.substring(3, ent.length-1), 16)) } return ent; }); + txt = txt.replace(/</g, '<'); + txt = txt.replace(/>/g, '>'); + txt = txt.replace(/"/g, '"'); + txt = txt.replace(/'/g, "'"); + txt = txt.replace(/&/g, '&'); + return txt; } From ea2454508dc622b06c8eae946b915181deb34e46 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 2 Jul 2012 15:55:07 -0700 Subject: [PATCH 066/119] delint --- source/lib/app/addons/ac/deploy.server.js | 8 ++++---- source/lib/app/commands/build.js | 12 ++++++++---- source/lib/app/commands/gv.js | 8 +++++--- 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index 3db80e32a..03b666132 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -237,10 +237,10 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { // fw & app scripts. if (useOnDemand) { // add all framework-level and app-level code - this.addScripts('bottom', store.store.config.getConfigFw( - 'client', contextClient).modules); - this.addScripts('bottom', store.store.config.getConfigApp( - 'client', contextClient).modules); + this.addScripts('bottom', store.store.config.getConfigFw('client', + contextClient).modules); + this.addScripts('bottom', store.store.config.getConfigApp('client', + contextClient).modules); } // add binders' dependencies diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index c2a8f59fd..75e319130 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -161,8 +161,12 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, appConfig, tunnelPrefix, dynamicURLs = {}, - mr, mojitRes, mojitRess, - sr, specRes, specRess, + mr, + mojitRes, + mojitRess, + sr, + specRes, + specRess, id; if (cmdOptions.context) { @@ -383,8 +387,8 @@ function attachManifest(root, relativePath, content, force) { function unEscape(txt) { txt = txt.replace(/(&[^;]+;)/g, function(all, ent) { - if ('&#x' === ent.substr(0,3)) { - return String.fromCharCode(parseInt(ent.substring(3, ent.length-1), 16)) + if ('&#x' === ent.substr(0, 3)) { + return String.fromCharCode(parseInt(ent.substring(3, ent.length - 1), 16)); } return ent; }); diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index 90eb6c8fb..68e4fbe06 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -29,8 +29,9 @@ var run, function parseReqs(dest, ress, options) { - var r, res, ress; - var src; + var r, + res, + src; for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (!res.yui || !res.yui.name) { @@ -70,7 +71,7 @@ function makeDepGraph(reqs, destFile) { for (src in reqs) { if (reqs.hasOwnProperty(src)) { - cluster++ + cluster += 1; graph += ' subgraph cluster' + cluster + ' {\n'; graph += ' label="' + src + '";\n'; graph += ' style="filled";\n'; @@ -123,6 +124,7 @@ run = function(params, options) { var env, store, reqs = {}, Y, + ress, m, mojit, mojits, resultsFile; From d02ffeb3ab834488f0c2665c41c4806495b2e767 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 3 Jul 2012 14:20:08 -0700 Subject: [PATCH 067/119] `mojito compile rollups` now works --- source/lib/app/commands/compile.js | 287 ++++++++++++++++------------- 1 file changed, 164 insertions(+), 123 deletions(-) diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index f3557ca1c..40dedee8d 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -8,13 +8,9 @@ /*jslint anon:true, sloppy:true, regexp:true, nomen:true*/ -var path = require('path'), - utils = require(path.join(__dirname, '../../management/utils')), - fs = require('fs'), - - // Mojito Resource Store - ResourceStore = require(path.join(__dirname, '../..', - 'store.server.js')), +var libpath = require('path'), + libfs = require('fs'), + libutils = require(libpath.join(__dirname, '../../management/utils')), // private compilation function container compile = {}, @@ -123,9 +119,25 @@ function getAllMojits(store, env, ctx) { return details; } +function makeStore(cfg) { + var store; + Y.applyConfig({ + useSync: true, + modules: { + 'mojito-resource-store': { + fullpath: libpath.join(__dirname, '../../store.server.js') + } + } + }); + Y.use('mojito-resource-store'); + store = new Y.mojito.ResourceStore(cfg); + Y.applyConfig({useSync: true}); + return store; +} + run = function(params, options, callback) { - var store = new ResourceStore(process.cwd()), + var cwd = process.cwd(), displayResults, type, context = {}; @@ -133,7 +145,7 @@ run = function(params, options, callback) { // TODO: don't assign to a parameter. options = options || {}; - utils.isMojitoApp(store._config.root, exports.usage, true); + libutils.isMojitoApp(cwd, exports.usage, true); if (options.context) { // TODO: parseURL. @@ -141,11 +153,11 @@ run = function(params, options, callback) { } displayResults = function(err) { - utils.log(''); + libutils.log(''); msgs.forEach(function(msg) { - utils.log(msg); + libutils.log(msg); }); - utils.log(''); + libutils.log(''); callback(err); }; @@ -161,11 +173,11 @@ run = function(params, options, callback) { compileType = type = params.shift(); if (!type) { - utils.error('Please provide the type of compilation you want.', + libutils.error('Please provide the type of compilation you want.', exports.usage, true); } if (!compile[type]) { - utils.error("Unknown type '" + type + "'", exports.usage, true); + libutils.error("Unknown type '" + type + "'", exports.usage, true); } compile[type](context, options, displayResults); @@ -205,14 +217,14 @@ compile.all = function(context, options, callback) { return callback(); } if (options.verbose) { - utils.log('executing -- ' + action.toUpperCase() + ' --'); + libutils.log('executing -- ' + action.toUpperCase() + ' --'); } compile[action](context, options, function(err) { if (err) { return callback(err); } if (options.verbose) { - utils.log('done -- ' + action.toUpperCase() + ' --\n\n'); + libutils.log('done -- ' + action.toUpperCase() + ' --\n\n'); } runOne(); }); @@ -229,7 +241,7 @@ compile.all = function(context, options, callback) { * @return {object} The return value from any optional callback function. */ compile.inlinecss = function(context, options, callback) { - var app = new utils.App({ + var app = new libutils.App({ port: options.port || 11111, verbose: options.verbose }), @@ -241,7 +253,7 @@ compile.inlinecss = function(context, options, callback) { inlineNext; if (options.app) { - utils.warn('Creating app-level inline css not supported\n'); + libutils.warn('Creating app-level inline css not supported\n'); return callback(); } @@ -284,7 +296,7 @@ compile.inlinecss = function(context, options, callback) { if (options.remove) { if (removeFile(inline.dest)) { if (options.verbose) { - utils.log('Removed: ' + inline.dest); + libutils.log('Removed: ' + inline.dest); } processed += 1; // on to the next one @@ -330,10 +342,10 @@ compile.inlinecss = function(context, options, callback) { if ('data:' === url.substr(0, 5)) { return whole; } - srcDir = path.dirname(inline.srcs[srcKey]); - fs = path.join(srcDir, url); + srcDir = libpath.dirname(inline.srcs[srcKey]); + fs = libpath.join(srcDir, url); if (!fs2url[fs]) { - utils.warn('couldn\'t normalize url(' + url + + libutils.warn('couldn\'t normalize url(' + url + ')(' + fs + ') in ' + inline.srcs[srcKey]); return whole; } @@ -374,19 +386,19 @@ compile.inlinecss = function(context, options, callback) { var store; if (err) { - utils.error(err); + libutils.error(err); } else { store = appInstance.store; inlines = store.getInlineCssMojits('client', context); - utils.log((options.remove ? 'Removing' : 'Creating') + + libutils.log((options.remove ? 'Removing' : 'Creating') + ' inline css...'); inlineNext(store, function(err) { try { app.close(); } catch (err2) { - utils.warn('(app server was not running) ' + err2); + libutils.warn('(app server was not running) ' + err2); } if (err) { @@ -411,88 +423,115 @@ compile.inlinecss = function(context, options, callback) { */ compile.rollups = function(context, options, callback) { var cwd = process.cwd(), - action = options.remove ? 'Removed' : 'Created', + store = makeStore({root: cwd, appConfig: { assumeRollups: true}}), + rollups = {}, processed = 0, - store = new ResourceStore(cwd), - rollups, - rollup, - mojitName; + r, + res, + ress, + m, + mojit, + mojits, + mojitRes, + dest, + s, + src, + srcs, + shortDest, + rollupBody; store.preload(); - utils.log((options.remove ? 'Removing' : 'Creating') + ' rollups...'); + libutils.log((options.remove ? 'Removing' : 'Creating') + ' rollups...'); - function rollOneUp(rollup) { - var i, - src, - rollupBody, - shortDest; - - shortDest = rollup.dest; - if (cwd === shortDest.substr(0, cwd.length)) { - shortDest = shortDest.substr(cwd.length + 1); - } + if (options.app || options.core) { + // FUTURE: rollup true-app-level resources somewhere? + mojits = [ 'shared' ]; + } else { + mojits = store.listAllMojits(); + } + if (options.mojit) { + mojits = [ options.mojit ]; + } - if (options.remove) { - if (path.existsSync(rollup.dest)) { - try { - fs.unlinkSync(rollup.dest); - } catch (err) { - return callback(err); - } + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; - if (options.verbose) { - utils.log('Removed: ' + shortDest); - } - processed += 1; + if ('shared' !== mojit) { + mojitRes = store.getResources('client', context, {type: 'mojit', name: mojit}); + if (!mojitRes || !mojitRes.length) { + callback('Unknown "' + mojit + '"'); + return; + } + mojitRes = mojitRes[0]; + if ('mojito' === mojitRes.source.pkg.name) { + // don't write framework-provided rollups into the framework directory + continue; } - return; } - rollupBody = ''; - for (i = 0; i < rollup.srcs.length; i += 1) { - src = rollup.srcs[i]; - if (!options.core || src.match(/\/mojito\//)) { - rollupBody += fs.readFileSync(src, 'utf-8'); + ress = store.getResources('client', context, {mojit: mojit}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (res.mojit !== mojit) { + continue; + } + if (options.core && 'mojito' !== res.source.pkg.name) { + continue; + } + dest = res.source.fs.rollupPath; + if (dest) { + if (!rollups[dest]) { + rollups[dest] = []; + } + rollups[dest].push(res.source.fs.fullPath); + continue; } } - fs.writeFileSync(rollup.dest, rollupBody, 'utf-8'); - if (options.verbose) { - utils.log('Rolled up: ' + shortDest); - } - processed += 1; } - if (options.app || options.core) { - rollup = store.getRollupsApp('client', context); - rollOneUp(rollup); - utils.log('All rollups have been ' + - (options.remove ? 'removed' : 'created') + '\n'); - callback(); - return; - } - - rollups = store.getRollupsMojits('client', context); + for (dest in rollups) { + if (rollups.hasOwnProperty(dest)) { + srcs = rollups[dest]; - if (options.mojit && !rollups[options.mojit]) { - callback('Unknown "' + options.mojit + '"'); - return; - } + shortDest = dest; + if (cwd === shortDest.substr(0, cwd.length)) { + shortDest = shortDest.substr(cwd.length + 1); + } - for (mojitName in rollups) { - if (rollups.hasOwnProperty(mojitName)) { + if (options.remove) { + if (libpath.existsSync(dest)) { + try { + libfs.unlinkSync(dest); + } catch (err) { + return callback(err); + } + if (options.verbose) { + libutils.log('Removed: ' + shortDest); + } + processed += 1; + } + continue; + } - // TODO: verify the logic inversion here. - //if (options['mojit'] && mojitName != options['mojit']) {continue}; - if (!options.mojit || (mojitName === options.mojit)) { - rollup = rollups[mojitName]; - rollOneUp(rollup); + if (!srcs.length) { + continue; + } + rollupBody = ''; + for (s = 0; s < srcs.length; s += 1) { + src = srcs[s]; + rollupBody += libfs.readFileSync(src, 'utf-8'); } + libfs.writeFileSync(dest, rollupBody, 'utf-8'); + if (options.verbose) { + libutils.log('Rolled up: ' + shortDest); + } + processed += 1; } } - msgs.push(action + ' compiled rollup YUI modules for ' + processed + - ' mojits.'); + msgs.push((options.remove ? 'Removed' : 'Created') + + ' compiled rollup YUI modules for ' + processed + ' mojits.'); callback(); }; @@ -505,7 +544,8 @@ compile.rollups = function(context, options, callback) { * @return {object} The return value from any optional callback function. */ compile.views = function(context, options, callback) { - var store = new ResourceStore(process.cwd()), + var cwd = process.cwd(), + store = makeStore({root: cwd}), compiledFilename = '/autoload/compiled/views.common.js', mojits, yuiConfig, @@ -522,13 +562,13 @@ compile.views = function(context, options, callback) { // there are no views in the app, so no need to do this if (options.app) { - utils.warn('Compiling app-level views not supported\n'); + libutils.warn('Compiling app-level views not supported\n'); return callback(); } store.preload(); - utils.log((options.remove ? 'Removing compiled' : 'Compiling') + + libutils.log((options.remove ? 'Removing compiled' : 'Compiling') + ' views...'); // Get all the Mojits @@ -550,7 +590,7 @@ compile.views = function(context, options, callback) { if (options.remove) { if (removeFile(outputFilepath)) { if (options.verbose) { - utils.log('Removed: ' + outputFilepath); + libutils.log('Removed: ' + outputFilepath); } processed += 1; } @@ -620,8 +660,9 @@ compile.views = function(context, options, callback) { * @return {object} The return value from any optional callback function. */ compile.json = function(context, options, callback) { - var store = new ResourceStore(process.cwd()), - app = new utils.App({ + var cwd = process.cwd(), + store = makeStore({root: cwd}), + app = new libutils.App({ port: options.port || 11111, verbose: options.verbose, appConfig: { @@ -649,7 +690,7 @@ compile.json = function(context, options, callback) { // there are no json configs in the app, so no need to do this if (options.app) { - utils.warn('Compiling app-level json not supported\n'); + libutils.warn('Compiling app-level json not supported\n'); return callback(); } @@ -680,7 +721,7 @@ compile.json = function(context, options, callback) { } if (options.verbose) { - utils.log('\tprocessing spec... ' + fullSpecName); + libutils.log('\tprocessing spec... ' + fullSpecName); } parts = fullSpecName.split(':'); @@ -692,7 +733,7 @@ compile.json = function(context, options, callback) { specUrl = '/' + mName + '/specs/' + specName + '.json'; if (options.verbose) { - utils.log('found spec (' + specName + ') for ' + mName); + libutils.log('found spec (' + specName + ') for ' + mName); } getContentFromUrl(app, specUrl, jsonOpts, function(spec) { yuiModuleCacheWriter.createNamespace('compiled.' + mojitNs + @@ -741,7 +782,7 @@ compile.json = function(context, options, callback) { */ setTimeout(function() { if (options.verbose) { - utils.log('looking for definition.json for ' + mojitName); + libutils.log('looking for definition.json for ' + mojitName); } var specsToPreload = [], @@ -752,7 +793,7 @@ compile.json = function(context, options, callback) { if (Object.keys(definition).length > 0) { if (definition.preload) { if (options.verbose) { - utils.log('processing preload mojits for ' + mojitName); + libutils.log('processing preload mojits for ' + mojitName); } definition.preload.forEach(function(toPreload) { @@ -768,9 +809,9 @@ compile.json = function(context, options, callback) { }); if (options.verbose) { - utils.log('processing the preload specs for ' + + libutils.log('processing the preload specs for ' + mojitName); - utils.log(specsToPreload.join(', ')); + libutils.log(specsToPreload.join(', ')); } specsToPreload.forEach(function(stp) { @@ -807,7 +848,7 @@ compile.json = function(context, options, callback) { } if (options.verbose) { - utils.log('Processing mojit... ' + mojitName); + libutils.log('Processing mojit... ' + mojitName); } outputFilepath = store._mojitPaths[mojitName] + compiledFilename; @@ -825,7 +866,7 @@ compile.json = function(context, options, callback) { if (outputFilepath.indexOf('lib/mojits') >= 0 || outputFilepath.indexOf('mojito/mojits') >= 0) { if (options.verbose) { - utils.log('skipping ' + outputFilepath); + libutils.log('skipping ' + outputFilepath); } return processNextMojit(store, cb); } @@ -856,18 +897,18 @@ compile.json = function(context, options, callback) { // start up the server app.start(function(err, appInst) { if (err) { - utils.error(err); + libutils.error(err); return; } - utils.log((options.remove ? 'Removing compiled' : 'Compiling') + + libutils.log((options.remove ? 'Removing compiled' : 'Compiling') + ' json...'); processNextMojit(store, function() { try { app.close(); } catch (err2) { - utils.info('(app server was not running) ' + err2); + libutils.info('(app server was not running) ' + err2); } msgs.push(action + ' compiled JSON YUI modules for ' + processed + ' mojits.'); @@ -878,7 +919,7 @@ compile.json = function(context, options, callback) { clean = function(context, options, cb) { - utils.warn('Cleaning all compiled files!'); + libutils.warn('Cleaning all compiled files!'); options.remove = true; options.app = true; compile.all(context, options, function() { @@ -899,12 +940,12 @@ everything = function(context, options, cb) { mkdirP = function(p, mode) { - var ps = path.normalize(p).split('/'), + var ps = libpath.normalize(p).split('/'), i; for (i = 0; i <= ps.length; i += 1) { try { - fs.mkdirSync(ps.slice(0, i).join('/'), mode); + libfs.mkdirSync(ps.slice(0, i).join('/'), mode); } catch (err) { // Dirty way to check dir } @@ -913,23 +954,23 @@ mkdirP = function(p, mode) { rmdirR = function(path) { - var files = fs.readdirSync(path), + var files = libfs.readdirSync(path), i, currFile; /* Loop through and delete everything in the sub-tree after checking it */ for (i = 0; i < files.length; i += 1) { - currFile = fs.statSync(path + '/' + files[i]); + currFile = libfs.statSync(path + '/' + files[i]); if (currFile.isDirectory()) { // Recursive function back to the beginning rmdirR(path + '/' + files[i]); } else if (currFile.isSymbolicLink()) { // Unlink symlinks - fs.unlinkSync(path + '/' + files[i]); + libfs.unlinkSync(path + '/' + files[i]); } else { // Assume it's a file - perhaps a try/catch belongs here? - fs.unlinkSync(path + '/' + files[i]); + libfs.unlinkSync(path + '/' + files[i]); } } @@ -937,7 +978,7 @@ rmdirR = function(path) { * Now that we know everything in the sub-tree has been deleted, * we can delete the main directory. Huzzah for the shopkeep. */ - return fs.rmdirSync(path); + return libfs.rmdirSync(path); }; @@ -948,8 +989,8 @@ getContentFromUrl = function(app, url, opts, callback) { } app.getWebPage(url, opts, function(err, url, content) { if (err) { - utils.error('FAILED to get ' + url); - utils.error(err); + libutils.error('FAILED to get ' + url); + libutils.error(err); } else { callback(content); } @@ -958,9 +999,9 @@ getContentFromUrl = function(app, url, opts, callback) { removeFile = function(file) { - if (path.existsSync(file)) { + if (libpath.existsSync(file)) { try { - fs.unlinkSync(file); + libfs.unlinkSync(file); } catch (err) { return false; } @@ -1041,7 +1082,7 @@ YuiModuleCacheWriter = function(name, file, options) { /** * Create a clean prototype instance. */ -YuiModuleCacheWriter.prototype = utils.heir(YuiModuleCacher.prototype); +YuiModuleCacheWriter.prototype = libutils.heir(YuiModuleCacher.prototype); /** @@ -1062,18 +1103,18 @@ YuiModuleCacheWriter.prototype.write = function() { // only write the file if there is something to write if (Object.keys(namespaces).length > 0) { try { - mkdirP(path.dirname(file), parseInt('755', 8)); + mkdirP(libpath.dirname(file), parseInt('755', 8)); if (this.opts.verbose) { - utils.log('writing file: ' + file); + libutils.log('writing file: ' + file); } - fs.writeFileSync(file, output, 'utf8'); + libfs.writeFileSync(file, output, 'utf8'); if (this.opts.verbose) { - utils.log('Created: ' + file); + libutils.log('Created: ' + file); } return true; } catch (err) { - utils.error('Error writing file: ' + file); - utils.error(err); + libutils.error('Error writing file: ' + file); + libutils.error(err); return false; } } From 5b063b63d08dcbb16a9cd3f2d5a96436d11e4064 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 3 Jul 2012 14:48:50 -0700 Subject: [PATCH 068/119] fixed the tests (again) --- source/lib/app/addons/ac/deploy.server.js | 4 ++-- source/lib/app/addons/rs/url.server.js | 2 +- .../app/addons/ac/deploy-tests.server.js | 5 +++++ .../app/addons/rs/url-tests.server.js | 22 ++++--------------- .../lib/tests/autoload/store.server-tests.js | 4 ++-- 5 files changed, 14 insertions(+), 23 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index 03b666132..e2b288a90 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -237,9 +237,9 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { // fw & app scripts. if (useOnDemand) { // add all framework-level and app-level code - this.addScripts('bottom', store.store.config.getConfigFw('client', + this.addScripts('bottom', store.store.yui.getConfigFw('client', contextClient).modules); - this.addScripts('bottom', store.store.config.getConfigApp('client', + this.addScripts('bottom', store.store.yui.getConfigApp('client', contextClient).modules); } diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index d1537ce19..45a532a5d 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -142,7 +142,7 @@ YUI.add('addon-rs-url', function(Y, NAME) { getMojitTypeDetails: function(evt) { var ress = this.rs.getResources(evt.args.env, evt.args.ctx, {type: 'mojit', name: evt.args.mojitType}); - evt.mojit.assetRoot = ress[0].url + '/assets'; + evt.mojit.assetsRoot = ress[0].url + '/assets'; }, diff --git a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js index 7ae141f55..4d35be672 100644 --- a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js +++ b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js @@ -23,6 +23,7 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { addon = null; }, + 'YUI_config uses application.json yui.config': function() { var realRouteMaker = Y.mojito.RouteMaker; @@ -100,6 +101,7 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { A.areSame('klingon', config.lang, 'wrong lang used'); }, + 'honor yui.config.fetchCSS=false in application.json': function() { var realLoader = Y.mojito.Loader; @@ -195,6 +197,7 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { A.areSame(1, counts['blob bottom'], 'wrong number of blob:bottom'); }, + 'dependencyCalculations precomputed': function() { var calledYuiModules; var realLoader = Y.mojito.Loader; @@ -282,6 +285,7 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { A.areSame("'*'", YUI_use); }, + 'dependencyCalculations ondemand': function() { var calledYuiModules; var realLoader = Y.mojito.Loader; @@ -369,6 +373,7 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { A.areSame("'mojito-client'", YUI_use); }, + 'dependencyCalculations precomputed+ondemand': function() { var calledYuiModules; var realLoader = Y.mojito.Loader; diff --git a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js index 6a1b01216..0698aa2b8 100644 --- a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js @@ -269,6 +269,9 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { root: fixtures, appConfig: {} }); + store.getResources = function() { + return [ { url: 'TEST' } ]; + }; store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); var mojit = {}; store.fire('getMojitTypeDetails', { @@ -279,24 +282,7 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { }, mojit: mojit }); - A.areSame('/static/Foo/assets', mojit.assetsRoot); - - // honor empty prefix - store = new MockRS({ - root: fixtures, - appConfig: { staticHandling: {prefix:''} } - }); - store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); - mojit = {}; - store.fire('getMojitTypeDetails', { - args: { - env: 'server', - ctx: {}, - mojitType: 'Foo' - }, - mojit: mojit - }); - A.areSame('/Foo/assets', mojit.assetsRoot); + A.areSame('TEST/assets', mojit.assetsRoot); }, diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 4feb977e0..7cda262af 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -608,12 +608,12 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { switch (res.source.fs.basename) { case 'x': A.areSame('*', res.selector); - A.areSame('client', res.affinity); + A.areSame('common', res.affinity); A.areSame('.js', res.source.fs.ext); break; case 'x.iphone': A.areSame('iphone', res.selector); - A.areSame('client', res.affinity); + A.areSame('common', res.affinity); A.areSame('.js', res.source.fs.ext); break; default: From 5b67bf5d92b7833bc1548c98cd5dfd09318e1255 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 3 Jul 2012 16:14:08 -0700 Subject: [PATCH 069/119] `mojito compile views` now works --- .../lib/app/addons/view-engines/mu.server.js | 2 +- source/lib/app/commands/compile.js | 38 ++++++++++--------- 2 files changed, 22 insertions(+), 18 deletions(-) diff --git a/source/lib/app/addons/view-engines/mu.server.js b/source/lib/app/addons/view-engines/mu.server.js index b62dd8eef..8cff75345 100644 --- a/source/lib/app/addons/view-engines/mu.server.js +++ b/source/lib/app/addons/view-engines/mu.server.js @@ -76,4 +76,4 @@ YUI.add('mojito-mu', function(Y, NAME) { Y.namespace('mojito.addons.viewEngines').mu = MuAdapter; -}, '0.1.0', {requires: []}); +}, '0.1.0', {requires: ['json-stringify']}); diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 40dedee8d..b305164b4 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -571,22 +571,33 @@ compile.views = function(context, options, callback) { libutils.log((options.remove ? 'Removing compiled' : 'Compiling') + ' views...'); - // Get all the Mojits - mojits = getAllMojits(store, 'server', context); - - if (options.mojit && !mojits[options.mojit]) { - callback('Unknown "' + options.mojit + '"'); - return; + if (options.mojit) { + mojits = [ options.mojit ]; + } else { + mojits = store.listAllMojits(); } - // loop through all mojits one at a time, only once per mojit - Object.keys(mojits).forEach(function(mojitName) { - var outputFilepath = store._mojitPaths[mojitName] + compiledFilename, + Y.Array.each(mojits, function(mojitName) { + var mojitRes, + outputFilepath, mojitNs = mojitName.replace(/\./g, '_'), yuiModuleCacheWriter, viewName, MojY; + mojitRes = store.getResources('server', context, {type: 'mojit', name: mojitName}); + if (!mojitRes || !mojitRes.length) { + callback('Unknown mojit "' + options.mojit + '"'); + } + mojitRes = mojitRes[0]; + + outputFilepath = libpath.join(mojitRes.source.fs.fullPath, 'autoload/compiled/views.common.js'); + + if ('mojito' === mojitRes.source.pkg.name) { + // don't write framework-provided views into the framework directory + return; + } + if (options.remove) { if (removeFile(outputFilepath)) { if (options.verbose) { @@ -597,17 +608,10 @@ compile.views = function(context, options, callback) { return; } - // Skip anything in the "lib/mojits" (open source) or - // "mojit/mojits" (ynodejs_mojito) directories as it's internal - if (outputFilepath.indexOf('lib/mojits') >= 0 || - outputFilepath.indexOf('mojito/mojits') >= 0) { - return; - } - yuiModuleCacheWriter = new YuiModuleCacheWriter('views/' + mojitName, outputFilepath, options); - mojit = mojits[mojitName]; + mojit = store.getMojitTypeDetails('server', context, mojitName); if (mojit.views) { // Check each view for a template and engine From 43676e43930eef01fff49adf4787bcf3b686710b Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 3 Jul 2012 17:27:17 -0700 Subject: [PATCH 070/119] `mojito compile inlinecss` now works --- source/lib/app/addons/rs/yui.server.js | 2 +- source/lib/app/commands/compile.js | 91 ++++++++++++++++++++++++-- 2 files changed, 87 insertions(+), 6 deletions(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index 0fcfe9907..10a568627 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -341,7 +341,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { } if (controller && modules['inlinecss/' + mojit]) { // TODO: does this polute something? need to make a copy somewhere? - modules[controller.yui.module].requires.push('inlinecss/' + mojit); + modules[controller.yui.name].requires.push('inlinecss/' + mojit); } if (!this.modules[env]) { diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index b305164b4..a4f9845ba 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -119,6 +119,84 @@ function getAllMojits(store, env, ctx) { return details; } + +/** + * Returns details on how to make inline CSS for mojits. + * + * This example comes from (a modified) GSG5. + * [ { + * mojitName: 'FlickrDetail', + * yuiModuleName: 'inlinecss/FlickrDetail', + * dest: '/blah/mojits/FlickrDetail/autoload/compiled' + + * '/css.iphone.client.js', + * srcs: { + * '/static/FlickrDetail/assets/index.css': true, + * '/static/FlickrDetail/assets/message.css': true, + * } + * ] + * + * @method getInlineCssMojits + * @param store {string} resource store + * @param env {string} "client" or "server" + * @param context {object} runtime context + * @return {array} object describing where to put the inline CSS file and what it should contain + */ +function getInlineCssMojits(store, env, context) { + var m, + mojit, + mojits, + mojitRes, + r, + res, + ress, + selector, + dest, + srcs, + inlines = []; + + mojits = store.listAllMojits(); + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; + + mojitRes = store.getResources('client', context, {type: 'mojit', name: mojit}); + mojitRes = mojitRes[0]; + if ('mojito' === mojitRes.source.pkg.name) { + // don't write framework-provided inlinecss into the framework directory + continue; + } + + // TODO: This isn't quite right, since multiple contexts might map to + // posls with the same lead selector. + selector = store.selector.getListFromContext(context)[0]; + + srcs = []; + ress = store.getResources(env, context, {mojit: mojit}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (mojit !== res.mojit) { + continue; + } + if ((res.type === 'asset') && (res.subtype === 'css')) { + srcs[res.url] = true; + } + } + dest = 'autoload/compiled/inlinecss' + ('*' === selector ? '' : '.' + + selector) + '.' + env + '.js'; + dest = libpath.join(mojitRes.source.fs.fullPath, dest); + if (Object.keys(srcs).length) { + inlines.push({ + mojitName: mojit, + yuiModuleName: 'inlinecss/' + mojit, + dest: dest, + srcs: srcs + }); + } + } // for each mojit + + return inlines; +} + + function makeStore(cfg) { var store; Y.applyConfig({ @@ -241,10 +319,7 @@ compile.all = function(context, options, callback) { * @return {object} The return value from any optional callback function. */ compile.inlinecss = function(context, options, callback) { - var app = new libutils.App({ - port: options.port || 11111, - verbose: options.verbose - }), + var app, action = options.remove ? 'Removed' : 'Created', processed = 0, cwd = process.cwd(), @@ -252,6 +327,11 @@ compile.inlinecss = function(context, options, callback) { inlines, inlineNext; + app = new libutils.App({ + port: options.port || 11111, + verbose: options.verbose + }); + if (options.app) { libutils.warn('Creating app-level inline css not supported\n'); return callback(); @@ -389,7 +469,7 @@ compile.inlinecss = function(context, options, callback) { libutils.error(err); } else { store = appInstance.store; - inlines = store.getInlineCssMojits('client', context); + inlines = getInlineCssMojits(store, 'client', context); libutils.log((options.remove ? 'Removing' : 'Creating') + ' inline css...'); @@ -701,6 +781,7 @@ compile.json = function(context, options, callback) { store.preload(); // Get all the Mojits + // TODO: use store.listAllMojits() instead? mojits = getAllMojits(store, 'server', context); mojitNames = Object.keys(mojits); total = mojitNames.length; From 2a82f8e8bd27df619892391cf845cacc9e627601 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 08:48:44 -0700 Subject: [PATCH 071/119] cleaned up metadata leak between resource versions --- source/lib/app/addons/rs/yui.server.js | 8 +++++--- source/lib/store.server.js | 8 ++++++-- 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index 10a568627..d4d2ad855 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -339,9 +339,8 @@ YUI.add('addon-rs-yui', function(Y, NAME) { } } } - if (controller && modules['inlinecss/' + mojit]) { - // TODO: does this polute something? need to make a copy somewhere? - modules[controller.yui.name].requires.push('inlinecss/' + mojit); + if (controller && ('client' === env) && modules['inlinecss/' + mojit]) { + controller.yui.meta.requires.push('inlinecss/' + mojit); } if (!this.modules[env]) { @@ -518,6 +517,9 @@ YUI.add('addon-rs-yui', function(Y, NAME) { yui.name = name; yui.version = version; yui.meta = meta || {}; + if (!yui.meta.requires) { + yui.meta.requires = []; + } } } }; diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 76f25e516..784385974 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1553,7 +1553,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { versions = {}, // id: priority: resource out = [], resid, - highest; + highest, + chosen; for (s = 0; s < srcs.length; s += 1) { src = srcs[s]; @@ -1582,7 +1583,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (versions.hasOwnProperty(resid)) { highest = Math.max.apply(Math, Object.keys(versions[resid])); //console.log('--DEBUG-- highest=' + highest + ' -- ' + resid); - out.push(versions[resid][highest]); + chosen = this.cloneObj(versions[resid][highest]); + delete chosen.selector; + delete chosen.affinity; + out.push(chosen); } } return out; From 195bc87727f7ef08f0ca7ffffe7d76f64c4174e2 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 08:51:20 -0700 Subject: [PATCH 072/119] binders shouldn't be a dependency for the controllers --- source/lib/app/addons/rs/yui.server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index d4d2ad855..d493ae04f 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -328,7 +328,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { requires: res.yui.meta.requires, fullpath: (('client' === env) ? res.url : res.source.fs.fullPath) }; - if (res.mojit === mojit && res.type !== 'yui-lang') { + if (res.mojit === mojit && res.type !== 'yui-lang' && res.type !== 'binder') { controllerRequired[res.yui.name] = true; } if ('binder' === res.type) { From d9594b4a89e9e28aa09c5e6eb3821a7b4ae83268 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 09:19:08 -0700 Subject: [PATCH 073/119] fixed params handling of routes --- examples/getting-started-guide/part5/flickr-list/routes.json | 2 +- source/lib/app/middleware/mojito-router.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/examples/getting-started-guide/part5/flickr-list/routes.json b/examples/getting-started-guide/part5/flickr-list/routes.json index 2e80c1c1b..54c7733e3 100644 --- a/examples/getting-started-guide/part5/flickr-list/routes.json +++ b/examples/getting-started-guide/part5/flickr-list/routes.json @@ -11,7 +11,7 @@ "flickr_base": { "verbs": ["get"], "path": "/flickr", - "param": "page=1&image=0", + "params": "page=1&image=0", "call": "flickr.index" } diff --git a/source/lib/app/middleware/mojito-router.js b/source/lib/app/middleware/mojito-router.js index 78d80b6df..b949475f6 100644 --- a/source/lib/app/middleware/mojito-router.js +++ b/source/lib/app/middleware/mojito-router.js @@ -106,7 +106,7 @@ Router.prototype = { //and is never a string here. i.e. this assert always passes: //require('assert').ok(typeof routeMatch.param !== 'string'); command.params = { - route: simpleMerge(routeMatch.query, routeMatch.param), + route: simpleMerge(routeMatch.query, routeMatch.params), url: query || {}, body: req.body || {}, file: {} // FUTURE: add multi-part file data here From 91798e66339508fc4edbd3c06441e075e8f0a2c8 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 09:35:46 -0700 Subject: [PATCH 074/119] inlinecss is common --- source/lib/app/addons/rs/yui.server.js | 2 +- source/lib/app/commands/compile.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index d493ae04f..ee529e0cc 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -339,7 +339,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { } } } - if (controller && ('client' === env) && modules['inlinecss/' + mojit]) { + if (controller && modules['inlinecss/' + mojit]) { controller.yui.meta.requires.push('inlinecss/' + mojit); } diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index a4f9845ba..95bc80eb2 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -181,7 +181,7 @@ function getInlineCssMojits(store, env, context) { } } dest = 'autoload/compiled/inlinecss' + ('*' === selector ? '' : '.' + - selector) + '.' + env + '.js'; + selector) + '.common.js'; dest = libpath.join(mojitRes.source.fs.fullPath, dest); if (Object.keys(srcs).length) { inlines.push({ From 59f4dfda788f225b7119a913c2e05ef58ca10c3f Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 11:17:16 -0700 Subject: [PATCH 075/119] `mojito compile json` somewhat working (significant legacy issues with this) --- source/lib/app/commands/compile.js | 114 ++++++++++++++++------------- 1 file changed, 62 insertions(+), 52 deletions(-) diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 95bc80eb2..083145e87 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -105,21 +105,6 @@ options = [ ]; -// Returns details about all the mojits in the application. -function getAllMojits(store, env, ctx) { - var details = {}, - mojits, - m, - mojit; - mojits = store.listAllMojits(); - for (m = 0; m < mojits.length; m += 1) { - mojit = mojits[m]; - details[mojit] = store.getMojitTypeDetails(env, ctx, mojit); - } - return details; -} - - /** * Returns details on how to make inline CSS for mojits. * @@ -759,7 +744,7 @@ compile.json = function(context, options, callback) { processed = 0, total = 0, action = options.remove ? 'Removed' : 'Created', - compiledFilename = '/autoload/compiled/json.common.js', + compiledFilename = 'autoload/compiled/json.common.js', processNextMojit, processSpecs, processNextSpec, @@ -780,10 +765,11 @@ compile.json = function(context, options, callback) { store.preload(); - // Get all the Mojits - // TODO: use store.listAllMojits() instead? - mojits = getAllMojits(store, 'server', context); - mojitNames = Object.keys(mojits); + if (options.mojit) { + mojitNames = [ options.mojit ]; + } else { + mojitNames = store.listAllMojits(); + } total = mojitNames.length; processSpecs = function(newSpecs, mojitName, store, yuiModuleCacheWriter, @@ -815,6 +801,10 @@ compile.json = function(context, options, callback) { specName = parts[1] || 'default'; if (!mojitName || mName === mojitName) { + /* NOTE_2: During the resource store redesign, it was noticed + * that this branch is never called, which STRONGLY suggests that + * this feature was never used. + */ specUrl = '/' + mName + '/specs/' + specName + '.json'; if (options.verbose) { @@ -830,14 +820,18 @@ compile.json = function(context, options, callback) { } }; - processDefinitionJSON = function(mojitName, store, yuiModuleCacheWriter, + processDefinitionJSON = function(mojitRes, store, yuiModuleCacheWriter, cb) { - var processFullDefinition = function(mojitName, ymcw, cb) { + var mojitName = mojitRes.name, + processFullDefinition, + processPreloadDefinitions; + + processFullDefinition = function(mojitName, ymcw, cb) { + // TODO: probably want to use mojitRes.url instead var url = staticPrefix + mojitName + '/definition.json'; getContentFromUrl(app, url, jsonOpts, function(definition) { var defObj = Y.JSON.parse(definition); - ymcw.createNamespace('compiled.' + mojitName.replace(/\./g, '_') + '.definitions').cache( 'definition', @@ -845,21 +839,21 @@ compile.json = function(context, options, callback) { ); cb(); }); - }, - processPreloadDefinitions = function(mojitNames, ymcw, cb) { - var continuation = function() { - if (mojitNames.length) { - processFullDefinition(mojitNames.shift(), ymcw, - continuation); - } else { - cb(); - } - }; - - processFullDefinition(mojitNames.shift(), ymcw, - continuation); + }; + processPreloadDefinitions = function(mojitNames, ymcw, cb) { + var continuation = function() { + if (mojitNames.length) { + processFullDefinition(mojitNames.shift(), ymcw, + continuation); + } else { + cb(); + } }; + processFullDefinition(mojitNames.shift(), ymcw, + continuation); + }; + /* * The resource store doesn't respond well if you call * store._getMojitConfig() a bunch of times in a row synchronously. @@ -872,10 +866,18 @@ compile.json = function(context, options, callback) { var specsToPreload = [], mojitNamesToPreload = [], - definition = store._getMojitConfig('server', {}, - mojitName, 'definition'); + path, + definition; + + path = libpath.join(mojitRes.source.fs.fullPath, 'definition.json'); + + // TODO: use commandline context instead? + definition = store.config.readConfigYCB(path, {}); if (Object.keys(definition).length > 0) { + /* NOTE_1: During the resource store redesign, it was noticed + * that the "toPreload" variable was never defined, which + * STRONGLY suggests that this feature was never used. if (definition.preload) { if (options.verbose) { libutils.log('processing preload mojits for ' + mojitName); @@ -914,8 +916,11 @@ compile.json = function(context, options, callback) { }); }); } else { + */ processFullDefinition(mojitName, yuiModuleCacheWriter, cb); + /* see NOTE_1 above } + */ } else { cb(); } @@ -924,6 +929,7 @@ compile.json = function(context, options, callback) { processNextMojit = function(store, cb) { var mojitName = mojitNames.shift(), + mojitRes, outputFilepath, theCloser, yuiModuleCacheWriter; @@ -935,23 +941,25 @@ compile.json = function(context, options, callback) { if (options.verbose) { libutils.log('Processing mojit... ' + mojitName); } - outputFilepath = store._mojitPaths[mojitName] + compiledFilename; count += 1; - if (options.remove) { - if (removeFile(outputFilepath)) { - processed += 1; - } + mojitRes = store.getResources('server', context, {type: 'mojit', name: mojitName}); + if (!mojitRes || !mojitRes.length) { return processNextMojit(store, cb); } + mojitRes = mojitRes[0]; - // Skip anything in the "lib/mojits" (open source) or - // "mojit/mojits" (ynodejs_mojito) directories as it's internal - if (outputFilepath.indexOf('lib/mojits') >= 0 || - outputFilepath.indexOf('mojito/mojits') >= 0) { - if (options.verbose) { - libutils.log('skipping ' + outputFilepath); + if ('mojito' === mojitRes.source.pkg.name) { + // don't write framework-provided json into the framework directory + return processNextMojit(store, cb); + } + + outputFilepath = libpath.join(mojitRes.source.fs.fullPath, compiledFilename); + + if (options.remove) { + if (removeFile(outputFilepath)) { + processed += 1; } return processNextMojit(store, cb); } @@ -967,14 +975,16 @@ compile.json = function(context, options, callback) { }; // look for definitions - processDefinitionJSON(mojitName, store, yuiModuleCacheWriter, + // TODO: really only need to do this after all the mojits are processed + processDefinitionJSON(mojitRes, store, yuiModuleCacheWriter, function() { // look for specs + var appConfig = store.getStaticAppConfig(); var specs = []; - if (store._appConfigStatic.specs) { + if (appConfig.specs) { specs = Object.keys(store._appConfigStatic.specs); } - processSpecs(specs, mojitName, store, yuiModuleCacheWriter, + processSpecs(specs, mojitRes.name, store, yuiModuleCacheWriter, theCloser); }); }; From c2dbe603550f6d71918739f645c831e928384498 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 11:18:27 -0700 Subject: [PATCH 076/119] delint --- source/lib/app/commands/compile.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 083145e87..08d801b5d 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -917,7 +917,7 @@ compile.json = function(context, options, callback) { }); } else { */ - processFullDefinition(mojitName, yuiModuleCacheWriter, cb); + processFullDefinition(mojitName, yuiModuleCacheWriter, cb); /* see NOTE_1 above } */ @@ -979,8 +979,8 @@ compile.json = function(context, options, callback) { processDefinitionJSON(mojitRes, store, yuiModuleCacheWriter, function() { // look for specs - var appConfig = store.getStaticAppConfig(); - var specs = []; + var appConfig = store.getStaticAppConfig(), + specs = []; if (appConfig.specs) { specs = Object.keys(store._appConfigStatic.specs); } From 226a7fd9b7e8ae22611fa06257acbbd8c52eb799 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 11:23:03 -0700 Subject: [PATCH 077/119] fixed store-server test controllers no longer depend on binders, even on the client --- source/lib/tests/autoload/store.server-tests.js | 1 - 1 file changed, 1 deletion(-) diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 7cda262af..47a94e416 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -301,7 +301,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var spec = { type: 'rollups' }; store.expandInstanceForEnv('client', spec, {}, function(err, instance) { A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollups']); - A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollupsBinderIndex']); A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollupsModelClient']); }); } From 86c4504ae3812a2a4e10b2ad67f74e7c4e719116 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 11:38:57 -0700 Subject: [PATCH 078/119] small variable name cleanup --- source/lib/store.server.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 784385974..9e5275f70 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1262,8 +1262,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { _expandSpec: function(env, ctx, spec) { var appConfig, base, - idParts, - mojitType, + specParts, + mojitName, specName, ress; @@ -1278,10 +1278,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (!base) { // look in resources - idParts = spec.base.split(':'); - mojitType = idParts.shift(); - specName = idParts.join(':') || 'default'; - ress = this.getResources(env, ctx, {type: 'spec', mojit: mojitType, name: specName}); + specParts = spec.base.split(':'); + mojitName = specParts.shift(); + specName = specParts.join(':') || 'default'; + ress = this.getResources(env, ctx, {type: 'spec', mojit: mojitName, name: specName}); if (1 === ress.length) { base = this.config.readConfigYCB(ress[0].source.fs.fullPath, ctx); } From c33fe8197180c2be8badd2fb7580573455bbdfa9 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 16:32:33 -0700 Subject: [PATCH 079/119] fixed `mojito test --coverage` --- source/lib/app/commands/test.js | 37 +++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 16 deletions(-) diff --git a/source/lib/app/commands/test.js b/source/lib/app/commands/test.js index d93fed074..e72f1c1a6 100644 --- a/source/lib/app/commands/test.js +++ b/source/lib/app/commands/test.js @@ -7,11 +7,12 @@ /*jslint anon:true, sloppy:true, regexp:true, nomen:true*/ + var pathlib = require('path'), fs = require('fs'), - utils = require(pathlib.join(__dirname, '../../management/utils')), - ymc = require(pathlib.join(__dirname, '../../management/yui-module-configurator')), + utils = require('../../management/utils'), + ymc = require('../../management/yui-module-configurator'), exec = require('child_process').exec, copyExclude = utils.copyExclude, copyFile = utils.copyFile, @@ -117,11 +118,15 @@ function collectRunResults(results) { } } -function configureYUI(Y, store) { - Y.applyConfig(store.yui.getConfigFw('server', {})); - Y.applyConfig(store.yui.getConfigApp('server', {})); - Y.applyConfig(store.yui.getConfigAllMojits('server', {})); - Y.applyConfig({useSync: true}); +function configureYUI(YUI, store) { + YUI.applyConfig({ + useSync: true, + groups: { + 'mojito-fw': store.yui.getConfigFw('server', {}), + 'mojito-app': store.yui.getConfigApp('server', {}), + 'mojito-mojits': store.yui.getConfigAllMojits('server', {}) + } + }); } @@ -418,7 +423,7 @@ function processResults() { function executeTestsWithinY(tests, cb) { - var Y, + var YUIInst, suiteName = ''; function handleEvent(event) { @@ -468,14 +473,14 @@ function executeTestsWithinY(tests, cb) { TestRunner.clear(); // create new YUI instance using tests and mojito - Y = YUI({core: [ + YUIInst = YUI({core: [ 'get', 'features', 'intl-base', 'mojito' ]}); - Y.use.apply(Y, tests); + YUIInst.use.apply(YUIInst, tests); } @@ -662,7 +667,7 @@ runTests = function(opts) { testModuleNames = ['mojito', 'mojito-test']; testRunner = function(testPath) { - var Y, + var Ystore, testConfigs, sourceConfigs; @@ -686,17 +691,17 @@ runTests = function(opts) { modules: testConfigs }); } else { - Y = YUI(); - Y.applyConfig({ + Ystore = YUI(); + Ystore.applyConfig({ useSync: true, modules: { 'mojito-resource-store': { - fullpath: pathlib.join(__dirname, '../../store.server.js') + fullpath: pathlib.join(targetMojitoPath, 'lib/store.server.js') } } }); - Y.use('mojito-resource-store'); - store = new Y.mojito.ResourceStore({ + Ystore.use('mojito-resource-store'); + store = new Ystore.mojito.ResourceStore({ root: testPath, context: {}, appConfig: { env: 'test' } From e9ab62e4e2fc8596bcf9e689769544359c63c348 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 4 Jul 2012 22:32:56 -0700 Subject: [PATCH 080/119] documentation cleanup (using yuidocjs@0.3.14) --- source/lib/app/addons/rs/config.server.js | 42 ++++- source/lib/app/addons/rs/selector.server.js | 15 ++ source/lib/app/addons/rs/url.server.js | 50 +++++- source/lib/app/addons/rs/yui.server.js | 116 ++++++++++++- .../lib/app/addons/view-engines/mu.client.js | 11 ++ .../lib/app/addons/view-engines/mu.server.js | 3 + source/lib/store.server.js | 154 ++++++++++-------- 7 files changed, 308 insertions(+), 83 deletions(-) diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js index e1317192f..ced7aa2cf 100644 --- a/source/lib/app/addons/rs/config.server.js +++ b/source/lib/app/addons/rs/config.server.js @@ -7,6 +7,19 @@ /*jslint anon:true, sloppy:true, nomen:true*/ /*global YUI*/ + +/** + * The Resource Store is a Y.Base -- a host for Y.Plugins. + * Each Addon provides additional functions through a namespace that is + * attached directly to the resource store. + * @module ResourceStoreAddon + */ + + +/** + * @class RSAddonConfig + * @extension ResourceStore.server + */ YUI.add('addon-rs-config', function(Y, NAME) { var libfs = require('fs'), @@ -40,14 +53,17 @@ YUI.add('addon-rs-config', function(Y, NAME) { }, + /** + * @method getDimensions + * @return {object} the YCB dimentions structure for the app + */ getDimensions: function() { return this.rs.cloneObj(this._ycbDims); }, /** - * Reads and parses a JSON file - * + * Reads and parses a JSON file. * @method readConfigJSON * @param fullPath {string} path to JSON file * @return {mixed} contents of JSON file @@ -74,8 +90,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { /** - * reads a configuration file that is in YCB format - * + * Reads a configuration file that is in YCB format. * @method readConfigYCB * @param ctx {object} runtime context * @param fullPath {string} path to the YCB file @@ -100,6 +115,13 @@ YUI.add('addon-rs-config', function(Y, NAME) { }, + /** + * Using AOP, this is called after the ResourceStore's version. + * @method findResourceByConvention + * @param source {object} metadata about where the resource is located + * @param mojitType {string} name of mojit to which the resource likely belongs + * @return {object||null} for config file resources, returns metadata signifying that + */ findResourceByConvention: function(source, mojitType) { var fs = source.fs, use = false; @@ -138,6 +160,15 @@ YUI.add('addon-rs-config', function(Y, NAME) { }, + /** + * Using AOP, this is called before the ResourceStore's version. + * @method parseResource + * @param source {object} metadata about where the resource is located + * @param type {string} type of the resource + * @param subtype {string} subtype of the resource + * @param mojitType {string} name of mojit to which the resource likely belongs + * @return {object||null} for config file resources, returns the resource metadata + */ parseResource: function(source, type, subtype, mojitType) { var baseParts, res; @@ -169,10 +200,9 @@ YUI.add('addon-rs-config', function(Y, NAME) { /** * Read the application's dimensions.json file for YCB processing. If not * available, fall back to the framework's default dimensions.json. - * + * @private * @method _readYcbDimensions * @return {array} contents of the dimensions.json file - * @private */ _readYcbDimensions: function() { var path = libpath.join(this.appRoot, 'dimensions.json'); diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js index 63f7baebe..ff126d38d 100644 --- a/source/lib/app/addons/rs/selector.server.js +++ b/source/lib/app/addons/rs/selector.server.js @@ -7,6 +7,15 @@ /*jslint anon:true, sloppy:true, nomen:true*/ /*global YUI*/ + +/** + * @module ResourceStoreAddon + */ + +/** + * @class RSAddonSelector + * @extension ResourceStore.server + */ YUI.add('addon-rs-selector', function(Y, NAME) { var libpath = require('path'), @@ -42,6 +51,12 @@ YUI.add('addon-rs-selector', function(Y, NAME) { }, + /** + * Returns the priority-ordered selector list (POSL) for the context. + * @method getListForContext + * @param ctx {object} runtime context + * @return {array} priority-ordered selector list + */ getListFromContext: function(ctx) { var sels = ['*'], p, diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 45a532a5d..a07927299 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -7,6 +7,15 @@ /*jslint anon:true, sloppy:true, nomen:true*/ /*global YUI*/ + +/** + * @module ResourceStoreAddon + */ + +/** + * @class RSAddonUrl + * @extension ResourceStore.server + */ YUI.add('addon-rs-url', function(Y, NAME) { var libfs = require('fs'), @@ -47,20 +56,34 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, - // TODO DOCS + /** + * Returns the full filesystem path for a URL. + * This is primarily used by the static handler middleware. + * @method getPathForURL + * @param url {string} URL to lookup + * @return {string} path on disk which the URL points to + */ getPathForURL: function(url) { return this.URLpaths[url]; }, - // TODO DOCS + /** + * This is primarily used by the commandline tools (`build` and `compile`). + * @method getURLPaths + * @return {object} all URLs and cooresponding paths + */ getURLPaths: function() { return Y.clone(this.URLpaths, true); }, - // utility for `build html5app` command - // TODO DOCS + /** + * This is primarily used by the commandline tool `build`. + * @method getSpecURL + * @param id {string} spec ID + * @return {string} static handler URL for the spec + */ getSpecURL: function(id) { var urlParts = []; if (this.config.prefix) { @@ -72,6 +95,12 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, + /** + * Using AOP, this is called after the ResourceStore's version. + * It computes the static handler URL for all resources in all the + * mojits (as well as the mojit itself). + * @method preloadResourceVersions + */ preloadResourceVersions: function() { var mojits = this.rs.listAllMojits(), m, @@ -140,12 +169,25 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, + /** + * This is called when the ResourceStore fires this event. + * It calculates the `assetsRoot` for the mojit. + * @method getMojitTypeDetails + * @param evt {object} + */ getMojitTypeDetails: function(evt) { var ress = this.rs.getResources(evt.args.env, evt.args.ctx, {type: 'mojit', name: evt.args.mojitType}); evt.mojit.assetsRoot = ress[0].url + '/assets'; }, + /** + * Calculates the static handler URL for the resource. + * @private + * @method _calcResourceURL + * @param res {object} the resource for which to calculate the URL + * @param mojitRes {object} the resource for the mojit + */ _calcResourceURL: function(res, mojitRes) { var fs = res.source.fs, relativePath = fs.fullPath.substr(fs.rootDir.length + 1), diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index ee529e0cc..a0b1d1273 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -7,6 +7,16 @@ /*jslint anon:true, sloppy:true, nomen:true*/ /*global YUI*/ + +/** + * @module ResourceStoreAddon + */ + + +/** + * @class RSAddonYUI + * @extension ResourceStore.server + */ YUI.add('addon-rs-yui', function(Y, NAME) { var libfs = require('fs'), @@ -49,7 +59,14 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, - // TODO DOCS + /** + * Returns a datastructure which tells a YUI instance where to find + * the YUI modules that are part of the Mojito framework. + * @method getConfigFw + * @param env {string} runtime environment (either `client`, or `server`) + * @param ctx {object} runtime context + * @return {object} datastructure for configuring YUI + */ getConfigFw: function(env, ctx) { var r, res, @@ -76,7 +93,14 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, - // TODO DOCS + /** + * Returns a datastructure which tells a YUI instance where to find + * the YUI modules that are app-level Mojito resources. + * @method getConfigApp + * @param env {string} runtime environment (either `client`, or `server`) + * @param ctx {object} runtime context + * @return {object} datastructure for configuring YUI + */ getConfigApp: function(env, ctx) { var r, res, @@ -103,7 +127,14 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, - // TODO DOCS + /** + * Returns a datastructure which tells a YUI instance where to find + * the YUI modules that are in all the mojits. + * @method getConfigAllMojits + * @param env {string} runtime environment (either `client`, or `server`) + * @param ctx {object} runtime context + * @return {object} datastructure for configuring YUI + */ getConfigAllMojits: function(env, ctx) { var m, mojit, @@ -138,6 +169,13 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, + /** + * Using AOP, this is called after the ResourceStore's version. + * @method findResourceByConvention + * @param source {object} metadata about where the resource is located + * @param mojitType {string} name of mojit to which the resource likely belongs + * @return {object||null} for yui modules or lang bundles, returns metadata signifying that + */ findResourceByConvention: function(source, mojitType) { var fs = source.fs; @@ -164,6 +202,15 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, + /** + * Using AOP, this is called before the ResourceStore's version. + * @method parseResource + * @param source {object} metadata about where the resource is located + * @param type {string} type of the resource + * @param subtype {string} subtype of the resource + * @param mojitType {string} name of mojit to which the resource likely belongs + * @return {object||null} for yui modules or lang bundles, returns the resource metadata + */ parseResource: function(source, type, subtype, mojitType) { var fs = source.fs, baseParts, @@ -219,6 +266,13 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, + /** + * Using AOP, this is called before the ResourceStore's version. + * If the resource is a YUI module, augments the metadata with details + * about the YUI module. + * @method addResourceVersion + * @param res {object} resource version metadata + */ addResourceVersion: function(res) { if ('.js' !== res.source.fs.ext) { return; @@ -235,6 +289,13 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, + /** + * This is called when the ResourceStore fires this event. + * It augments the mojit type details with the precomputed YUI module + * dependencies. + * @method getMojitTypeDetails + * @param evt {object} + */ getMojitTypeDetails: function(evt) { var dest = evt.mojit, env = evt.args.env, @@ -286,6 +347,13 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, + /** + * This is called when the ResourceStore fires this event. + * It precomputes the YUI module dependencies, to be used later during + * getMojitTypeDetails. + * @method mojitResourcesResolved + * @param evt {object} + */ mojitResourcesResolved: function(evt) { var env = evt.env, posl = evt.posl, @@ -399,7 +467,17 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, - // TODO DOCS + /** + * Precomputes a set of dependencies. + * @private + * @method _sortYUIModules + * @param lang {string} YUI language code + * @param env {string} runtime environment (either `client`, or `server`) + * @param mojit {string} name of the mojit + * @param modules {object} YUI module metadata + * @param required {object} lookup hash of YUI module names that are required + * @return {object} precomputed (and sorted) module dependencies + */ _sortYUIModules: function(lang, env, mojit, modules, required) { var Y, loader, @@ -458,7 +536,16 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, - // TODO DOCS + /** + * Saves the precomputed YUI module dependencies for later. + * @private + * @method _setYUISorted + * @param env {string} runtime environment (either `client`, or `server`) + * @param poslKey {string} key (representing the POSL) under which to save the moduldes + * @param lang {string} YUI language code + * @param module {string} YUI module name for which the precomputed dependencies are for + * @param sorted {object} the precomputed dependencies + */ _setYUISorted: function(env, poslKey, lang, module, sorted) { if (!this.sortedModules[env]) { this.sortedModules[env] = {}; @@ -473,7 +560,16 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, - // TODO DOCS + /** + * Returns precomputed dependencies saved by _setYuiSorted. + * @private + * @method _setYUISorted + * @param env {string} runtime environment (either `client`, or `server`) + * @param poslKey {string} key (representing the POSL) under which to save the moduldes + * @param lang {string} YUI language code + * @param module {string} YUI module name for which the precomputed dependencies are for + * @return {object} the precomputed dependencies + */ _getYUISorted: function(env, poslKey, lang, module) { lang = lang || ''; var parts = lang.split('-'), @@ -497,7 +593,13 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, - // TODO DOCS + /** + * If the resource is a YUI module, augments its metadata with metadata + * about the YUI module. + * @private + * @method _parseYUIModule + * @param res {object} resource metadata + */ _parseYUIModule: function(res) { var file, ctx, diff --git a/source/lib/app/addons/view-engines/mu.client.js b/source/lib/app/addons/view-engines/mu.client.js index 86a96d977..d706449fb 100644 --- a/source/lib/app/addons/view-engines/mu.client.js +++ b/source/lib/app/addons/view-engines/mu.client.js @@ -8,7 +8,18 @@ /*jslint anon:true, sloppy:true, nomen:true*/ /*global YUI*/ +/** + * View engines. + * + * Please see the [documentation](http://developer.yahoo.com/cocktails/mojito/docs/topics/mojito_extensions.html#view-engines). + * + * @module ViewEngines + */ + +/** + * @Module ViewEngines + */ YUI.add('mojito-mu', function(Y, NAME) { var CACHE = {}, diff --git a/source/lib/app/addons/view-engines/mu.server.js b/source/lib/app/addons/view-engines/mu.server.js index 8cff75345..509b4cee9 100644 --- a/source/lib/app/addons/view-engines/mu.server.js +++ b/source/lib/app/addons/view-engines/mu.server.js @@ -9,6 +9,9 @@ /*global YUI*/ +/** + * @Module ViewEngines + */ YUI.add('mojito-mu', function(Y, NAME) { var mu = YUI.require(__dirname + '/../../libs/Mulib/Mu'), diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 9e5275f70..30c645c5d 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -58,7 +58,6 @@ * * * @module ResourceStore - * @main */ YUI.add('mojito-resource-store', function(Y, NAME) { @@ -134,10 +133,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @class ResourceStore.server * @constructor * @requires addon-rs-config, addon-rs-selector - * @param config {object} - * @param root {string} directory to manage (usually the application directory) - * @param context {object} static context - * @param appConfig {object} overrides for `application.json` + * @param config {object} configuration for the store + * @param config.root {string} directory to manage (usually the application directory) + * @param config.context {object} static context + * @param config.appConfig {object} overrides for `application.json` */ function ResourceStore(config) { ResourceStore.superclass.constructor.apply(this, arguments); @@ -264,7 +263,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * the framework. * * @method preload - * @return {nothing} */ preload: function() { // We need to do an initial sweep to find the resource store addons. @@ -283,6 +281,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * to call `listAllMojits()` and iterate over that list, calling this * method with `mojit:` in the filter.) * + * @method getResourceVersions * @param filter {object} limit returned resource versions to only those whose keys/values match the filter * @return {array of objects} list of matching resource versions */ @@ -323,7 +322,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * `listAllMojits()` and iterate over that list, calling this method * with `mojit:` in the filter.) * - * @param env {string} the runtime environment + * @method getResources + * @param env {string} the runtime environment (either `client` or `server`) * @param ctx {object} the context * @param filter {object} limit returned resources to only those whose keys/values match the filter * @return {array of objects} list of matching resources @@ -396,12 +396,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * Returns, via callback, the fully expanded mojit instance specification. * + * @async * @method getSpec - * @param env {string} either "client" or "server" + * @param env {string} the runtime environment (either `client` or `server`) * @param id {string} the ID of the spec to return * @param ctx {object} the runtime context for the spec * @param callback {function(err,spec)} callback used to return the results (or error) - * @return {nothing} results returned via the callback parameter */ getSpec: function(env, id, ctx, callback) { this.expandInstanceForEnv(env, {base: id}, ctx, function(err, obj) { @@ -420,12 +420,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * Returns, via callback, the details of the mojit type. * + * @async * @method getType - * @param env {string} either "client" or "server" + * @param env {string} the runtime environment (either `client` or `server`) * @param type {string} the mojit type * @param ctx {object} the runtime context for the type * @param callback {function(err,spec)} callback used to return the results (or error) - * @return {nothing} results returned via the callback parameter */ getType: function(env, type, ctx, callback) { this.expandInstanceForEnv(env, {type: type}, ctx, function(err, obj) { @@ -442,13 +442,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** - * This just calls expandInstanceForEnv() with `env` set to `server`. + * This just calls `expandInstanceForEnv()` with `env` set to `server`. * + * @async * @method expandInstance * @param instance {map} partial instance to expand * @param ctx {object} the context * @param cb {function(err,instance)} callback used to return the results (or error) - * @return {nothing} results returned via the callback parameter */ expandInstance: function(instance, ctx, cb) { this.expandInstanceForEnv('server', instance, ctx, cb); @@ -456,6 +456,14 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + /** + * Expands the instance into all details necessary to dispatch the mojit. + * @method expandInstanceForEnv + * @param env {string} the runtime environment (either `client` or `server`) + * @param instance {object} + * @param ctx {object} the context + * @param cb {function(err,instance)} callback used to return the results (or error) + */ expandInstanceForEnv: function(env, instance, ctx, cb) { var cacheKey = Y.JSON.stringify(instance) + Y.JSON.stringify(ctx), cacheValue = this._expandInstanceCache[env][cacheKey], @@ -504,15 +512,27 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * Returns details about a mojit type. * - * TODO DOCS: use onHostMethod() and afterHostMethod() instead of AOP methods + * As the last step of execution, this fires the `getMojitTypeDetails` + * event so that Resource Store addons can augment the returned structure. * * @method getMojitTypeDetails - * @param env {string} "client" or "server" + * @param env {string} the runtime environment (either `client` or `server`) * @param ctx {object} the context * @param mojitType {string} mojit type * @param dest {object} object in which to place the results * @return {object} returns the "dest" parameter, which has had details added to it */ + /** + * Fired at the end of the `getMojitTypeDetails()` method to allow + * modification of the results. + * @event getMojitTypeDetails + * @param args {object} input arguments + * @param args.env {string} the runtime environment (either `client` or `server`) + * @param args.ctx {object} runtime context + * @param args.posl {array} priority-ordered seletor list + * @param args.mojitType {string} name of mojit + * @param mojit {object} the mojit type details + */ getMojitTypeDetails: function(env, ctx, mojitType, dest) { //Y.log('getMojitTypeDetails('+env+', '+JSON.stringify(ctx)+', '+mojitType+')', 'debug', NAME); var ress, @@ -619,7 +639,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * Returns the routes configured in the application. - * + * @method getRoutes * @param ctx {object} the context * @return {object} routes */ @@ -659,14 +679,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * Recursively merge one object onto another. - * From http://stackoverflow.com/questions/171251/ - * how-can-i-merge-properties-of-two-javascript-objects-dynamically/ - * 383245#383245. + * [original implementation](http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically/383245#383245) * * @method mergeRecursive * @param dest {object} object to merge into * @param src {object} object to merge onto "dest" - * @param matchType {boolean} controls whether a non-object in the src is + * @param typeMatch {boolean} controls whether a non-object in the src is * allowed to clobber a non-object in the dest (if a different type) * @return {object} the modified "dest" object is also returned directly */ @@ -697,7 +715,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * @method cloneObj - * @param o {mixed} + * @param o {mixed} the object to clone * @return {mixed} deep copy of argument */ cloneObj: function(o) { @@ -741,12 +759,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * * You most often don't want to call this directly, but instead to hook * into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.afterHostMethod('loadAddons', this._myLoadAddons, this); - * ``` + * + * this.afterHostMethod('loadAddons', this._myLoadAddons, this); * * @method loadAddons - * @return {nothing} */ loadAddons: function() { var modules = {}, @@ -781,15 +797,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * Preload metadata about all resource versions in the application * (and Mojito framework). * - * * You most often don't want to call this directly, but instead to hook * into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.afterHostMethod('preloadResourceVersions', this._myPreloadResourceVersions, this); - * ``` + * + * this.afterHostMethod('preloadResourceVersions', this._myPreloadResourceVersions, this); * * @method preloadResourceVersions - * @return {nothing} work down via other called methods */ preloadResourceVersions: function() { var me = this, @@ -850,9 +863,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * Called by the ResourceStore to decide if a file should be considered * a resource. You most often don't want to call this directly, but * instead to hook into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.afterHostMethod('findResourceByConvention', this._myFindResourceByConvention, this); - * ``` + * + * this.afterHostMethod('findResourceByConvention', this._myFindResourceByConvention, this); * * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. * This method figures out the type (and subtype) of a file, and `parseResource()` turns @@ -865,10 +877,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * True indicates that the directory contents should be scanned, while false * indicates that the directory should be skipped. * If the source does represent a resource, then an object with the following - * fields should be returned; - * @param type {string} type of the resource - * @param subtype {string} optional subtype of the resource - * @param skipSubdirParts {integer} number of path parts of `source.fs.subDir` to skip + * fields should be returned: + * type {string} type of the resource, + * subtype {string} optional subtype of the resource, + * skipSubdirParts {integer} number of path parts of `source.fs.subDir` to skip */ findResourceByConvention: function(source, mojitType) { var fs = source.fs, @@ -969,9 +981,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * Called by the ResourceStore to turn a file into a resource. * You most often don't want to call this directly, but instead to hook * into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.beforeHostMethod('parseResource', this._myParseResource, this); - * ``` + * + * this.beforeHostMethod('parseResource', this._myParseResource, this); * * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. * `findResourceByConvention()` figures out the type (and subtype) of a file, and @@ -1113,13 +1124,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * Called by the ResourceStore to register a resource version. * You most often don't want to call this directly, but instead to hook * into it using the AOP mechanism of `Y.Plugin.Base`: - * ``` - * this.beforeHostMethod('parseResource', this._myParseResource, this); - * ``` + * + * this.beforeHostMethod('parseResource', this._myParseResource, this); * * @method addResourceVersion * @param res {object} the resource version - * @return {nothing} */ addResourceVersion: function(res) { res.affinity = new Affinity(res.affinity); @@ -1152,8 +1161,15 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * affinity (env or "common"). * * @method resolveResourceVersions - * @return {nothing} */ + /** + * Fired after the resources for a mojit have been resolved. + * @event mojitResourcesResolved + * @param env {string} the runtime environment (either `client` or `server`) + * @param posl {array} priority-ordered seletor list + * @param mojit {string} name of mojit + * @param ress {array} list of mojit metadata (for the `env` and `posl`) + */ resolveResourceVersions: function() { var c, ctx, ctxs, poslKey, posl, posls = {}, @@ -1205,7 +1221,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (this._mojitRVs.hasOwnProperty(type)) { ress = this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); this._mojitResources[env][poslKey][type] = ress; - // TODO DOCS: document event this.fire('mojitResourcesResolved', { env: env, posl: posl, @@ -1251,14 +1266,28 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // PRIVATE METHODS - // used for unit testing - // TODO DOCS + /** + * Used for unit testing. + * @private + * @method _mockLib + * @param name {string} name of library to mock out + * @param lib {mixed} library to mock out + */ _mockLib: function(name, lib) { this._libs[name] = lib; }, - // TODO DOCS + /** + * Applies spec inheritance by following the `base` and merging up the + * results. + * @private + * @method _expandSpec + * @param env {string} the runtime environment (either `client` or `server`) + * @param ctx {object} runtime context + * @param spec {object} spec to expand + * @return {object} expanded sped + */ _expandSpec: function(env, ctx, spec) { var appConfig, base, @@ -1297,12 +1326,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * preloads metadata about resources in a package - * (but not subpackages in its node_modules/) + * (but not subpackages in its `node_modules/`) * * @private * @method _preloadPackage * @param info {object} metadata about the package - * @return {nothing} work down via other called methods */ _preloadPackage: function(info) { var dir, @@ -1346,12 +1374,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * preloads metadata about resources in the application directory - * (but not node_modules/) + * (but not `node_modules/`) * * @private * @method _preloadApp * @param pkg {object} metadata (name and version) about the app's package - * @return {nothing} work down via other called methods */ _preloadApp: function(pkg) { var ress, @@ -1384,12 +1411,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** - * preloads metadata about resource in a directory + * preloads metadata about resources in a directory * * @method _preloadDirBundle * @param dir {string} directory path * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods * @private */ _preloadDirBundle: function(dir, pkg) { @@ -1415,7 +1441,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @param dir {string} directory path * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods */ _preloadDirMojits: function(dir, dirType, pkg) { var i, @@ -1452,7 +1477,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @param dir {string} directory path * @param dirType {string} type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit" * @param pkg {object} metadata (name and version) about the package - * @return {nothing} work down via other called methods */ _preloadDirMojit: function(dir, dirType, pkg) { var mojitType, @@ -1535,14 +1559,14 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * Resolves versions for a list of resources. - * The priority is based on passed-in configuration. See - * resolveResourceVersions() for details. + * The priority is based on passed-in configuration. + * See `resolveResourceVersions()` for details. * * @private * @method _resolveVersions * @param affinities {object} lookup hash for priority adjustment for each affinity * @param selectors {object} lookup hash for priority adjustment for each selector - * @param sourceBase {int} multiplier for order in priority list + * @param sourceBase {int} multiplier for order in source list * @param srcs {array of arrays} resource versions to resolve * @return {array} list of resolved resources */ @@ -1791,8 +1815,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** - * A wrapper for fs.readdirSync() that guarantees ordering. The order in - * which the file system is walked is significant within the resource + * A wrapper for `fs.readdirSync()` that guarantees ordering. The order + * in which the file system is walked is significant within the resource * store, e.g., when looking up a matching context. * * @private @@ -1808,13 +1832,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * Recursively walks a directory - * * @private * @method _walkDirRecursive * @param dir {string} directory to start at * @param cb {function(error, subdir, name, isFile)} callback called for each file - * @param _subdir {string} INTERNAL argument, please ignore - * @return {nothing} value returned via callback + * @param _subdir {string} INTERNAL argument for recursion, please ignore */ _walkDirRecursive: function(dir, cb, _subdir) { var subdir, @@ -1858,6 +1880,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** * takes a list of globs and turns it into a list of matching paths * @private + * @method _globList * @param prefix {string} prefix for every path in the list * @param list {array} list of globs * @return {array} list of paths matching the globs @@ -1882,7 +1905,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @private * @method _yuiUseSync * @param modules {object} YUI module configuration information - * @return {nothing} */ _yuiUseSync: function(modules) { Y.applyConfig({ From ca8e2ae61bf876f470ffc9fb5a3353f4c1f5d8e9 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 5 Jul 2012 09:56:45 -0700 Subject: [PATCH 081/119] ack, I was wrong about toPreload being undefined --- source/lib/app/commands/compile.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 08d801b5d..888dca572 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -801,7 +801,7 @@ compile.json = function(context, options, callback) { specName = parts[1] || 'default'; if (!mojitName || mName === mojitName) { - /* NOTE_2: During the resource store redesign, it was noticed + /* NOTE_1: During the resource store redesign, it was noticed * that this branch is never called, which STRONGLY suggests that * this feature was never used. */ @@ -875,9 +875,6 @@ compile.json = function(context, options, callback) { definition = store.config.readConfigYCB(path, {}); if (Object.keys(definition).length > 0) { - /* NOTE_1: During the resource store redesign, it was noticed - * that the "toPreload" variable was never defined, which - * STRONGLY suggests that this feature was never used. if (definition.preload) { if (options.verbose) { libutils.log('processing preload mojits for ' + mojitName); @@ -887,7 +884,6 @@ compile.json = function(context, options, callback) { Object.keys(store._appConfigStatic.specs).forEach( function(fullSpecName) { var mName = fullSpecName.split(':').shift(); - if (mName === toPreload) { specsToPreload.push(fullSpecName); } @@ -916,11 +912,8 @@ compile.json = function(context, options, callback) { }); }); } else { - */ - processFullDefinition(mojitName, yuiModuleCacheWriter, cb); - /* see NOTE_1 above + processFullDefinition(mojitName, yuiModuleCacheWriter, cb); } - */ } else { cb(); } From 686371328b10829280e843ce2020f6dcefdd67d6 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 6 Jul 2012 09:18:13 -0700 Subject: [PATCH 082/119] cleanups and renames inspired by writing the skeleton doc --- source/lib/app/addons/rs/config.server.js | 14 ++--- source/lib/app/addons/rs/url.server.js | 3 +- source/lib/app/addons/rs/yui.server.js | 12 ++--- source/lib/store.server.js | 51 +++++++++++-------- .../app/addons/rs/config-tests.server.js | 24 ++++----- .../app/addons/rs/yui-tests.server.js | 26 +++++----- .../lib/tests/autoload/store.server-tests.js | 4 +- 7 files changed, 72 insertions(+), 62 deletions(-) diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js index ced7aa2cf..044685187 100644 --- a/source/lib/app/addons/rs/config.server.js +++ b/source/lib/app/addons/rs/config.server.js @@ -38,8 +38,8 @@ YUI.add('addon-rs-config', function(Y, NAME) { this.rs = config.host; this.appRoot = config.appRoot; this.mojitoRoot = config.mojitoRoot; - this.afterHostMethod('findResourceByConvention', this.findResourceByConvention, this); - this.beforeHostMethod('parseResource', this.parseResource, this); + this.afterHostMethod('findResourceVersionByConvention', this.findResourceVersionByConvention, this); + this.beforeHostMethod('parseResourceVersion', this.parseResourceVersion, this); this._jsonCache = {}; // fullPath: contents as JSON object this._ycbCache = {}; // fullPath: YCB config object @@ -55,7 +55,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { /** * @method getDimensions - * @return {object} the YCB dimentions structure for the app + * @return {object} the YCB dimensions structure for the app */ getDimensions: function() { return this.rs.cloneObj(this._ycbDims); @@ -117,12 +117,12 @@ YUI.add('addon-rs-config', function(Y, NAME) { /** * Using AOP, this is called after the ResourceStore's version. - * @method findResourceByConvention + * @method findResourceVersionByConvention * @param source {object} metadata about where the resource is located * @param mojitType {string} name of mojit to which the resource likely belongs * @return {object||null} for config file resources, returns metadata signifying that */ - findResourceByConvention: function(source, mojitType) { + findResourceVersionByConvention: function(source, mojitType) { var fs = source.fs, use = false; @@ -162,14 +162,14 @@ YUI.add('addon-rs-config', function(Y, NAME) { /** * Using AOP, this is called before the ResourceStore's version. - * @method parseResource + * @method parseResourceVersion * @param source {object} metadata about where the resource is located * @param type {string} type of the resource * @param subtype {string} subtype of the resource * @param mojitType {string} name of mojit to which the resource likely belongs * @return {object||null} for config file resources, returns the resource metadata */ - parseResource: function(source, type, subtype, mojitType) { + parseResourceVersion: function(source, type, subtype, mojitType) { var baseParts, res; diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index a07927299..8d94c48cb 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -102,7 +102,7 @@ YUI.add('addon-rs-url', function(Y, NAME) { * @method preloadResourceVersions */ preloadResourceVersions: function() { - var mojits = this.rs.listAllMojits(), + var mojits, m, mojit, mojitRes, @@ -113,6 +113,7 @@ YUI.add('addon-rs-url', function(Y, NAME) { res, skip; + mojits = this.rs.listAllMojits(), mojits.push('shared'); for (m = 0; m < mojits.length; m += 1) { mojit = mojits[m]; diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index a0b1d1273..eb5ec393a 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -35,8 +35,8 @@ YUI.add('addon-rs-yui', function(Y, NAME) { this.rs = config.host; this.appRoot = config.appRoot; this.mojitoRoot = config.mojitoRoot; - this.afterHostMethod('findResourceByConvention', this.findResourceByConvention, this); - this.beforeHostMethod('parseResource', this.parseResource, this); + this.afterHostMethod('findResourceVersionByConvention', this.findResourceVersionByConvention, this); + this.beforeHostMethod('parseResourceVersion', this.parseResourceVersion, this); this.beforeHostMethod('addResourceVersion', this.addResourceVersion, this); this.onHostEvent('getMojitTypeDetails', this.getMojitTypeDetails, this); this.onHostEvent('mojitResourcesResolved', this.mojitResourcesResolved, this); @@ -171,12 +171,12 @@ YUI.add('addon-rs-yui', function(Y, NAME) { /** * Using AOP, this is called after the ResourceStore's version. - * @method findResourceByConvention + * @method findResourceVersionByConvention * @param source {object} metadata about where the resource is located * @param mojitType {string} name of mojit to which the resource likely belongs * @return {object||null} for yui modules or lang bundles, returns metadata signifying that */ - findResourceByConvention: function(source, mojitType) { + findResourceVersionByConvention: function(source, mojitType) { var fs = source.fs; if (!fs.isFile) { @@ -204,14 +204,14 @@ YUI.add('addon-rs-yui', function(Y, NAME) { /** * Using AOP, this is called before the ResourceStore's version. - * @method parseResource + * @method parseResourceVersion * @param source {object} metadata about where the resource is located * @param type {string} type of the resource * @param subtype {string} subtype of the resource * @param mojitType {string} name of mojit to which the resource likely belongs * @return {object||null} for yui modules or lang bundles, returns the resource metadata */ - parseResource: function(source, type, subtype, mojitType) { + parseResourceVersion: function(source, type, subtype, mojitType) { var fs = source.fs, baseParts, res; diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 30c645c5d..147380507 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -173,8 +173,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { server: {} }; - // all selectors that are actually in the app - // hash: key is selector, value is just boolean true + /** + * All selectors that are actually in the app. + * Key is selector, value is just boolean `true`. + * This won't be populated until `preloadResourceVersions()` is done. + * @property selectors + * @type Object + */ this.selectors = {}; // Y.Plugin AOP doesn't allow afterHostMethod() callbacks to @@ -617,8 +622,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } else { dest.views[res.name]['content-path'] = res.source.fs.fullPath; } - dest.views[res.name].engine = res.viewEngine; - engines[res.viewEngine] = true; + dest.views[res.name].engine = res.view.engine; + engines[res.view.engine] = true; } } @@ -811,6 +816,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { dir, info; + this.selectors = {}; this._appRVs = []; this._mojitRVs = {}; @@ -864,13 +870,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * a resource. You most often don't want to call this directly, but * instead to hook into it using the AOP mechanism of `Y.Plugin.Base`: * - * this.afterHostMethod('findResourceByConvention', this._myFindResourceByConvention, this); + * this.afterHostMethod('findResourceVersionByConvention', this._myFindResourceByConvention, this); * - * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. - * This method figures out the type (and subtype) of a file, and `parseResource()` turns + * Generally `findResourceVersionByConvention()` and `parseResourceVersion()` are meant to work together. + * This method figures out the type (and subtype) of a file, and `parseResourceVersion()` turns * the file into an actual resource. * - * @method findResourceByConvention + * @method findResourceVersionByConvention * @param source {object} the same as the `source` part of a resource * @param mojitType {string} the name of the mojit * @return {boolean|object} If the source is a directory, a boolean can be returned. @@ -882,7 +888,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * subtype {string} optional subtype of the resource, * skipSubdirParts {integer} number of path parts of `source.fs.subDir` to skip */ - findResourceByConvention: function(source, mojitType) { + findResourceVersionByConvention: function(source, mojitType) { var fs = source.fs, baseParts = fs.basename.split('.'), type; @@ -982,20 +988,20 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * You most often don't want to call this directly, but instead to hook * into it using the AOP mechanism of `Y.Plugin.Base`: * - * this.beforeHostMethod('parseResource', this._myParseResource, this); + * this.beforeHostMethod('parseResourceVersion', this._myParseResource, this); * - * Generally `findResourceByConvention()` and `parseResource()` are meant to work together. - * `findResourceByConvention()` figures out the type (and subtype) of a file, and + * Generally `findResourceVersionByConvention()` and `parseResourceVersion()` are meant to work together. + * `findResourceVersionByConvention()` figures out the type (and subtype) of a file, and * this method turns the file into an actual resource. * - * @method parseResource + * @method parseResourceVersion * @param source {object} the same as the `source` part of a resource * @param type {string} the resource type of the file * @param subtype {string} the optional resource subtype of the file * @param mojitType {string} the name of the mojit * @return {object|undefined} the resource version */ - parseResource: function(source, type, subtype, mojitType) { + parseResourceVersion: function(source, type, subtype, mojitType) { var fs = source.fs, baseParts = fs.basename.split('.'), res; @@ -1098,8 +1104,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { mojit: mojitType, type: type, subtype: subtype, - viewOutputFormat: fs.ext.substr(1), - viewEngine: baseParts.pop(), + view: { + outputFormat: fs.ext.substr(1), + engine: baseParts.pop() + }, affinity: DEFAULT_AFFINITIES[type], selector: '*' }; @@ -1125,7 +1133,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * You most often don't want to call this directly, but instead to hook * into it using the AOP mechanism of `Y.Plugin.Base`: * - * this.beforeHostMethod('parseResource', this._myParseResource, this); + * this.beforeHostMethod('parseResourceVersion', this._myParseResource, this); * * @method addResourceVersion * @param res {object} the resource version @@ -1167,8 +1175,8 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @event mojitResourcesResolved * @param env {string} the runtime environment (either `client` or `server`) * @param posl {array} priority-ordered seletor list - * @param mojit {string} name of mojit - * @param ress {array} list of mojit metadata (for the `env` and `posl`) + * @param mojit {string} name of the mojit + * @param ress {array} list of resources in the mojit (for the `env` and `posl`) */ resolveResourceVersions: function() { var c, ctx, ctxs, @@ -1288,6 +1296,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @param spec {object} spec to expand * @return {object} expanded sped */ + // FUTURE: expose this to RS addons? _expandSpec: function(env, ctx, spec) { var appConfig, base, @@ -1778,13 +1787,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { return false; } - ret = me.findResourceByConvention(source, mojitType); + ret = me.findResourceVersionByConvention(source, mojitType); if ('object' === typeof ret) { if (ret.skipSubdirParts) { source.fs.subDirArray = source.fs.subDirArray.slice(ret.skipSubdirParts); source.fs.subDir = source.fs.subDirArray.join('/') || '.'; } - res = me.parseResource(source, ret.type, ret.subtype, mojitType); + res = me.parseResourceVersion(source, ret.type, ret.subtype, mojitType); if ('object' === typeof res) { ress.push(res); } diff --git a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js index eeeec682b..f072a10ed 100644 --- a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js @@ -57,11 +57,11 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { return dest; }, - findResourceByConvention: function(source, mojitType) { + findResourceVersionByConvention: function(source, mojitType) { // no-op }, - parseResource: function(source, type, subtype, mojitType) { + parseResourceVersion: function(source, type, subtype, mojitType) { // no-op } @@ -152,49 +152,49 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { // skip non-json files var source = makeSource(fixtures, 'app', '.', 'server.js', true); - var have = store.findResourceByConvention(source, null); + var have = store.findResourceVersionByConvention(source, null); var want = undefined; cmp(have, want, 'skip non-json files'); // include all json files in the app source = makeSource(fixtures, 'app', '.', 'x.json', true); - have = store.findResourceByConvention(source, null); + have = store.findResourceVersionByConvention(source, null); want = { type: 'config' }; cmp(have, want, 'include all json files in the app'); // ... explicitly including package.json source = makeSource(fixtures, 'app', '.', 'package.json', true); - have = store.findResourceByConvention(source, null); + have = store.findResourceVersionByConvention(source, null); want = { type: 'config' }; cmp(have, want, 'include package.json in the app'); // exclude all json files in a bundle source = makeSource(fixtures, 'bundle', '.', 'x.json', true); - have = store.findResourceByConvention(source, null); + have = store.findResourceVersionByConvention(source, null); want = undefined; cmp(have, want, 'exclude all json files in a bundle'); // ... explicitly excluding package.json source = makeSource(fixtures, 'bundle', '.', 'package.json', true); - have = store.findResourceByConvention(source, null); + have = store.findResourceVersionByConvention(source, null); want = undefined; cmp(have, want, 'exclude package.json in a bundle'); // include all json files in a mojit source = makeSource(fixtures, 'mojit', '.', 'x.json', true); - have = store.findResourceByConvention(source, 'foo'); + have = store.findResourceVersionByConvention(source, 'foo'); want = { type: 'config' }; cmp(have, want, 'include all json files in a mojit'); // ... except for the 'shared' mojit source = makeSource(fixtures, 'mojit', '.', 'x.json', true); - have = store.findResourceByConvention(source, 'shared'); + have = store.findResourceVersionByConvention(source, 'shared'); want = undefined; cmp(have, want, 'exclude all json files in the "shared" mojit'); // ... explicitly including package.json source = makeSource(fixtures, 'mojit', '.', 'package.json', true); - have = store.findResourceByConvention(source, 'shared'); + have = store.findResourceVersionByConvention(source, 'shared'); want = { type: 'config' }; cmp(have, want, 'include package.json in the "shared" mojit'); }, @@ -206,7 +206,7 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); var source = makeSource(fixtures, 'app', '.', 'application.json', true); - var res = store.parseResource(source, 'config'); + var res = store.parseResourceVersion(source, 'config'); A.isNotUndefined(res); cmp(res.source, source); A.areSame('config', res.type); @@ -217,7 +217,7 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { A.isUndefined(res.mojit); source = makeSource(fixtures, 'mojit', '.', 'defaults.json', true); - res = store.parseResource(source, 'config', undefined, 'x'); + res = store.parseResourceVersion(source, 'config', undefined, 'x'); A.isNotUndefined(res); cmp(res.source, source); A.areSame('config', res.type); diff --git a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js index fbf1e2f9f..fc9bf7fdc 100644 --- a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js @@ -85,11 +85,11 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { return out; }, - findResourceByConvention: function(source, mojitType) { + findResourceVersionByConvention: function(source, mojitType) { // no-op }, - parseResource: function(source, type, subtype, mojitType) { + parseResourceVersion: function(source, type, subtype, mojitType) { // no-op }, @@ -200,27 +200,27 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); var source = makeSource(fixtures, 'app', 'autoload', 'x.server.txt', true); - var have = store.findResourceByConvention(source, null); + var have = store.findResourceVersionByConvention(source, null); var want = undefined; cmp(have, want); source = makeSource(fixtures, 'app', 'blix', 'x.server.js', true); - have = store.findResourceByConvention(source, null); + have = store.findResourceVersionByConvention(source, null); want = undefined; cmp(have, want); source = makeSource(fixtures, 'app', 'autoload', 'x.server.js', true); - have = store.findResourceByConvention(source, null); + have = store.findResourceVersionByConvention(source, null); want = { type: 'yui-module', skipSubdirParts: 1 }; cmp(have, want); source = makeSource(fixtures, 'app', 'yui_modules', 'x.server.js', true); - have = store.findResourceByConvention(source, null); + have = store.findResourceVersionByConvention(source, null); want = { type: 'yui-module', skipSubdirParts: 1 }; cmp(have, want); source = makeSource(fixtures, 'app', 'lang', 'x.server.js', true); - have = store.findResourceByConvention(source, null); + have = store.findResourceVersionByConvention(source, null); want = { type: 'yui-lang', skipSubdirParts: 1 }; cmp(have, want); }, @@ -232,7 +232,7 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); var source = makeSource(fixtures, 'app', 'autoload', 'm.common.js', true); - var res = store.parseResource(source, 'yui-module'); + var res = store.parseResourceVersion(source, 'yui-module'); A.isNotUndefined(res); cmp(res.source, source); A.areSame('yui-module', res.type); @@ -243,7 +243,7 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { A.isUndefined(res.mojit); source = makeSource(fixtures, 'app', 'autoload', 'm.common.iphone.js', true); - res = store.parseResource(source, 'yui-module'); + res = store.parseResourceVersion(source, 'yui-module'); A.isNotUndefined(res); cmp(res.source, source); A.areSame('yui-module', res.type); @@ -254,7 +254,7 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { A.isUndefined(res.mojit); source = makeSource(fixtures, 'app', 'yui_modules', 'x.common.js', true); - res = store.parseResource(source, 'yui-module'); + res = store.parseResourceVersion(source, 'yui-module'); A.isNotUndefined(res); cmp(res.source, source); A.areSame('yui-module', res.type); @@ -265,7 +265,7 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { A.isUndefined(res.mojit); source = makeSource(fixtures, 'bundle', 'lang', 'testing.js', true); - res = store.parseResource(source, 'yui-lang', undefined, 'testing'); + res = store.parseResourceVersion(source, 'yui-lang', undefined, 'testing'); A.isNotUndefined(res); cmp(res.source, source); A.areSame('yui-lang', res.type); @@ -276,7 +276,7 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { A.areSame('testing', res.mojit); source = makeSource(fixtures, 'bundle', 'lang', 'testing_de.js', true); - res = store.parseResource(source, 'yui-lang', undefined, 'testing'); + res = store.parseResourceVersion(source, 'yui-lang', undefined, 'testing'); A.isNotUndefined(res); cmp(res.source, source); A.areSame('yui-lang', res.type); @@ -287,7 +287,7 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { A.areSame('testing', res.mojit); source = makeSource(fixtures, 'bundle', 'lang', 'testing_en-US.js', true); - res = store.parseResource(source, 'yui-lang', undefined, 'testing'); + res = store.parseResourceVersion(source, 'yui-lang', undefined, 'testing'); A.isNotUndefined(res); cmp(res.source, source); A.areSame('yui-lang', res.type); diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 47a94e416..c80817f12 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -679,8 +679,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.areSame(pkg, res.source.pkg); A.areSame('view', res.type); A.areSame('x', res.name); - A.areSame('html', res.viewOutputFormat); - A.areSame('mu', res.viewEngine); + A.areSame('html', res.view.outputFormat); + A.areSame('mu', res.view.engine); switch (res.source.fs.basename) { case 'x.mu': A.areSame('*', res.selector); From 8ae219ea06f5179145a4578d8b799ce00bb5e443 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sat, 7 Jul 2012 16:33:57 -0700 Subject: [PATCH 083/119] store.getRoutes() returns the default route if none set by user --- source/lib/store.server.js | 3 +++ source/lib/tests/autoload/store.server-tests.js | 9 +++++++++ 2 files changed, 12 insertions(+) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 147380507..7fdaec2c6 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -678,6 +678,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } } + if (!Object.keys(out).length) { + return this._fwConfig.defaultRoutes; + } return out; }, diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index c80817f12..19443abea 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -402,6 +402,15 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isTrue(store._config.root === fixtures); }, + 'default routes': function() { + var fixtures = libpath.join(__dirname, '../fixtures/store_no_app_config'), + store = new Y.mojito.ResourceStore({ root: fixtures }); + store.preload(); + + var have = store.getRoutes(); + A.isObject(have._default_path); + }, + 'bad files': function() { var fixtures = libpath.join(__dirname, '../fixtures/badfiles'), store = new Y.mojito.ResourceStore({ root: fixtures }); From 4decbc601109488ed573a020b831ee02e85f4627 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 9 Jul 2012 09:19:45 -0700 Subject: [PATCH 084/119] any AC addon can get the store if they define a setStore() setter --- source/lib/app/autoload/action-context.common.js | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/source/lib/app/autoload/action-context.common.js b/source/lib/app/autoload/action-context.common.js index 0d6103538..1124c63de 100644 --- a/source/lib/app/autoload/action-context.common.js +++ b/source/lib/app/autoload/action-context.common.js @@ -191,8 +191,9 @@ YUI.add('mojito-action-context', function(Y, NAME) { * @param {object} command The command object. * @param {object} adapter The output adapter. * @param {Y.mojito.ActionContext} ac The action context. + * @param {ResourceStore} store the resource store */ - function attachActionContextAddons(addons, command, adapter, ac) { + function attachActionContextAddons(addons, command, adapter, ac, store) { var addonName, addon, dependencies = {}; @@ -217,6 +218,9 @@ YUI.add('mojito-action-context', function(Y, NAME) { addon = new addons[addonName](command, adapter, ac); if (addon.namespace) { ac[addon.namespace] = addon; + if (Y.Lang.isFunction(addon.setStore)) { + addon.setStore(store); + } } } } @@ -281,15 +285,7 @@ YUI.add('mojito-action-context', function(Y, NAME) { // this is where the addons list is injected onto the action // context...yay! - attachActionContextAddons(Y.mojito.addons.ac, command, adapter, this); - - // There is only one addon that requires the store so check for it here. - // TODO: how can we generalize this so it's not hard-coded to only the - // deploy add-on. Oh, and note we don't make sure that setStore is a - // callable function ;). - if (this.deploy) { - this.deploy.setStore(store); - } + attachActionContextAddons(Y.mojito.addons.ac, command, adapter, this, store); Y.log('ActionContext created for "' + (instance.id || '@' + instance.type) + '/' + command.action + '"', 'mojito', NAME); From d7f12f8e89c69e18ee3976c026933399425269a4 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 9 Jul 2012 13:49:27 -0700 Subject: [PATCH 085/119] addon-rs-url honors a URL set by another RS addon --- source/lib/app/addons/rs/url.server.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 8d94c48cb..f41e6ba80 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -113,7 +113,7 @@ YUI.add('addon-rs-url', function(Y, NAME) { res, skip; - mojits = this.rs.listAllMojits(), + mojits = this.rs.listAllMojits(); mojits.push('shared'); for (m = 0; m < mojits.length; m += 1) { mojit = mojits[m]; @@ -196,6 +196,11 @@ YUI.add('addon-rs-url', function(Y, NAME) { rollupParts = [], rollupFsPath; + // Don't clobber a URL calculated by another RS addon. + if (res.hasOwnProperty('url')) { + return; + } + if (this.config.prefix) { urlParts.push(this.config.prefix); rollupParts.push(this.config.prefix); From 330ac6da6b89829839df38b6e8606c96e2d63124 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 9 Jul 2012 15:07:04 -0700 Subject: [PATCH 086/119] use store.getAllURLs() instead of methods on store.url --- source/lib/app/addons/ac/deploy.server.js | 14 +++--- source/lib/app/addons/rs/url.server.js | 22 ---------- source/lib/app/commands/build.js | 2 +- source/lib/app/commands/compile.js | 2 +- .../app/middleware/mojito-handler-static.js | 5 ++- source/lib/store.server.js | 37 ++++++++++++++++ .../app/addons/ac/deploy-tests.server.js | 30 +++---------- .../app/addons/rs/url-tests.server.js | 44 +++---------------- .../lib/tests/autoload/store.server-tests.js | 12 ++--- 9 files changed, 70 insertions(+), 98 deletions(-) diff --git a/source/lib/app/addons/ac/deploy.server.js b/source/lib/app/addons/ac/deploy.server.js index e2b288a90..d583ad159 100644 --- a/source/lib/app/addons/ac/deploy.server.js +++ b/source/lib/app/addons/ac/deploy.server.js @@ -108,7 +108,8 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { type, module, path, - pathToRoot; + pathToRoot, + urls; contextClient = Y.mojito.util.copy(contextServer); contextClient.runtime = 'client'; @@ -152,6 +153,8 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { useOnDemand = true; } + urls = store.store.getAllURLs(); + // Set the YUI URL to use on the client (This has to be done // before any other scripts are added) if (appConfigClient.yui && appConfigClient.yui.url) { @@ -206,7 +209,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { path = binder.needs[module]; // Anything we don't know about we'll assume is // a YUI library module. - if (!store.store.url.getPathForURL(path)) { + if (!urls[path]) { yuiModules.push(module); yuiJsUrlContains[module] = true; } @@ -309,7 +312,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { // Add all the scripts we have collected assetHandler.addAssets( - this.getScripts(appConfigServer.embedJsFilesInHtmlFrame) + this.getScripts(appConfigServer.embedJsFilesInHtmlFrame, urls) ); // Add the boot script assetHandler.addAsset('blob', 'bottom', initializer); @@ -341,9 +344,10 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { * @private * @param {bool} embed Should returned scripts be embedded in script * tags. + * @param {object} urls mapping of URLs to filesystem paths * @return {object} An object containing script descriptors. */ - getScripts: function(embed) { + getScripts: function(embed, urls) { var i, path, x, @@ -357,7 +361,7 @@ YUI.add('mojito-deploy-addon', function(Y, NAME) { // Walk over the scripts and check what we can do for (i in this.scripts) { if (this.scripts.hasOwnProperty(i)) { - path = this.rs.store.url.getPathForURL[i]; + path = urls[i]; if (embed && path) { this.scripts[i] = { type: 'blob', diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index f41e6ba80..861d8720f 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -56,28 +56,6 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, - /** - * Returns the full filesystem path for a URL. - * This is primarily used by the static handler middleware. - * @method getPathForURL - * @param url {string} URL to lookup - * @return {string} path on disk which the URL points to - */ - getPathForURL: function(url) { - return this.URLpaths[url]; - }, - - - /** - * This is primarily used by the commandline tools (`build` and `compile`). - * @method getURLPaths - * @return {object} all URLs and cooresponding paths - */ - getURLPaths: function() { - return Y.clone(this.URLpaths, true); - }, - - /** * This is primarily used by the commandline tool `build`. * @method getSpecURL diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 75e319130..7daff68df 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -195,7 +195,7 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, manifest += 'CACHE:\n'; // Copy all the files into the destination directory - storeURLs = store.url.getURLPaths(); + storeURLs = store.getAllURLs(); for (url in storeURLs) { if (storeURLs.hasOwnProperty(url)) { from = storeURLs[url]; // filesystem path diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index 888dca572..a2aab1af6 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -345,7 +345,7 @@ compile.inlinecss = function(context, options, callback) { mojitName = inline.mojitName; // need a reverse mapping - storeURLs = store.url.getURLPaths(); + storeURLs = store.getAllURLs(); for (url in storeURLs) { if (storeURLs.hasOwnProperty(url)) { fs2url[storeURLs[url]] = url; diff --git a/source/lib/app/middleware/mojito-handler-static.js b/source/lib/app/middleware/mojito-handler-static.js index 65e87ffd7..3fff6cf39 100644 --- a/source/lib/app/middleware/mojito-handler-static.js +++ b/source/lib/app/middleware/mojito-handler-static.js @@ -167,7 +167,8 @@ function staticProvider(store, globalLogger) { var appConfig = store.getStaticAppConfig(), options = appConfig.staticHandling || {}, cache = options.cache, - maxAge = options.maxAge; + maxAge = options.maxAge, + urls = store.getAllURLs(); if (cache && !maxAge) { maxAge = cache; @@ -197,7 +198,7 @@ function staticProvider(store, globalLogger) { // Use the resource store as a URI "rewriter" here. // /favicon.ico is sent to ./my_app_folder/assets/favicon.ico - filename = store.url.getPathForURL(path); + filename = urls[path]; // TODO: [Issue 80] remove this for performance if ((!filename) && (path === '/favicon.ico')) { filename = pa.join(store._config.root, 'assets', path); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 7fdaec2c6..09b8d4d43 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -685,6 +685,43 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + /** + * Sugar method that returns all "url" metadata of all resources. + * @method getAllURLs + * @return {object} for all resources with a "url" metadatum, the key is + * that URL and the value the filesystem path + */ + getAllURLs: function() { + var r, + res, + ress, + m, + mojit, + mojits, + urls = {}; + ress = this.getResourceVersions({}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (res.url) { + urls[res.url] = res.source.fs.fullPath; + } + } + mojits = this.listAllMojits(); + mojits.push('shared'); + for (m = 0; m < mojits.length; m += 1) { + mojit = mojits[m]; + ress = this.getResourceVersions({mojit: mojit}); + for (r = 0; r < ress.length; r += 1) { + res = ress[r]; + if (res.url) { + urls[res.url] = res.source.fs.fullPath; + } + } + } + return urls; + }, + + /** * Recursively merge one object onto another. * [original implementation](http://stackoverflow.com/questions/171251/how-can-i-merge-properties-of-two-javascript-objects-dynamically/383245#383245) diff --git a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js index 4d35be672..5c1c4cc7d 100644 --- a/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js +++ b/source/lib/tests/autoload/app/addons/ac/deploy-tests.server.js @@ -52,17 +52,13 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { return 'clientstore'; }, store: { + getAllURLs: function() { return {}; }, getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; }, yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } - }, - url: { - getPathForURL: function() { - return 'path'; - } } } }); @@ -141,17 +137,13 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { return 'clientstore'; }, store: { + getAllURLs: function() { return {}; }, getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; }, yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } - }, - url: { - getPathForURL: function() { - return 'path'; - } } } }); @@ -238,17 +230,13 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { return 'clientstore'; }, store: { + getAllURLs: function() { return {}; }, getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; }, yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } - }, - url: { - getPathForURL: function() { - return 'path'; - } } } }); @@ -326,17 +314,13 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { return 'clientstore'; }, store: { + getAllURLs: function() { return {}; }, getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; }, yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } - }, - url: { - getPathForURL: function() { - return 'path'; - } } } }); @@ -414,17 +398,13 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) { return 'clientstore'; }, store: { + getAllURLs: function() { return {}; }, getFrameworkConfig: function() { return { ondemandBaseYuiModules:[] }; }, yui: { getConfigFw: function() { return {}; }, getConfigApp: function() { return {}; } - }, - url: { - getPathForURL: function() { - return 'path'; - } } } }); diff --git a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js index 0698aa2b8..c843ac040 100644 --- a/source/lib/tests/autoload/app/addons/rs/url-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js @@ -1,10 +1,10 @@ /* - * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved. + * Copyright (c) 2012, Yahoo! Inc. All rights reserved. * Copyrights licensed under the New BSD License. * See the accompanying LICENSE file for terms. */ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { - + var suite = new YUITest.TestSuite(NAME), libfs = require('fs'), libpath = require('path'), @@ -135,9 +135,9 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { suite.add(new YUITest.TestCase({ - + name: 'url rs addon tests', - + 'skip mojito-provided server-only mojits': function() { var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); var store = new MockRS({ @@ -283,41 +283,11 @@ YUI.add('mojito-addon-rs-url-tests', function(Y, NAME) { mojit: mojit }); A.areSame('TEST/assets', mojit.assetsRoot); - }, - - - 'resource URLs': function() { - var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); - var store = new MockRS({ - root: fixtures, - appConfig: { staticHandling: {} } - }); - store.plug(Y.mojito.addons.rs.url, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); - - store._makeResource('mojito', 'shared', 'x', 'y', 'common'); - store._makeResource('orange', 'shared', 'x', 'y', 'common'); - store._makeResource('orange', null, 'mojit', 'X', 'common'); - store._makeResource('orange', 'X', 'x', 'y', 'common'); - store._makeResource('orange', null, 'mojit', 'Y', 'common'); - store._makeResource('orange', 'Y', 'x', 'y', 'common'); - store.preloadResourceVersions(); - A.areSame('path/for/x--y.common.ext', store.url.getPathForURL('/static/X/x--y.common.ext')); - A.areSame('path/for/x--y.common.ext', store.url.getPathForURL('/static/Y/x--y.common.ext')); - A.areSame('path/for/x--y.common.ext', store.url.getPathForURL('/static/mojito/x--y.common.ext')); - A.areSame('path/for/x--y.common.ext', store.url.getPathForURL('/static/store/x--y.common.ext')); - var have = store.url.getURLPaths(); - var want = { - '/static/X/x--y.common.ext': 'path/for/x--y.common.ext', - '/static/Y/x--y.common.ext': 'path/for/x--y.common.ext', - '/static/mojito/x--y.common.ext': 'path/for/x--y.common.ext', - '/static/store/x--y.common.ext': 'path/for/x--y.common.ext' - }; - cmp(have, want); } - + })); - + YUITest.TestRunner.add(suite); - + }, '0.0.1', {requires: ['base', 'oop', 'addon-rs-url']}); diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 19443abea..3fa5c28a8 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -148,8 +148,9 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server mojit is NOT loaded because of package mojito version mismatch': function(){ - A.isTrue(typeof store.url.getPathForURL('/static/test_mojit_4/package.json') === 'undefined'); - A.isTrue(typeof store.url.getPathForURL('/static/TestMojit4/package.json') === 'undefined'); + var urls = store.getAllURLs(); + A.isUndefined(urls['/static/test_mojit_4/package.json']); + A.isUndefined(urls['/static/TestMojit4/package.json']); }, 'server mojit is loaded because of package mojito version match': function(){ @@ -160,9 +161,10 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'server a mojits package.json file is available as appropriate': function() { - A.isUndefined(store.url.getPathForURL('/static/TestMojit2/package.json')); - A.isNotUndefined(store.url.getPathForURL('/static/TestMojit3/package.json')); - A.isUndefined(store.url.getPathForURL('/static/TestMojit5/package.json')); + var urls = store.getAllURLs(); + A.isUndefined(urls['/static/TestMojit2/package.json']); + A.isNotUndefined(urls['/static/TestMojit3/package.json']); + A.isUndefined(urls['/static/TestMojit5/package.json']); }, 'server mojit view index.mu.html is loaded correctly': function() { From 9ad99653abc4d0b2c80e0405dcf83eceed349581 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 9 Jul 2012 15:23:21 -0700 Subject: [PATCH 087/119] dropped getSpecURL() from addons-rs-url --- source/lib/app/addons/rs/url.server.js | 17 ----------------- source/lib/app/commands/build.js | 21 ++++++++++++++++++++- source/lib/store.server.js | 7 ------- 3 files changed, 20 insertions(+), 25 deletions(-) diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 861d8720f..4702b8c9e 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -56,23 +56,6 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, - /** - * This is primarily used by the commandline tool `build`. - * @method getSpecURL - * @param id {string} spec ID - * @return {string} static handler URL for the spec - */ - getSpecURL: function(id) { - var urlParts = []; - if (this.config.prefix) { - urlParts.push(this.config.prefix); - } - urlParts.push(id); - urlParts.push('specs/default.json'); - return '/' + urlParts.join('/'); - }, - - /** * Using AOP, this is called after the ResourceStore's version. * It computes the static handler URL for all resources in all the diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 7daff68df..2abef79da 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -13,6 +13,7 @@ var libpath = require('path'), fs = require('fs'), libqs = require('querystring'), MODE_755 = parseInt('755', 8), + getSpecURL, mkdirP, rmdirR, writeWebPagesToFiles, @@ -234,7 +235,7 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, for (id in appConfig.specs) { if (appConfig.specs.hasOwnProperty(id)) { - url = store.url.getSpecURL(id); + url = getSpecURL(appConfig, id); dynamicURLs[url] = true; } } @@ -265,6 +266,24 @@ exports.buildhtml5app = function(cmdOptions, store, config, destination, }; +getSpecURL = function(appConfig, id) { + var prefix = '/static', + parts = id.split(':'), + typeName = parts[0], + specName = parts[1] || 'default', + ns = typeName.replace(/\./g, '_'), + url; + + if (appConfig && appConfig.staticHandling && + appConfig.staticHandling.hasOwnProperty('prefix')) { + staticPrefix = (appConfig.staticHandling.prefix ? '/' + + appConfig.staticHandling.prefix : ''); + } + url = prefix + '/' + typeName + '/specs/' + specName + '.json'; + return url; +}; + + mkdirP = function(p, mode) { var ps = libpath.normalize(p).split('/'), i; diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 09b8d4d43..25827d4c3 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -699,13 +699,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { mojit, mojits, urls = {}; - ress = this.getResourceVersions({}); - for (r = 0; r < ress.length; r += 1) { - res = ress[r]; - if (res.url) { - urls[res.url] = res.source.fs.fullPath; - } - } mojits = this.listAllMojits(); mojits.push('shared'); for (m = 0; m < mojits.length; m += 1) { From dde6bb8d4524f7ce9011e2f46a9417c39767a5a7 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 9 Jul 2012 15:55:27 -0700 Subject: [PATCH 088/119] fixed variable name error found by jenkins jslint --- source/lib/app/commands/build.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/app/commands/build.js b/source/lib/app/commands/build.js index 2abef79da..8bdd5e3d9 100644 --- a/source/lib/app/commands/build.js +++ b/source/lib/app/commands/build.js @@ -276,7 +276,7 @@ getSpecURL = function(appConfig, id) { if (appConfig && appConfig.staticHandling && appConfig.staticHandling.hasOwnProperty('prefix')) { - staticPrefix = (appConfig.staticHandling.prefix ? '/' + + prefix = (appConfig.staticHandling.prefix ? '/' + appConfig.staticHandling.prefix : ''); } url = prefix + '/' + typeName + '/specs/' + specName + '.json'; From 1ed0688e37b3a6b5dd67a142bed376f6bd7c4449 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 9 Jul 2012 16:11:22 -0700 Subject: [PATCH 089/119] fixed `mojito test app` --- source/lib/app/addons/rs/yui.server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index eb5ec393a..9f01cd0cc 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -186,7 +186,9 @@ YUI.add('addon-rs-yui', function(Y, NAME) { return; } - if (fs.subDirArray.length >= 1 && ('autoload' === fs.subDirArray[0] || 'yui_modules' === fs.subDirArray[0])) { + if (fs.subDirArray.length >= 1 && ('autoload' === fs.subDirArray[0] + || 'yui_modules' === fs.subDirArray[0] + || 'tests' === fs.subDirArray[0])) { return new Y.Do.AlterReturn(null, { type: 'yui-module', skipSubdirParts: 1 From f960e3ee087744f9d656be05f8b42bb257ab7194 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 12 Jul 2012 09:51:56 -0700 Subject: [PATCH 090/119] fixed rollups --- source/lib/app/addons/rs/url.server.js | 3 --- source/lib/store.server.js | 2 +- source/lib/tests/autoload/store.server-tests.js | 3 +++ 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 4702b8c9e..4d055e8ca 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -34,7 +34,6 @@ YUI.add('addon-rs-url', function(Y, NAME) { this.rs = config.host; this.appRoot = config.appRoot; this.mojitoRoot = config.mojitoRoot; - this.URLpaths = {}; this.afterHostMethod('preloadResourceVersions', this.preloadResourceVersions, this); this.onHostEvent('getMojitTypeDetails', this.getMojitTypeDetails, this); @@ -201,11 +200,9 @@ YUI.add('addon-rs-url', function(Y, NAME) { if (rollupFsPath && (this.assumeRollups || libpath.existsSync(rollupFsPath))) { res.url = '/' + rollupParts.join('/'); - this.URLpaths[res.url] = rollupFsPath; fs.rollupPath = rollupFsPath; } else { res.url = '/' + urlParts.join('/'); - this.URLpaths[res.url] = fs.fullPath; } } diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 25827d4c3..40d354347 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -707,7 +707,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (res.url) { - urls[res.url] = res.source.fs.fullPath; + urls[res.url] = res.source.fs.rollupPath || res.source.fs.fullPath; } } } diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 3fa5c28a8..1b7096ed7 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -300,10 +300,13 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'app with rollups': function() { + var fixtures = libpath.join(__dirname, '../fixtures/store'); var spec = { type: 'rollups' }; store.expandInstanceForEnv('client', spec, {}, function(err, instance) { A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollups']); A.areSame('/static/rollups/rollup.client.js', instance.yui.sortedPaths['rollupsModelClient']); + var urls = store.getAllURLs(); + A.areSame(libpath.join(fixtures, 'mojits/rollups/rollup.client.js'), urls['/static/rollups/rollup.client.js']); }); } From 439905ad297feaf02637a85db4b2481846d8b059 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 12 Jul 2012 11:27:02 -0700 Subject: [PATCH 091/119] don't try to find the YUI module names of js files in the assets/ dirs --- source/lib/app/addons/rs/yui.server.js | 3 +++ .../autoload/app/addons/rs/yui-tests.server.js | 15 +++++++++++++++ 2 files changed, 18 insertions(+) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index 9f01cd0cc..c94641ee2 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -287,6 +287,9 @@ YUI.add('addon-rs-yui', function(Y, NAME) { if (!res.mojit) { return; } + if ('asset' === res.type) { + return; + } this._parseYUIModule(res); }, diff --git a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js index fc9bf7fdc..2265e4858 100644 --- a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js @@ -319,6 +319,21 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { cmp(res.source, source); A.isNotUndefined(res.yui); A.areSame('X', res.yui.name); + + source = makeSource(fixtures+'/mojits/X', 'mojit', 'assets', 'foo.common.js', true); + res = { + source: source, + mojit: 'X', + type: 'asset', + name: 'foo', + id: 'asset-js-foo', + affinity: 'common', + selector: '*' + }; + store.addResourceVersion(res); + res = store.RVs['common/*' + '/asset-js-foo']; + cmp(res.source, source); + A.isUndefined(res.yui); }, From 6584e529c1b4388408a8fa863ed10b5bddf2de8e Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 13 Jul 2012 07:37:39 -0700 Subject: [PATCH 092/119] when loading tests, also load "libs" tests --- source/lib/store.server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 40d354347..1546c117b 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1795,7 +1795,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if ('node_modules' === file) { return false; } - if ('libs' === file) { + if ('libs' === file && 'test' !== me._appConfigStatic.env) { return false; } if ('tests' === file && 'test' !== me._appConfigStatic.env) { From c2b29b923f04e30a6ca05263741da4484e2fcd13 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 13 Jul 2012 08:47:06 -0700 Subject: [PATCH 093/119] fixed YCB tests --- source/lib/libs/ycb.js | 22 +- source/lib/tests/autoload/libs/ycb-tests.js | 344 ++++++++------------ 2 files changed, 150 insertions(+), 216 deletions(-) diff --git a/source/lib/libs/ycb.js b/source/lib/libs/ycb.js index 00e69e4c9..2193fe00f 100644 --- a/source/lib/libs/ycb.js +++ b/source/lib/libs/ycb.js @@ -169,7 +169,7 @@ Ycb.prototype = { } } } else { - config[key] = 'error'; + config[key] = '--YCB-SUBSTITUTION-ERROR--'; } } } @@ -207,7 +207,7 @@ Ycb.prototype = { /* * @private - * @method _getContextPath + * @method _getLookupPaths * @param context {object} Key/Value list * @result {string} */ @@ -219,7 +219,7 @@ Ycb.prototype = { current = lookupList.length-1, combination = []; - // This is our combination that we will tubmle over + // This is our combination that we will tumble over for (pos = 0; pos < lookupList.length; pos += 1) { combination.push({ current: 0, @@ -292,7 +292,7 @@ Ycb.prototype = { key = this._getLookupPath(context); // Add the section to the settings list with it's full key -// IMY Bug 5439377 configuration does not accept neither null nor false values? + // IMY Bug 5439377 configuration does not accept neither null nor false values? if (!this.settings[key]) { this.settings[key] = section; } else { @@ -352,6 +352,7 @@ Ycb.prototype = { var pos, name, path, + value, chains = {}; for (pos = 0; pos < this.dimensions.length; pos += 1) { @@ -365,7 +366,7 @@ Ycb.prototype = { } if (chains[name]) { // Convert to an ordered list - chains[name] = reverseList(chains[name].split(SEPARATOR)).concat(DEFAULT); + chains[name] = chains[name].split(SEPARATOR).reverse().concat(DEFAULT); } else{ // If there was no match set to default chains[name] = [DEFAULT]; @@ -485,17 +486,6 @@ module.exports = { //--------------------------------------------------------------- // UTILITY FUNCTIONS -function reverseList(from) { - var to = [], - pos = from.length; - - while (pos) { - to.push(from[--pos]); - } - return to; -} - - function objectToList(from) { var to = [], pos = 0, diff --git a/source/lib/tests/autoload/libs/ycb-tests.js b/source/lib/tests/autoload/libs/ycb-tests.js index ab848ac59..5998f8cc9 100644 --- a/source/lib/tests/autoload/libs/ycb-tests.js +++ b/source/lib/tests/autoload/libs/ycb-tests.js @@ -5,110 +5,107 @@ */ YUI.add('mojito-ycb-tests', function(Y, NAME) { - var libycb = require(__dirname + '/../../../libs/ycb.js'), + var libpath = require('path'), + libfs = require('fs'), + libycb = require(libpath.join(__dirname, '../../../libs/ycb.js')), suite = new YUITest.TestSuite(NAME), A = YUITest.Assert, - OA = YUITest.ObjectAssert, AA = YUITest.ArrayAssert; + + function readFixtureFile(file){ + var path = libpath.join(__dirname, '../../', 'fixtures/ycb' , file); + var data = libfs.readFileSync(path, 'utf8'); + return Y.JSON.parse(data); + } + + suite.add(new YUITest.TestCase({ name: 'ycb', - setUp: function() { - - }, + setUp: function() {}, - tearDown: function() { + tearDown: function() {}, - }, 'test if we can use the module': function() { - A.isTrue(libycb.version === '2.0.0'); }, - 'test _flattenDimension': function() { + 'test _flattenDimension': function() { var dims = readFixtureFile('dimensions.json'), - flat; - - flat = libycb._flattenDimension('', dims[0].dimensions[6]['lang']); - - //Y.log(Y.JSON.stringify(flat,null,4)); + ycb = new libycb.Ycb(dims), + flat = ycb._flattenDimension('', dims[0].dimensions[6]['lang']); - A.isTrue(flat['en'] === 'en'); - A.isTrue(flat['en/en_CA'] === 'en_CA'); - A.isTrue(flat['fr'] === 'fr'); - A.isTrue(flat['fr/fr_FR/fr_CA'] === 'fr_CA'); + A.areSame('en', flat['en']); + A.areSame('en_CA', flat['en/en_CA']); + A.areSame('fr', flat['fr']); + A.areSame('fr_CA', flat['fr/fr_FR/fr_CA']); }, - 'test _flattenDimensions': function() { + 'test _flattenDimensions': function() { var dims = readFixtureFile('dimensions.json'), - flat; - - flat = libycb._flattenDimensions(dims[0].dimensions); - - //Y.log(Y.JSON.stringify(flat,null,4)); + ycb = new libycb.Ycb(dims), + flat = ycb._dimensionPaths; - A.isTrue(flat['lang']['en'] === 'en'); - A.isTrue(flat['lang']['en/en_CA'] === 'en_CA'); - A.isTrue(flat['lang']['fr'] === 'fr'); - A.isTrue(flat['lang']['fr/fr_FR/fr_CA'] === 'fr_CA'); + A.areSame('en', flat['lang']['en']); + A.areSame('en_CA', flat['lang']['en/en_CA']); + A.areSame('fr', flat['lang']['fr']); + A.areSame('fr_CA', flat['lang']['fr/fr_FR/fr_CA']); }, - 'test _makeOrderedLookupList': function() { + 'test _makeOrderedLookupList': function() { var dims = readFixtureFile('dimensions.json'), + ycb = new libycb.Ycb(dims), context, list; - context = { - 'region': 'ir', - 'environment': 'preproduction', - 'lang': 'fr_CA' - }; - - list = libycb._makeOrderedLookupList(dims[0].dimensions, context); - - //Y.log(Y.JSON.stringify(list,null,4)); - - A.isTrue(list['environment'][0] === 'preproduction'); - A.isTrue(list['lang'][0] === 'fr_CA'); - A.isTrue(list['region'][0] === 'ir'); + 'region': 'ir', + 'environment': 'preproduction', + 'lang': 'fr_CA' + }; + list = ycb._makeOrderedLookupList(context); + + A.areSame('preproduction', list['environment'][0]); + A.areSame('*', list['environment'][1]); + A.areSame('fr_CA', list['lang'][0]); + A.areSame('fr_FR', list['lang'][1]); + A.areSame('fr', list['lang'][2]); + A.areSame('*', list['lang'][3]); + A.areSame('ir', list['region'][0]); + A.areSame('gb', list['region'][1]); + A.areSame('*', list['region'][2]); }, - 'test _getLookupPath': function() { + 'test _getLookupPath': function() { var dims = readFixtureFile('dimensions.json'), + ycb = new libycb.Ycb(dims), context, path; - context = { - 'region': 'ir', - 'environment': 'preproduction', - 'lang': 'fr_FR' - }; - - path = libycb._getLookupPath(dims[0].dimensions, context); + 'region': 'ir', + 'environment': 'preproduction', + 'lang': 'fr_FR' + }; + path = ycb._getLookupPath(context); - //Y.log(Y.JSON.stringify(paths,null,4)); - - A.isTrue(path === 'preproduction/*/*/*/*/*/fr_FR/ir/*/*/*'); + A.areSame('preproduction/*/*/*/*/*/fr_FR/ir/*/*/*', path); }, - 'test _getLookupPaths': function() { + 'test _getLookupPaths': function() { var dims = readFixtureFile('dimensions.json'), + ycb = new libycb.Ycb(dims), context, paths, expected; - context = { - 'region': 'ir', - 'environment': 'preproduction', - 'lang': 'fr_FR' - }; - - paths = libycb._getLookupPaths(dims[0].dimensions, context); - //Y.log(Y.JSON.stringify(paths,null,4)); + 'region': 'ir', + 'environment': 'preproduction', + 'lang': 'fr_FR' + }; + paths = ycb._getLookupPaths(context); expected = [ "*/*/*/*/*/*/*/*/*/*/*", @@ -133,205 +130,162 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { AA.itemsAreEqual(expected, paths); }, - 'test _processRawBundle': function() { + 'test _processRawBundle': function() { var bundle, ycb; - bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')[0]); + ycb = new libycb.Ycb(bundle), - //Y.log(Y.JSON.stringify(bundle,null,4)); - - ycb = libycb._processRawBundle(bundle); - - //Y.log(Y.JSON.stringify(ycb,null,4)); - - A.isTrue(ycb.settings['*/*/*/*/*/*/*/*/*/*/*'].title_key === 'YRB_YAHOO'); - A.isTrue(typeof ycb.dimensions[7].region.us !== 'undefined'); + A.areSame('YRB_YAHOO', ycb.settings['*/*/*/*/*/*/*/*/*/*/*'].title_key); + A.isNotUndefined(ycb.dimensions[7].region.us); }, - 'test _processRawBundle with dupe error': function() { + 'test _processRawBundle with dupe error': function() { var bundle, ycb; - bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) .concat(readFixtureFile('simple-2.json')); - //Y.log(Y.JSON.stringify(bundle,null,4)); - // This should throw an error for us to trap - try{ - ycb = libycb._processRawBundle(bundle); - }catch(err){ + try { + ycb = new libycb.Ycb(bundle); + } catch(err) { A.isTrue(true); return; } - - //Y.log(Y.JSON.stringify(ycb,null,4)); - A.isTrue(false); }, - 'test _processRawBundle with many settings': function() { + 'test _processRawBundle with many settings': function() { var bundle, ycb; - bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) .concat(readFixtureFile('simple-3.json')); + ycb = new libycb.Ycb(bundle); - //Y.log(Y.JSON.stringify(bundle,null,4)); - - ycb = libycb._processRawBundle(bundle); - - //Y.log(Y.JSON.stringify(ycb,null,4)); - - A.isTrue(ycb.settings['*/*/*/*/*/*/*/*/*/*/*'].title_key === 'YRB_YAHOO'); - A.isTrue(ycb.settings['*/*/*/*/*/*/*/fr/*/*/*'].links.home === 'http://fr.yahoo.com'); - A.isTrue(ycb.settings['*/*/*/*/*/*/*/fr/*/*/bt'].logo === 'yahoo_bt_FR.png'); - A.isTrue(typeof ycb.dimensions[7].region.us !== 'undefined'); + A.areSame('YRB_YAHOO', ycb.settings['*/*/*/*/*/*/*/*/*/*/*'].title_key); + A.areSame('http://fr.yahoo.com', ycb.settings['*/*/*/*/*/*/*/fr/*/*/*'].links.home); + A.areSame('yahoo_bt_FR.png', ycb.settings['*/*/*/*/*/*/*/fr/*/*/bt'].logo); + A.isNotUndefined(ycb.dimensions[7].region.us); }, - 'test _applySubstitutions': function() { + 'test _applySubstitutions': function() { var config, ycb; - config = readFixtureFile('substitutions.json'); - - //Y.log(Y.JSON.stringify(config,null,4)); - - libycb._applySubstitutions(config); - - //Y.log(Y.JSON.stringify(config,null,4)); + ycb = new libycb.Ycb([]); + ycb._applySubstitutions(config); A.isTrue(config.key0.key4 === 'The value of key0.key2 is value2'); A.isTrue(config.key5.key4 === 'The value of key0.key2 is value2'); A.isTrue(config.key6.key7.key8.key4 === 'The value of key0.key2 is value2'); A.isTrue(config.key6.key9[2] === 'The value of key0.key2 is value2'); - A.isTrue(config['$$key0.key1$$'] === 'error'); + A.isTrue(config['$$key0.key1$$'] === '--YCB-SUBSTITUTION-ERROR--'); A.isTrue(config.key10.key11.key4 === 'The value of key0.key2 is value2'); A.isTrue(config.key11[4] === 'The value of key0.key2 is value2'); A.isTrue(config.key8.key4 === 'The value of key0.key2 is value2'); }, - 'test if we can use a simple config': function() { - - var bundle, ycb; + 'test if we can use a simple config': function() { + var bundle, config; bundle = readFixtureFile('simple-1.json'); + config = libycb.read(bundle); - ycb = libycb.read(bundle); - - //Y.log(Y.JSON.stringify(ycb,null,4)); - - A.isTrue(ycb.title_key === 'YRB_YAHOO'); - A.isTrue(ycb.links.home === 'http://www.yahoo.com'); - A.isTrue(ycb.links.mail === 'http://mail.yahoo.com'); + A.areSame('YRB_YAHOO', config.title_key); + A.areSame('http://www.yahoo.com', config.links.home); + A.areSame('http://mail.yahoo.com', config.links.mail); }, - 'test if we can use a simple config with dimensions': function() { - - var bundle, ycb; + 'test if we can use a simple config with dimensions': function() { + var bundle, config; bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')); + config = libycb.read(bundle); - ycb = libycb.read(bundle); - - A.isTrue(ycb.title_key === 'YRB_YAHOO'); - A.isTrue(ycb.links.home === 'http://www.yahoo.com'); - A.isTrue(ycb.links.mail === 'http://mail.yahoo.com'); + A.areSame('YRB_YAHOO', config.title_key); + A.areSame('http://www.yahoo.com', config.links.home); + A.areSame('http://mail.yahoo.com', config.links.mail); }, - 'test if we can use a simple config with dimensions and extra settings': function() { - - var bundle, ycb; + 'test if we can use a simple config with dimensions and extra settings': function() { + var bundle, config; bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) .concat(readFixtureFile('simple-3.json')); + config = libycb.read(bundle); - ycb = libycb.read(bundle); - - A.isTrue(ycb.title_key === 'YRB_YAHOO'); - A.isTrue(ycb.links.home === 'http://www.yahoo.com'); - A.isTrue(ycb.links.mail === 'http://mail.yahoo.com'); + A.areSame('YRB_YAHOO', config.title_key); + A.areSame('http://www.yahoo.com', config.links.home); + A.areSame('http://mail.yahoo.com', config.links.mail); }, - 'test if we can use a simple config with dimensions and conext IR': function() { - - var bundle, context, ycb; + 'test if we can use a simple config with dimensions and conext IR': function() { + var bundle, context, config; bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) .concat(readFixtureFile('simple-3.json')); - context = { - 'region': 'ir', - 'environment': 'preproduction', - 'lang': 'fr_FR' - }; - - ycb = libycb.read(bundle, context); - //Y.log(Y.JSON.stringify(ycb,null,4)); - - A.isTrue(ycb.title_key === 'YRB_YAHOO'); - A.isTrue(ycb.logo === 'yahoo_FR.png'); - A.isTrue(ycb.links.home === 'http://gb.yahoo.com'); - A.isTrue(ycb.links.mail === 'http://gb.mail.yahoo.com'); + 'region': 'ir', + 'environment': 'preproduction', + 'lang': 'fr_FR' + }; + config = libycb.read(bundle, context); + + A.areSame('YRB_YAHOO', config.title_key); + A.areSame('yahoo_FR.png', config.logo); + A.areSame('http://gb.yahoo.com', config.links.home); + A.areSame('http://gb.mail.yahoo.com', config.links.mail); }, - 'test if we can use a simple config with dimensions and conext FR': function() { - - var bundle, context, ycb; + 'test if we can use a simple config with dimensions and conext FR': function() { + var bundle, context, config; bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) .concat(readFixtureFile('simple-3.json')); - context = { - 'region': 'fr', - 'environment': 'preproduction', - 'lang': 'fr_FR' - }; - - ycb = libycb.read(bundle, context); - - //Y.log(Y.JSON.stringify(ycb,null,4)); - - A.isTrue(ycb.title_key === 'YRB_YAHOO'); - A.isTrue(ycb.logo === 'yahoo_FR.png'); - A.isTrue(ycb.links.home === 'http://fr.yahoo.com'); - A.isTrue(ycb.links.mail === 'http://fr.mail.yahoo.com'); + 'region': 'fr', + 'environment': 'preproduction', + 'lang': 'fr_FR' + }; + config = libycb.read(bundle, context); + + A.areSame('YRB_YAHOO', config.title_key); + A.areSame('yahoo_FR.png', config.logo); + A.areSame('http://fr.yahoo.com', config.links.home); + A.areSame('http://fr.mail.yahoo.com', config.links.mail); }, - 'test if we can use a simple config with dimensions and conext GB & BT': function() { - - var bundle, context, ycb; + 'test if we can use a simple config with dimensions and conext GB & BT': function() { + var bundle, context, config; bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) .concat(readFixtureFile('simple-3.json')); - context = { - 'region': 'gb', - 'environment': 'preproduction', - 'flavor': 'bt' - }; - - ycb = libycb.read(bundle, context); - //Y.log(Y.JSON.stringify(ycb,null,4)); - - A.isTrue(ycb.title_key === 'YRB_YAHOO'); - A.isTrue(ycb.logo === 'yahoo_bt_GB.png'); - A.isTrue(ycb.links.home === 'http://gb.yahoo.com'); - A.isTrue(ycb.links.mail === 'http://gb.mail.yahoo.com'); + 'region': 'gb', + 'environment': 'preproduction', + 'flavor': 'bt' + }; + config = libycb.read(bundle, context); + + A.areSame('YRB_YAHOO', config.title_key); + A.areSame('yahoo_bt_GB.png', config.logo); + A.areSame('http://gb.yahoo.com', config.links.home); + A.areSame('http://gb.mail.yahoo.com', config.links.mail); }, + 'test ycb accepts falsey config values': function() { var bundle, - ycb, + config, foo = { settings: [ 'master' ], title_key: 'YRB_YAHOO', @@ -344,31 +298,21 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { }; bundle = readFixtureFile('dimensions.json').concat([foo]); - - ycb = libycb.read(bundle); - - A.areEqual(ycb['data-url'], foo['data-url']); - - A.isTrue('false_ok' in ycb); - A.areEqual(ycb.false_ok, foo.false_ok); - - A.isTrue('undef' in ycb); - A.areEqual(ycb.undef, foo.undef); - - A.isTrue('zero' in ycb); - A.areEqual(ycb.zero, foo.zero); + config = libycb.read(bundle); + + A.areEqual(config['data-url'], foo['data-url']); + A.isTrue('false_ok' in config); + A.areEqual(config.false_ok, foo.false_ok); + A.isTrue('undef' in config); + A.areEqual(config.undef, foo.undef); + A.isTrue('zero' in config); + A.areEqual(config.zero, foo.zero); } - })); - - function readFixtureFile(file){ - var path = require('path').join(__dirname, '../../', 'fixtures/ycb' , file), - data = require('fs').readFileSync(path, 'utf8'); + })); - return Y.JSON.parse(data); - } YUITest.TestRunner.add(suite); -}, '0.0.1', {requires: []}); +}, '0.0.1', {requires: ['json']}); From 85f8ee1bfd8dbd3de6b50e6764e7eea4502ac231 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 13 Jul 2012 08:52:23 -0700 Subject: [PATCH 094/119] delint --- source/lib/libs/ycb.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/source/lib/libs/ycb.js b/source/lib/libs/ycb.js index 2193fe00f..0a57d2a05 100644 --- a/source/lib/libs/ycb.js +++ b/source/lib/libs/ycb.js @@ -5,6 +5,9 @@ */ +/*jslint anon:true, sloppy:true, nomen:true*/ + + var VERSION = '2.0.0', DEFAULT = '*', SEPARATOR = '/', From de06a82c3238be3f0046459a3572df63a05620fd Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sat, 14 Jul 2012 16:10:32 -0700 Subject: [PATCH 095/119] optimization: only track those dimensions that are actually used in the YCB file --- source/lib/libs/ycb.js | 126 ++++++++++---------- source/lib/tests/autoload/libs/ycb-tests.js | 52 +++++++- 2 files changed, 109 insertions(+), 69 deletions(-) diff --git a/source/lib/libs/ycb.js b/source/lib/libs/ycb.js index 0a57d2a05..bb56c60e4 100644 --- a/source/lib/libs/ycb.js +++ b/source/lib/libs/ycb.js @@ -13,18 +13,20 @@ var VERSION = '2.0.0', SEPARATOR = '/', SUBMATCH = /\$\$[a-zA-Z0-9.-_]*\$\$/, Y = require('yui').YUI({useSync: true}).use('json-parse', 'json-stringify'); - + Y.applyConfig({useSync: false}); //--------------------------------------------------------------- // OBJECT ORIENTED INTERFACE -function Ycb(bundle) { +function Ycb(bundle, options) { + this.options = options || {}; this.dimensions = {}; this.settings = {}; this.schema = {}; - this._processRawBundle(bundle); + this.dimsUsed = {}; // dim name: value: true + this._processRawBundle(bundle, this.options); } Ycb.prototype = { @@ -40,17 +42,17 @@ Ycb.prototype = { path, config = {}; - if (!context) { - context = {}; - } - lookupPaths = this._getLookupPaths(context); + context = context || {}; + options = objectMerge(this.options, options || {}); + + lookupPaths = this._getLookupPaths(context, options); if (options.debug) { - console.log(Y.JSON.stringify(context,null,4)); - console.log(Y.JSON.stringify(this.dimensions,null,4)); - console.log(Y.JSON.stringify(this.settings,null,4)); - console.log(Y.JSON.stringify(this.schema,null,4)); - console.log(Y.JSON.stringify(lookupPaths,null,4)); + console.log(Y.JSON.stringify(context, null, 4)); + console.log(Y.JSON.stringify(this.dimensions, null, 4)); + console.log(Y.JSON.stringify(this.settings, null, 4)); + console.log(Y.JSON.stringify(this.schema, null, 4)); + console.log(Y.JSON.stringify(lookupPaths, null, 4)); } // Now we simply merge each macting settings section we find into the config @@ -58,7 +60,7 @@ Ycb.prototype = { if (this.settings[lookupPaths[path]]) { if (options.debug) { console.log('----USING---- ' + lookupPaths[path]); - console.log(Y.JSON.stringify(this.settings[lookupPaths[path]],null,4)); + console.log(Y.JSON.stringify(this.settings[lookupPaths[path]], null, 4)); } config = objectMerge(this.settings[lookupPaths[path]], config); } @@ -88,18 +90,16 @@ Ycb.prototype = { path, config = []; - if (!context) { - context = {}; - } + context = context || {}; - lookupPaths = this._getLookupPaths(context); + lookupPaths = this._getLookupPaths(context, options); if (options.debug) { - console.log(Y.JSON.stringify(context,null,4)); - console.log(Y.JSON.stringify(this.dimensions,null,4)); - console.log(Y.JSON.stringify(this.settings,null,4)); - console.log(Y.JSON.stringify(this.schema,null,4)); - console.log(Y.JSON.stringify(lookupPaths,null,4)); + console.log(Y.JSON.stringify(context, null, 4)); + console.log(Y.JSON.stringify(this.dimensions, null, 4)); + console.log(Y.JSON.stringify(this.settings, null, 4)); + console.log(Y.JSON.stringify(this.schema, null, 4)); + console.log(Y.JSON.stringify(lookupPaths, null, 4)); } // Now we simply merge each macting settings section we find into the config @@ -107,7 +107,7 @@ Ycb.prototype = { if (this.settings[lookupPaths[path]]) { if (options.debug) { console.log('----USING---- ' + lookupPaths[path]); - console.log(Y.JSON.stringify(this.settings[lookupPaths[path]],null,4)); + console.log(Y.JSON.stringify(this.settings[lookupPaths[path]], null, 4)); } config.push(this.settings[lookupPaths[path]]); } @@ -132,13 +132,8 @@ Ycb.prototype = { find, item; - if (!base) { - base = config; - } - - if (!parent) { - parent = {ref: config, key: null}; - } + base = base || config; + parent = parent || {ref: config, key: null}; for (key in config) { if (config.hasOwnProperty(key)) { @@ -212,10 +207,11 @@ Ycb.prototype = { * @private * @method _getLookupPaths * @param context {object} Key/Value list + * @param options {object} runtime options * @result {string} */ - _getLookupPaths: function(context) { - var lookupList = objectToList(this._makeOrderedLookupList(context)), + _getLookupPaths: function(context, options) { + var lookupList = Y.Object.values(this._makeOrderedLookupList(context, options)), path = [], paths = [], pos, @@ -264,9 +260,10 @@ Ycb.prototype = { * @private * @method _processRawBundle * @param bundle {object} + * @param options {object} * @return {nothing} */ - _processRawBundle: function(bundle) { + _processRawBundle: function(bundle, options) { var pos, section, part, @@ -286,13 +283,19 @@ Ycb.prototype = { context = {}; for (part = 0; part < section.settings.length; part += 1) { kv = section.settings[part].split(':'); + if ('master' !== section.settings[0]) { + if (!this.dimsUsed[kv[0]]) { + this.dimsUsed[kv[0]] = {}; + } + this.dimsUsed[kv[0]][kv[1]] = true; + } context[kv[0]] = kv[1]; } // Remove the settings key now we are done with it delete section.settings; // Build the full context path - key = this._getLookupPath(context); + key = this._getLookupPath(context, options); // Add the section to the settings list with it's full key // IMY Bug 5439377 configuration does not accept neither null nor false values? @@ -310,10 +313,11 @@ Ycb.prototype = { * @private * @method _getContextPath * @param context {object} Key/Value list + * @param options {object} * @result {string} */ - _getLookupPath: function(context) { - var lookupList = this._makeOrderedLookupList(context), + _getLookupPath: function(context, options) { + var lookupList = this._makeOrderedLookupList(context, options), name, list, lookup = {}, @@ -348,22 +352,35 @@ Ycb.prototype = { /* * @private * @method _makeOrderedLookupList - * @param {object} context Key/Value list + * @param context {object} Key/Value list + * @param options {object} * @result {object} list of lists */ - _makeOrderedLookupList: function(context) { + _makeOrderedLookupList: function(context, options) { var pos, name, path, value, + used, chains = {}; for (pos = 0; pos < this.dimensions.length; pos += 1) { for (name in this.dimensions[pos]) { if (this.dimensions[pos].hasOwnProperty(name)) { for (path in this._dimensionPaths[name]) { - if (this._dimensionPaths[name].hasOwnProperty(path) && - this._dimensionPaths[name][path] === context[name]) { + if (!this._dimensionPaths[name].hasOwnProperty(path)) { + continue; + } + value = this._dimensionPaths[name][path]; + used = options.useAllDimensions || false; + if (!options.useAllDimensions) { + Y.Array.forEach(path.split(SEPARATOR), function (part) { + if ((this.dimsUsed[name] && this.dimsUsed[name][part])) { + used = true; + } + }, this); + } + if (used && value === context[name]) { chains[name] = path; } } @@ -394,9 +411,7 @@ Ycb.prototype = { newPrefix, nextDimension; - if (!build) { - build = {}; - } + build = build || {}; if (typeof dimension === 'object') { for (key in dimension) { if (dimension.hasOwnProperty(key)) { @@ -451,9 +466,9 @@ module.exports = { * Processes an Object representing a YCB 2.0 Bundle as defined in the spec. * * @method read - * @param {object} context - * @param {boolean} validate - * @param {boolean} debug + * @param context {object} + * @param validate {boolean} + * @param debug {boolean} * @return {object} */ read: function(bundle, context, validate, debug) { @@ -471,8 +486,8 @@ module.exports = { * * @method readNoMerge * @param context {object} - * @param {boolean} validate - * @param {boolean} debug + * @param validate {boolean} + * @param debug {boolean} * @return {array of objects} */ readNoMerge: function(bundle, context, validate, debug) { @@ -489,21 +504,6 @@ module.exports = { //--------------------------------------------------------------- // UTILITY FUNCTIONS -function objectToList(from) { - var to = [], - pos = 0, - key = ''; - - for (key in from) { - if (from.hasOwnProperty(key)) { - to[pos] = from[key]; - pos++; - } - } - return to; -} - - function objectMerge(from, to) { var key; for (key in from) { diff --git a/source/lib/tests/autoload/libs/ycb-tests.js b/source/lib/tests/autoload/libs/ycb-tests.js index 5998f8cc9..0c2d81f8c 100644 --- a/source/lib/tests/autoload/libs/ycb-tests.js +++ b/source/lib/tests/autoload/libs/ycb-tests.js @@ -67,7 +67,7 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { 'environment': 'preproduction', 'lang': 'fr_CA' }; - list = ycb._makeOrderedLookupList(context); + list = ycb._makeOrderedLookupList(context, {useAllDimensions: true}); A.areSame('preproduction', list['environment'][0]); A.areSame('*', list['environment'][1]); @@ -90,7 +90,7 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { 'environment': 'preproduction', 'lang': 'fr_FR' }; - path = ycb._getLookupPath(context); + path = ycb._getLookupPath(context, {useAllDimensions: true}); A.areSame('preproduction/*/*/*/*/*/fr_FR/ir/*/*/*', path); }, @@ -105,7 +105,7 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { 'environment': 'preproduction', 'lang': 'fr_FR' }; - paths = ycb._getLookupPaths(context); + paths = ycb._getLookupPaths(context, {useAllDimensions: true}); expected = [ "*/*/*/*/*/*/*/*/*/*/*", @@ -226,7 +226,7 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { }, - 'test if we can use a simple config with dimensions and conext IR': function() { + 'test if we can use a simple config with dimensions and context IR': function() { var bundle, context, config; bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) @@ -245,7 +245,7 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { }, - 'test if we can use a simple config with dimensions and conext FR': function() { + 'test if we can use a simple config with dimensions and context FR': function() { var bundle, context, config; bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) @@ -264,7 +264,7 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { }, - 'test if we can use a simple config with dimensions and conext GB & BT': function() { + 'test if we can use a simple config with dimensions and context GB & BT': function() { var bundle, context, config; bundle = readFixtureFile('dimensions.json') .concat(readFixtureFile('simple-1.json')) @@ -307,6 +307,46 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { A.areEqual(config.undef, foo.undef); A.isTrue('zero' in config); A.areEqual(config.zero, foo.zero); + }, + + + 'skip unused dimensions': function() { + var bundle, ycb; + bundle = readFixtureFile('dimensions.json') + .concat(readFixtureFile('simple-1.json')) + .concat(readFixtureFile('simple-3.json')); + ycb = new libycb.Ycb(bundle); + + A.areSame(3, Object.keys(ycb.dimsUsed).length); + A.isNotUndefined(ycb.dimsUsed.region); + A.areSame(3, Object.keys(ycb.dimsUsed.region).length); + A.isTrue(ycb.dimsUsed.region.ca); + A.isTrue(ycb.dimsUsed.region.gb); + A.isTrue(ycb.dimsUsed.region.fr); + A.areSame(1, Object.keys(ycb.dimsUsed.lang).length); + A.isTrue(ycb.dimsUsed.lang.fr); + A.areSame(2, Object.keys(ycb.dimsUsed.flavor).length); + A.isTrue(ycb.dimsUsed.flavor.att); + A.isTrue(ycb.dimsUsed.flavor.bt); + + var context = { + 'region': 'ir', + 'environment': 'preproduction', + 'lang': 'fr_FR' + }; + var paths = ycb._getLookupPaths(context, {}); + var expected = [ + '*/*/*/*/*/*/*/*/*/*/*', + '*/*/*/*/*/*/*/gb/*/*/*', + '*/*/*/*/*/*/*/ir/*/*/*', + '*/*/*/*/*/*/fr/*/*/*/*', + '*/*/*/*/*/*/fr/gb/*/*/*', + '*/*/*/*/*/*/fr/ir/*/*/*', + '*/*/*/*/*/*/fr_FR/*/*/*/*', + '*/*/*/*/*/*/fr_FR/gb/*/*/*', + '*/*/*/*/*/*/fr_FR/ir/*/*/*' + ]; + AA.itemsAreEqual(expected, paths); } From de3b70909d945ccdd56442191304fd2a3493b793 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sat, 14 Jul 2012 16:27:49 -0700 Subject: [PATCH 096/119] renamed addon-rs-selector.getListFromContext() to getPOSLFromContext() --- source/lib/app/addons/rs/selector.server.js | 4 ++-- source/lib/app/commands/compile.js | 2 +- source/lib/store.server.js | 6 +++--- .../app/addons/rs/selector-tests.server.js | 14 +++++++------- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js index ff126d38d..27b45d35b 100644 --- a/source/lib/app/addons/rs/selector.server.js +++ b/source/lib/app/addons/rs/selector.server.js @@ -53,11 +53,11 @@ YUI.add('addon-rs-selector', function(Y, NAME) { /** * Returns the priority-ordered selector list (POSL) for the context. - * @method getListForContext + * @method getPOSLFromContext * @param ctx {object} runtime context * @return {array} priority-ordered selector list */ - getListFromContext: function(ctx) { + getPOSLFromContext: function(ctx) { var sels = ['*'], p, part, diff --git a/source/lib/app/commands/compile.js b/source/lib/app/commands/compile.js index a2aab1af6..6dc6000a1 100644 --- a/source/lib/app/commands/compile.js +++ b/source/lib/app/commands/compile.js @@ -152,7 +152,7 @@ function getInlineCssMojits(store, env, context) { // TODO: This isn't quite right, since multiple contexts might map to // posls with the same lead selector. - selector = store.selector.getListFromContext(context)[0]; + selector = store.selector.getPOSLFromContext(context)[0]; srcs = []; ress = store.getResources(env, context, {mojit: mojit}); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 1546c117b..8ee759df3 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -342,7 +342,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { k, use; - posl = JSON.stringify(this.selector.getListFromContext(ctx)); + posl = JSON.stringify(this.selector.getPOSLFromContext(ctx)); if (filter.mojit) { if (!this._mojitResources[env] || !this._mojitResources[env][posl] || @@ -545,7 +545,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { res, engine, engines = {}, // view engines - posl = this.selector.getListFromContext(ctx), + posl = this.selector.getPOSLFromContext(ctx), ctxKey, module; @@ -1222,7 +1222,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { ctxs = this._listAllContexts(); for (c = 0; c < ctxs.length; c += 1) { ctx = ctxs[c]; - posl = this.selector.getListFromContext(ctx); + posl = this.selector.getPOSLFromContext(ctx); posls[JSON.stringify(posl)] = posl; } ctxs = []; // free a bunch of memory diff --git a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js index e0e8807d6..4dbd89c68 100644 --- a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js @@ -69,31 +69,31 @@ YUI.add('mojito-addon-rs-selector-tests', function(Y, NAME) { store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); store.plug(Y.mojito.addons.rs.selector, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); - var have = store.selector.getListFromContext({}); + var have = store.selector.getPOSLFromContext({}); var want = ['*']; cmp(have, want); - var have = store.selector.getListFromContext({runtime:'client'}); + var have = store.selector.getPOSLFromContext({runtime:'client'}); var want = ['right', '*']; cmp(have, want); - var have = store.selector.getListFromContext({runtime:'server'}); + var have = store.selector.getPOSLFromContext({runtime:'server'}); var want = ['shelves', '*']; cmp(have, want); - var have = store.selector.getListFromContext({device:'android'}); + var have = store.selector.getPOSLFromContext({device:'android'}); var want = ['droid', '*']; cmp(have, want); - var have = store.selector.getListFromContext({runtime:'server', device:'android'}); + var have = store.selector.getPOSLFromContext({runtime:'server', device:'android'}); var want = ['shelves', 'droid', '*']; cmp(have, want); - var have = store.selector.getListFromContext({device:'android', environment:'dev'}); + var have = store.selector.getPOSLFromContext({device:'android', environment:'dev'}); var want = ['devdroid', 'droid', '*']; cmp(have, want); - var have = store.selector.getListFromContext({runtime:'server', device:'android', environment:'dev'}); + var have = store.selector.getPOSLFromContext({runtime:'server', device:'android', environment:'dev'}); var want = ['shelves', 'devdroid', 'droid', '*']; cmp(have, want); } From 8beb315bd31a590d35d684eab740cbbdc594ac2b Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sat, 14 Jul 2012 19:17:59 -0700 Subject: [PATCH 097/119] created addon-rs-selector.getAllPOSLs(), and used for deep optimizations --- source/lib/app/addons/rs/selector.server.js | 107 ++++++++++ source/lib/libs/ycb.js | 61 +++++- source/lib/store.server.js | 197 ++++-------------- .../app/addons/rs/selector-tests.server.js | 19 ++ source/lib/tests/autoload/libs/ycb-tests.js | 59 +++++- 5 files changed, 283 insertions(+), 160 deletions(-) diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js index 27b45d35b..440d1bd25 100644 --- a/source/lib/app/addons/rs/selector.server.js +++ b/source/lib/app/addons/rs/selector.server.js @@ -51,6 +51,28 @@ YUI.add('addon-rs-selector', function(Y, NAME) { }, + /** + * Returns a list of all priority-ordered selector lists (POSLs). + * @method getAllPOSLs + * @return {array} list of priority-ordered selector lists + */ + getAllPOSLs: function() { + var c, + ctx, + ctxs, + posl, + posls = {}; + ctxs = this._listUsedContexts(); + for (c = 0; c < ctxs.length; c += 1) { + ctx = ctxs[c]; + posl = this.getPOSLFromContext(ctx); + posls[JSON.stringify(posl)] = posl; + } + ctxs = []; // free a bunch of memory + return Y.Object.values(posls); + }, + + /** * Returns the priority-ordered selector list (POSL) for the context. * @method getPOSLFromContext @@ -71,6 +93,91 @@ YUI.add('addon-rs-selector', function(Y, NAME) { } } return sels; + }, + + + /** + * @private + * @method _listUsedDimensions + * @return {array} list of dimensions and values + * (values have no structure) + */ + _listUsedDimensions: function() { + var ctxs = [], + ctxValues = {}; // dimName: value: true + this._appConfigYCB.walkSettings(function(settings, config) { + Y.Object.each(settings, function(val, name) { + if (!ctxValues[name]) { + ctxValues[name] = {}; + } + ctxValues[name][val] = true; + }); + return true; + }); + Y.Object.each(ctxValues, function(vals, name) { + ctxs[name] = Object.keys(vals); + }); + return ctxs; + }, + + + /** + * Generates a list of contexts to which application.json is sensitive. + * @private + * @method _listUsedContexts + * @return {array of objects} all contexts in application.json + */ + _listUsedContexts: function() { + var dims = this._listUsedDimensions(), + nctxs, + c, + ctxs = [], + dn, + dname, + dnames = Object.keys(dims), + dv, + dval, + dvals, + e, + each, + mod; + + nctxs = 1; + for (dn = 0; dn < dnames.length; dn += 1) { + dname = dnames[dn]; + dvals = dims[dname]; + if (dname !== 'runtime') { + // we never have indeterminant runtime + dvals.push('*'); + } + nctxs *= dvals.length; + } + + for (c = 0; c < nctxs; c += 1) { + ctxs[c] = {}; + } + mod = 1; + for (dn = 0; dn < dnames.length; dn += 1) { + dname = dnames[dn]; + dvals = dims[dname]; + mod *= dvals.length; + each = nctxs / mod; + + e = each; + dv = 0; + for (c = 0; c < nctxs; e -= 1, c += 1) { + if (0 === e) { + e = each; + dv += 1; + dv = dv % dvals.length; + } + dval = dvals[dv]; + if ('*' !== dval) { + ctxs[c][dname] = dval; + } + } + } + return ctxs; } diff --git a/source/lib/libs/ycb.js b/source/lib/libs/ycb.js index bb56c60e4..605c8eda9 100644 --- a/source/lib/libs/ycb.js +++ b/source/lib/libs/ycb.js @@ -12,7 +12,7 @@ var VERSION = '2.0.0', DEFAULT = '*', SEPARATOR = '/', SUBMATCH = /\$\$[a-zA-Z0-9.-_]*\$\$/, - Y = require('yui').YUI({useSync: true}).use('json-parse', 'json-stringify'); + Y = require('yui').YUI({useSync: true}).use('json-parse', 'json-stringify', 'oop'); Y.applyConfig({useSync: false}); @@ -31,6 +31,39 @@ function Ycb(bundle, options) { Ycb.prototype = { + /** + * @method getDimensions + * @return {object} the dimensions + */ + getDimensions: function() { + return Y.clone(this.dimensions, true); + }, + + + /** + * Iterates over all the setting sections in the YCB file, calling the + * callback for each section. + * @method walkSettings + * @param callback {function(settings, config)} + * @param callback.settings {object} the condition under which section will be used + * @param callback.config {object} the configuration in the section + * @param callback.return {boolean} if the callback returns false, then walking is stopped + * @return {nothing} results returned via callback + */ + walkSettings: function(callback) { + var path, + context; + for (path in this.settings) { + if (this.settings.hasOwnProperty(path)) { + context = this._getContextFromLookupPath(path); + if (!callback(context, this.settings[path])) { + break; + } + } + } + }, + + /** * @method read * @param context {object} @@ -349,6 +382,32 @@ Ycb.prototype = { }, + /** + * @private + * @method _getContextFromLookupPath + * @param path {string} the path + * @return {object} the cooresponding context (really a partial context) + */ + _getContextFromLookupPath: function(path) { + var parts = path.split(SEPARATOR), + p, + part, + dimName, + ctx = {}; + for (p = 0; p < this.dimensions.length; p += 1) { + part = parts[p]; + if ('*' === part) { + continue; + } + // Having more than one key in the dimensions structure is against + // the YCB spec. + dimName = Object.keys(this.dimensions[p])[0]; + ctx[dimName] = part; + } + return ctx; + }, + + /* * @private * @method _makeOrderedLookupList diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 8ee759df3..2625c669a 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -549,6 +549,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { ctxKey, module; + if ('shared' === mojitType) { + throw new Error('Mojit name "shared" is special and isn\'t a real mojit.'); + } + if (!dest) { dest = {}; } @@ -1212,20 +1216,13 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @param ress {array} list of resources in the mojit (for the `env` and `posl`) */ resolveResourceVersions: function() { - var c, ctx, ctxs, - poslKey, posl, posls = {}, + var p, poslKey, posl, posls = {}, e, env, envs = [ 'client', 'server' ], affinities, selectors, sourceBase, type, ress, - p; + s; - ctxs = this._listAllContexts(); - for (c = 0; c < ctxs.length; c += 1) { - ctx = ctxs[c]; - posl = this.selector.getPOSLFromContext(ctx); - posls[JSON.stringify(posl)] = posl; - } - ctxs = []; // free a bunch of memory + posls = this.selector.getAllPOSLs(); for (e = 0; e < envs.length; e += 1) { env = envs[e]; @@ -1234,41 +1231,40 @@ YUI.add('mojito-resource-store', function(Y, NAME) { affinities[env] = 1; affinities.common = 0; - for (poslKey in posls) { - if (posls.hasOwnProperty(poslKey)) { - posl = posls[poslKey]; - selectors = {}; // selector: priority modifier - for (p = 0; p < posl.length; p += 1) { - selectors[posl[p]] = (posl.length - p - 1) * 2; - } - sourceBase = posl.length * 2; - //console.log('-- source base ' + sourceBase); - //console.log(selectors); - //console.log(affinities); + for (p = 0; p < posls.length; p += 1) { + posl = posls[p]; + poslKey = Y.JSON.stringify(posl); + selectors = {}; // selector: priority modifier + for (s = 0; s < posl.length; s += 1) { + selectors[posl[s]] = (posl.length - s - 1) * 2; + } + sourceBase = posl.length * 2; + //console.log('-- source base ' + sourceBase); + //console.log(selectors); + //console.log(affinities); - if (!this._appResources[env]) { - this._appResources[env] = {}; - } - this._appResources[env][poslKey] = - this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs ]); + if (!this._appResources[env]) { + this._appResources[env] = {}; + } + this._appResources[env][poslKey] = + this._resolveVersions(affinities, selectors, sourceBase, [ this._appRVs ]); - if (!this._mojitResources[env]) { - this._mojitResources[env] = {}; - } - if (!this._mojitResources[env][poslKey]) { - this._mojitResources[env][poslKey] = {}; - } - for (type in this._mojitRVs) { - if (this._mojitRVs.hasOwnProperty(type)) { - ress = this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); - this._mojitResources[env][poslKey][type] = ress; - this.fire('mojitResourcesResolved', { - env: env, - posl: posl, - mojit: type, - ress: ress - }); - } + if (!this._mojitResources[env]) { + this._mojitResources[env] = {}; + } + if (!this._mojitResources[env][poslKey]) { + this._mojitResources[env][poslKey] = {}; + } + for (type in this._mojitRVs) { + if (this._mojitRVs.hasOwnProperty(type)) { + ress = this._resolveVersions(affinities, selectors, sourceBase, [ this._mojitRVs.shared, this._mojitRVs[type] ]); + this._mojitResources[env][poslKey][type] = ress; + this.fire('mojitResourcesResolved', { + env: env, + posl: posl, + mojit: type, + ress: ress + }); } } } @@ -1577,9 +1573,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, pkg: pkg }, - mojit: null, type: 'mojit', - subtype: null, name: mojitType, id: 'mojit--' + mojitType, affinity: 'common', @@ -1659,119 +1653,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, - /** - * Generates a list of all possible context (which is a lot!). - * @private - * @method _listAllContext - * @return {array of objects} all possible contexts - */ - _listAllContexts: function() { - var dims = this.config.getDimensions(), - nctxs, - c, - ctxs = [], - dn, - dname, - dnames, - dv, - dval, - dvals, - e, - each, - mod, - // only because we might want to change it at some point - // (not including it helps reduce the number of contexts) - SKIP_RUNTIME = true; - - dims = dims[0].dimensions; - dims = this._flattenDims(dims); - dnames = Object.keys(dims); - - nctxs = 1; - for (dn = 0; dn < dnames.length; dn += 1) { - dname = dnames[dn]; - if (SKIP_RUNTIME && dname === 'runtime') { - continue; - } - dvals = dims[dname]; - if (dname !== 'runtime') { - // we never have indeterminant runtime - dvals.push('*'); - } - nctxs *= dvals.length; - } - - for (c = 0; c < nctxs; c += 1) { - ctxs[c] = {}; - } - mod = 1; - for (dn = 0; dn < dnames.length; dn += 1) { - dname = dnames[dn]; - if (SKIP_RUNTIME && dname === 'runtime') { - continue; - } - dvals = dims[dname]; - mod *= dvals.length; - each = nctxs / mod; - - e = each; - dv = 0; - for (c = 0; c < nctxs; e -= 1, c += 1) { - if (0 === e) { - e = each; - dv += 1; - dv = dv % dvals.length; - } - dval = dvals[dv]; - if ('*' !== dval) { - ctxs[c][dname] = dval; - } - } - } - return ctxs; - }, - - - /** - * Flattens dimensions so that the structure of the dimension values doesn't matter. - * @private - * @method _flattenDims - * @param dims {object} dimensions structure - * @return {object} - */ - _flattenDims: function(dims) { - var d, dim, - name, out = {}; - for (d = 0; d < dims.length; d += 1) { - dim = dims[d]; - name = Object.keys(dim)[0]; - out[name] = this._listKeys(dim[name]); - } - return out; - }, - - - /** - * Recursively finds all keys for the object (plus child objects). - * @private - * @method _listKeys - * @param obj {object} - * @return {array} list of all keys in the object (no matter how deep) - */ - _listKeys: function(obj) { - var k, keys = []; - for (k in obj) { - if (obj.hasOwnProperty(k)) { - keys.push(k); - if ('object' === typeof obj[k]) { - keys = keys.concat(this._listKeys(obj[k])); - } - } - } - return keys; - }, - - /** * Finds resources based on our conventions. * -Doesn't- load mojits or their contents. That's done elsewhere. diff --git a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js index 4dbd89c68..f30cb9ac0 100644 --- a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js @@ -96,6 +96,25 @@ YUI.add('mojito-addon-rs-selector-tests', function(Y, NAME) { var have = store.selector.getPOSLFromContext({runtime:'server', device:'android', environment:'dev'}); var want = ['shelves', 'devdroid', 'droid', '*']; cmp(have, want); + }, + + + 'get all posls': function() { + var fixtures = libpath.join(__dirname, '../../../../fixtures/store'); + var store = new MockRS({ root: fixtures }); + store.plug(Y.mojito.addons.rs.config, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + store.plug(Y.mojito.addons.rs.selector, { appRoot: fixtures, mojitoRoot: mojitoRoot } ); + + var have = store.selector.getAllPOSLs(); + var want = [ + [ 'shelves', '*' ], + [ 'shelves', 'devdroid', 'droid', '*' ], + [ 'shelves', 'droid', '*' ], + [ 'right', '*' ], + [ 'right', 'devdroid', 'droid', '*' ], + [ 'right', 'droid', '*' ] + ]; + cmp(want, have); } diff --git a/source/lib/tests/autoload/libs/ycb-tests.js b/source/lib/tests/autoload/libs/ycb-tests.js index 0c2d81f8c..3cc89c576 100644 --- a/source/lib/tests/autoload/libs/ycb-tests.js +++ b/source/lib/tests/autoload/libs/ycb-tests.js @@ -20,6 +20,31 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { } + function cmp(x, y, msg) { + if (Y.Lang.isArray(x)) { + A.isArray(x, msg || 'first arg should be an array'); + A.isArray(y, msg || 'second arg should be an array'); + A.areSame(x.length, y.length, msg || 'arrays are different lengths'); + for (var i = 0; i < x.length; i += 1) { + cmp(x[i], y[i], msg); + } + return; + } + if (Y.Lang.isObject(x)) { + A.isObject(x, msg || 'first arg should be an object'); + A.isObject(y, msg || 'second arg should be an object'); + A.areSame(Object.keys(x).length, Object.keys(y).length, msg || 'object keys are different lengths'); + for (var i in x) { + if (x.hasOwnProperty(i)) { + cmp(x[i], y[i], msg); + } + } + return; + } + A.areSame(x, y, msg || 'args should be the same'); + } + + suite.add(new YUITest.TestCase({ name: 'ycb', @@ -347,6 +372,38 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { '*/*/*/*/*/*/fr_FR/ir/*/*/*' ]; AA.itemsAreEqual(expected, paths); + }, + + + 'get dimensions': function() { + var bundle, ycb; + bundle = readFixtureFile('dimensions.json'); + ycb = new libycb.Ycb(Y.clone(bundle, true)); + cmp(bundle[0].dimensions, ycb.getDimensions()); + }, + + + 'walk settings': function() { + var bundle, ycb; + bundle = readFixtureFile('dimensions.json') + .concat(readFixtureFile('simple-1.json')) + .concat(readFixtureFile('simple-3.json')); + ycb = new libycb.Ycb(bundle); + var ctxs = {}; + ycb.walkSettings(function(settings, config) { + ctxs[JSON.stringify(settings)] = true; + return true; + }); + A.areSame(9, Object.keys(ctxs).length); + A.isTrue(ctxs['{}']); + A.isTrue(ctxs['{"region":"ca"}']); + A.isTrue(ctxs['{"region":"gb"}']); + A.isTrue(ctxs['{"lang":"fr"}']); + A.isTrue(ctxs['{"region":"fr"}']); + A.isTrue(ctxs['{"flavor":"att"}']); + A.isTrue(ctxs['{"region":"ca","flavor":"att"}']); + A.isTrue(ctxs['{"region":"gb","flavor":"bt"}']); + A.isTrue(ctxs['{"region":"fr","flavor":"bt"}']); } @@ -355,4 +412,4 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) { YUITest.TestRunner.add(suite); -}, '0.0.1', {requires: ['json']}); +}, '0.0.1', {requires: ['json', 'oop']}); From 96194ef32fc893fd027bf6cc6ed0da8d82e515bb Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Tue, 17 Jul 2012 11:38:22 -0700 Subject: [PATCH 098/119] better test for if the controller exists on the client --- source/lib/app/autoload/mojito-client.client.js | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/source/lib/app/autoload/mojito-client.client.js b/source/lib/app/autoload/mojito-client.client.js index 436107ab3..1498400f4 100644 --- a/source/lib/app/autoload/mojito-client.client.js +++ b/source/lib/app/autoload/mojito-client.client.js @@ -692,9 +692,8 @@ YUI.add('mojito-client', function(Y, NAME) { command.instance, this.context, function(err, details) { // if there is a controller in the client type details, that - // means the controller exists here "cast details.controller - // to Boolean" ;) - var existsOnClient = Boolean(details.controller); + // means the controller exists here + var existsOnClient = Boolean(details['controller-module']); command.context = my.context; From 8d4ce6459da442e88b89f61299efa422a3d32603 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 18 Jul 2012 17:20:15 -0700 Subject: [PATCH 099/119] validate context agains dimensions.json, to guard against spurious values --- source/lib/app/addons/rs/config.server.js | 4 ++ source/lib/app/addons/rs/selector.server.js | 5 ++ source/lib/store.server.js | 70 +++++++++++++++++++ .../app/addons/rs/config-tests.server.js | 4 ++ .../app/addons/rs/selector-tests.server.js | 1 + .../app/addons/rs/yui-tests.server.js | 12 ++-- .../lib/tests/autoload/store.server-tests.js | 10 +++ 7 files changed, 100 insertions(+), 6 deletions(-) diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js index 044685187..0e069e011 100644 --- a/source/lib/app/addons/rs/config.server.js +++ b/source/lib/app/addons/rs/config.server.js @@ -102,6 +102,10 @@ YUI.add('addon-rs-config', function(Y, NAME) { json, ycb; + if (!this.rs.isValidContext(ctx)) { + throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); + } + ctx = this.rs.mergeRecursive(this.rs.getStaticContext(), ctx); ycb = this._ycbCache[fullPath]; diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js index 440d1bd25..59aba9a64 100644 --- a/source/lib/app/addons/rs/selector.server.js +++ b/source/lib/app/addons/rs/selector.server.js @@ -84,6 +84,11 @@ YUI.add('addon-rs-selector', function(Y, NAME) { p, part, parts; + + if (!this.rs.isValidContext(ctx)) { + throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); + } + // TODO: use rs.config for this too parts = this._appConfigYCB.readNoMerge(ctx, {}); for (p = 0; p < parts.length; p += 1) { diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 2625c669a..344805384 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -197,6 +197,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this._fwConfig = this.config.readConfigJSON(this._libs.path.join(this._config.mojitoRoot, 'config.json')); this._appConfigStatic = this.getAppConfig({}); + this._validDims = this._parseValidDims(this.config.getDimensions()); + if (!this.isValidContext(this._config.context)) { + throw new Error('INVALID context ' + Y.JSON.stringify(this._config.context)); + } }, destructor: function() {}, @@ -205,6 +209,24 @@ YUI.add('mojito-resource-store', function(Y, NAME) { // PUBLIC METHODS + /** + * @method isValidContext + * @param ctx {object} the context + * @return {boolean} whether context is valid according to dimensions.json or not + */ + isValidContext: function(ctx) { + var k; + for (k in ctx) { + if (ctx.hasOwnProperty(k)) { + if (!this._validDims[k] || !this._validDims[k][ctx[k]]) { + return false; + } + } + } + return true; + }, + + /** * Returns the static (non-runtime-sensitive) context * @method getStaticContext @@ -245,6 +267,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { var appConfig, ycb; + if (!this.isValidContext(ctx)) { + throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); + } + if (this._appConfigStatic && (!ctx || !Object.keys(ctx).length)) { return this.cloneObj(this._appConfigStatic); } @@ -342,6 +368,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { k, use; + if (!this.isValidContext(ctx)) { + throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); + } + posl = JSON.stringify(this.selector.getPOSLFromContext(ctx)); if (filter.mojit) { if (!this._mojitResources[env] || @@ -476,6 +506,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { typeDetails, config; + if (!this.isValidContext(ctx)) { + throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); + } + if (cacheValue) { cb(null, this.cloneObj(cacheValue)); return; @@ -549,6 +583,10 @@ YUI.add('mojito-resource-store', function(Y, NAME) { ctxKey, module; + if (!this.isValidContext(ctx)) { + throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); + } + if ('shared' === mojitType) { throw new Error('Mojit name "shared" is special and isn\'t a real mojit.'); } @@ -1315,6 +1353,38 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }, + /** + * @private + * @method @parseValidDims + * @param dims {object} contents of dimensions.json + * @return {object} lookup hash for dimension keys and values + */ + _parseValidDims: function(dims) { + var d, + dim, + dimName, + out = {}; + function grabKeys(dimName, o) { + var k; + for (k in o) { + if (o.hasOwnProperty(k)) { + out[dimName][k] = true; + if (Y.Lang.isObject(o[k])) { + grabKeys(dimName, o[k]); + } + } + } + } + for (d = 0; d < dims[0].dimensions.length; d += 1) { + dim = dims[0].dimensions[d]; + dimName = Object.keys(dim)[0]; + out[dimName] = {}; + grabKeys(dimName, dim[dimName]); + } + return out; + }, + + /** * Applies spec inheritance by following the `base` and merging up the * results. diff --git a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js index f072a10ed..7c6aac494 100644 --- a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js @@ -25,6 +25,10 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { this._config = cfg || {}; }, + isValidContext: function() { + return true; + }, + cloneObj: function(o) { return Y.clone(o); }, diff --git a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js index f30cb9ac0..e17ead930 100644 --- a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js @@ -27,6 +27,7 @@ YUI.add('mojito-addon-rs-selector-tests', function(Y, NAME) { '*': true }; }, + isValidContext: function() { return true; }, cloneObj: function(o) { return Y.clone(o); } diff --git a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js index 2265e4858..1d601aa85 100644 --- a/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js @@ -662,21 +662,21 @@ YUI.add('mojito-addon-rs-yui-tests', function(Y, NAME) { A.isUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is not undefined {lang:en}'); // third test - ctx = { lang: 'de-US' }; + ctx = { lang: 'de-AT' }; spec = { type: 'PagedFlickr' }; store.expandInstance(spec, ctx, function(err, instance) { - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_de'], 'de is undefined {lang:de-US}'); - A.isUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is not undefined {lang:de-US}'); + A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_de'], 'de is undefined {lang:de-AT}'); + A.isUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is not undefined {lang:de-AT}'); // fourth test - ctx = { lang: 'xy-ZU' }; + ctx = { lang: 'tr-TR' }; spec = { type: 'PagedFlickr' }; store.expandInstance(spec, ctx, function(err, instance) { A.isTrue( Boolean(instance.yui.sortedPaths['lang/PagedFlickr_de']), - 'de is undefined {lang:xy-ZU}' + 'de is undefined {lang:tr-TR}' ); - A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:xy-ZU}'); + A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:tr-TR}'); // fifth test ctx = {}; diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 1b7096ed7..d8b292ed5 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -55,6 +55,16 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { A.isTrue(store._config.root === fixtures); }, + 'valid context': function() { + A.isTrue(store.isValidContext({}), '{} should be valid'); + A.isTrue(store.isValidContext({device:'iphone'}), '{device:iphone} should be valid'); + A.isTrue(store.isValidContext({device:'iphone',lang:'en'}), '{device:iphone,lang:en} should be valid'); + A.isTrue(store.isValidContext({device:'iphone',runtime:'common'}), '{device:iphone,runtime:common} should be valid'); + A.isFalse(store.isValidContext({device:'blender'}), '{device:blender} should be invalid'); + A.isFalse(store.isValidContext({device:'iphone',texture:'corrugated'}), '{device:iphone,texture:corrugated} should be invalid'); + A.isFalse(store.isValidContext({device:'iphone',runtime:'kite'}), '{device:iphone,runtime:kite} should be invalid'); + }, + 'server app config value': function() { var config = store.getAppConfig(null); A.isTrue(config.testKey1 === 'testVal1'); From f58867c33f8588dce4ea4819705da1551f701500 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 19 Jul 2012 09:37:53 -0700 Subject: [PATCH 100/119] resolved this.rs and destructor() question --- source/lib/app/addons/rs/config.server.js | 18 ++++-------- source/lib/app/addons/rs/selector.server.js | 18 ++++-------- source/lib/app/addons/rs/url.server.js | 24 ++++++---------- source/lib/app/addons/rs/yui.server.js | 31 +++++++++------------ 4 files changed, 34 insertions(+), 57 deletions(-) diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js index 0e069e011..fcee3043d 100644 --- a/source/lib/app/addons/rs/config.server.js +++ b/source/lib/app/addons/rs/config.server.js @@ -35,7 +35,6 @@ YUI.add('addon-rs-config', function(Y, NAME) { Y.extend(RSAddonConfig, Y.Plugin.Base, { initializer: function(config) { - this.rs = config.host; this.appRoot = config.appRoot; this.mojitoRoot = config.mojitoRoot; this.afterHostMethod('findResourceVersionByConvention', this.findResourceVersionByConvention, this); @@ -47,18 +46,12 @@ YUI.add('addon-rs-config', function(Y, NAME) { }, - destructor: function() { - // TODO: needed to break cycle so we don't leak memory? - this.rs = null; - }, - - /** * @method getDimensions * @return {object} the YCB dimensions structure for the app */ getDimensions: function() { - return this.rs.cloneObj(this._ycbDims); + return this.get('host').cloneObj(this._ycbDims); }, @@ -85,7 +78,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { } this._jsonCache[fullPath] = json; } - return this.rs.cloneObj(json); + return this.get('host').cloneObj(json); }, @@ -98,15 +91,16 @@ YUI.add('addon-rs-config', function(Y, NAME) { */ // TODO: async interface readConfigYCB: function(fullPath, ctx) { - var cacheKey, + var store = this.get('host'), + cacheKey, json, ycb; - if (!this.rs.isValidContext(ctx)) { + if (!store.isValidContext(ctx)) { throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); } - ctx = this.rs.mergeRecursive(this.rs.getStaticContext(), ctx); + ctx = store.mergeRecursive(store.getStaticContext(), ctx); ycb = this._ycbCache[fullPath]; if (!ycb) { diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js index 59aba9a64..95fbf6f61 100644 --- a/source/lib/app/addons/rs/selector.server.js +++ b/source/lib/app/addons/rs/selector.server.js @@ -33,24 +33,17 @@ YUI.add('addon-rs-selector', function(Y, NAME) { initializer: function(config) { var dims, json; - this.rs = config.host; this.appRoot = config.appRoot; this.mojitoRoot = config.mojitoRoot; - dims = this.rs.config.getDimensions(); - json = this.rs.config.readConfigJSON(libpath.join(this.appRoot, 'application.json')); + dims = config.host.config.getDimensions(); + json = config.host.config.readConfigJSON(libpath.join(this.appRoot, 'application.json')); json = dims.concat(json); // TODO: use rs.config for this too this._appConfigYCB = new libycb.Ycb(json); }, - destructor: function() { - // TODO: needed to break cycle so we don't leak memory? - this.rs = null; - }, - - /** * Returns a list of all priority-ordered selector lists (POSLs). * @method getAllPOSLs @@ -80,12 +73,13 @@ YUI.add('addon-rs-selector', function(Y, NAME) { * @return {array} priority-ordered selector list */ getPOSLFromContext: function(ctx) { - var sels = ['*'], + var store = this.get('host'), + sels = ['*'], p, part, parts; - if (!this.rs.isValidContext(ctx)) { + if (!store.isValidContext(ctx)) { throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); } @@ -93,7 +87,7 @@ YUI.add('addon-rs-selector', function(Y, NAME) { parts = this._appConfigYCB.readNoMerge(ctx, {}); for (p = 0; p < parts.length; p += 1) { part = parts[p]; - if (part.selector && this.rs.selectors[part.selector]) { + if (part.selector && store.selectors[part.selector]) { sels.unshift(part.selector); } } diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 4d055e8ca..53f9be167 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -31,13 +31,12 @@ YUI.add('addon-rs-url', function(Y, NAME) { initializer: function(config) { var appConfig; - this.rs = config.host; this.appRoot = config.appRoot; this.mojitoRoot = config.mojitoRoot; this.afterHostMethod('preloadResourceVersions', this.preloadResourceVersions, this); this.onHostEvent('getMojitTypeDetails', this.getMojitTypeDetails, this); - appConfig = this.rs.getStaticAppConfig(); + appConfig = config.host.getStaticAppConfig(); this.config = appConfig.staticHandling || {}; this.config.appName = this.config.appName || libpath.basename(this.appRoot); this.config.frameworkName = this.config.frameworkName || 'mojito'; @@ -49,12 +48,6 @@ YUI.add('addon-rs-url', function(Y, NAME) { }, - destructor: function() { - // TODO: needed to break cycle so we don't leak memory? - this.rs = null; - }, - - /** * Using AOP, this is called after the ResourceStore's version. * It computes the static handler URL for all resources in all the @@ -62,7 +55,8 @@ YUI.add('addon-rs-url', function(Y, NAME) { * @method preloadResourceVersions */ preloadResourceVersions: function() { - var mojits, + var store = this.get('host'), + mojits, m, mojit, mojitRes, @@ -73,11 +67,11 @@ YUI.add('addon-rs-url', function(Y, NAME) { res, skip; - mojits = this.rs.listAllMojits(); + mojits = store.listAllMojits(); mojits.push('shared'); for (m = 0; m < mojits.length; m += 1) { mojit = mojits[m]; - mojitRes = this.rs.getResourceVersions({id: 'mojit--' + mojit})[0]; + mojitRes = store.getResourceVersions({id: 'mojit--' + mojit})[0]; if (mojitRes) { this._calcResourceURL(mojitRes, mojitRes); } @@ -88,7 +82,7 @@ YUI.add('addon-rs-url', function(Y, NAME) { // preload JSON specs for specific mojits during the compile step // (`mojito compile json`) for Livestand. if ('shared' !== mojit && 'mojito' === mojitRes.source.pkg.name) { - mojitControllerRess = this.rs.getResourceVersions({mojit: mojit, id: 'controller--controller'}); + mojitControllerRess = store.getResourceVersions({mojit: mojit, id: 'controller--controller'}); if (mojitControllerRess.length === 1 && mojitControllerRess[0].affinity.affinity === 'server') { continue; @@ -97,10 +91,10 @@ YUI.add('addon-rs-url', function(Y, NAME) { if (mojitRes) { packageJson = libpath.join(mojitRes.source.fs.fullPath, 'package.json'); - packageJson = this.rs.config.readConfigJSON(packageJson); + packageJson = store.config.readConfigJSON(packageJson); } - ress = this.rs.getResourceVersions({mojit: mojit}); + ress = store.getResourceVersions({mojit: mojit}); for (r = 0; r < ress.length; r += 1) { res = ress[r]; @@ -137,7 +131,7 @@ YUI.add('addon-rs-url', function(Y, NAME) { * @param evt {object} */ getMojitTypeDetails: function(evt) { - var ress = this.rs.getResources(evt.args.env, evt.args.ctx, {type: 'mojit', name: evt.args.mojitType}); + var ress = this.get('host').getResources(evt.args.env, evt.args.ctx, {type: 'mojit', name: evt.args.mojitType}); evt.mojit.assetsRoot = ress[0].url + '/assets'; }, diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index c94641ee2..f398dc5bc 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -32,7 +32,6 @@ YUI.add('addon-rs-yui', function(Y, NAME) { Y.extend(RSAddonYUI, Y.Plugin.Base, { initializer: function(config) { - this.rs = config.host; this.appRoot = config.appRoot; this.mojitoRoot = config.mojitoRoot; this.afterHostMethod('findResourceVersionByConvention', this.findResourceVersionByConvention, this); @@ -40,7 +39,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { this.beforeHostMethod('addResourceVersion', this.addResourceVersion, this); this.onHostEvent('getMojitTypeDetails', this.getMojitTypeDetails, this); this.onHostEvent('mojitResourcesResolved', this.mojitResourcesResolved, this); - this.yuiConfig = this.rs.getStaticAppConfig().yui; + this.yuiConfig = config.host.getStaticAppConfig().yui; this.modules = {}; // env: poslKey: module: details this.sortedModules = {}; // env: poslKey: lang: module: details @@ -53,12 +52,6 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }, - destructor: function() { - // TODO: needed to break cycle so we don't leak memory? - this.rs = null; - }, - - /** * Returns a datastructure which tells a YUI instance where to find * the YUI modules that are part of the Mojito framework. @@ -72,7 +65,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { res, ress, modules = {}; - ress = this.rs.getResources(env, ctx, { mojit: 'shared' }); + ress = this.get('host').getResources(env, ctx, { mojit: 'shared' }); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (!res.yui || !res.yui.name) { @@ -106,7 +99,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { res, ress, modules = {}; - ress = this.rs.getResources(env, ctx, { mojit: 'shared' }); + ress = this.get('host').getResources(env, ctx, { mojit: 'shared' }); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (!res.yui || !res.yui.name) { @@ -136,17 +129,18 @@ YUI.add('addon-rs-yui', function(Y, NAME) { * @return {object} datastructure for configuring YUI */ getConfigAllMojits: function(env, ctx) { - var m, + var store = this.get('host'), + m, mojit, mojits, r, res, ress, modules = {}; - mojits = this.rs.listAllMojits(); + mojits = store.listAllMojits(); for (m = 0; m < mojits.length; m += 1) { mojit = mojits[m]; - ress = this.rs.getResources(env, ctx, { mojit: mojit }); + ress = store.getResources(env, ctx, { mojit: mojit }); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (!res.yui || !res.yui.name) { @@ -302,7 +296,8 @@ YUI.add('addon-rs-yui', function(Y, NAME) { * @param evt {object} */ getMojitTypeDetails: function(evt) { - var dest = evt.mojit, + var store = this.get('host'), + dest = evt.mojit, env = evt.args.env, ctx = evt.args.ctx, posl = evt.args.posl, @@ -325,7 +320,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { dest.yui.config.modules = this.modules[env][poslKey][mojitType]; } - ress = this.rs.getResources(evt.args.env, evt.args.ctx, {mojit: mojitType}); + ress = store.getResources(evt.args.env, evt.args.ctx, {mojit: mojitType}); for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (res.type === 'binder') { @@ -335,7 +330,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { dest.views[res.name]['binder-module'] = res.yui.name; sorted = this._getYUISorted('client', poslKey, ctx.lang, res.yui.name); if (sorted && sorted.paths) { - dest.views[res.name]['binder-yui-sorted'] = this.rs.cloneObj(sorted.paths); + dest.views[res.name]['binder-yui-sorted'] = store.cloneObj(sorted.paths); } } if (res.type === 'controller') { @@ -345,7 +340,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { dest.yui.sorted = sorted.sorted.slice(); } if (this.usePrecomputed && sorted && sorted.paths) { - dest.yui.sortedPaths = this.rs.cloneObj(sorted.paths); + dest.yui.sortedPaths = store.cloneObj(sorted.paths); } } } @@ -422,7 +417,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { if (!this.modules[env][poslKey]) { this.modules[env][poslKey] = {}; } - this.modules[env][poslKey][mojit] = this.rs.cloneObj(modules); + this.modules[env][poslKey][mojit] = this.get('host').cloneObj(modules); // we always want to do calculations for no-lang if (!langs['']) { From 7afaf7db42a1f147d60f8d85ae1c17eefa9a91b0 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 19 Jul 2012 10:44:47 -0700 Subject: [PATCH 101/119] more lenient when interpretting user-provided dimensions.json files --- source/lib/app/addons/rs/config.server.js | 4 ++-- source/lib/store.server.js | 13 ++++++++----- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js index fcee3043d..5bdc729d4 100644 --- a/source/lib/app/addons/rs/config.server.js +++ b/source/lib/app/addons/rs/config.server.js @@ -96,12 +96,12 @@ YUI.add('addon-rs-config', function(Y, NAME) { json, ycb; + ctx = store.mergeRecursive(store.getStaticContext(), ctx); + if (!store.isValidContext(ctx)) { throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); } - ctx = store.mergeRecursive(store.getStaticContext(), ctx); - ycb = this._ycbCache[fullPath]; if (!ycb) { json = this.readConfigJSON(fullPath); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 344805384..2d754e244 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -195,12 +195,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { }); this.plug(Y.mojito.addons.rs.config, { appRoot: this._config.root, mojitoRoot: this._config.mojitoRoot }); - this._fwConfig = this.config.readConfigJSON(this._libs.path.join(this._config.mojitoRoot, 'config.json')); - this._appConfigStatic = this.getAppConfig({}); this._validDims = this._parseValidDims(this.config.getDimensions()); if (!this.isValidContext(this._config.context)) { throw new Error('INVALID context ' + Y.JSON.stringify(this._config.context)); } + this._fwConfig = this.config.readConfigJSON(this._libs.path.join(this._config.mojitoRoot, 'config.json')); + this._appConfigStatic = this.getAppConfig({}); }, destructor: function() {}, @@ -1377,9 +1377,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } for (d = 0; d < dims[0].dimensions.length; d += 1) { dim = dims[0].dimensions[d]; - dimName = Object.keys(dim)[0]; - out[dimName] = {}; - grabKeys(dimName, dim[dimName]); + for (dimName in dim) { + if (dim.hasOwnProperty(dimName)) { + out[dimName] = {}; + grabKeys(dimName, dim[dimName]); + } + } } return out; }, From b34c6b395aa983568d4ebd0ffbe8eb70f7046471 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 19 Jul 2012 15:33:34 -0700 Subject: [PATCH 102/119] better context validation --- source/lib/app/addons/rs/config.server.js | 4 +- source/lib/app/addons/rs/selector.server.js | 4 +- source/lib/store.server.js | 41 ++++++------- .../app/addons/rs/config-tests.server.js | 3 +- .../app/addons/rs/selector-tests.server.js | 2 +- .../lib/tests/autoload/store.server-tests.js | 60 ++++++++++++++++--- 6 files changed, 78 insertions(+), 36 deletions(-) diff --git a/source/lib/app/addons/rs/config.server.js b/source/lib/app/addons/rs/config.server.js index 5bdc729d4..d38b36b25 100644 --- a/source/lib/app/addons/rs/config.server.js +++ b/source/lib/app/addons/rs/config.server.js @@ -98,9 +98,7 @@ YUI.add('addon-rs-config', function(Y, NAME) { ctx = store.mergeRecursive(store.getStaticContext(), ctx); - if (!store.isValidContext(ctx)) { - throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); - } + store.validateContext(ctx); ycb = this._ycbCache[fullPath]; if (!ycb) { diff --git a/source/lib/app/addons/rs/selector.server.js b/source/lib/app/addons/rs/selector.server.js index 95fbf6f61..ae9457ed4 100644 --- a/source/lib/app/addons/rs/selector.server.js +++ b/source/lib/app/addons/rs/selector.server.js @@ -79,9 +79,7 @@ YUI.add('addon-rs-selector', function(Y, NAME) { part, parts; - if (!store.isValidContext(ctx)) { - throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); - } + store.validateContext(ctx); // TODO: use rs.config for this too parts = this._appConfigYCB.readNoMerge(ctx, {}); diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 2d754e244..39880f502 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -196,9 +196,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { this.plug(Y.mojito.addons.rs.config, { appRoot: this._config.root, mojitoRoot: this._config.mojitoRoot }); this._validDims = this._parseValidDims(this.config.getDimensions()); - if (!this.isValidContext(this._config.context)) { - throw new Error('INVALID context ' + Y.JSON.stringify(this._config.context)); - } + this.validateContext(this._config.context); this._fwConfig = this.config.readConfigJSON(this._libs.path.join(this._config.mojitoRoot, 'config.json')); this._appConfigStatic = this.getAppConfig({}); }, @@ -210,16 +208,27 @@ YUI.add('mojito-resource-store', function(Y, NAME) { /** - * @method isValidContext + * Validates the context, and throws an exception if it isn't. + * @method validateContext * @param ctx {object} the context - * @return {boolean} whether context is valid according to dimensions.json or not + * @return {nothing} if this method returns at all then the context is valid */ - isValidContext: function(ctx) { + validateContext: function(ctx) { var k; for (k in ctx) { if (ctx.hasOwnProperty(k)) { - if (!this._validDims[k] || !this._validDims[k][ctx[k]]) { - return false; + if (!ctx[k]) { + continue; + } + if ('langs' === k) { + // pseudo-context variable created by our middleware + continue; + } + if (!this._validDims[k]) { + throw new Error('INVALID dimension key "' + k + '"'); + } + if (!this._validDims[k][ctx[k]]) { + throw new Error('INVALID dimension value "' + ctx[k] + '" for key "' + k + '"'); } } } @@ -267,9 +276,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { var appConfig, ycb; - if (!this.isValidContext(ctx)) { - throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); - } + this.validateContext(ctx); if (this._appConfigStatic && (!ctx || !Object.keys(ctx).length)) { return this.cloneObj(this._appConfigStatic); @@ -368,9 +375,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { k, use; - if (!this.isValidContext(ctx)) { - throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); - } + this.validateContext(ctx); posl = JSON.stringify(this.selector.getPOSLFromContext(ctx)); if (filter.mojit) { @@ -506,9 +511,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { typeDetails, config; - if (!this.isValidContext(ctx)) { - throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); - } + this.validateContext(ctx); if (cacheValue) { cb(null, this.cloneObj(cacheValue)); @@ -583,9 +586,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { ctxKey, module; - if (!this.isValidContext(ctx)) { - throw new Error('INVALID context ' + Y.JSON.stringify(ctx)); - } + this.validateContext(ctx); if ('shared' === mojitType) { throw new Error('Mojit name "shared" is special and isn\'t a real mojit.'); diff --git a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js index 7c6aac494..5effb8f14 100644 --- a/source/lib/tests/autoload/app/addons/rs/config-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js @@ -25,8 +25,7 @@ YUI.add('mojito-addon-rs-config-tests', function(Y, NAME) { this._config = cfg || {}; }, - isValidContext: function() { - return true; + validateContext: function() { }, cloneObj: function(o) { diff --git a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js index e17ead930..a9638b6e0 100644 --- a/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js +++ b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js @@ -27,7 +27,7 @@ YUI.add('mojito-addon-rs-selector-tests', function(Y, NAME) { '*': true }; }, - isValidContext: function() { return true; }, + validateContext: function() {}, cloneObj: function(o) { return Y.clone(o); } diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index d8b292ed5..6fd67ed8b 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -56,13 +56,59 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { }, 'valid context': function() { - A.isTrue(store.isValidContext({}), '{} should be valid'); - A.isTrue(store.isValidContext({device:'iphone'}), '{device:iphone} should be valid'); - A.isTrue(store.isValidContext({device:'iphone',lang:'en'}), '{device:iphone,lang:en} should be valid'); - A.isTrue(store.isValidContext({device:'iphone',runtime:'common'}), '{device:iphone,runtime:common} should be valid'); - A.isFalse(store.isValidContext({device:'blender'}), '{device:blender} should be invalid'); - A.isFalse(store.isValidContext({device:'iphone',texture:'corrugated'}), '{device:iphone,texture:corrugated} should be invalid'); - A.isFalse(store.isValidContext({device:'iphone',runtime:'kite'}), '{device:iphone,runtime:kite} should be invalid'); + var success; + + try { + store.validateContext({}); + } catch(e) { + A.fail('{} should be valid'); + } + + try { + store.validateContext({device:'iphone'}); + } catch(e) { + A.fail('{device:iphone} should be valid'); + } + + try { + store.validateContext({device:'iphone',lang:'en'}); + } catch(e) { + A.fail('{device:iphone,lang:en} should be valid'); + } + + try { + store.validateContext({device:'iphone',runtime:'common'}); + } catch(e) { + A.fail('{device:iphone,runtime:common} should be valid'); + } + + try { + success = undefined; + store.validateContext({device:'blender'}); + success = true; + } catch(e) { + success = false; + } + A.isFalse(success, '{device:blender} should be invalid'); + + try { + success = undefined; + store.validateContext({device:'iphone',texture:'corrugated'}); + success = true; + } catch(e) { + success = false; + } + A.isFalse(success, '{device:iphone,texture:corrugated} should be invalid'); + + try { + success = undefined; + store.validateContext({device:'iphone',runtime:'kite'}); + success = true; + } catch(e) { + success = false; + } + A.isFalse(success, '{device:iphone,runtime:kite} should be invalid'); + }, 'server app config value': function() { From fb1624e6632f24b38925d077ee16a21f42e33d09 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 19 Jul 2012 18:38:34 -0700 Subject: [PATCH 103/119] guard against wierd files, mainly broken symlinks --- source/lib/store.server.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 39880f502..fc6d5869e 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1862,7 +1862,12 @@ YUI.add('mojito-resource-store', function(Y, NAME) { } childPath = this._libs.path.join(subdir, childName); childFullPath = this._libs.path.join(dir, childPath); - childStat = this._libs.fs.statSync(childFullPath); + try { + childStat = this._libs.fs.statSync(childFullPath); + } catch(e) { + Y.log('invalid file. skipping ' + childFullPath, 'warn', NAME); + continue; + } if (childStat.isFile()) { cb(null, subdir, childName, true); } else if (childStat.isDirectory()) { From 118eb6c0d8cefd3c912e752d5c13bd59f005d36f Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 19 Jul 2012 18:39:39 -0700 Subject: [PATCH 104/119] nifty features * binders skipped when runtime:server * graph has nice title * language bundles skipped unless --lang is given --- source/lib/app/commands/gv.js | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index 68e4fbe06..cf6110f9c 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -40,6 +40,12 @@ function parseReqs(dest, ress, options) { if (('mojito' === res.source.pkg.name) && (!options.framework)) { continue; } + if ('binder' === res.type && !options.client) { + continue; + } + if ('yui-lang' === res.type && !options.lang) { + continue; + } src = 'package ' + res.source.pkg.name; if (res.mojit && 'shared' !== res.mojit) { src = 'mojit ' + res.mojit; @@ -52,7 +58,7 @@ function parseReqs(dest, ress, options) { } -function makeDepGraph(reqs, destFile) { +function makeDepGraph(title, reqs, destFile) { var graph, src, mod, @@ -62,7 +68,7 @@ function makeDepGraph(reqs, destFile) { edges = '', graphAttrs; - graph = 'digraph yui {\n'; + graph = 'digraph "' + title + '" {\n'; graph += ' rankdir="LR";\n'; graph += ' fontsize=11;\n'; graph += ' node [shape=Mrecord,fontsize=11];\n'; @@ -76,7 +82,7 @@ function makeDepGraph(reqs, destFile) { graph += ' label="' + src + '";\n'; graph += ' style="filled";\n'; graph += ' color="lightgrey";\n'; - graph += ' node [style="filled",color="white"];\n'; + graph += ' node [style="filled",fillcolor="white"];\n'; for (mod in reqs[src]) { if (reqs[src].hasOwnProperty(mod)) { graph += ' "' + mod + '";\n'; @@ -126,6 +132,8 @@ run = function(params, options) { Y, ress, m, mojit, mojits, + appConfigRes, + title, resultsFile; options = options || {}; @@ -173,9 +181,12 @@ run = function(params, options) { parseReqs(reqs, ress, options); } + appConfigRes = store.getResources('server', {}, {id:'config--application'})[0]; + title = [appConfigRes.source.pkg.name, appConfigRes.source.pkg.version, env].join(' '); + // generate graph resultsFile = libpath.join(resultsDir, 'yui.' + env + '.dot'); - makeDepGraph(reqs, resultsFile); + makeDepGraph(title, reqs, resultsFile); console.log('Dotfile generated.' + ' To turn it into a graph, run the following:'); @@ -193,6 +204,8 @@ usage = 'mojito gv // generates a GraphViz[1] file' + '\t -c: short for --client\n' + '\t --framework: include framework (Mojito) modules\n' + '\t -f: short for --framework\n' + + '\t --lang: also show language bundles\n' + + '\t -l: short for --lang\n' + '\n' + '[1] http://en.wikipedia.org/wiki/Graphviz\n'; @@ -207,6 +220,11 @@ options = [ longName: 'client', shortName: 'c', hasValue: false + }, + { + longName: 'lang', + shortName: 'l', + hasValue: false } ]; From 1aceed9efb7301404c2ace526633774aeb89f99e Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 20 Jul 2012 07:10:51 -0700 Subject: [PATCH 105/119] no longer trim .affinity and .selector from resolved resources, since useful for debugging --- source/lib/store.server.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index fc6d5869e..e3b6ed3d7 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1718,8 +1718,6 @@ YUI.add('mojito-resource-store', function(Y, NAME) { highest = Math.max.apply(Math, Object.keys(versions[resid])); //console.log('--DEBUG-- highest=' + highest + ' -- ' + resid); chosen = this.cloneObj(versions[resid][highest]); - delete chosen.selector; - delete chosen.affinity; out.push(chosen); } } From dd74db46afc33ecf1681a8ad83be220d2e40954a Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 20 Jul 2012 08:59:06 -0700 Subject: [PATCH 106/119] rewrite to use much of the power of the new resource store --- source/lib/app/commands/gv.js | 467 +++++++++++++++++++++++++++------- 1 file changed, 374 insertions(+), 93 deletions(-) diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index cf6110f9c..d24546aa9 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -9,9 +9,11 @@ // TODO: -// color-code or shape-code module types (from yui, mojito fw, app-level, -// mojits) use store.getAppLevelYuiModules() to de-stress (or not draw) -// modules that aren't used by the app. +// * include YUI internal structure (but not edges/dependencies) +// * --trace=foo to trace all transitive dependencies on module "foo" +// * [warning][server] trace anything that leads to a YUI module that uses the DOM +// * color-code or shape-code module types (from yui, mojito fw, app-level, +// mojits, affinity) var run, usage, @@ -21,6 +23,7 @@ var run, libfs = require('fs'), libutils = require(libpath.join(__dirname, '../../management/utils')), YUI = require('yui').YUI, + Y = YUI(), MODE_ALL = parseInt('777', 8), @@ -28,113 +31,336 @@ var run, resultsDir = 'artifacts/gv'; -function parseReqs(dest, ress, options) { + +function gvQuote(str) { + str = str.replace(/"/g, '\\"'); + return '"' + str + '"'; +} + + +function gvStyle(style, nopad) { + var pairs = []; + Y.Object.each(style, function(v, k) { + pairs.push(k + '=' + gvQuote(v)); + }); + if (!pairs.length) { + return ''; + } + return (nopad ? '' : ' ') + '[' + pairs.join(',') + ']'; +} + + +function Node(name) { + this.name = name; + this.attrs = {}; // arbitrary user-defined attributes + this.style = {}; // graphviz style attributes +} + +function Edge(tail, head, directed) { + this.tail = tail; + this.head = head; + this.directed = directed || false; + this.attrs = {}; // arbitrary user-defined attributes + this.style = {}; // graphviz style attributes +} + +function Graph(name) { + this.name = name; + this.type = 'graph';// top-level: graph | digraph + // subgraph: group | subgraph | cluster + this.title = name; // title can be different than name + // (name is used as the identifier) + // (for .type===cluster you might want to set .style.label) + this.attrs = {}; // arbitrary user-defined attributes + this.style = {}; // graphviz style attributes for the Graph itself + this.styles = { // type: default graphviz style attributes + node: {}, // ... for nodes + edge: {}, // ... for nodes + graph: {}, // ... for nodes + all: {} // ... for everything + }; + this._nodes = {}; // id: Node + this._edges = {}; // id: Edge + this._subgraphs = {}; // id: Graph +} +Graph.prototype = { + + // finds the node, no matter how deeply nested + // creates the node if it doesn't already exist + // @param name {string} name of node + getNode: function(name) { + var node; + this._find('_nodes', name, function(parent, found) { + node = found; + }); + if (!node) { + node = new Node(name); + this._nodes[name] = node; + } + return node; + }, + + // finds the edge, no matter how deeply nested + // creates the edge if it doesn't already exist + // @param tail {string} name of tail node + // @param head {string} name of head node + getEdge: function(tail, head, directed) { + var id = this._makeEdgeID(tail, head, directed); + var edge; + this._find('_edges', id, function(parent, found) { + edge = found; + }); + if (!edge) { + edge = new Edge(tail, head, directed); + this._edges[id] = edge; + } + return edge; + }, + + // finds the subgraph, no matter how deeply nested + // creates the subgraph if it doesn't already exist + // @param name {string} name of subgraph + // @return {Graph} found subgraph + getSubgraph: function(name) { + var subgraph; + this._find('_subgraphs', name, function(parent, found) { + subgraph = found; + }); + if (!subgraph) { + subgraph = new Graph(name); + this._subgraphs[name] = subgraph; + } + return subgraph; + }, + + // moves the node from existing subgraph to the one speciefied + // @param node {string} name of node to move + // @param parent {string} name of new parent parent + moveNodeToSubgraph: function(node, parent) { + var found; + this._find('_nodes', node, function(foundParent, foundItem) { + delete foundParent._nodes[node]; + found = foundItem; + }); + if (!found) { + found = new Node(node); + } + this.getSubgraph(parent)._nodes[node] = found; + }, + + // arranges for child subgraph to be drawn inside parent subgraph + // @param child {string} child subgraph name + // @param parent {string} parent subgraph name + moveSubgraphToSubgraph: function(child, parent) { + var found; + this._find('_subgraphs', child, function(foundParent, foundItem) { + delete foundParent._subgraphs[child]; + found = foundItem; + }); + if (!found) { + found = new Graph(child); + } + this.getSubgraph(parent)._subgraphs[node] = found; + }, + + // if a filter returns false, that node/edge/subgraph is skipped + // @param filters {object} callbacks to det + // @param filters.node {function(Node)} + // @param filters.edge {function(Edge)} + // @param filters.subgraph {function(Graph)} + // @param _ctx {object} [private] graph drawing context, for recursion + // @return {string} graphviz DOT notation + render: function(filters, _ctx) { + _ctx = _ctx || {depth: 0, count: 0}; + _ctx.count += 1; + var out = '', section; + var indent = Array(_ctx.depth + 1).join(' '); + + if ('group' === this.type) { + out += indent + '{\n'; + } else if ('cluster' === this.type) { + out += indent + 'subgraph ' + gvQuote('cluster_' + _ctx.count) + ' {\n'; + } else { + out += indent + this.type + ' ' + gvQuote(this.title) + ' {\n'; + } + + section = ''; + if (Object.keys(this.styles.all).length) { + Y.Object.each(this.styles.all, function(v, k) { + section += indent + ' ' + k + '=' + gvQuote(v) + ';\n'; + }); + } + if (Object.keys(this.styles.node).length) { + section += indent + ' node' + gvStyle(this.styles.node) + ';\n'; + } + if (Object.keys(this.styles.edge).length) { + section += indent + ' edge' + gvStyle(this.styles.edge) + ';\n'; + } + if (Object.keys(this.styles.graph).length) { + section += indent + ' graph' + gvStyle(this.styles.graph) + ';\n'; + } + if (section) { + out += indent + ' // defaults\n'; + out += section; + out += '\n'; + } + + section = ''; + this._filter(this._nodes, filters.node, function(key, val) { + section += indent + ' ' + gvQuote(val.name) + gvStyle(val.style) + ';\n'; + }); + if (section) { + out += indent + ' // nodes\n'; + out += section; + out += '\n'; + } + + section = ''; + this._filter(this._edges, filters.edge, function(key, val) { + section += indent + ' ' + gvQuote(val.tail) + + ' ' + (val.directed ? '->' : '--') + ' ' + + gvQuote(val.head) + gvStyle(val.style) + ';\n'; + }); + if (section) { + out += indent + ' // edges\n'; + out += section; + out += '\n'; + } + + section = ''; + this._filter(this._subgraphs, filters.subgraph, function(key, val) { + var c = { depth: _ctx.depth + 1, count: _ctx.count }; + section += val.render(filters, c); + _ctx.count = c.count; + }); + if (section) { + out += indent + ' // subgraphs\n'; + out += section; + out += '\n'; + } + + section = ''; + Y.Object.each(this.style, function(v, k) { + section += indent + ' ' + k + '=' + gvQuote(v) + ';\n'; + }); + if (section) { + out += indent + ' // this graph\n'; + out += section; + } + out += indent + '};\n'; + return out; + }, + + _find: function(source, target, cb) { + var id, subgraph; + if (source) { + for (id in this[source]) { + if (this[source].hasOwnProperty(id) && id === target) { + cb(this, this[source][id]); + return true; + } + } + } + for (id in this._subgraphs) { + if (this._subgraphs.hasOwnProperty(id)) { + subgraph = this._subgraphs[id]; + if (subgraph._find(source, target, cb)) { + return true; + } + } + } + return false; + }, + + _filter: function(obj, filter, cb) { + Y.Object.each(obj, function(val, key) { + if (filter && !filter(val)) { + return; + } + cb(key, val); + }); + }, + + _makeEdgeID: function(tail, head, directed) { + return [tail, directed, head].join(','); + } + +}; + + +function parseRess(graph, ress, options) { var r, res, - src; + subgraph, subgraphName, + tail, tailName, + head, headName, + edge, + rs, reqs; for (r = 0; r < ress.length; r += 1) { res = ress[r]; + if (!res.yui || !res.yui.name) { continue; } - if (('mojito' === res.source.pkg.name) && (!options.framework)) { + if ('yui-lang' === res.type && !options.lang) { continue; } if ('binder' === res.type && !options.client) { continue; } - if ('yui-lang' === res.type && !options.lang) { - continue; - } - src = 'package ' + res.source.pkg.name; + + // The idea here is that we -do- want to collect information about + // every module, so that we know which subgraph to draw it in. + // Later (during the render filters) we'll drop those that aren't + // of particular interest. + + tailName = res.yui.name; + + subgraphName = 'package ' + res.source.pkg.name + '@' + res.source.pkg.version; if (res.mojit && 'shared' !== res.mojit) { - src = 'mojit ' + res.mojit; - } - if (!dest[src]) { - dest[src] = {}; + subgraphName = 'mojit ' + res.mojit; } - dest[src][res.yui.name] = res.yui.meta.requires || []; - } -} - -function makeDepGraph(title, reqs, destFile) { - var graph, - src, - mod, - i, - req, - cluster = 0, - edges = '', - graphAttrs; - - graph = 'digraph "' + title + '" {\n'; - graph += ' rankdir="LR";\n'; - graph += ' fontsize=11;\n'; - graph += ' node [shape=Mrecord,fontsize=11];\n'; - graph += ' edge [color=grey33,arrowsize=0.5,fontsize=8];\n'; - graph += '\n'; - - for (src in reqs) { - if (reqs.hasOwnProperty(src)) { - cluster += 1; - graph += ' subgraph cluster' + cluster + ' {\n'; - graph += ' label="' + src + '";\n'; - graph += ' style="filled";\n'; - graph += ' color="lightgrey";\n'; - graph += ' node [style="filled",fillcolor="white"];\n'; - for (mod in reqs[src]) { - if (reqs[src].hasOwnProperty(mod)) { - graph += ' "' + mod + '";\n'; - } - } - graph += ' };\n'; + // create subgraph and node for -everything- (we'll filter later) + subgraph = graph.getSubgraph(subgraphName); + subgraph.type = 'cluster'; + subgraph.style.label = subgraphName; + subgraph.attrs.pkg = res.source.pkg; + + tail = graph.getNode(tailName); + graph.moveNodeToSubgraph(tailName, subgraphName); + // TODO: This might be handy to style different types differently. + //['type', 'subtype', 'affinity'].forEach(function(k) { + // tail.attrs[k] = res[k]; + //}); + + if ('mojito' === res.source.pkg.name && !options.framework) { + // TODO: I think this might have a bug where overriding HTMLFrameMojit ress + // leads to a sparse local "mojit HTMLFrameMojit" subgraph + subgraph.attrs.sparse = true; + continue; + } - for (mod in reqs[src]) { - if (reqs[src].hasOwnProperty(mod)) { - for (i = 0; i < reqs[src][mod].length; i += 1) { - req = reqs[src][mod][i]; - edges += ' "' + mod + '" -> "' + req + '";\n'; - } - } - } + reqs = res.yui.meta.requires || []; + for (rs = 0; rs < reqs.length; rs += 1) { + headName = reqs[rs]; + edge = graph.getEdge(tailName, headName, true); + tail.attrs.hasEdge = true; + head = graph.getNode(headName); + head.attrs.hasEdge = true; } } - - graph += '\n'; - graph += edges; - graph += '\n'; - - graphAttrs = [ - 'remincross=true', - // 'rankdir=LR', - 'ranksep=1.5', - 'clusterrank=local', - 'model=circuit', - 'overlap=false', - 'splines=compound', - // 'pack=true', - // 'packmode=clust', - // 'concentrate=true', // sometimes causes crash/coredump - // 'start=self', - 'compound=true' - ]; - graph += ' graph [' + graphAttrs.join(',') + '];\n'; - graph += '}\n'; - - libfs.writeFileSync(destFile, graph, 'utf-8'); } run = function(params, options) { var env, store, - reqs = {}, - Y, + graph, ress, m, mojit, mojits, appConfigRes, - title, - resultsFile; + contents, + file; options = options || {}; env = options.client ? 'client' : 'server'; @@ -152,7 +378,6 @@ run = function(params, options) { libfs.mkdirSync(resultsDir, MODE_ALL); } - Y = YUI(); Y.applyConfig({ useSync: true, modules: { @@ -170,27 +395,83 @@ run = function(params, options) { }); store.preload(); + appConfigRes = store.getResources('server', {}, {id:'config--application'})[0]; + title = appConfigRes.source.pkg.name + '@' + appConfigRes.source.pkg.version + ' ' + env; + graph = new Graph(title); + graph.type = 'digraph'; + graph.attrs.pkg = appConfigRes.source.pkg; + + graph.styles.all.rankdir = 'LR'; + graph.styles.all.fontsize = '11'; + graph.styles.node.fontsize = '11'; + graph.styles.node.shape = 'Mrecord'; + graph.styles.node.style = 'filled'; + graph.styles.node.fillcolor = 'white'; + graph.styles.edge.color = 'grey33'; + graph.styles.edge.arrowsize = '0.5'; + graph.styles.edge.fontsize = '8'; + graph.styles.graph.style = 'filled'; + graph.styles.graph.color = 'lightgrey'; + + graph.style.clusterrank = 'local'; // DOT -- special handling for clusters + graph.style.compound = 'true'; // allow edges between clusters (also requires edge[lhead,ltail]) +// graph.style.concentrate = 'true'; // SOMETIMES CRASHES -- reduce number of edges + graph.style.model = 'circuit'; // NEATO -- + graph.style.overlap = 'false'; // DOT -- +// graph.style.pack = 'true'; +// graph.style.packmode = 'clust'; +// graph.style.rankdir = 'LR'; // DOT -- direction of graph + graph.style.ranksep = '1.5'; // TWOPI,DOT -- space between ranks + graph.style.remincross = 'true'; // DOT -- if clusters, rerun cross minimization + graph.style.splines = 'polyline'; // how to draw edges + graph.style.start = 'self'; // FDP,NEATO -- + ress = store.getResources(env, {}, {}); - parseReqs(reqs, ress, options); + parseRess(graph, ress, options); mojits = store.listAllMojits(); mojits.push('shared'); for (m = 0; m < mojits.length; m += 1) { mojit = mojits[m]; ress = store.getResources(env, {}, { mojit: mojit }); - parseReqs(reqs, ress, options); + parseRess(graph, ress, options); } - appConfigRes = store.getResources('server', {}, {id:'config--application'})[0]; - title = [appConfigRes.source.pkg.name, appConfigRes.source.pkg.version, env].join(' '); - // generate graph - resultsFile = libpath.join(resultsDir, 'yui.' + env + '.dot'); - makeDepGraph(title, reqs, resultsFile); + contents = graph.render({ + node: function(node) { + // TODO: tweak each node style somehow + //node.style.label = node.name; + //if (node.attrs.affinity) { + // node.style.label += ' (' + node.attrs.affinity.affinity + ')'; + //} + return true; + }, + + subgraph: function(subgraph) { + if (subgraph.attrs.sparse) { + var doomed = []; + Y.Object.each(subgraph._nodes, function(node) { + if (!node.attrs.hasEdge) { + doomed.push(node.name); + } + }); + Y.Array.each(doomed, function(name) { + delete subgraph._nodes[name]; + }); + if (!Object.keys(subgraph._nodes).length) { + return false; + } + } + return true; + } + }); + file = libpath.join(resultsDir, 'yui.' + env + '.dot'); + libfs.writeFileSync(file, contents, 'utf-8'); console.log('Dotfile generated.' + ' To turn it into a graph, run the following:'); - console.log('$ dot -Tgif ' + resultsFile + ' > ' + + console.log('$ dot -Tgif ' + file + ' > ' + libpath.join(resultsDir, 'yui.' + env + '.gif')); }; From f1b1bf32787630dedaf0a6ebe3f5d7cd2c5c651e Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 20 Jul 2012 10:51:59 -0700 Subject: [PATCH 107/119] option --trace --- source/lib/app/commands/gv.js | 102 ++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 24 deletions(-) diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index d24546aa9..4878aefee 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -10,15 +10,12 @@ // TODO: // * include YUI internal structure (but not edges/dependencies) -// * --trace=foo to trace all transitive dependencies on module "foo" // * [warning][server] trace anything that leads to a YUI module that uses the DOM +// * also draw meta.optional edges // * color-code or shape-code module types (from yui, mojito fw, app-level, // mojits, affinity) var run, - usage, - options, - libpath = require('path'), libfs = require('fs'), libutils = require(libpath.join(__dirname, '../../management/utils')), @@ -33,7 +30,7 @@ var run, function gvQuote(str) { - str = str.replace(/"/g, '\\"'); + str = str.toString().replace(/"/g, '\\"'); return '"' + str + '"'; } @@ -288,7 +285,7 @@ Graph.prototype = { }; -function parseRess(graph, ress, options) { +function parseResources(graph, ress, options) { var r, res, subgraph, subgraphName, @@ -296,6 +293,7 @@ function parseRess(graph, ress, options) { head, headName, edge, rs, reqs; + for (r = 0; r < ress.length; r += 1) { res = ress[r]; @@ -353,6 +351,39 @@ function parseRess(graph, ress, options) { } +function trace(graph, options) { + var doneNodes = {}, // name: true + todoNodes = [], + headName, head, + edges = {}; // headName: [ Edge ] + + // TODO: detect if options.trace doesn't exist in graph + todoNodes.push(options.trace); + + Y.Object.each(graph._edges, function(edge) { + if (!edges[edge.head]) { + edges[edge.head] = []; + } + edges[edge.head].push(edge); + }); + + while (todoNodes.length) { + headName = todoNodes.shift(); + if (doneNodes[headName]) { + continue; + } + head = graph.getNode(headName); + head.attrs.trace = true; + doneNodes[headName] = true; + + Y.Array.each(edges[headName], function(edge) { + edge.attrs.trace = true; + todoNodes.push(edge.tail); + }); + } +} + + run = function(params, options) { var env, store, graph, @@ -404,8 +435,8 @@ run = function(params, options) { graph.styles.all.rankdir = 'LR'; graph.styles.all.fontsize = '11'; graph.styles.node.fontsize = '11'; - graph.styles.node.shape = 'Mrecord'; - graph.styles.node.style = 'filled'; + graph.styles.node.shape = 'rectangle'; + graph.styles.node.style = 'filled,rounded'; graph.styles.node.fillcolor = 'white'; graph.styles.edge.color = 'grey33'; graph.styles.edge.arrowsize = '0.5'; @@ -427,14 +458,18 @@ run = function(params, options) { graph.style.start = 'self'; // FDP,NEATO -- ress = store.getResources(env, {}, {}); - parseRess(graph, ress, options); + parseResources(graph, ress, options); mojits = store.listAllMojits(); mojits.push('shared'); for (m = 0; m < mojits.length; m += 1) { mojit = mojits[m]; ress = store.getResources(env, {}, { mojit: mojit }); - parseRess(graph, ress, options); + parseResources(graph, ress, options); + } + + if (options.trace) { + trace(graph, options); } // generate graph @@ -445,6 +480,22 @@ run = function(params, options) { //if (node.attrs.affinity) { // node.style.label += ' (' + node.attrs.affinity.affinity + ')'; //} + if (node.attrs.trace) { + node.style.penwidth = 1.5; + node.style.color = '#CC0000'; + node.style.fillcolor = '#FFDDDD'; + if (node.name === options.trace) { + node.style.peripheries = 2; + } + } + return true; + }, + + edge: function(edge) { + if (edge.attrs.trace) { + edge.style.penwidth = 1.5; + edge.style.color = '#CC0000'; + } return true; }, @@ -476,7 +527,10 @@ run = function(params, options) { }; -usage = 'mojito gv // generates a GraphViz[1] file' + +/** + * Standard usage string export. + */ +exports.usage = 'mojito gv // generates a GraphViz[1] file' + ' describing the dependencies\n' + ' // between the YUI modules\n' + '\n' + @@ -487,11 +541,16 @@ usage = 'mojito gv // generates a GraphViz[1] file' + '\t -f: short for --framework\n' + '\t --lang: also show language bundles\n' + '\t -l: short for --lang\n' + + '\t --trace target: hightlight all modules leading to the target\n' + + '\t -t target: short for --trace\n' + '\n' + '[1] http://en.wikipedia.org/wiki/Graphviz\n'; -options = [ +/** + * Standard options list export. + */ +exports.options = [ { longName: 'framework', shortName: 'f', @@ -506,23 +565,18 @@ options = [ longName: 'lang', shortName: 'l', hasValue: false + }, + { + longName: 'trace', + shortName: 't', + hasValue: true } ]; -/** - * Standard usage string export. - */ -exports.usage = usage; - - -/** - * Standard options list export. - */ -exports.options = options; - - /** * Standard run method hook export. */ exports.run = run; + + From fd870d2a7c0505e5bcb5c35cb1ed515ebd2b0a89 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 20 Jul 2012 11:08:57 -0700 Subject: [PATCH 108/119] tweaked traced edges a skosh --- source/lib/app/commands/gv.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index 4878aefee..81146746f 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -493,8 +493,8 @@ run = function(params, options) { edge: function(edge) { if (edge.attrs.trace) { - edge.style.penwidth = 1.5; - edge.style.color = '#CC0000'; + edge.style.penwidth = 1.1; + edge.style.color = '#AA4444'; } return true; }, From 55c26ac9f08d347b2154189d1196f96f0d54689f Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 20 Jul 2012 13:26:48 -0700 Subject: [PATCH 109/119] more styling tweaks --- source/lib/app/commands/gv.js | 1 + 1 file changed, 1 insertion(+) diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index 81146746f..4195ffc34 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -483,6 +483,7 @@ run = function(params, options) { if (node.attrs.trace) { node.style.penwidth = 1.5; node.style.color = '#CC0000'; + node.style.fontcolor = '#AA0000'; node.style.fillcolor = '#FFDDDD'; if (node.name === options.trace) { node.style.peripheries = 2; From 33670a9896df36645827b3db98c5a7ef651b19ea Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 22 Jul 2012 09:00:34 -0700 Subject: [PATCH 110/119] app resources override framework resources --- source/lib/store.server.js | 4 +++- source/lib/tests/autoload/store.server-tests.js | 7 +++++++ .../store/mojits/HTMLFrameMojit/controller.server.js | 8 ++++++++ 3 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 source/lib/tests/fixtures/store/mojits/HTMLFrameMojit/controller.server.js diff --git a/source/lib/store.server.js b/source/lib/store.server.js index e3b6ed3d7..d49dc39f8 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1710,7 +1710,9 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (!versions[res.id]) { versions[res.id] = {}; } - versions[res.id][priority] = res; + if (!versions[res.id][priority]) { + versions[res.id][priority] = res; + } } } for (resid in versions) { diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js index 6fd67ed8b..c60e2884c 100644 --- a/source/lib/tests/autoload/store.server-tests.js +++ b/source/lib/tests/autoload/store.server-tests.js @@ -364,6 +364,13 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) { var urls = store.getAllURLs(); A.areSame(libpath.join(fixtures, 'mojits/rollups/rollup.client.js'), urls['/static/rollups/rollup.client.js']); }); + }, + + 'app resource overrides framework resource': function() { + var fixtures = libpath.join(__dirname, '../fixtures/store'), + ress; + ress = store.getResources('server', {}, {mojit: 'HTMLFrameMojit', type: 'controller'}); + A.areSame(libpath.join(fixtures, 'mojits/HTMLFrameMojit/controller.server.js'), ress[0].source.fs.fullPath); } })); diff --git a/source/lib/tests/fixtures/store/mojits/HTMLFrameMojit/controller.server.js b/source/lib/tests/fixtures/store/mojits/HTMLFrameMojit/controller.server.js new file mode 100644 index 000000000..5288b5c25 --- /dev/null +++ b/source/lib/tests/fixtures/store/mojits/HTMLFrameMojit/controller.server.js @@ -0,0 +1,8 @@ +/* + * Copyright (c) 2012, Yahoo! Inc. All rights reserved. + * Copyrights licensed under the New BSD License. + * See the accompanying LICENSE file for terms. + */ +YUI.add('HTMLFrameMojit', function(Y, NAME) { + // just the existence is important +}, '0.1.0', {requires: []}); From 0b4d26c7b28efcb26b7f521b773018eb19a5b6b7 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 22 Jul 2012 09:31:23 -0700 Subject: [PATCH 111/119] resolved issue of mojits that come from multiple sources (such as HTMLFrameMojit with overrides in the app) --- source/lib/app/commands/gv.js | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index 4195ffc34..abd0c0e28 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -323,7 +323,6 @@ function parseResources(graph, ress, options) { subgraph = graph.getSubgraph(subgraphName); subgraph.type = 'cluster'; subgraph.style.label = subgraphName; - subgraph.attrs.pkg = res.source.pkg; tail = graph.getNode(tailName); graph.moveNodeToSubgraph(tailName, subgraphName); @@ -331,10 +330,9 @@ function parseResources(graph, ress, options) { //['type', 'subtype', 'affinity'].forEach(function(k) { // tail.attrs[k] = res[k]; //}); + tail.attrs.pkg = res.source.pkg; if ('mojito' === res.source.pkg.name && !options.framework) { - // TODO: I think this might have a bug where overriding HTMLFrameMojit ress - // leads to a sparse local "mojit HTMLFrameMojit" subgraph subgraph.attrs.sparse = true; continue; } @@ -430,7 +428,6 @@ run = function(params, options) { title = appConfigRes.source.pkg.name + '@' + appConfigRes.source.pkg.version + ' ' + env; graph = new Graph(title); graph.type = 'digraph'; - graph.attrs.pkg = appConfigRes.source.pkg; graph.styles.all.rankdir = 'LR'; graph.styles.all.fontsize = '11'; @@ -504,7 +501,8 @@ run = function(params, options) { if (subgraph.attrs.sparse) { var doomed = []; Y.Object.each(subgraph._nodes, function(node) { - if (!node.attrs.hasEdge) { + // always draw nodes found in the application + if ('mojito' === node.attrs.pkg.name && !node.attrs.hasEdge) { doomed.push(node.name); } }); From a76b935822b58350e48dede7a4a7ade73cbb6194 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 23 Jul 2012 09:30:03 -0700 Subject: [PATCH 112/119] delint --- source/lib/app/commands/gv.js | 52 ++++++++++++++++++++++++----------- source/lib/store.server.js | 2 +- 2 files changed, 37 insertions(+), 17 deletions(-) diff --git a/source/lib/app/commands/gv.js b/source/lib/app/commands/gv.js index abd0c0e28..d01304f9e 100644 --- a/source/lib/app/commands/gv.js +++ b/source/lib/app/commands/gv.js @@ -102,8 +102,8 @@ Graph.prototype = { // @param tail {string} name of tail node // @param head {string} name of head node getEdge: function(tail, head, directed) { - var id = this._makeEdgeID(tail, head, directed); - var edge; + var id = this._makeEdgeID(tail, head, directed), + edge; this._find('_edges', id, function(parent, found) { edge = found; }); @@ -157,7 +157,7 @@ Graph.prototype = { if (!found) { found = new Graph(child); } - this.getSubgraph(parent)._subgraphs[node] = found; + this.getSubgraph(parent)._subgraphs[child] = found; }, // if a filter returns false, that node/edge/subgraph is skipped @@ -170,8 +170,16 @@ Graph.prototype = { render: function(filters, _ctx) { _ctx = _ctx || {depth: 0, count: 0}; _ctx.count += 1; - var out = '', section; - var indent = Array(_ctx.depth + 1).join(' '); + var out = '', + section, + i, + indent = ''; + + // I would just do the following, but jslint is overly strict. + // indent = new Array(_ctx.depth + 1).join(' '); + for (i = 0; i < _ctx.depth; i += 1) { + indent += ' '; + } if ('group' === this.type) { out += indent + '{\n'; @@ -288,11 +296,15 @@ Graph.prototype = { function parseResources(graph, ress, options) { var r, res, - subgraph, subgraphName, - tail, tailName, - head, headName, + subgraph, + subgraphName, + tail, + tailName, + head, + headName, edge, - rs, reqs; + rs, + reqs; for (r = 0; r < ress.length; r += 1) { res = ress[r]; @@ -331,7 +343,7 @@ function parseResources(graph, ress, options) { // tail.attrs[k] = res[k]; //}); tail.attrs.pkg = res.source.pkg; - + if ('mojito' === res.source.pkg.name && !options.framework) { subgraph.attrs.sparse = true; continue; @@ -352,7 +364,10 @@ function parseResources(graph, ress, options) { function trace(graph, options) { var doneNodes = {}, // name: true todoNodes = [], - headName, head, + headName, + head, + e, + edge, edges = {}; // headName: [ Edge ] // TODO: detect if options.trace doesn't exist in graph @@ -374,19 +389,24 @@ function trace(graph, options) { head.attrs.trace = true; doneNodes[headName] = true; - Y.Array.each(edges[headName], function(edge) { + for (e = 0; e < edges[headName].length; e += 1) { + edge = edges[headName][e]; edge.attrs.trace = true; todoNodes.push(edge.tail); - }); + } } } run = function(params, options) { - var env, store, + var env, + store, + title, graph, ress, - m, mojit, mojits, + m, + mojit, + mojits, appConfigRes, contents, file; @@ -424,7 +444,7 @@ run = function(params, options) { }); store.preload(); - appConfigRes = store.getResources('server', {}, {id:'config--application'})[0]; + appConfigRes = store.getResources('server', {}, {id: 'config--application'})[0]; title = appConfigRes.source.pkg.name + '@' + appConfigRes.source.pkg.version + ' ' + env; graph = new Graph(title); graph.type = 'digraph'; diff --git a/source/lib/store.server.js b/source/lib/store.server.js index d49dc39f8..6b6e2ad59 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -1864,7 +1864,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { childFullPath = this._libs.path.join(dir, childPath); try { childStat = this._libs.fs.statSync(childFullPath); - } catch(e) { + } catch (e) { Y.log('invalid file. skipping ' + childFullPath, 'warn', NAME); continue; } From afa371cb38759707bc445a5ebb16184501a4581f Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Mon, 23 Jul 2012 18:07:16 +0000 Subject: [PATCH 113/119] don't generate a URL for the "shared" mojit --- source/lib/app/addons/rs/url.server.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/source/lib/app/addons/rs/url.server.js b/source/lib/app/addons/rs/url.server.js index 53f9be167..52595e453 100644 --- a/source/lib/app/addons/rs/url.server.js +++ b/source/lib/app/addons/rs/url.server.js @@ -186,7 +186,9 @@ YUI.add('addon-rs-url', function(Y, NAME) { } if ('mojit' === res.type) { - res.url = '/' + urlParts.join('/'); + if ('shared' !== res.name) { + res.url = '/' + urlParts.join('/'); + } return; } From ee33039144bc3266346c8493171b2a54fd50f38d Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 25 Jul 2012 20:23:48 +0000 Subject: [PATCH 114/119] optimization: don't make a new Y object every time we want to make a Y.Loader --- source/lib/app/addons/rs/yui.server.js | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/source/lib/app/addons/rs/yui.server.js b/source/lib/app/addons/rs/yui.server.js index f398dc5bc..c7c783aaf 100644 --- a/source/lib/app/addons/rs/yui.server.js +++ b/source/lib/app/addons/rs/yui.server.js @@ -479,8 +479,7 @@ YUI.add('addon-rs-yui', function(Y, NAME) { * @return {object} precomputed (and sorted) module dependencies */ _sortYUIModules: function(lang, env, mojit, modules, required) { - var Y, - loader, + var loader, m, module, info, @@ -500,9 +499,6 @@ YUI.add('addon-rs-yui', function(Y, NAME) { }; } - Y = YUI({ useSync: true }).use('loader-base'); - Y.applyConfig({ useSync: false }); - // We need to clear YUI's cached dependencies, since there's no // guarantee that the previously calculated dependencies have been done // using the same context as this calculation. @@ -641,4 +637,4 @@ YUI.add('addon-rs-yui', function(Y, NAME) { Y.namespace('mojito.addons.rs'); Y.mojito.addons.rs.yui = RSAddonYUI; -}, '0.0.1', { requires: ['plugin', 'oop']}); +}, '0.0.1', { requires: ['plugin', 'oop', 'loader-base']}); From f16c78398b5def6cf62833b021bb1ca612b185f3 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Wed, 25 Jul 2012 20:55:23 +0000 Subject: [PATCH 115/119] updating examples, since selector no longer defaults to device --- .../device_assets/application.json | 3 +- .../device_views/application.json | 5 ++- .../scroll_views/application.json | 33 ++++++++++--------- .../scroll/lang/{en.js => scroll_en.js} | 0 .../part5/flickr-list/application.json | 3 +- examples/newsboxes/application.json | 4 ++- examples/sandbox/ex1/application.json | 3 +- 7 files changed, 30 insertions(+), 21 deletions(-) rename examples/developer-guide/scroll_views/mojits/scroll/lang/{en.js => scroll_en.js} (100%) diff --git a/examples/developer-guide/device_assets/application.json b/examples/developer-guide/device_assets/application.json index 353343b81..da3e53a8b 100644 --- a/examples/developer-guide/device_assets/application.json +++ b/examples/developer-guide/device_assets/application.json @@ -13,5 +13,6 @@ } } } - } + }, + { "settings": [ "device:iphone" ], "selector": "iphone" } ] diff --git a/examples/developer-guide/device_views/application.json b/examples/developer-guide/device_views/application.json index 2191ce90d..2d8e0704e 100644 --- a/examples/developer-guide/device_views/application.json +++ b/examples/developer-guide/device_views/application.json @@ -7,6 +7,9 @@ "type" : "device" } } - } + }, + { "settings": [ "device:android" ], "selector": "android" }, + { "settings": [ "device:blackberry" ], "selector": "blackberry" }, + { "settings": [ "device:iphone" ], "selector": "iphone" } ] diff --git a/examples/developer-guide/scroll_views/application.json b/examples/developer-guide/scroll_views/application.json index 2f3874a3d..5ae51dccc 100644 --- a/examples/developer-guide/scroll_views/application.json +++ b/examples/developer-guide/scroll_views/application.json @@ -1,23 +1,24 @@ [ { - "settings": [ "master" ], - "specs": { - "frame" : { - "type" : "HTMLFrameMojit", - "config": { - "deploy": true, - "child" : { - "type" : "scroll" - }, - "assets": { - "top": { - "css":[ - "/static/scroll/assets/index.css" - ] + "settings": [ "master" ], + "specs": { + "frame" : { + "type" : "HTMLFrameMojit", + "config": { + "deploy": true, + "child" : { + "type" : "scroll" + }, + "assets": { + "top": { + "css":[ + "/static/scroll/assets/index.css" + ] + } } } } } - } -} + }, + { "settings": [ "device:iphone" ], "selector": "iphone" } ] diff --git a/examples/developer-guide/scroll_views/mojits/scroll/lang/en.js b/examples/developer-guide/scroll_views/mojits/scroll/lang/scroll_en.js similarity index 100% rename from examples/developer-guide/scroll_views/mojits/scroll/lang/en.js rename to examples/developer-guide/scroll_views/mojits/scroll/lang/scroll_en.js diff --git a/examples/getting-started-guide/part5/flickr-list/application.json b/examples/getting-started-guide/part5/flickr-list/application.json index a9421a3f9..a1666d1b7 100644 --- a/examples/getting-started-guide/part5/flickr-list/application.json +++ b/examples/getting-started-guide/part5/flickr-list/application.json @@ -50,5 +50,6 @@ "type": "FlickrDetail" } } - } + }, + { "settings": [ "device:iphone" ], "selector": "iphone" } ] diff --git a/examples/newsboxes/application.json b/examples/newsboxes/application.json index 74a4827b9..e048096ab 100644 --- a/examples/newsboxes/application.json +++ b/examples/newsboxes/application.json @@ -68,4 +68,6 @@ } } } -}] +}, +{ "settings": [ "device:opera-mini" ], "selector": "opera-mini" } +] diff --git a/examples/sandbox/ex1/application.json b/examples/sandbox/ex1/application.json index 1f28639a5..814ea9c16 100644 --- a/examples/sandbox/ex1/application.json +++ b/examples/sandbox/ex1/application.json @@ -40,5 +40,6 @@ "action": "jsonReturn" } } - } + }, + { "settings": [ "device:iphone" ], "selector": "iphone" } ] From 31c3939a139219cf8aba5c561737a2288f4726ab Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Thu, 26 Jul 2012 05:02:14 +0000 Subject: [PATCH 116/119] clone the routes in store.getRoutes(), because the router modifies the route object in-place --- source/lib/store.server.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 6b6e2ad59..6b760185b 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -716,7 +716,7 @@ YUI.add('mojito-resource-store', function(Y, NAME) { for (r = 0; r < ress.length; r += 1) { res = ress[r]; if (fixedPaths[res.source.fs.fullPath]) { - routes = this.config.readConfigYCB(res.source.fs.fullPath, ctx); + routes = this.cloneObj(this.config.readConfigYCB(res.source.fs.fullPath, ctx)); out = Y.merge(out, routes); } } From 66978deae255f4bb857ed120f3b9b515f247add8 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Fri, 27 Jul 2012 17:20:50 +0000 Subject: [PATCH 117/119] more defensive about poluting internal structures --- source/lib/libs/ycb.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/lib/libs/ycb.js b/source/lib/libs/ycb.js index 605c8eda9..d7d8aa486 100644 --- a/source/lib/libs/ycb.js +++ b/source/lib/libs/ycb.js @@ -56,7 +56,8 @@ Ycb.prototype = { for (path in this.settings) { if (this.settings.hasOwnProperty(path)) { context = this._getContextFromLookupPath(path); - if (!callback(context, this.settings[path])) { + // clone, so that noone mutates us + if (!callback(context, Y.clone(this.settings[path], true))) { break; } } @@ -95,7 +96,8 @@ Ycb.prototype = { console.log('----USING---- ' + lookupPaths[path]); console.log(Y.JSON.stringify(this.settings[lookupPaths[path]], null, 4)); } - config = objectMerge(this.settings[lookupPaths[path]], config); + // merge a copy so that we don't modify the source + config = objectMerge(Y.clone(this.settings[lookupPaths[path]], true), config); } } From 70cae36d4c69a85c77623f0e42bfb31f0e6783d4 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 29 Jul 2012 20:59:05 +0000 Subject: [PATCH 118/119] even more defenses against poluting internal structures --- source/lib/libs/ycb.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/libs/ycb.js b/source/lib/libs/ycb.js index d7d8aa486..f078897a4 100644 --- a/source/lib/libs/ycb.js +++ b/source/lib/libs/ycb.js @@ -144,7 +144,7 @@ Ycb.prototype = { console.log('----USING---- ' + lookupPaths[path]); console.log(Y.JSON.stringify(this.settings[lookupPaths[path]], null, 4)); } - config.push(this.settings[lookupPaths[path]]); + config.push(Y.clone(this.settings[lookupPaths[path]], true)); } } return config; From f78f3bd381bf669c6af2606d0795d9c322a9bf96 Mon Sep 17 00:00:00 2001 From: Drew Folta Date: Sun, 29 Jul 2012 21:25:20 +0000 Subject: [PATCH 119/119] fallback when validating "lang" context key --- source/lib/store.server.js | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/source/lib/store.server.js b/source/lib/store.server.js index 6b760185b..989c49f71 100644 --- a/source/lib/store.server.js +++ b/source/lib/store.server.js @@ -214,7 +214,11 @@ YUI.add('mojito-resource-store', function(Y, NAME) { * @return {nothing} if this method returns at all then the context is valid */ validateContext: function(ctx) { - var k; + var k, + parts, + p, + test, + found; for (k in ctx) { if (ctx.hasOwnProperty(k)) { if (!ctx[k]) { @@ -227,6 +231,22 @@ YUI.add('mojito-resource-store', function(Y, NAME) { if (!this._validDims[k]) { throw new Error('INVALID dimension key "' + k + '"'); } + // we need to support language fallbacks + if ('lang' === k) { + found = false; + parts = ctx[k].split('-'); + for (p = parts.length; p > 0; p -= 1) { + test = parts.slice(0, p).join('-'); + if (this._validDims[k][test]) { + found = true; + break; + } + } + if (!found) { + throw new Error('INVALID dimension value "' + ctx[k] + '" for key "' + k + '"'); + } + continue; + } if (!this._validDims[k][ctx[k]]) { throw new Error('INVALID dimension value "' + ctx[k] + '" for key "' + k + '"'); }