]');
diff --git a/source/lib/management/colors.js b/source/lib/management/colors.js
index cf981ce49..f780d1ca4 100644
--- a/source/lib/management/colors.js
+++ b/source/lib/management/colors.js
@@ -54,32 +54,42 @@ function stylize(str, style) {
(['bold', 'underline', 'italic', 'inverse', 'grey', 'yellow', 'red', 'green',
'blue', 'white', 'cyan', 'magenta']).forEach(
function(style) {
- Object.defineProperty(String.prototype, style, {
- get: function() {
- return stylize(this, style);
- }
- });
+ try {
+ Object.defineProperty(String.prototype, style, {
+ get: function() {
+ return stylize(this, style);
+ }
+ });
+ } catch (e) {
+ // just ignore
+ }
}
);
// prototypes string with method "rainbow"
// rainbow will apply a the color spectrum to a string, changing colors every
// letter
-Object.defineProperty(String.prototype, 'rainbow', {
- get: function() {
- //RoY G BiV
- var rainbowcolors = ['red', 'yellow', 'green', 'blue', 'magenta'],
- exploded = this.split(''),
- i = 0;
+try {
+ Object.defineProperty(String.prototype, 'rainbow', {
+ get: function() {
+ //RoY G BiV
+ var rainbowcolors = ['red', 'yellow', 'green', 'blue', 'magenta'],
+ exploded = this.split(''),
+ i = 0;
+
+ exploded = exploded.map(function(letter) {
+ if (letter === ' ') {
+ return letter;
+ } else {
+ return stylize(letter, rainbowcolors[(i += 1) %
+ rainbowcolors.length]);
+ }
+ });
+ return exploded.join('');
+ }
+ });
+} catch (e) {
+ // just ignore
+}
+
- exploded = exploded.map(function(letter) {
- if (letter === ' ') {
- return letter;
- } else {
- return stylize(letter, rainbowcolors[(i += 1) %
- rainbowcolors.length]);
- }
- });
- return exploded.join('');
- }
-});
diff --git a/source/lib/management/commands/gv.js b/source/lib/management/commands/gv.js
deleted file mode 100644
index 72dd95b28..000000000
--- a/source/lib/management/commands/gv.js
+++ /dev/null
@@ -1,171 +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*/
-
-
-// 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.
-
-var run,
- usage,
- options,
-
- libpath = require('path'),
- libfs = require('fs'),
- libutils = require('../utils'),
-
- MODE_ALL = parseInt('777', 8),
-
- ResourceStore = require('../../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 makeDepGraph(reqs, destFile) {
- var graph,
- mod,
- i,
- req,
- graphAttrs;
-
- graph = 'digraph yui {\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';
-
- 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';
- }
- }
- }
-
- 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 = {},
- resultsFile;
-
- options = options || {};
- env = options.client ? 'client' : 'server';
-
- if (params.length) {
- libutils.error('Unknown extra parameters.');
- return;
- }
-
- // make results dir
- if (!libpath.existsSync(artifactsDir)) {
- libfs.mkdirSync(artifactsDir, MODE_ALL);
- }
- if (!libpath.existsSync(resultsDir)) {
- libfs.mkdirSync(resultsDir, MODE_ALL);
- }
-
- // load details
- store = new ResourceStore(process.cwd());
- store.preload();
- if (options.framework) {
- parseReqs(reqs, store.getYuiConfigFw(env, {}).modules);
- }
- parseReqs(reqs, store.getYuiConfigApp(env, {}).modules);
- parseReqs(reqs, store.getYuiConfigAllMojits(env, {}).modules);
-
- // generate graph
- resultsFile = libpath.join(resultsDir, 'yui.' + env + '.dot');
- makeDepGraph(reqs, resultsFile);
-
- console.log('Dotfile generated.' +
- ' To turn it into a graph, run the following:');
- console.log('$ dot -Tgif ' + resultsFile + ' > ' +
- libpath.join(resultsDir, 'yui.' + env + '.gif'));
-};
-
-
-usage = 'mojito gv // generates a GraphViz[1] file' +
- ' describing the dependencies\n' +
- ' // between the YUI modules\n' +
- '\n' +
- 'OPTIONS:\n' +
- '\t --client: use modules for the client\n' +
- '\t -c: short for --client\n' +
- '\t --framework: include framework (Mojito) modules\n' +
- '\t -f: short for --framework\n' +
- '\n' +
- '[1] http://en.wikipedia.org/wiki/Graphviz\n';
-
-
-options = [
- {
- longName: 'framework',
- shortName: 'f',
- hasValue: false
- },
- {
- longName: 'client',
- shortName: 'c',
- hasValue: false
- }
-];
-
-
-/**
- * Standard usage string export.
- */
-exports.usage = usage;
-
-
-/**
- * Standard options list export.
- */
-exports.options = options;
-
-
-/**
- * Standard run method hook export.
- */
-exports.run = run;
diff --git a/source/lib/management/utils.js b/source/lib/management/utils.js
index 7490df727..49544c3f9 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);
@@ -23,11 +23,15 @@ if (!isatty) {
// fake out the getters that the "color" library would have added
(['bold', 'underline', 'italic', 'inverse', 'grey', 'yellow', 'red',
'green', 'blue', 'white', 'cyan', 'magenta']).forEach(function(style) {
- Object.defineProperty(String.prototype, style, {
- get: function() {
- return this;
+ try {
+ Object.defineProperty(String.prototype, style, {
+ get: function() {
+ return this;
+ }
+ });
+ } catch (e) {
+ // just ignore
}
- });
});
} else {
require('./colors');
@@ -74,6 +78,30 @@ function heir(o) {
}
+/**
+ * Decodes XML entities in the string.
+ * Only decodes a subset of named entities.
+ * @method decodeHTMLEntities
+ * @param {string} txt String to decode
+ * @return {string} input string with with XML entities decoded
+ */
+// TODO: find a node module that can do this well
+function decodeHTMLEntities(txt) {
+ txt = txt.replace(/(&[^;]+;)/g, function(all, ent) {
+ if ('' === 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;
+}
+
+
function process_template(archetype_path, file, mojit_dir, template) {
var archetype_file = path.join(archetype_path, file),
@@ -652,3 +680,6 @@ exports.heir = heir;
/**
*/
exports.contextCsvToObject = contextCsvToObject;
+
+exports.decodeHTMLEntities = decodeHTMLEntities;
+
diff --git a/source/lib/store.server.js b/source/lib/store.server.js
index d2c737a0b..7f1a088a5 100644
--- a/source/lib/store.server.js
+++ b/source/lib/store.server.js
@@ -4,1121 +4,656 @@
* 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', 'json-parse', 'json-stringify'),
-
- 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
*/
-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('mojito-resource-store', function(Y, NAME) {
+
+ var libs = {},
+
+ isNotAlphaNum = /[^a-zA-Z0-9]/,
+
+ mojitoRoot = __dirname,
+ mojitoVersion = '0.666.666', // special case for weird packaging situations
+
+ CONVENTION_SUBDIR_TYPES = {
+ // subdir: resource type
+ 'actions': 'action',
+ 'binders': 'binder',
+ 'commands': 'command',
+ 'middleware': 'middleware',
+ 'models': 'model',
+ 'specs': 'spec',
+ 'views': 'view'
+ },
+ CONVENTION_SUBDIR_TYPE_IS_JS = {
+ 'action': true,
+ 'binder': true,
+ 'model': true
+ },
+ // which addon subtypes are app-level
+ ADDON_SUBTYPES_APPLEVEL = {
+ 'rs': true
+ },
+ DEFAULT_AFFINITIES = {
+ 'action': 'server',
+ 'addon': 'server',
+ 'archetype': 'server',
+ 'asset': 'common',
+ 'binder': 'common', // need to be common so that binders meta-bubble
+ 'command': 'server',
+ 'controller': 'server',
+ 'middleware': 'server',
+ 'model': 'server',
+ 'spec': 'common',
+ 'view': 'common'
+ };
- // 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;
- }
-}
+ libs.fs = require('fs');
+ libs.glob = require('glob');
+ libs.path = require('path');
+ libs.semver = require('semver');
+ libs.walker = require('./package-walker.server');
-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 {object} config configuration for the store
+ * @param {string} config.root directory to manage (usually the application directory)
+ * @param {object} config.context static context
+ * @param {object} config.appConfig 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];
+
+ Y.extend(ResourceStore, Y.Base, {
+
+ /**
+ * This methods is part of Y.Base. See documentation for that for details.
+ * @method initializer
+ * @param {object} cfg Configuration object as per Y.Base
+ * @return {nothing}
+ */
+ initializer: function(cfg) {
+ var i;
+
+ 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
+
+ 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
+ this._mojitResources = {}; // env: posl: mojitType: array of resources
+ this._expandInstanceCache = { // env: cacheKey: instance
+ client: {},
+ server: {}
+ };
+
+ /**
+ * 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
+ // 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({
+ 'addon-rs-config': {
+ 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 });
- // generates metadata about each resource
- this._preloadMeta();
+ this._validDims = this._parseValidDims(this.config.getDimensions());
+ this.validateContext(this._config.context);
+ this._fwConfig = this.config.readConfigJSON(this._libs.path.join(this._config.mojitoRoot, 'config.json'));
+ this._appConfigStatic = this.getAppConfig({});
+ },
+ destructor: function() {},
- // takes the preloaded info about each resource and resolves
- // version priority (.server. more specific that .common. etc)
- this._cookdown();
- // 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]);
- }
- }
+ //====================================================================
+ // PUBLIC METHODS
- // 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;
+
+ /**
+ * Validates the context, and throws an exception if it isn't.
+ * @method validateContext
+ * @param {object} ctx the context
+ * @return {nothing} if this method returns at all then the context is valid
+ */
+ validateContext: function(ctx) {
+ var k,
+ parts,
+ p,
+ test,
+ found;
+ for (k in ctx) {
+ if (ctx.hasOwnProperty(k)) {
+ 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 + '"');
+ }
+ // 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 + '"');
}
}
}
- }
- },
-
+ return true;
+ },
- /**
- * Sets the logger object.
- *
- * @method setLogger
- * @param l {object} object containing a log(message,level,source) function
- * @return {nothing}
- */
- setLogger: function(l) {
- logger = l;
- },
+ /**
+ * Returns the static (non-runtime-sensitive) context
+ * @method getStaticContext
+ * @return {object} the context
+ */
+ getStaticContext: function() {
+ return Y.clone(this._config.context, true);
+ },
- /**
- * 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) version of the application.json.
+ * @method getStaticAppConfig
+ * @return {object} the configuration from applications.json
+ */
+ getStaticAppConfig: function() {
+ return Y.clone(this._appConfigStatic, true);
+ },
- if (env === 'client' && obj) {
- delete obj.assets;
- }
- callback(null, obj);
- });
- },
+ /**
+ * Returns Mojito's built-in configuration.
+ * @method getFrameworkConfig
+ * @return {object} the configuration for mojito
+ */
+ getFrameworkConfig: function() {
+ return Y.clone(this._fwConfig, true);
+ },
- /**
- * 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 {object} ctx 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;
- }
+ this.validateContext(ctx);
- if (env === 'client' && obj) {
- delete obj.assets;
+ if (this._appConfigStatic && (!ctx || !Object.keys(ctx).length)) {
+ return Y.clone(this._appConfigStatic, true);
}
- 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 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;
- },
-
-
- /**
- * 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 = Y.JSON.stringify(instance) + Y.JSON.stringify(
- this._getValidYCBContext(ctx)
- ),
- cacheValue = this._expandInstanceCache[env][cacheKey];
-
- if (cacheValue) {
- cb(null, this._cloneObj(cacheValue));
- return;
- }
+ // start with the base
+ appConfig = Y.clone(this._fwConfig.appConfigBase, true);
- 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;
+ // apply the read values from the file
+ ycb = this.config.readConfigYCB(this._libs.path.join(this._config.root, 'application.json'), ctx);
+ this.mergeRecursive(appConfig, ycb);
- try {
- self.getMojitTypeDetails(env, ctx, out.type, out);
- } catch (err) {
- return cb(err);
- }
+ // apply the passed-in overrides
+ this.mergeRecursive(appConfig, Y.clone(this._config.appConfig, true));
- // 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;
- }
+ return appConfig;
+ },
- 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) {
- if (err) {
- return cb(err);
+ /**
+ * 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();
+ },
+
+
+ /**
+ * 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.)
+ *
+ * @method getResourceVersions
+ * @param {object} filter 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;
+ 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;
+ }
+ }
}
- var temp = baseInstance;
- self._mergeRecursive(temp, instance);
- gotBase(temp, true);
- });
- } else {
- gotBase(this._cloneObj(instance), false);
- }
- },
-
-
- /**
- * 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);
- },
-
-
- /**
- * 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;
+ if (use) {
+ out.push(res);
}
- r = this._readConfigYCB(ctx, res.fsPath);
- this._mergeRecursive(routes, r);
}
- }
- if (!Object.keys(routes).length) {
- routes = this._cloneObj(this._fwConfig.defaultRoutes);
- }
- return routes;
- },
-
+ return out;
+ },
- /**
- * 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];
- },
-
- /**
- * 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;
+ /**
+ * 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.)
+ *
+ * @method getResources
+ * @param {string} env the runtime environment (either `client` or `server`)
+ * @param {object} ctx the context
+ * @param {object} filter 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;
+
+ this.validateContext(ctx);
+
+ posl = Y.JSON.stringify(this.selector.getPOSLFromContext(ctx));
+ 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;
+ 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;
}
- modules[res.yuiModuleName] = {
- fullpath: ('client' === env) ?
- res.staticHandlerURL :
- res.fsPath,
- requires: res.yuiModuleMeta.requires
- };
}
}
-
- // 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
- };
- }
+ if (use) {
+ out.push(res);
}
}
- }
+ return out;
+ },
- return {modules: modules};
- },
-
- /**
- * 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;
+ /**
+ * 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 = [];
+ for (mojitType in this._mojitRVs) {
+ if (this._mojitRVs.hasOwnProperty(mojitType)) {
+ if ('shared' !== mojitType) {
+ list.push(mojitType);
+ }
}
- modules[res.yuiModuleName] = {
- fullpath: ('client' === env) ?
- res.staticHandlerURL :
- res.fsPath,
- requires: res.yuiModuleMeta.requires
- };
}
- }
- return {modules: modules};
- },
+ return list;
+ },
- /**
- * Returns the YUI configuration object which tells YUI about the
- * YUI modules in the application (which aren't part of a mojit).
- *
- * @method getYuiConfigApp
- * @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;
+ /**
+ * Returns, via callback, the fully expanded mojit instance specification.
+ *
+ * @async
+ * @method getSpec
+ * @param {string} env the runtime environment (either `client` or `server`)
+ * @param {string} id the ID of the spec to return
+ * @param {object} ctx the runtime context for the spec
+ * @param {function(err,spec)} callback callback used to return the results (or error)
+ */
+ getSpec: function(env, id, ctx, callback) {
+ this.expandInstanceForEnv(env, {base: id}, ctx, function(err, obj) {
+ if (err) {
+ callback(err);
+ return;
}
- if ('mojito' === res.pkg.name) {
- continue;
+ if (env === 'client' && obj) {
+ delete obj.assets;
}
- modules[res.yuiModuleName] = {
- fullpath: ('client' === env) ?
- res.staticHandlerURL :
- res.fsPath,
- requires: res.yuiModuleMeta.requires
- };
- }
- }
- 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');
-
- 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];
- }
- }
-
- for (type in types) {
- if (types.hasOwnProperty(type)) {
- out.mojits[type] = {};
- this.getMojitTypeDetails('client', ctx, type, out.mojits[type]);
- }
- }
+ callback(null, obj);
+ });
+ },
+
+
+ /**
+ * Returns, via callback, the details of the mojit type.
+ *
+ * @async
+ * @method getType
+ * @param {string} env the runtime environment (either `client` or `server`)
+ * @param {string} type the mojit type
+ * @param {object} ctx the runtime context for the type
+ * @param {function(err,spec)} callback callback used to return the results (or error)
+ */
+ 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);
+ });
+ },
- out.routes = this.getRoutes(ctx);
- // these aren't needed on the client
- delete out.appConfig.mojitsDirs;
- delete out.appConfig.routesFiles;
- delete out.appConfig.specs;
+ /**
+ * This just calls `expandInstanceForEnv()` with `env` set to `server`.
+ *
+ * @async
+ * @method expandInstance
+ * @param {map} instance partial instance to expand
+ * @param {object} ctx the context
+ * @param {function(err,instance)} cb callback used to return the results (or error)
+ */
+ expandInstance: function(instance, ctx, cb) {
+ this.expandInstanceForEnv('server', instance, ctx, cb);
+ return;
+ },
- return out;
- },
+ /**
+ * Expands the instance into all details necessary to dispatch the mojit.
+ * @method expandInstanceForEnv
+ * @param {string} env the runtime environment (either `client` or `server`)
+ * @param {object} instance
+ * @param {object} ctx the context
+ * @param {function(err,instance)} cb 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],
+ spec,
+ typeDetails,
+ config;
- /**
- * 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 = [];
+ this.validateContext(ctx);
- for (mojitType in this._mojitMeta[env]) {
- if (this._mojitMeta[env].hasOwnProperty(mojitType)) {
- list.push(mojitType);
+ if (cacheValue) {
+ cb(null, Y.clone(cacheValue, true));
+ return;
}
- }
- return list;
- },
+ // TODO: should this be done here, or somewhere else?
+ ctx.runtime = env;
- /**
- * 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) {
+ try {
+ spec = this._expandSpec(env, ctx, instance);
+ } catch (err) {
+ return cb(err);
+ }
+ spec.config = spec.config || {};
+ spec.action = spec.action || 'index';
+ if (!spec.instanceId) {
+ spec.instanceId = Y.guid();
+ }
- var mojits,
- mojit,
- list = {};
+ spec.appConfig = this.getAppConfig(ctx);
+ delete spec.appConfig.specs;
- if (!ctx) {
- ctx = {};
- }
+ try {
+ this.getMojitTypeDetails(env, ctx, spec.type, spec);
+ } catch (err2) {
+ return cb(err2);
+ }
+ if (spec.defaults && spec.defaults.config) {
+ config = Y.clone(spec.defaults.config, true);
+ this.mergeRecursive(config, spec.config);
+ spec.config = config;
+ }
+
+ this._expandInstanceCache[env][cacheKey] = Y.clone(spec, true);
+ cb(null, spec);
+ },
+
+
+ /**
+ * Returns details about a mojit type.
+ *
+ * As the last step of execution, this fires the `getMojitTypeDetails`
+ * event so that Resource Store addons can augment the returned structure.
+ *
+ * @method getMojitTypeDetails
+ * @param {string} env the runtime environment (either `client` or `server`)
+ * @param {object} ctx the context
+ * @param {string} mojitType mojit type
+ * @param {object} dest 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 {object} args input arguments
+ * @param {string} args.env the runtime environment (either `client` or `server`)
+ * @param {object} args.ctx runtime context
+ * @param {array} args.posl priority-ordered seletor list
+ * @param {string} args.mojitType name of mojit
+ * @param {object} mojit the mojit type details
+ */
+ getMojitTypeDetails: function(env, ctx, mojitType, dest) {
+ //Y.log('getMojitTypeDetails('+env+', '+JSON.stringify(ctx)+', '+mojitType+')', 'debug', NAME);
+ var ress,
+ r,
+ res,
+ engine,
+ engines = {}, // view engines
+ posl = this.selector.getPOSLFromContext(ctx),
+ ctxKey,
+ module;
- mojits = this.listAllMojits(env);
+ this.validateContext(ctx);
- for (mojit in mojits) {
- if (mojits.hasOwnProperty(mojit)) {
- list[mojits[mojit]] =
- this.getMojitTypeDetails(env, ctx, mojits[mojit]);
+ if ('shared' === mojitType) {
+ throw new Error('Mojit name "shared" is special and isn\'t a real mojit.');
}
- }
- return list;
- },
-
-
- /**
- * 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);
- }
+ if (!dest) {
+ dest = {};
}
- }
- // 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;
- }
- }
+ if (!dest.assets) {
+ dest.assets = {};
+ }
+ if (!dest.models) {
+ dest.models = {};
+ }
+ if (!dest.views) {
+ dest.views = {};
}
- }
-
- return bestCtxKey ||
- (matchingKeys.length && matchingKeys[0]) ||
- null;
- },
-
-
- /**
- * 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)) {
+ dest.definition = {};
+ dest.defaults = {};
- res = ress[resid];
+ ress = this.getResources(env, ctx, { mojit: mojitType });
+ for (r = 0; r < ress.length; r += 1) {
+ res = ress[r];
- 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 === '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 === 'asset') {
- name = res.name;
if (env === 'client') {
- if (assumeRollups) {
- dest.assets[name] = res.rollupURL;
- } else {
- dest.assets[name] = res.staticHandlerURL;
- }
+ dest.assets[res.name + res.source.fs.ext] = res.url;
} else {
- dest.assets[name] = res.fsPath;
+ dest.assets[res.name + res.source.fs.ext] = res.source.fs.fullPath;
}
}
@@ -1126,57 +661,26 @@ ServerStore.prototype = {
if (!dest.views[res.name]) {
dest.views[res.name] = {};
}
- dest.views[res.name]['binder-url'] = res.staticHandlerURL;
+ dest.views[res.name]['binder-url'] = res.url;
if (env === 'client') {
- if (assumeRollups) {
- dest.views[res.name]['binder-path'] = res.rollupURL;
- } else {
- dest.views[res.name]['binder-path'] =
- res.staticHandlerURL;
- }
+ dest.views[res.name]['binder-path'] = res.url;
} 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;
+ 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
- dest.controllerModuleName = res.yuiModuleName;
if (env === 'client') {
- if (assumeRollups) {
- dest.controller = res.rollupURL;
- } else {
- dest.controller = res.staticHandlerURL;
- }
+ dest['controller-path'] = res.url;
} else {
- dest.controller = res.fsPath;
+ dest['controller-path'] = res.source.fs.fullPath;
}
}
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);
- }
+ dest.models[res.name] = true;
}
if (res.type === 'view') {
@@ -1184,3223 +688,1248 @@ ServerStore.prototype = {
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;
- }
+ dest.views[res.name]['content-path'] = res.url;
} else {
- dest.views[res.name]['content-path'] = res.fsPath;
+ 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;
}
+ }
- if (res.type === 'yui-lang') {
- dest.yui.langs[res.langCode] = res.yuiModuleName;
- }
+ // YUI AOP doesn't give plugins enough control, so use
+ // onHostMethod() and afterHostMethod().
+ this.fire('getMojitTypeDetails', {
+ args: {
+ env: env,
+ ctx: ctx,
+ posl: posl,
+ mojitType: mojitType
+ },
+ mojit: dest
+ });
+ return dest;
+ },
- 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];
- }
+ /**
+ * Returns the routes configured in the application.
+ * @method getRoutes
+ * @param {object} ctx 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 = this._libs.path.join(this._config.root, path);
}
+ fixedPaths[path] = true;
}
- }
- 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);
+
+ 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 = Y.clone(this.config.readConfigYCB(res.source.fs.fullPath, ctx), true);
+ out = Y.merge(out, routes);
}
}
- }
- // 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 (!Object.keys(out).length) {
+ return this._fwConfig.defaultRoutes;
+ }
+ return out;
+ },
+
+
+ /**
+ * 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 = {};
+ 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.rollupPath || res.source.fs.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 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)
+ *
+ * @method mergeRecursive
+ * @param {object} dest object to merge into
+ * @param {object} src object to merge onto "dest"
+ * @param {boolean} typeMatch 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;
+ },
- return dest;
- },
+ //====================================================================
+ // 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.
- /**
- * 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
- };
- },
+ /**
+ * Augments this resource store with addons that we know about.
+ * To find the addons, call `preloadResourceVersions()` first.
+ *
+ * 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}
+ */
+ loadAddons: function() {
+ var modules = {},
+ ress,
+ r,
+ res;
- /**
- * 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
+ 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
};
}
}
- }
- return rollups;
- },
+ this._yuiUseSync(modules);
+ Y.Object.each(Y.mojito.addons.rs, function(fn, name) {
+ this.plug(fn, { appRoot: this._config.root, mojitoRoot: this._config.mojitoRoot });
+ }, this);
+ },
- /**
- * 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;
- }
+ /**
+ * 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);
+ *
+ * @method preloadResourceVersions
+ * @return {nothing}
+ */
+ preloadResourceVersions: function() {
+ var me = this,
+ walker,
+ walkedMojito = false,
+ dir,
+ info;
+
+ this.selectors = {};
+ this._appRVs = [];
+ this._mojitRVs = {};
+
+ walker = new this._libs.walker.BreadthFirst(this._config.root);
+ walker.walk(function(err, info) {
+ if (err) {
+ throw err;
+ }
+ if ('mojito' === info.pkg.name) {
+ walkedMojito = true;
+ }
+ me._preloadPackage(info);
+ });
- 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;
+ // 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 = this._libs.path.join(this._config.mojitoRoot, '..');
+ info = {
+ depth: 999,
+ parents: [],
+ dir: dir
+ };
+ info.pkg = this.config.readConfigJSON(this._libs.path.join(dir, 'package.json'));
+
+ 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: mojitoVersion,
+ yahoo: {
+ mojito: {
+ type: 'bundle',
+ location: 'app'
}
}
- 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);
- }
+ this._preloadPackage(info);
+ }
+ },
+
+
+ /**
+ * 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('findResourceVersionByConvention', this._myFindResourceByConvention, this);
+ *
+ * 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 findResourceVersionByConvention
+ * @param {object} source the same as the `source` part of a resource
+ * @param {string} mojitType 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:
+ * 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
+ */
+ findResourceVersionByConvention: function(source, mojitType) {
+ var fs = source.fs,
+ baseParts = fs.basename.split('.'),
+ type;
- return config;
- },
+ 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;
+ }
+ if ('spec' === type && '.json' !== 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
+ };
+ }
- /**
- * 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;
+ // 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
+ };
+ }
- if (!libpath.existsSync(path)) {
- path = libpath.join(mojitoRoot, 'dimensions.json');
- }
+ // 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
+ };
+ }
- dims = this._readConfigJSON(path);
- if (!this._isValidYcbDimensions(dims)) {
- throw new Error('Invalid dimensions.json: ' + path);
- }
- return dims;
- },
+ // 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;
+ }
- /**
- * 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;
+ // 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('parseResourceVersion', this._myParseResource, this);
+ *
+ * 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 parseResourceVersion
+ * @param {object} source the same as the `source` part of a resource
+ * @param {string} type the resource type of the file
+ * @param {string} subtype the optional resource subtype of the file
+ * @param {string} mojitType the name of the mojit
+ * @return {object|undefined} the resource version
+ */
+ parseResourceVersion: function(source, type, subtype, mojitType) {
+ var fs = source.fs,
+ baseParts = fs.basename.split('.'),
+ res;
- return isArray(dims) &&
- dims.length === 1 &&
- isArray(dims[0].dimensions) &&
- dims[0].dimensions.length > 0;
- },
+ // 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: DEFAULT_AFFINITIES[type],
+ 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: DEFAULT_AFFINITIES[type],
+ 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 = 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]) {
+ res.mojit = null;
+ }
+ return res;
+ }
- /**
- * 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;
+ // mojit parts with format {name}.{selector}
+ if ('asset' === type || 'binder' === type) {
+ res = {
+ source: source,
+ mojit: mojitType,
+ type: type,
+ subtype: subtype,
+ affinity: DEFAULT_AFFINITIES[type],
+ 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 = this._libs.path.join(fs.subDirArray.join('/'), baseParts.join('.'));
+ res.id = [res.type, res.subtype, res.name].join('-');
+ return res;
}
- if ('mojito' === info.pkg.name) {
- walkedMojito = true;
+
+ // special case: spec
+ if ('spec' === type) {
+ res = {
+ source: source,
+ mojit: mojitType,
+ type: 'spec',
+ affinity: DEFAULT_AFFINITIES[type],
+ selector: '*'
+ };
+ if (baseParts.length !== 1) {
+ Y.log('invalid spec filename. skipping ' + source.fs.fullPath, 'warn', NAME);
+ return;
+ }
+ res.name = this._libs.path.join(source.fs.subDir, baseParts.join('.'));
+ res.id = [res.type, res.subtype, res.name].join('-');
+ return res;
}
- 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'
- }
- }
+
+ // special case: view
+ if ('view' === type) {
+ res = {
+ source: source,
+ mojit: mojitType,
+ type: type,
+ subtype: subtype,
+ view: {
+ outputFormat: fs.ext.substr(1),
+ engine: baseParts.pop()
+ },
+ affinity: DEFAULT_AFFINITIES[type],
+ 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 = this._libs.path.join(fs.subDirArray.join('/'), baseParts.join('.'));
+ res.id = [res.type, res.subtype, res.name].join('-');
+ return res;
}
- this._preloadPackage(info);
- }
- },
+ // 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('parseResourceVersion', this._myParseResource, this);
+ *
+ * @method addResourceVersion
+ * @param {object} res the resource version
+ * @return {nothing}
+ */
+ addResourceVersion: function(res) {
+ res.affinity = new Affinity(res.affinity);
+ if (this._appConfigStatic.deferAllOptionalAutoloads &&
+ 'optional' === res.affinity.type) {
+ return;
+ }
- /**
- * 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 (res.selector) {
+ this.selectors[res.selector] = true;
}
- if (!libpath.existsSync(path)) {
- logger.log('missing routes file. skipping ' + path, 'warn', NAME);
- continue;
+ if (res.mojit) {
+ if (!this._mojitRVs[res.mojit]) {
+ this._mojitRVs[res.mojit] = [];
+ }
+ this._mojitRVs[res.mojit].push(res);
+ } else {
+ this._appRVs.push(res);
}
- 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);
- }
+ /**
+ * 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}
+ */
+ /**
+ * Fired after the resources for a mojit have been resolved.
+ * @event mojitResourcesResolved
+ * @param {string} env the runtime environment (either `client` or `server`)
+ * @param {array} posl priority-ordered seletor list
+ * @param {string} mojit name of the mojit
+ * @param {array} ress list of resources in the mojit (for the `env` and `posl`)
+ */
+ resolveResourceVersions: function() {
+ var p, poslKey, posl, posls = {},
+ e, env, envs = [ 'client', 'server' ],
+ affinities, selectors, sourceBase,
+ type, ress,
+ s;
+
+ posls = this.selector.getAllPOSLs();
+
+ for (e = 0; e < envs.length; e += 1) {
+ env = envs[e];
+
+ affinities = {}; // affinity: priority modifier
+ affinities[env] = 1;
+ affinities.common = 0;
+
+ 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 ]);
- // 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);
+ 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
+ });
+ }
+ }
+ }
}
- }
- },
+ },
- /**
- * 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-- ' + Y.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;
- }
- },
+ /**
+ * 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 {object} ctx 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;
- /**
- * 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;
+ out.routes = this.getRoutes(ctx);
- // app-level
- case 'archetype':
- case 'command':
- case 'middleware':
- this._preloadResource(res, null);
- break;
+ return out;
+ },
- // 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
- },
+ //====================================================================
+ // PRIVATE METHODS
- /**
- * @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;
+ /**
+ * Used for unit testing.
+ * @private
+ * @method _mockLib
+ * @param {string} name name of library to mock out
+ * @param {situation-dependent} lib library to mock out
+ * @return {nothing}
+ */
+ _mockLib: function(name, lib) {
+ this._libs[name] = lib;
+ },
- // 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;
- },
+ /**
+ * @private
+ * @method @parseValidDims
+ * @param {object} dims 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];
+ for (dimName in dim) {
+ if (dim.hasOwnProperty(dimName)) {
+ out[dimName] = {};
+ grabKeys(dimName, dim[dimName]);
+ }
+ }
+ }
+ return out;
+ },
- /**
- * @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;
+ /**
+ * Applies spec inheritance by following the `base` and merging up the
+ * results.
+ * @private
+ * @method _expandSpec
+ * @param {string} env the runtime environment (either `client` or `server`)
+ * @param {object} ctx runtime context
+ * @param {object} spec spec to expand
+ * @return {object} expanded sped
+ */
+ // FUTURE: expose this to RS addons?
+ _expandSpec: function(env, ctx, spec) {
+ var appConfig,
+ base,
+ specParts,
+ mojitName,
+ specName,
+ ress;
- // 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);
+ if (!spec.base) {
+ return spec;
+ }
- res.name = libpath.join(dir, file);
- res.type = 'command';
- res.id = 'command-' + res.name;
- return res;
- },
+ // 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
+ 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);
+ }
+ }
+ if (!base) {
+ throw new Error('Unknown base of "' + spec.base + '"');
+ }
- /**
- * @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;
+ delete spec.base;
+ return this.mergeRecursive(this._expandSpec(env, ctx, base), spec);
+ },
- // 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;
- },
+ /**
+ * preloads metadata about resources in a package
+ * (but not subpackages in its `node_modules/`)
+ *
+ * @private
+ * @method _preloadPackage
+ * @param {object} info metadata about the package
+ * @return {nothing}
+ */
+ _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-- ' + Y.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 = this._libs.path.join(info.dir, info.pkg.yahoo.mojito.location);
+ this._preloadDirBundle(dir, pkg);
+ break;
+ case 'mojit':
+ dir = this._libs.path.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;
+ }
+ },
- /**
- * @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
- };
+ /**
+ * preloads metadata about resources in the application directory
+ * (but not `node_modules/`)
+ *
+ * @private
+ * @method _preloadApp
+ * @param {object} pkg metadata (name and version) about the app's package
+ * @return {nothing}
+ */
+ _preloadApp: function(pkg) {
+ var ress,
+ r,
+ res,
+ list,
+ i;
- 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;
- },
+ 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);
+ }
- /**
- * @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.sync(dir, {});
- 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.sync(dir, {});
- 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 = Y.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 = Y.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);
+ // 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);
}
- 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;
+ /**
+ * preloads metadata about resources in a directory
+ *
+ * @private
+ * @method _preloadDirBundle
+ * @param {string} dir directory path
+ * @param {object} pkg metadata (name and version) about the package
+ * @return {nothing}
+ */
+ _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(this._libs.path.join(dir, 'mojits'), 'bundle', pkg);
+ },
+
+
+ /**
+ * preloads a directory containing many mojits
+ *
+ * @private
+ * @method _preloadDirMojits
+ * @param {string} dir directory path
+ * @param {string} dirType type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit"
+ * @param {object} pkg metadata (name and version) about the package
+ * @return {nothing}
+ */
+ _preloadDirMojits: function(dir, dirType, pkg) {
+ var i,
+ realDirs,
+ children,
+ childName,
+ childPath;
- if (!this._libs.path.existsSync(path)) {
- return {};
- }
- if (ycb) {
- return this._readConfigYCB({}, path);
- }
- try {
- contents = this._libs.fs.readFileSync(path, 'utf-8');
- json = Y.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;
+ if ('/' !== dir.charAt(0)) {
+ dir = this._libs.path.join(this._config.root, dir);
}
- }
- },
-
-
- /**
- * 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;
+ if (!this._libs.path.existsSync(dir)) {
+ return;
}
- // 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;
- },
+ children = this._sortedReaddirSync(dir);
+ for (i = 0; i < children.length; i += 1) {
+ childName = children[i];
+ if ('.' === childName.substring(0, 1)) {
+ continue;
+ }
+ childPath = this._libs.path.join(dir, childName);
+ this._preloadDirMojit(childPath, dirType, pkg);
+ }
+ },
- /**
- * 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;
- }
- }
+ /**
+ * preloads a directory that represents a single mojit
+ *
+ * @private
+ * @method _preloadDirMojit
+ * @param {string} dir directory path
+ * @param {string} dirType type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit"
+ * @param {object} pkg metadata (name and version) about the package
+ * @return {nothing}
+ */
+ _preloadDirMojit: function(dir, dirType, pkg) {
+ var mojitType,
+ packageJson,
+ definitionJson,
+ ress,
+ r,
+ res;
- 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._appMeta[env].contexts) {
- this._appMeta[env].contexts = {};
- }
- 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];
- }
- }
- }
- }
+ if ('/' !== dir.charAt(0)) {
+ dir = this._libs.path.join(this._config.root, dir);
}
- }
- 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._sharedMeta[env].contexts) {
- this._sharedMeta[env].contexts = {};
- }
- 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];
- }
- }
- }
- }
+ if (!this._libs.path.existsSync(dir)) {
+ return;
}
- }
-
- 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;
+ if ('pkg' === dirType) {
+ mojitType = pkg.name;
+ } else {
+ mojitType = this._libs.path.basename(dir);
}
- for (a = 0; a < affinities.length; a += 1) {
- affinity = affinities[a];
- if (!src[affinity]) {
- continue;
+ packageJson = this.config.readConfigJSON(this._libs.path.join(dir, 'package.json'));
+ if (packageJson) {
+ if (packageJson.name) {
+ mojitType = packageJson.name;
}
- 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 (packageJson.engines && packageJson.engines.mojito) {
+ if (!this._libs.semver.satisfies(mojitoVersion, packageJson.engines.mojito)) {
+ Y.log('skipping mojit because of version check ' + dir, 'warn', NAME);
+ return;
}
}
- }
- }
- 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] = {};
+ // 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)) {
-
- 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
-
- // log warning if server mojit has dom dependency
- Y.Object.each(this._mojitYuiRequired.server, function(val, mojit) {
- var deps = (val['*'] && val['*'].join()) || '',
- badre = /\b(dom-\w+|node-\w+|io-upload-iframe)/g,
- isbad = deps.match(badre);
-
- if (isbad) {
- logger.log('your mojit "' + mojit + '" has a server affinity and these client-related deps: ' + isbad.join(', '), 'WARN', NAME);
- logger.log('Mojito may be unable to start, unless you have provided server-side DOM/host-object suppport', 'WARN', NAME);
+ definitionJson = this.config.readConfigYCB(this._libs.path.join(dir, 'definition.json'), {});
+ if (definitionJson.appLevel) {
+ mojitType = 'shared';
}
- });
-
- },
-
-
- // 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;
+
+ // the mojit itself is registered as an app-level resource
+ res = {
+ source: {
+ fs: {
+ fullPath: dir,
+ rootDir: dir,
+ rootType: dirType,
+ subDir: '.',
+ subDirArray: ['.'],
+ basename: this._libs.path.basename(dir),
+ isFile: false,
+ ext: null
+ },
+ pkg: pkg
+ },
+ type: 'mojit',
+ 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?
+ }
+ },
+
+
+ /**
+ * Resolves versions for a list of resources.
+ * The priority is based on passed-in configuration.
+ * See `resolveResourceVersions()` for details.
+ *
+ * @private
+ * @method _resolveVersions
+ * @param {object} affinities lookup hash for priority adjustment for each affinity
+ * @param {object} selectors lookup hash for priority adjustment for each selector
+ * @param {int} sourceBase multiplier for order in source list
+ * @param {array of arrays} srcs 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,
+ chosen;
+
+ 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.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) {
+ if (!affinities.hasOwnProperty(res.affinity)) {
continue;
}
- if (!res.sharedMojit) {
- dest.required[res.yuiModuleName] = true;
+ 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] = {};
}
- dest.modules[res.yuiModuleName] = {
- requires: res.yuiModuleMeta.requires,
- fullpath: (('client' === env) ?
- res.staticHandlerURL :
- res.fsPath)
- };
- if (('controller' === res.type)) {
- dest.controller = res;
+ if (!versions[res.id][priority]) {
+ 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;
- }
- }
- 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;
-
- //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) {
- continue;
+ for (resid in versions) {
+ if (versions.hasOwnProperty(resid)) {
+ highest = Math.max.apply(Math, Object.keys(versions[resid]));
+ //console.log('--DEBUG-- highest=' + highest + ' -- ' + resid);
+ chosen = Y.clone(versions[resid][highest], true);
+ out.push(chosen);
}
- sortedPaths[module] = info.fullpath || loader._url(info.path);
}
- }
- return { sorted: loader.sorted, paths: sortedPaths };
- },
+ return out;
+ },
- /**
- * 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.
+ /**
+ * Finds resources based on our conventions.
+ * -Doesn't- load mojits or their contents. That's done elsewhere.
+ *
+ * @private
+ * @method _findResourcesByConvention
+ * @param {string} dir directory from which to find resources
+ * @param {string} dirType type represented by the "dir" argument. values are "app", "bundle", "pkg", or "mojit"
+ * @param {object} pkg metadata (name and version) about the package
+ * @param {string|null} mojitType name of mojit to which the resource belongs
+ * @return {array} list of resources
*/
- // 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;
- }
+ _findResourcesByConvention: function(dir, dirType, pkg, mojitType) {
+ var me = this,
+ ress = [];
+ //console.log('-- FIND RESOURCES BY CONVENTION -- ' + pkg.name + '@' + pkg.version + ' -- ' + mojitType);
- if (!config.hasOwnProperty('prefix')) {
- prefix = 'static';
- }
- if (prefix) {
- parts.push(prefix);
- rollupParts.push(prefix);
- }
+ this._walkDirRecursive(dir, function(error, subdir, file, isFile) {
+ var source, ret, res;
- 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');
+ if ('node_modules' === file) {
+ return false;
}
- } else {
- parts.push(appName);
- if (config.useRollups && res.yuiModuleName) {
- rollupParts.push(appName);
- rollupFsPath = libpath.join(this._root, 'rollup.client.js');
+ if ('libs' === file && 'test' !== me._appConfigStatic.env) {
+ return false;
}
- }
- } 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');
- }
- }
-
- // 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 || {};
- }
+ if ('tests' === file && 'test' !== me._appConfigStatic.env) {
+ return false;
}
- };
-
- 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);
- }
- },
-
-
- /**
- * 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);
- },
+ source = {
+ fs: {
+ fullPath: me._libs.path.join(dir, subdir, file),
+ rootDir: dir,
+ rootType: dirType,
+ subDir: subdir,
+ subDirArray: subdir.split('/'),
+ isFile: isFile,
+ ext: me._libs.path.extname(file)
+ },
+ pkg: pkg
+ };
+ source.fs.basename = me._libs.path.basename(file, source.fs.ext);
- /**
- * 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 (me._skipBadPath(source.fs)) {
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];
- }
- }
- }
- for (ctxKey in src.contexts) {
- if (src.contexts.hasOwnProperty(ctxKey)) {
- if ('*' === ctxKey) {
- continue;
- }
- if (!this._matchContext(ctx, src.contexts[ctxKey])) {
- continue;
- }
- for (resid in src[ctxKey]) {
- if (src[ctxKey].hasOwnProperty(resid)) {
- list[resid] = src[ctxKey][resid];
+ 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.parseResourceVersion(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 {object} pathParts 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 {string} path directory to read
+ * @return {array} files in the directory
+ */
+ _sortedReaddirSync: function(path) {
+ var out = this._libs.fs.readdirSync(path);
+ return out.sort();
+ },
+
+
+ /**
+ * Recursively walks a directory
+ * @private
+ * @method _walkDirRecursive
+ * @param {string} dir directory to start at
+ * @param {function(error, subdir, name, isFile)} cb callback called for each file
+ * @param {string} _subdir INTERNAL argument for recursion, please ignore
+ */
+ _walkDirRecursive: function(dir, cb, _subdir) {
+ var subdir,
+ fulldir,
+ children,
+ i,
+ childName,
+ childPath,
+ childFullPath,
+ childStat;
+
+ subdir = _subdir || '.';
+ fulldir = this._libs.path.join(dir, subdir);
+ if (!this._libs.path.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 = this._libs.path.join(subdir, childName);
+ childFullPath = this._libs.path.join(dir, childPath);
+ try {
+ childStat = this._libs.fs.statSync(childFullPath);
+ } catch (e) {
+ Y.log('invalid file. skipping ' + childFullPath, 'warn', NAME);
+ continue;
}
- }
- }
-
- 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];
- }
- } else {
- dest[p] = src[p];
+ if (childStat.isFile()) {
+ cb(null, subdir, childName, true);
+ } else if (childStat.isDirectory()) {
+ if (cb(null, subdir, childName, false)) {
+ this._walkDirRecursive(dir, cb, childPath);
}
}
}
- }
- 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;
- }
-
- 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;
- },
+ },
- /**
- * 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;
+ /**
+ * Takes a list of globs and turns it into a list of matching paths.
+ * @private
+ * @method _globList
+ * @param {string} prefix prefix for every path in the list
+ * @param {array} list 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 = this._libs.path.join(prefix, glob);
+ }
+ found = found.concat(this._libs.glob.sync(glob, {}));
+ }
+ return found;
+ },
+
+
+ /**
+ * Augments this resource store's Y object with the specified YUI modules.
+ * @private
+ * @method _yuiUseSync
+ * @param {object} modules 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 });
}
- 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 = 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);
- }
- }
- }
- }
+ });
-};
+ Y.namespace('mojito');
+ Y.mojito.ResourceStore = ResourceStore;
-module.exports = ServerStore;
+}, '0.0.1', { requires: [
+ 'base',
+ 'json-stringify',
+ 'oop'
+]});
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 4e2cd08ac..61bcdc976 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() {
@@ -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;
@@ -50,11 +51,14 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) {
serializeClientStore: function() {
return 'clientstore';
},
- store: { _fwConfig: { ondemandBaseYuiModules:[] } },
- getYuiConfigFw: function() { return {}; },
- getYuiConfigApp: function() { return {}; },
- fileFromStaticHandlerURL: function() {
- return 'path';
+ store: {
+ getAllURLs: function() { return {}; },
+ getFrameworkConfig: function() {
+ return { ondemandBaseYuiModules:[] };
+ },
+ yui: {
+ getConfigShared: function() { return {}; }
+ }
}
});
@@ -92,6 +96,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;
@@ -130,11 +135,14 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) {
serializeClientStore: function() {
return 'clientstore';
},
- store: { _fwConfig: { ondemandBaseYuiModules:[] } },
- getYuiConfigFw: function() { return {}; },
- getYuiConfigApp: function() { return {}; },
- fileFromStaticHandlerURL: function() {
- return 'path';
+ store: {
+ getAllURLs: function() { return {}; },
+ getFrameworkConfig: function() {
+ return { ondemandBaseYuiModules:[] };
+ },
+ yui: {
+ getConfigShared: function() { return {}; }
+ }
}
});
@@ -179,6 +187,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;
@@ -218,11 +227,14 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) {
serializeClientStore: function() {
return 'clientstore';
},
- store: { _fwConfig: { ondemandBaseYuiModules:[] } },
- getYuiConfigFw: function() { return {}; },
- getYuiConfigApp: function() { return {}; },
- fileFromStaticHandlerURL: function() {
- return 'path';
+ store: {
+ getAllURLs: function() { return {}; },
+ getFrameworkConfig: function() {
+ return { ondemandBaseYuiModules:[] };
+ },
+ yui: {
+ getConfigShared: function() { return {}; }
+ }
}
});
@@ -258,6 +270,7 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) {
A.areSame("'*'", YUI_use);
},
+
'dependencyCalculations ondemand': function() {
var calledYuiModules;
var realLoader = Y.mojito.Loader;
@@ -297,11 +310,14 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) {
serializeClientStore: function() {
return 'clientstore';
},
- store: { _fwConfig: { ondemandBaseYuiModules:[] } },
- getYuiConfigFw: function() { return {}; },
- getYuiConfigApp: function() { return {}; },
- fileFromStaticHandlerURL: function() {
- return 'path';
+ store: {
+ getAllURLs: function() { return {}; },
+ getFrameworkConfig: function() {
+ return { ondemandBaseYuiModules:[] };
+ },
+ yui: {
+ getConfigShared: function() { return {}; }
+ }
}
});
@@ -337,6 +353,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;
@@ -376,11 +393,14 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) {
serializeClientStore: function() {
return 'clientstore';
},
- store: { _fwConfig: { ondemandBaseYuiModules:[] } },
- getYuiConfigFw: function() { return {}; },
- getYuiConfigApp: function() { return {}; },
- fileFromStaticHandlerURL: function() {
- return 'path';
+ store: {
+ getAllURLs: function() { return {}; },
+ getFrameworkConfig: function() {
+ return { ondemandBaseYuiModules:[] };
+ },
+ yui: {
+ getConfigShared: function() { return {}; }
+ }
}
});
@@ -420,7 +440,7 @@ YUI.add('mojito-deploy-addon-tests', function(Y, NAME) {
}));
YUITest.TestRunner.add(suite);
-
+
}, '0.0.1', {requires: [
'mojito-deploy-addon',
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/addons/rs/config-tests.server.js b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js
new file mode 100644
index 000000000..c08798a64
--- /dev/null
+++ b/source/lib/tests/autoload/app/addons/rs/config-tests.server.js
@@ -0,0 +1,307 @@
+/*
+ * 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 || {};
+ },
+
+ validateContext: function() {
+ },
+
+ 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;
+ },
+
+ findResourceVersionByConvention: function(source, mojitType) {
+ // no-op
+ },
+
+ parseResourceVersion: 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 Y.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');
+ 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.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.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.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.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.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.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.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.findResourceVersionByConvention(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.parseResourceVersion(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.parseResourceVersion(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);
+ },
+
+
+ '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"
+ }
+ },
+ "selector": "shelves",
+ "pathos": "portended"
+ };
+ cmp(have, want);
+ },
+
+
+ '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);
+ }
+
+
+ }));
+
+ YUITest.TestRunner.add(suite);
+
+}, '0.0.1', {requires: ['base', 'oop', 'addon-rs-config', 'json-parse']});
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..6ba1bff2c
--- /dev/null
+++ b/source/lib/tests/autoload/app/addons/rs/selector-tests.server.js
@@ -0,0 +1,172 @@
+/*
+ * 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 || {};
+ this.selectors = {
+ 'devdroid': true,
+ 'droid': true,
+ 'shelves': true,
+ 'right': true,
+ '*': true
+ };
+ },
+ validateContext: function() {},
+ 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.getPOSLFromContext({});
+ var want = ['*'];
+ cmp(have, want);
+
+ var have = store.selector.getPOSLFromContext({runtime:'client'});
+ var want = ['right', '*'];
+ cmp(have, want);
+
+ var have = store.selector.getPOSLFromContext({runtime:'server'});
+ var want = ['shelves', '*'];
+ cmp(have, want);
+
+ var have = store.selector.getPOSLFromContext({device:'android'});
+ var want = ['droid', '*'];
+ cmp(have, want);
+
+ var have = store.selector.getPOSLFromContext({runtime:'server', device:'android'});
+ var want = ['shelves', 'droid', '*'];
+ cmp(have, want);
+
+ var have = store.selector.getPOSLFromContext({device:'android', environment:'dev'});
+ var want = ['devdroid', 'droid', '*'];
+ cmp(have, want);
+
+ 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);
+ },
+
+
+ 'list used dimensions': 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._listUsedDimensions();
+ var want = {
+ runtime: ['server', 'client'],
+ device: ['iphone', 'android'],
+ environment: ['dev']
+ }
+ cmp(want, have);
+ },
+
+
+ 'list used contexts': 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._listUsedContexts();
+ var want = [
+ { runtime: 'server', device: 'iphone', environment: 'dev' },
+ { runtime: 'server', device: 'iphone' },
+ { runtime: 'server', device: 'android', environment: 'dev' },
+ { runtime: 'server', device: 'android' },
+ { runtime: 'server', environment: 'dev' },
+ { runtime: 'server' },
+ { runtime: 'client', device: 'iphone', environment: 'dev' },
+ { runtime: 'client', device: 'iphone' },
+ { runtime: 'client', device: 'android', environment: 'dev' },
+ { runtime: 'client', device: 'android' },
+ { runtime: 'client', environment: 'dev' },
+ { runtime: 'client' }
+ ];
+ cmp(want, have);
+ }
+
+
+ }));
+
+ YUITest.TestRunner.add(suite);
+
+}, '0.0.1', {requires: [
+ 'base',
+ 'oop',
+ 'addon-rs-config',
+ 'addon-rs-selector'
+]});
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..c843ac040
--- /dev/null
+++ b/source/lib/tests/autoload/app/addons/rs/url-tests.server.js
@@ -0,0 +1,293 @@
+/*
+ * 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'),
+ 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 = {};
+ this.publish('getMojitTypeDetails', {emitFacade: true, preventable: false});
+ this.config = {
+ readConfigJSON: function() { return {} }
+ };
+ },
+
+ 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
+ },
+
+ _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');
+ }
+
+
+ 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);
+ },
+
+
+ 'augment getMojitTypeDetails': function() {
+ var fixtures = libpath.join(__dirname, '../../../../fixtures/store');
+
+ var store = new MockRS({
+ 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', {
+ args: {
+ env: 'server',
+ ctx: {},
+ mojitType: 'Foo'
+ },
+ mojit: mojit
+ });
+ A.areSame('TEST/assets', mojit.assetsRoot);
+ }
+
+
+ }));
+
+ YUITest.TestRunner.add(suite);
+
+}, '0.0.1', {requires: ['base', 'oop', 'addon-rs-url']});
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..a3a6bc3bc
--- /dev/null
+++ b/source/lib/tests/autoload/app/addons/rs/yui-tests.server.js
@@ -0,0 +1,771 @@
+/*
+ * 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,
+ 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.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});
+ 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 = [],
+ r,
+ res,
+ k,
+ use;
+
+ ctx = Y.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;
+ },
+
+ findResourceVersionByConvention: function(source, mojitType) {
+ // no-op
+ },
+
+ parseResourceVersion: function(source, type, subtype, mojitType) {
+ // no-op
+ },
+
+ addResourceVersion: function(res) {
+ this.RVs[[res.affinity, res.selector, res.id].join('/')] = res;
+ },
+
+ _makeResource: function(env, ctx, mojit, type, name, yuiName, pkgName) {
+ if (mojit && mojit !== 'shared') {
+ this._mojits[mojit] = true;
+ }
+ var res = {
+ source: {
+ fs: {
+ fullPath: 'path/for/' + type + '--' + name + '.common.ext',
+ rootDir: 'path/for'
+ },
+ pkg: { name: (pkgName || 'testing') }
+ },
+ mojit: mojit,
+ type: type,
+ name: name,
+ id: type + '--' + name
+ }
+ if (yuiName) {
+ res.yui = { name: yuiName };
+ }
+ ctx = Y.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);
+ }
+ }
+
+ });
+
+
+ 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.findResourceVersionByConvention(source, null);
+ var want = undefined;
+ cmp(have, want);
+
+ source = makeSource(fixtures, 'app', 'blix', 'x.server.js', true);
+ have = store.findResourceVersionByConvention(source, null);
+ want = undefined;
+ cmp(have, want);
+
+ source = makeSource(fixtures, 'app', 'autoload', 'x.server.js', true);
+ 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.findResourceVersionByConvention(source, null);
+ want = { type: 'yui-module', skipSubdirParts: 1 };
+ cmp(have, want);
+
+ source = makeSource(fixtures, 'app', 'lang', 'x.server.js', true);
+ have = store.findResourceVersionByConvention(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.parseResourceVersion(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.parseResourceVersion(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.parseResourceVersion(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.parseResourceVersion(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.parseResourceVersion(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.parseResourceVersion(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);
+
+ 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);
+ },
+
+
+ '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']);
+ },
+
+
+ '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, mojitoRoot: mojitoRoot });
+ 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-AT' };
+ spec = { type: 'PagedFlickr' };
+ store.expandInstance(spec, ctx, function(err, instance) {
+ 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: '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:tr-TR}'
+ );
+ A.isNotUndefined(instance.yui.sortedPaths['lang/PagedFlickr_en-US'], 'en-US is undefined {lang:tr-TR}');
+
+ // 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));
+ });
+ },
+
+
+ 'get config shared': function() {
+ var fixtures,
+ store,
+ config;
+ fixtures = libpath.join(__dirname, '../../../../fixtures/store');
+ store = new MockRS({ root: fixtures });
+ store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } );
+
+ store._makeResource('server', {}, 'shared', 'binder', 'index', 'FooBinderIndex');
+ store._makeResource('server', {}, 'shared', 'binder', 'list', 'FooBinderList', 'mojito');
+ store._makeResource('server', {}, 'Foo', 'controller', 'controller', 'FooController');
+
+ config = store.yui.getConfigShared('server', {}, false);
+ A.isNotUndefined(config.modules);
+ A.isNotUndefined(config.modules.FooBinderIndex);
+ A.isNotUndefined(config.modules.FooBinderList);
+ A.isUndefined(config.modules.FooController);
+
+ config = store.yui.getConfigShared('server', {}, true);
+ A.isNotUndefined(config.modules);
+ A.isNotUndefined(config.modules.FooBinderIndex);
+ A.isUndefined(config.modules.FooBinderList);
+ A.isUndefined(config.modules.FooController);
+ },
+
+
+ 'get config all mojits': function() {
+ var fixtures,
+ store,
+ config;
+ fixtures = libpath.join(__dirname, '../../../../fixtures/store');
+ store = new MockRS({ root: fixtures });
+ store.plug(Y.mojito.addons.rs.yui, { appRoot: fixtures, mojitoRoot: mojitoRoot } );
+
+ store._makeResource('server', {}, 'shared', 'binder', 'index', 'FooBinderIndex');
+ store._makeResource('server', {}, 'Foo', 'binder', 'list', 'FooBinderList', 'mojito');
+ store._makeResource('server', {}, 'Bar', 'controller', 'controller', 'BarController');
+
+ config = store.yui.getConfigAllMojits('server', {}, false);
+ A.isNotUndefined(config.modules);
+ A.isUndefined(config.modules.FooBinderIndex);
+ A.isNotUndefined(config.modules.FooBinderList);
+ A.isNotUndefined(config.modules.BarController);
+ }
+
+
+ }));
+
+ YUITest.TestRunner.add(suite);
+
+}, '0.0.1', {requires: [
+ 'base',
+ 'oop',
+ 'mojito-resource-store',
+ 'addon-rs-yui',
+ 'json-stringify'
+]});
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 fe883349a..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,7 +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 = require(path.join(__dirname, '../../../../store.server')),
+ resourceStore,
dummyLog = {log: function() {}},
A = YUITest.Assert;
@@ -16,47 +16,26 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) {
name: 'Resource Store Adapter Tests',
- 'pre load': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
+ init: function() {
+ resourceStore = new Y.mojito.ResourceStore({ root: fixtures });
resourceStore.preload();
+ },
- store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog);
-
- //Y.log(Y.JSON.stringify(store,null,4));
-
+ 'pre load': function() {
+ var store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog);
A.isTrue(store.getAppPath() === fixtures);
},
'server app config value': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- resourceStore.preload();
-
- store = Y.mojito.ResourceStoreAdapter.init('server', resourceStore, dummyLog);
-
- var config = store.getAppConfig(null, 'application');
-
+ 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 = new ResourceStore(fixtures),
- store;
-
- 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');
A.isTrue(instance.type === 'test_mojit_1');
A.isTrue(instance.config.testKey4 === 'testVal4');
@@ -64,18 +43,9 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) {
},
'server mojit config value via type': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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');
A.isTrue(instance.config.testKey4 === 'testVal4');
A.isTrue(instance.config.testKey6.testKey7 === 'testVal7');
@@ -83,21 +53,12 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) {
},
'server mojit config value via type & overrride': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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'}
};
-
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,220 +66,92 @@ YUI.add('mojito-resource-store-adapter-tests', function(Y, NAME) {
},
'server mojit config assets': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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);
A.isTrue(instance.assets['js/main.js'] !== undefined);
});
},
'server mojit config views': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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);
A.isTrue(instance.views['test_2'] !== undefined);
});
},
'server mojit config models': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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);
A.isTrue(instance.models['test_2'] !== undefined);
});
},
'server mojit config actions': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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.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']);
});
},
'server mojit config appConfig': function() {
-
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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:{
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);
});
},
'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),
- store;
-
- 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: {
'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),
- store;
-
- 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);
+ A.isNotUndefined(instance['controller-path']);
A.areSame('/static/TestMojit2/assets', instance.assetsRoot);
A.isNotUndefined(instance.yui.config.modules.test_mojit_2);
});
},
- 'server mojit is NOT loaded becuase of pacakge mojito version miss-match': function(){
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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),
- store;
-
- 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 = new ResourceStore(fixtures),
- store;
-
- 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),
- store;
-
- 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),
- store;
-
- 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),
- store;
-
- 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());
@@ -326,72 +159,24 @@ 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),
- store;
-
- 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());
});
},
- 'server mojit view index1.mu.html is loaded correctly': function(){
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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 = new ResourceStore(fixtures),
- store;
-
- 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 = new ResourceStore(fixtures),
- store;
-
- 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');
});
},
'test getType()': function(){
- var resourceStore = new ResourceStore(fixtures),
- store;
-
- 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');
A.isTrue(instance.config.testKey6.testKey7 === 'testVal7');
@@ -402,4 +187,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/libs/ycb-tests.js b/source/lib/tests/autoload/libs/ycb-tests.js
index ab848ac59..3cc89c576 100644
--- a/source/lib/tests/autoload/libs/ycb-tests.js
+++ b/source/lib/tests/autoload/libs/ycb-tests.js
@@ -5,110 +5,132 @@
*/
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);
+ }
+
+
+ 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',
- 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']);
+ ycb = new libycb.Ycb(dims),
+ flat = ycb._flattenDimension('', dims[0].dimensions[6]['lang']);
- //Y.log(Y.JSON.stringify(flat,null,4));
-
- 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);
+ ycb = new libycb.Ycb(dims),
+ flat = ycb._dimensionPaths;
- //Y.log(Y.JSON.stringify(flat,null,4));
-
- 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, {useAllDimensions: true});
+
+ 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, {useAllDimensions: true});
- //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, {useAllDimensions: true});
expected = [
"*/*/*/*/*/*/*/*/*/*/*",
@@ -133,205 +155,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 context 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 context 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 context 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 +323,93 @@ YUI.add('mojito-ycb-tests', function(Y, NAME) {
};
bundle = readFixtureFile('dimensions.json').concat([foo]);
+ 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);
+ },
- ycb = libycb.read(bundle);
- A.areEqual(ycb['data-url'], foo['data-url']);
+ '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);
+ },
+
- A.isTrue('false_ok' in ycb);
- A.areEqual(ycb.false_ok, foo.false_ok);
+ 'get dimensions': function() {
+ var bundle, ycb;
+ bundle = readFixtureFile('dimensions.json');
+ ycb = new libycb.Ycb(Y.clone(bundle, true));
+ cmp(bundle[0].dimensions, ycb.getDimensions());
+ },
- A.isTrue('undef' in ycb);
- A.areEqual(ycb.undef, foo.undef);
- A.isTrue('zero' in ycb);
- A.areEqual(ycb.zero, foo.zero);
+ '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"}']);
}
- }));
-
- 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', 'oop']});
diff --git a/source/lib/tests/autoload/management/utils.server-tests.js b/source/lib/tests/autoload/management/utils.server-tests.js
new file mode 100644
index 000000000..069f7d05a
--- /dev/null
+++ b/source/lib/tests/autoload/management/utils.server-tests.js
@@ -0,0 +1,63 @@
+/*
+ * 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-management-utils-tests', function(Y, NAME) {
+
+ var suite = new YUITest.TestSuite(NAME),
+ libpath = require('path'),
+ libutils = require(libpath.join(__dirname, '../../../management/utils.js')),
+ 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: 'Management Utils tests',
+
+
+ 'decode HTML entities': function() {
+ A.isFunction(libutils.decodeHTMLEntities);
+ A.areSame('', libutils.decodeHTMLEntities(''));
+ A.areSame('orange & red', libutils.decodeHTMLEntities('orange & red'));
+ A.areSame('& red', libutils.decodeHTMLEntities('& red'));
+ A.areSame('orange &', libutils.decodeHTMLEntities('orange &'));
+ A.areSame('', libutils.decodeHTMLEntities('<orange & red>'));
+ A.areSame('orange & red', libutils.decodeHTMLEntities('orange & red'));
+ A.areSame('orange © red', libutils.decodeHTMLEntities('orange © red'));
+ A.areSame('orange y red', libutils.decodeHTMLEntities('orange y red'));
+ }
+
+
+ }));
+
+
+ YUITest.TestRunner.add(suite);
+
+}, '0.0.1', {requires: []});
diff --git a/source/lib/tests/autoload/store.server-tests.js b/source/lib/tests/autoload/store.server-tests.js
index afc49aa11..c60e2884c 100644
--- a/source/lib/tests/autoload/store.server-tests.js
+++ b/source/lib/tests/autoload/store.server-tests.js
@@ -7,53 +7,118 @@ 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, '../..'),
- ResourceStore = require(libpath.join(__dirname, '../../store.server')),
+ store,
Mock = YUITest.Mock,
A = YUITest.Assert,
AA = YUITest.ArrayAssert;
- suite.add(new YUITest.TestCase({
- name: 'Store tests',
+ 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');
+ }
- 'pre load': function() {
- var store = new ResourceStore(fixtures);
+ suite.add(new YUITest.TestCase({
+
+ name: 'Store tests -- preload fixture "store"',
+
+ init: function() {
+ var fixtures = libpath.join(__dirname, '../fixtures/store');
+ 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._root === fixtures);
+ A.isTrue(store._config.root === fixtures);
},
- 'pre load no application.json file': function() {
+ 'valid context': function() {
+ var success;
- var fixtures = libpath.join(__dirname, '../fixtures/store_no_app_config'),
- store = new ResourceStore(fixtures);
- store.preload();
+ try {
+ store.validateContext({});
+ } catch(e) {
+ A.fail('{} should be valid');
+ }
- //Y.log(Y.JSON.stringify(store,null,4));
- A.isTrue(store._root === fixtures);
- },
+ try {
+ store.validateContext({device:'iphone'});
+ } catch(e) {
+ A.fail('{device:iphone} should be valid');
+ }
- 'server app config value': function() {
+ try {
+ store.validateContext({device:'iphone',lang:'en'});
+ } catch(e) {
+ A.fail('{device:iphone,lang:en} should be valid');
+ }
- var store = new ResourceStore(fixtures);
- store.preload();
+ 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');
- var config = store.getAppConfig(null, 'application');
+ 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() {
+ var config = store.getAppConfig(null);
A.isTrue(config.testKey1 === 'testVal1');
},
'server mojit config value': function() {
-
- var store = new ResourceStore(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 +126,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
},
'server mojit config value via type': function() {
-
- var store = new ResourceStore(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,16 +135,11 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
},
'server mojit config value via type & overrride': function() {
-
- var store = new ResourceStore(fixtures);
- store.preload();
-
var instance = {
type:'test_mojit_1',
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 +147,10 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
},
'server mojit instance assets': function() {
-
- var store = new ResourceStore(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);
-
// 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,330 +159,71 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
},
'server mojit instance views & binders': function() {
-
- var store = new ResourceStore(fixtures);
- 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']);
});
},
'server mojit instance models': function() {
-
- var store = new ResourceStore(fixtures);
- store.preload();
-
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 ResourceStore(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']);
});
},
- 'server mojit instance yui': function() {
-
- var store = new ResourceStore(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 ResourceStore(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 ResourceStore(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 ResourceStore(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 ResourceStore(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);
- store.preload();
-
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);
});
},
- 'server mojit is NOT loaded because of package mojito version miss-match': function(){
- var store = new ResourceStore(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');
+ 'server mojit is NOT loaded because of package mojito version mismatch': function(){
+ 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(){
- var store = new ResourceStore(fixtures);
- store.preload();
-
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 store = new ResourceStore(fixtures);
- store.preload();
-
- A.isTrue(typeof store._staticURLs['/static/TestMojit2/package.json'] === 'undefined');
+ 'server a mojits package.json file is available as appropriate': function() {
+ 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 a mojits package.json file is publicly accessible': function(){
- var store = new ResourceStore(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);
- 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);
- store.preload();
-
+ 'server mojit view index.mu.html is loaded correctly': function() {
var instance = {type:'TestMojit3'};
store.expandInstance(instance, {}, function(err, instance){
A.areSame('index.mu.html', instance.views.index['content-path'].split('/').pop());
@@ -439,38 +231,13 @@ 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);
- 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());
});
},
- 'server mojit view index1.mu.html is loaded correctly': function(){
- var store = new ResourceStore(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 ResourceStore(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 ResourceStore(fixtures);
- store.preload();
var instance = { type: 'test_mojit_1' };
store.expandInstance(instance, {}, function(err, instance) {
A.isNotUndefined(instance.models.test_applevel);
@@ -478,104 +245,40 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
},
'mojitDirs setting': function() {
- var store = new ResourceStore(fixtures);
- store.preload();
var instance = { type: 'soloMojit' };
store.expandInstance(instance, {}, function(err, instance) {
- A.isNotUndefined(instance.controller);
+ A.isNotUndefined(instance['controller-path']);
});
},
'expandInstance caching': function() {
- var store = new ResourceStore(fixtures);
- 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);
});
},
- 'rollups mojits': function() {
- var store = new ResourceStore(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 ResourceStore(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 ResourceStore(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, and setLogger()': function() {
- var store = new ResourceStore(fixtures);
- var logsBefore, logs = 0;
- store.setLogger({ log: function() {
- logs++;
- } });
- store.preload();
- logsBefore = logs;
+ 'multi preload': function() {
+ 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() {
- var store = new ResourceStore(fixtures);
- store.preload();
store.getSpec('server', 'test1', {}, function(err, instance) {
A.areSame('test_mojit_1', instance.type);
A.areSame('test1', instance.id);
@@ -585,8 +288,6 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
},
'call getType()': function() {
- var store = new ResourceStore(fixtures);
- store.preload();
store.getType('server', 'test_mojit_1', {}, function(err, instance) {
A.areSame('test_mojit_1', instance.type);
A.isUndefined(instance.id);
@@ -596,21 +297,18 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
},
'instance with base pointing to non-existant spec': function() {
- var store = new ResourceStore(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);
A.isUndefined(instance);
});
},
'getAppConfig() returns contextualized info': function() {
- var store = new ResourceStore(fixtures),
- config,
- context = { runtime: 'server' };
- store.preload(context);
- config = store.getAppConfig(null, 'application');
+ var context = { runtime: 'server' },
+ config;
+ 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');
@@ -618,35 +316,15 @@ 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);
- 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 ResourceStore(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);
- 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);
@@ -663,10 +341,8 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
},
'call listAllMojits()': function() {
- var store = new ResourceStore(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);
@@ -675,137 +351,75 @@ 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);
},
- 'call getAllMojits()': function() {
- var store = new ResourceStore(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");
+ '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']);
+ });
},
- 'stuff with ctx{lang:}, in language fallback': function() {
- var fixtures = libpath.join(__dirname, '../fixtures/gsg5'),
- store = new ResourceStore(fixtures),
- ctx, spec;
+ '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);
+ }
+
+ }));
+
+
+ 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();
+ },
- // first test
- ctx = { lang: 'en-US' };
- spec = { type: 'PagedFlickr' };
+ '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.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 {}');
- });
- });
- });
- });
+ A.areSame(libpath.join(fixtures, 'mojits/PagedFlickr/controller.common.iphone.js'), instance['controller-path']);
});
},
- 'bad files': function() {
- var fixtures = libpath.join(__dirname, '../fixtures/badfiles'),
- store = new ResourceStore(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']);
+ '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']);
});
},
- 'malformed JSON for config file': function() {
- var fixtures = libpath.join(__dirname, '../fixtures/badfiles2'),
- store = new ResourceStore(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;
- }
- },
+ }));
+
+
+ suite.add(new YUITest.TestCase({
+
+ name: 'Store tests -- preload fixture "gsg5-appConfig"',
- 'JSON config file not YCB': function() {
- var fixtures = libpath.join(__dirname, '../fixtures/badfiles3'),
- store = new ResourceStore(fixtures),
- r, logCalled = 0;
- store.setLogger({ log: function(msg, lvl, who) {
- logCalled++;
- }});
+ init: function() {
+ var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig');
+ store = new Y.mojito.ResourceStore({ root: fixtures });
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 ResourceStore(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');
@@ -820,69 +434,70 @@ 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.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 ResourceStore(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);
- });
+ 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'),
- store = new ResourceStore(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();
- 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 ResourceStore(fixtures);
+ 'default routes': function() {
+ var fixtures = libpath.join(__dirname, '../fixtures/store_no_app_config'),
+ 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']);
- });
- },
- 'TODO app with app-level rollup': function() {
- A.skip();
+ var have = store.getRoutes();
+ A.isObject(have._default_path);
},
- 'appConfig yui.base': function() {
- var fixtures = libpath.join(__dirname, '../fixtures/gsg5-appConfig'),
- store = new ResourceStore(fixtures);
+ 'bad files': function() {
+ var fixtures = libpath.join(__dirname, '../fixtures/badfiles'),
+ store = new Y.mojito.ResourceStore({ root: fixtures });
store.preload();
- var spec = { type: 'PagedFlickr' };
+ var spec = { type: 'M' };
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));
+ 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']);
});
},
+ 'TODO app with app-level rollup': function() {
+ A.skip();
+ },
+
'sortedReaddirSync() sorts the result of fs.readdirSync()': function() {
+ var fixtures = libpath.join(__dirname, '../fixtures/store');
var mockfs = Mock();
Mock.expect(mockfs, {
@@ -891,145 +506,28 @@ YUI.add('mojito-store-server-tests', function(Y, NAME) {
returns: ['d', 'c', 'a', 'b']
});
- var store = new ResourceStore(fixtures, { 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);
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
- });
-
- store = new ResourceStore(fixtures, { path: mockpath });
- 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 ResourceStore(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 ResourceStore(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 ResourceStore(fixtures);
- A.isTrue(store._isValidYcbDimensions(dims));
- },
-
- '_isValidYcbDimensions() spots empty array': function() {
- var dims = [];
- var store = new ResourceStore(fixtures);
- A.isFalse(store._isValidYcbDimensions(dims));
- },
-
- '_isValidYcbDimensions() spots empty dimensions': function() {
- var dims = [{ dimensions: [] }];
- var store = new ResourceStore(fixtures);
- A.isFalse(store._isValidYcbDimensions(dims));
- },
-
- '_isValidYcbDimensions() spots too many top-level items': function() {
- var dims = [{ dimensions: [{ dimKey: {} }] }, { extraDimensions: [] }];
- var store = new ResourceStore(fixtures);
- A.isFalse(store._isValidYcbDimensions(dims));
- },
-
'_skipBadPath() does just that': function() {
- var store = new ResourceStore(fixtures);
- A.areSame(true, store._skipBadPath({ ext: '.js~' }));
- A.areSame(false, store._skipBadPath({ ext: '.js' }));
+ 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() {
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) {
+ 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.
@@ -1037,395 +535,259 @@ 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);
- A.isNotNull(details.controller.match(/a\/foo\/controller\.server\.js$/), 'controller should not be null');
+ var details = store.getMojitTypeDetails('server', {}, 'a');
+ A.isNotNull(details['controller-path'].match(/a\/foo\/controller\.server\.js$/), 'controller should not be null');
},
'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'));
- 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' };
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++) {
res = ress[r];
A.isNotUndefined(res.id, 'no resource id');
switch (res.id) {
- case 'action-x':
- A.areSame(pkg, res.pkg);
+ case 'action--x':
+ 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.fsPath);
+ A.fail('unknown resource ' + res.source.fs.fullPath);
break;
}
break;
- case 'action-y/z':
- A.areSame(pkg, res.pkg);
+ case 'action--y/z':
+ 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.fsPath);
+ A.fail('unknown resource ' + res.source.fs.fullPath);
break;
}
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.fsPath);
+ 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.fsPath);
+ A.fail('unknown resource ' + res.source.fs.fullPath);
break;
}
break;
- case 'binder-x':
- A.areSame(pkg, res.pkg);
+ case 'binder--x':
+ 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('common', 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('common', res.affinity);
+ A.areSame('.js', res.source.fs.ext);
break;
default:
- A.fail('unknown resource ' + res.fsPath);
+ A.fail('unknown resource ' + res.source.fs.fullPath);
break;
}
break;
- case 'command-x':
- A.areSame(pkg, res.pkg);
+ case 'command--x':
+ 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);
+ case 'config--config':
+ 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':
- A.areSame(pkg, res.pkg);
+ case 'controller--controller':
+ 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.fsPath);
+ A.fail('unknown resource ' + res.source.fs.fullPath);
break;
}
break;
- case 'middleware-x':
- A.areSame(pkg, res.pkg);
+ case 'middleware--x':
+ 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);
+ case 'spec--default':
+ 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);
+ case 'spec--x':
+ 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);
+ case 'view--x':
+ 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);
- 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);
- break;
- default:
- A.fail('unknown resource ' + res.fsPath);
- 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.fsPath);
- 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.fsPath);
+ A.areSame('html', res.view.outputFormat);
+ A.areSame('mu', res.view.engine);
+ 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;
- }
- 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);
+ 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.fsPath);
+ 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');
}
}));
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'
+]});
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 024372df2..75ccabd65 100644
--- a/source/lib/tests/fixtures/store/application.json
+++ b/source/lib/tests/fixtures/store/application.json
@@ -20,13 +20,30 @@
{
"settings": [ "runtime:server" ],
+ "selector": "shelves",
"testKey1": "testVal1-server",
"pathos": "portended"
},
{
"settings": [ "runtime:client" ],
+ "selector": "right",
"testKey2": "testVal2-client",
"testKey4": "testVal4-client"
+ },
+ {
+ "settings": [ "device:iphone" ],
+
+ "selector": "iphone"
+ },
+ {
+ "settings": [ "device:android" ],
+
+ "selector": "droid"
+ },
+ {
+ "settings": [ "device:android", "environment:dev" ],
+
+ "selector": "devdroid"
}
]
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: []});
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 a77511a5a..94463a5db 100644
--- a/source/package.json
+++ b/source/package.json
@@ -17,6 +17,7 @@
"express": "2.5.2",
"glob": "~3.1.11",
"mime": "1.2.4",
+ "semver": "1.0.14",
"yui": "~3.5.1",
"yuitest": "~0.7.4"
},