Skip to content
Browse files

Ported of YCB to npm package

  • Loading branch information...
0 parents commit 6b6f50a5f970523a133dbfae3f411c00fa98a9c9 @mridgway mridgway committed
1 .gitignore
@@ -0,0 +1 @@
+node_modules
605 index.js
@@ -0,0 +1,605 @@
+/*
+ * 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, node:true, nomen:true*/
+
+
+var VERSION = '2.0.0',
+ DEFAULT = '*',
+ SEPARATOR = '/',
+ SUBMATCH = /\$\$[a-zA-Z0-9.-_]*\$\$/,
+ Y = require('yui').YUI({useSync: true}).use('json-parse', 'json-stringify', 'oop');
+
+Y.applyConfig({useSync: false});
+
+
+//---------------------------------------------------------------
+// OBJECT ORIENTED INTERFACE
+
+function Ycb(bundle, options) {
+ this.options = options || {};
+ this.dimensions = {};
+ this.settings = {};
+ this.schema = {};
+ this.dimsUsed = {}; // dim name: value: true
+ this._processRawBundle(bundle, this.options);
+}
+Ycb.prototype = {
+
+
+ /**
+ * Returns the dimensions in the YCB file.
+ * @method getDimensions
+ * @return {object} the dimensions
+ */
+ getDimensions: function() {
+ return Y.clone(this.dimensions, true);
+ },
+
+
+ /**
+ * Iterates over all the setting sections in the YCB file, calling the
+ * callback for each section.
+ * @method walkSettings
+ * @param callback {function(settings, config)}
+ * @param callback.settings {object} the condition under which section will be used
+ * @param callback.config {object} the configuration in the section
+ * @param callback.return {boolean} if the callback returns false, then walking is stopped
+ * @return {nothing} results returned via callback
+ */
+ walkSettings: function(callback) {
+ var path,
+ context;
+ for (path in this.settings) {
+ if (this.settings.hasOwnProperty(path)) {
+ context = this._getContextFromLookupPath(path);
+ // clone, so that noone mutates us
+ if (!callback(context, Y.clone(this.settings[path], true))) {
+ break;
+ }
+ }
+ }
+ },
+
+
+ /**
+ * Read the file.
+ * @method read
+ * @param context {object}
+ * @param options {object}
+ * @return {object}
+ */
+ read: function(context, options) {
+ var lookupPaths,
+ path,
+ config = {};
+
+ context = context || {};
+ options = objectMerge(this.options, options || {});
+
+ lookupPaths = this._getLookupPaths(context, options);
+
+ if (options.debug) {
+ console.log(Y.JSON.stringify(context, null, 4));
+ console.log(Y.JSON.stringify(this.dimensions, null, 4));
+ console.log(Y.JSON.stringify(this.settings, null, 4));
+ console.log(Y.JSON.stringify(this.schema, null, 4));
+ console.log(Y.JSON.stringify(lookupPaths, null, 4));
+ }
+
+ // Now we simply merge each macting settings section we find into the config
+ for (path = 0; path < lookupPaths.length; path += 1) {
+ if (this.settings[lookupPaths[path]]) {
+ if (options.debug) {
+ console.log('----USING---- ' + lookupPaths[path]);
+ console.log(Y.JSON.stringify(this.settings[lookupPaths[path]], null, 4));
+ }
+ // merge a copy so that we don't modify the source
+ config = objectMerge(Y.clone(this.settings[lookupPaths[path]], true), config);
+ }
+ }
+
+ this._applySubstitutions(config);
+
+ if (options.validate) {
+ console.log('The YCB option "validate" is not implemented yet.');
+ }
+
+ return config;
+ },
+
+
+ /**
+ * Like read(), but doesn't merge the found sections.
+ * Also, doesn't do substitutions.
+ *
+ * @method readNoMerge
+ * @param context {object}
+ * @param options {object}
+ * @return {array of objects}
+ */
+ readNoMerge: function(context, options) {
+ var lookupPaths,
+ path,
+ config = [];
+
+ context = context || {};
+
+ lookupPaths = this._getLookupPaths(context, options);
+
+ if (options.debug) {
+ console.log(Y.JSON.stringify(context, null, 4));
+ console.log(Y.JSON.stringify(this.dimensions, null, 4));
+ console.log(Y.JSON.stringify(this.settings, null, 4));
+ console.log(Y.JSON.stringify(this.schema, null, 4));
+ console.log(Y.JSON.stringify(lookupPaths, null, 4));
+ }
+
+ // Now we simply merge each macting settings section we find into the config
+ for (path = 0; path < lookupPaths.length; path += 1) {
+ if (this.settings[lookupPaths[path]]) {
+ if (options.debug) {
+ console.log('----USING---- ' + lookupPaths[path]);
+ console.log(Y.JSON.stringify(this.settings[lookupPaths[path]], null, 4));
+ }
+ config.push(Y.clone(this.settings[lookupPaths[path]], true));
+ }
+ }
+ return config;
+ },
+
+
+ /**
+ * This is a first pass at hairball of a funciton.
+ *
+ * @private
+ * @method _applySubstitutions
+ * @param config {object}
+ * @param base {object}
+ * @param parent {object}
+ * @return void
+ */
+ _applySubstitutions: function(config, base, parent) {
+ var key,
+ sub,
+ find,
+ item;
+
+ base = base || config;
+ parent = parent || {ref: config, key: null};
+
+ for (key in config) {
+ if (config.hasOwnProperty(key)) {
+ // If the value is an "Object" or an "Array" drill into it
+ if (config[key] && (config[key].constructor === Object || config[key].constructor === Array)) {
+ // The whole {ref: config, key: key} is needed only when replacing "keys"
+ this._applySubstitutions(config[key], base, {ref: config, key: key});
+ } else {
+ // Test if the key is a "substitution" key
+ if (SUBMATCH.test(key)) {
+ // We have a matching so lets do some work
+ sub = SUBMATCH.exec(key);
+ // Is it the whole key or just something odd
+ if (sub[0] === key) {
+ // Pull out he key to "find"
+ find = extract(base, sub[0].slice(2, -2), null);
+
+ if (find.constructor === Object) {
+ // Remove the "substitution" key
+ delete config[key];
+ // Add the keys founds
+ // This should be inline at the point where the "substitution" key was.
+ // Currently they will be added out of order on the end of the map.
+ for (item in find) {
+ if (find.hasOwnProperty(item)) {
+ if (!parent.ref[parent.key]) {
+ parent.ref[item] = find[item];
+ } else {
+ parent.ref[parent.key][item] = find[item];
+ }
+ }
+ }
+ } else {
+ config[key] = '--YCB-SUBSTITUTION-ERROR--';
+ }
+ }
+ } else if (SUBMATCH.test(config[key])) {
+ // Test if the value is a "substitution" value
+ // We have a match so lets use it
+ sub = SUBMATCH.exec(config[key]);
+ // Pull out he key to "find"
+ find = sub[0].slice(2,-2);
+ // First see if it is the whole value
+ if (sub[0] === config[key]) {
+ // Replace the whole value with the value found by the sub string
+ find = extract(base, find, null);
+ // If we have an array in an array do it "special like"
+ if (find.constructor === Array && config.constructor === Array) {
+ // This has to be done on the parent or the referance is lost
+ // The whole {ref: config, key: key} is needed only when replacing "keys"
+ parent.ref[parent.key] = config.slice(0, parseInt(key, 10))
+ .concat(find)
+ .concat(config.slice(parseInt(key, 10) + 1));
+ } else {
+ config[key] = find;
+ }
+ } else {
+ // If not it's just part of the whole value
+ config[key] = config[key].replace(sub[0], extract(base, find, null));
+ }
+ }
+ }
+ }
+ }
+ },
+
+
+ /**
+ * @private
+ * @method _getLookupPaths
+ * @param context {object} Key/Value list
+ * @param options {object} runtime options
+ * @return {Array}
+ */
+ _getLookupPaths: function(context, options) {
+ var lookupList = Y.Object.values(this._makeOrderedLookupList(context, options)),
+ path = [],
+ paths = [],
+ pos,
+ current = lookupList.length-1,
+ combination = [];
+
+ // This is our combination that we will tumble over
+ for (pos = 0; pos < lookupList.length; pos += 1) {
+ combination.push({
+ current: 0,
+ total: lookupList[pos].length-1
+ });
+ }
+
+ function tumble(combination, location) {
+ // If the location is not found return
+ if(!combination[location]){
+ return false;
+ }
+
+ // Move along to the next item
+ combination[location].current++;
+
+ // If the next item is not found move to the prev location
+ if (combination[location].current > combination[location].total) {
+ combination[location].current = 0;
+ return tumble(combination, location-1);
+ }
+
+ return true;
+ }
+
+ do {
+ path = [];
+ for (pos = 0; pos < lookupList.length; pos += 1) {
+ path.push(lookupList[pos][combination[pos].current]);
+ }
+ paths.push(path.join(SEPARATOR));
+ } while (tumble(combination, current));
+
+ return paths.reverse();
+ },
+
+
+ /**
+ * @private
+ * @method _processRawBundle
+ * @param bundle {object}
+ * @param options {object}
+ * @return {nothing}
+ */
+ _processRawBundle: function(bundle, options) {
+ var pos,
+ section,
+ part,
+ kv,
+ context,
+ key;
+
+ // Extract each section from the bundle
+ for (pos = 0; pos < bundle.length; pos += 1) {
+ section = bundle[pos];
+ if (section.dimensions) {
+ this.dimensions = section.dimensions;
+ this._flattenDimensions();
+ } else if (section.schema) {
+ this.schema = section.schema;
+ } else if (section.settings) {
+ context = {};
+ for (part = 0; part < section.settings.length; part += 1) {
+ kv = section.settings[part].split(':');
+ if ('master' !== section.settings[0]) {
+ if (!this.dimsUsed[kv[0]]) {
+ this.dimsUsed[kv[0]] = {};
+ }
+ this.dimsUsed[kv[0]][kv[1]] = true;
+ }
+ context[kv[0]] = kv[1];
+ }
+ // Remove the settings key now we are done with it
+ delete section.settings;
+
+ // Build the full context path
+ key = this._getLookupPath(context, options);
+
+ // Add the section to the settings list with it's full key
+ // IMY Bug 5439377 configuration does not accept neither null nor false values?
+ if (!this.settings[key]) {
+ this.settings[key] = section;
+ } else {
+ throw new Error("The settings group '"+Y.JSON.stringify(context)+"' has already been added.");
+ }
+ }
+ }
+ },
+
+
+ /**
+ * @private
+ * @method _getContextPath
+ * @param context {object} Key/Value list
+ * @param options {object}
+ * @return {string}
+ */
+ _getLookupPath: function(context, options) {
+ var lookupList = this._makeOrderedLookupList(context, options),
+ name,
+ list,
+ lookup = {},
+ item,
+ path = [];
+
+ for (name in lookupList) {
+ if (lookupList.hasOwnProperty(name)) {
+ if (context[name]) {
+ for (list = 0; list < lookupList[name].length; list += 1) {
+ if (lookupList[name][list] === context[name]) {
+ lookup[name] = lookupList[name][list];
+ }
+ }
+ }
+ // If there was no match set to default
+ if (!lookup[name]) {
+ lookup[name] = DEFAULT;
+ }
+ }
+ }
+
+ for (item in lookup) {
+ if (lookup.hasOwnProperty(item)) {
+ path.push(lookup[item]);
+ }
+ }
+ return path.join(SEPARATOR);
+ },
+
+
+ /**
+ * @private
+ * @method _getContextFromLookupPath
+ * @param path {string} the path
+ * @return {object} the cooresponding context (really a partial context)
+ */
+ _getContextFromLookupPath: function(path) {
+ var parts = path.split(SEPARATOR),
+ p,
+ part,
+ dimName,
+ ctx = {};
+ for (p = 0; p < this.dimensions.length; p += 1) {
+ part = parts[p];
+ if ('*' === part) {
+ continue;
+ }
+ // Having more than one key in the dimensions structure is against
+ // the YCB spec.
+ dimName = Object.keys(this.dimensions[p])[0];
+ ctx[dimName] = part;
+ }
+ return ctx;
+ },
+
+
+ /**
+ * @private
+ * @method _makeOrderedLookupList
+ * @param context {object} Key/Value list
+ * @param options {object}
+ * @return {object} list of lists
+ */
+ _makeOrderedLookupList: function(context, options) {
+ var pos,
+ name,
+ path,
+ value,
+ used,
+ chains = {};
+
+ for (pos = 0; pos < this.dimensions.length; pos += 1) {
+ for (name in this.dimensions[pos]) {
+ if (this.dimensions[pos].hasOwnProperty(name)) {
+ for (path in this._dimensionPaths[name]) {
+ if (!this._dimensionPaths[name].hasOwnProperty(path)) {
+ continue;
+ }
+ value = this._dimensionPaths[name][path];
+ used = options.useAllDimensions || false;
+ if (!options.useAllDimensions) {
+ Y.Array.forEach(path.split(SEPARATOR), function (part) {
+ if ((this.dimsUsed[name] && this.dimsUsed[name][part])) {
+ used = true;
+ }
+ }, this);
+ }
+ if (used && value === context[name]) {
+ chains[name] = path;
+ }
+ }
+ if (chains[name]) {
+ // Convert to an ordered list
+ chains[name] = chains[name].split(SEPARATOR).reverse().concat(DEFAULT);
+ } else{
+ // If there was no match set to default
+ chains[name] = [DEFAULT];
+ }
+ }
+ }
+ }
+ return chains;
+ },
+
+
+ /**
+ * @private
+ * @method _flattenDimension
+ * @param prefix {string}
+ * @param dimension {object} A single YCB dimension structured object
+ * @param build {string}
+ * @return {object} k/v map
+ */
+ _flattenDimension: function(prefix, dimension, build) {
+ var key,
+ newPrefix,
+ nextDimension;
+
+ build = build || {};
+ if (typeof dimension === 'object') {
+ for (key in dimension) {
+ if (dimension.hasOwnProperty(key)) {
+ nextDimension = dimension[key];
+ newPrefix = (prefix ? prefix + SEPARATOR + key : key);
+ build[newPrefix] = key;
+ if (typeof nextDimension === 'object') {
+ build = this._flattenDimension(newPrefix, nextDimension, build);
+ }
+ }
+ }
+ }
+ return build;
+ },
+
+
+ /**
+ * @private
+ * @method _flattenDimensions
+ * @return {nothing}
+ */
+ _flattenDimensions: function() {
+ var pos,
+ name;
+ this._dimensionPaths = {};
+ for (pos = 0; pos < this.dimensions.length; pos += 1) {
+ for (name in this.dimensions[pos]) {
+ if (this.dimensions[pos].hasOwnProperty(name)) {
+ this._dimensionPaths[name] = this._flattenDimension('', this.dimensions[pos][name]);
+ }
+ }
+ }
+ }
+
+
+};
+
+
+
+//---------------------------------------------------------------
+// MODULE INTERFACE
+
+module.exports = {
+
+ version: VERSION,
+
+ // object-oriented interface
+ Ycb: Ycb,
+
+
+ /*
+ * Processes an Object representing a YCB 2.0 Bundle as defined in the spec.
+ *
+ * @method read
+ * @param context {object}
+ * @param validate {boolean}
+ * @param debug {boolean}
+ * @return {object}
+ */
+ read: function(bundle, context, validate, debug) {
+ var ycb = new Ycb(bundle),
+ opts = {
+ validate: validate,
+ debug: debug
+ };
+ return ycb.read(context, opts);
+ },
+
+
+ /*
+ * Like read(), but doesn't merge the found sections.
+ *
+ * @method readNoMerge
+ * @param context {object}
+ * @param validate {boolean}
+ * @param debug {boolean}
+ * @return {array of objects}
+ */
+ readNoMerge: function(bundle, context, validate, debug) {
+ var ycb = new Ycb(bundle),
+ opts = { debug: debug };
+ return ycb.readNoMerge(context, opts);
+ }
+
+
+};
+
+
+
+//---------------------------------------------------------------
+// UTILITY FUNCTIONS
+
+function objectMerge(from, to) {
+ var key;
+ for (key in from) {
+ if (from.hasOwnProperty(key)) {
+ try {
+ // Property in destination object set; update its value.
+ if (from[key].constructor === Object) {
+ to[key] = objectMerge(from[key], to[key]);
+ } else {
+ to[key] = from[key];
+ }
+ } catch (err) {
+ // Property in destination object not set; create it and set its value.
+ to[key] = from[key];
+ }
+ }
+ }
+ return to;
+}
+
+
+function extract(bag, key, def) {
+ var keys,
+ cur = bag,
+ i;
+ if (!key) {
+ return bag || {};
+ }
+ keys = key.split('.');
+ for (i = 0; i < keys.length; i += 1) {
+ if (cur[keys[i]]) {
+ cur = cur[keys[i]];
+ } else {
+ return def;
+ }
+ }
+ return cur;
+}
34 package.json
@@ -0,0 +1,34 @@
+{
+ "name": "ycb",
+ "version": "1.0.0",
+ "description": "Mojito provides an architecture, components and tools for developers to build complex web applications faster.",
+ "preferGlobal": true,
+ "author": "Drew Folta <folta@yahoo-inc.com>",
+ "contributors": [
+ "Michael Ridgway <mridgway@yahoo-inc.com>"
+ ],
+ "dependencies": {
+ "yui": "~3.5.1",
+ "yuitest": "~0.7.4"
+ },
+ "main": "index",
+ "scripts": {
+ "test": "node tests/unit"
+ },
+ "engines": {
+ "node": ">= 0.4.0 < 0.7.0",
+ "npm": ">= 1.0.0"
+ },
+ "devDependencies": {},
+ "repository": {
+ "type": "git",
+ "url": "git://github.com/yahoo/ycb.git"
+ },
+ "bugs": "https://github.com/yahoo/ycb/issues",
+ "yahoo": {
+ "bugzilla": {
+ "product": "Mojito",
+ "component": "YCB"
+ }
+ }
+}
65 tests/fixtures/dimensions.json
@@ -0,0 +1,65 @@
+[
+ {
+ "dimensions": [
+ {
+ "environment": {
+ "development": null,
+ "testing": null,
+ "preproduction": null,
+ "production": null
+ }
+ },
+ {
+ "cluster": {}
+ },
+ {
+ "netzone": {}
+ },
+ {
+ "xxx": {}
+ },
+ {
+ "site": {}
+ },
+ {
+ "device": {}
+ },
+ {
+ "lang": {
+ "en": {
+ "en_GB": null,
+ "en_US": null,
+ "en_CA": null
+ },
+ "fr": {
+ "fr_FR": {
+ "fr_CA": null
+ }
+ }
+ }
+ },
+ {
+ "region": {
+ "us": null,
+ "ca": null,
+ "gb": {
+ "ir": null
+ },
+ "fr": null
+ }
+ },
+ {
+ "jurisdiction": {}
+ },
+ {
+ "bucket": {}
+ },
+ {
+ "flavor": {
+ "att": null,
+ "bt": null
+ }
+ }
+ ]
+ }
+]
13 tests/fixtures/simple-1.json
@@ -0,0 +1,13 @@
+[
+ {
+ "settings": ["master"],
+
+ "title_key": "YRB_YAHOO",
+ "data-url": "http://service.yahoo.com",
+ "logo": "yahoo.png",
+ "links": {
+ "home": "http://www.yahoo.com",
+ "mail": "http://mail.yahoo.com"
+ }
+ }
+]
13 tests/fixtures/simple-2.json
@@ -0,0 +1,13 @@
+[
+ {
+ "settings": ["master"],
+
+ "title_key": "YRB_YAHOO",
+ "data-url": "http://service.yahoo.com",
+ "logo": "yahoo.png",
+ "links": {
+ "home": "http://www.yahoo.com",
+ "mail": "http://mail.yahoo.com"
+ }
+ }
+]
54 tests/fixtures/simple-3.json
@@ -0,0 +1,54 @@
+[
+ {
+ "settings": ["region:ca"],
+
+ "logo": "yahoo_CA.png",
+ "links": {
+ "home": "http://ca.yahoo.com",
+ "mail": "http://ca.mail.yahoo.com"
+ }
+ },
+ {
+ "settings": ["region:gb"],
+
+ "logo": "yahoo_GB.png",
+ "links": {
+ "home": "http://gb.yahoo.com",
+ "mail": "http://gb.mail.yahoo.com"
+ }
+ },
+ {
+ "settings": ["lang:fr"],
+
+ "logo": "yahoo_FR.png"
+ },
+ {
+ "settings": ["region:fr"],
+
+ "logo": "yahoo_FR.png",
+ "links": {
+ "home": "http://fr.yahoo.com",
+ "mail": "http://fr.mail.yahoo.com"
+ }
+ },
+ {
+ "settings": ["flavor:att"],
+
+ "logo": "yahoo_att.png"
+ },
+ {
+ "settings": ["flavor:att","region:ca"],
+
+ "logo": "yahoo_att_CA.png"
+ },
+ {
+ "settings": ["flavor:bt","region:gb"],
+
+ "logo": "yahoo_bt_GB.png"
+ },
+ {
+ "settings": ["region:fr","flavor:bt"],
+
+ "logo": "yahoo_bt_FR.png"
+ }
+]
36 tests/fixtures/substitutions.json
@@ -0,0 +1,36 @@
+{
+ "key0": {
+ "key1": "value1",
+ "key2": "value2",
+ "key3": "value3",
+ "key3": "$$key0.key1$$",
+ "key4": "The value of key0.key2 is $$key0.key2$$"
+ },
+ "key5": "$$key0$$",
+ "key6": {
+ "key7": {
+ "key8": "$$key5$$"
+ },
+ "key9": [
+ 1,
+ 2,
+ "$$key0.key4$$",
+ 3,
+ 4
+ ]
+ },
+ "$$key0.key1$$": null,
+ "$$key6.key7$$": null,
+ "key10": {
+ "key11": {
+ "$$key5$$": null
+ }
+ },
+ "key11": [
+ "a",
+ "b",
+ "$$key6.key9$$",
+ "c",
+ "d"
+ ]
+}
417 tests/unit.js
@@ -0,0 +1,417 @@
+/*
+ * Copyright (c) 2011-2012, Yahoo! Inc. All rights reserved.
+ * Copyrights licensed under the New BSD License.
+ * See the accompanying LICENSE file for terms.
+ */
+
+/*jslint node:true*/
+/*global YUI*/
+
+var Y = require('yui').YUI({useSync: true}).use('json', 'oop', 'test'),
+ libpath = require('path'),
+ libfs = require('fs'),
+ libycb = require('../index'),
+ suite = new Y.Test.Suite('ycb unit tests'),
+ A = Y.Assert,
+ AA = Y.ArrayAssert;
+
+
+function readFixtureFile(file){
+ var path = libpath.join(__dirname, 'fixtures' , 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 Y.Test.Case({
+
+ name: 'ycb',
+
+ setUp: function() {},
+
+ tearDown: function() {},
+
+
+ 'test if we can use the module': function() {
+ A.isTrue(libycb.version === '2.0.0');
+ },
+
+
+ 'test _flattenDimension': function() {
+ var dims = readFixtureFile('dimensions.json'),
+ ycb = new libycb.Ycb(dims),
+ flat = ycb._flattenDimension('', dims[0].dimensions[6]['lang']);
+
+ 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() {
+ var dims = readFixtureFile('dimensions.json'),
+ ycb = new libycb.Ycb(dims),
+ flat = ycb._dimensionPaths;
+
+ 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() {
+ var dims = readFixtureFile('dimensions.json'),
+ ycb = new libycb.Ycb(dims),
+ context, list;
+ context = {
+ '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() {
+ var dims = readFixtureFile('dimensions.json'),
+ ycb = new libycb.Ycb(dims),
+ context, path;
+ context = {
+ 'region': 'ir',
+ 'environment': 'preproduction',
+ 'lang': 'fr_FR'
+ };
+ path = ycb._getLookupPath(context, {useAllDimensions: true});
+
+ A.areSame('preproduction/*/*/*/*/*/fr_FR/ir/*/*/*', path);
+ },
+
+
+ '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 = ycb._getLookupPaths(context, {useAllDimensions: true});
+
+ expected = [
+ "*/*/*/*/*/*/*/*/*/*/*",
+ "*/*/*/*/*/*/*/gb/*/*/*",
+ "*/*/*/*/*/*/*/ir/*/*/*",
+ "*/*/*/*/*/*/fr/*/*/*/*",
+ "*/*/*/*/*/*/fr/gb/*/*/*",
+ "*/*/*/*/*/*/fr/ir/*/*/*",
+ "*/*/*/*/*/*/fr_FR/*/*/*/*",
+ "*/*/*/*/*/*/fr_FR/gb/*/*/*",
+ "*/*/*/*/*/*/fr_FR/ir/*/*/*",
+ "preproduction/*/*/*/*/*/*/*/*/*/*",
+ "preproduction/*/*/*/*/*/*/gb/*/*/*",
+ "preproduction/*/*/*/*/*/*/ir/*/*/*",
+ "preproduction/*/*/*/*/*/fr/*/*/*/*",
+ "preproduction/*/*/*/*/*/fr/gb/*/*/*",
+ "preproduction/*/*/*/*/*/fr/ir/*/*/*",
+ "preproduction/*/*/*/*/*/fr_FR/*/*/*/*",
+ "preproduction/*/*/*/*/*/fr_FR/gb/*/*/*",
+ "preproduction/*/*/*/*/*/fr_FR/ir/*/*/*"
+ ];
+ AA.itemsAreEqual(expected, paths);
+ },
+
+
+ 'test _processRawBundle': function() {
+ var bundle, ycb;
+ bundle = readFixtureFile('dimensions.json')
+ .concat(readFixtureFile('simple-1.json')[0]);
+ ycb = new libycb.Ycb(bundle),
+
+ A.areSame('YRB_YAHOO', ycb.settings['*/*/*/*/*/*/*/*/*/*/*'].title_key);
+ A.isNotUndefined(ycb.dimensions[7].region.us);
+ },
+
+
+ 'test _processRawBundle with dupe error': function() {
+ var bundle, ycb;
+ bundle = readFixtureFile('dimensions.json')
+ .concat(readFixtureFile('simple-1.json'))
+ .concat(readFixtureFile('simple-2.json'));
+
+ // This should throw an error for us to trap
+ try {
+ ycb = new libycb.Ycb(bundle);
+ } catch(err) {
+ A.isTrue(true);
+ return;
+ }
+ A.isTrue(false);
+ },
+
+
+ '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);
+
+ 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() {
+ var config, ycb;
+ config = readFixtureFile('substitutions.json');
+ 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$$'] === '--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, config;
+ bundle = readFixtureFile('simple-1.json');
+ config = libycb.read(bundle);
+
+ 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, config;
+ bundle = readFixtureFile('dimensions.json')
+ .concat(readFixtureFile('simple-1.json'));
+ config = libycb.read(bundle);
+
+ 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, config;
+ bundle = readFixtureFile('dimensions.json')
+ .concat(readFixtureFile('simple-1.json'))
+ .concat(readFixtureFile('simple-3.json'));
+ config = libycb.read(bundle);
+
+ 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 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'
+ };
+ 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 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'
+ };
+ 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 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'
+ };
+ 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,
+ config,
+ foo = {
+ settings: [ 'master' ],
+ title_key: 'YRB_YAHOO',
+ 'data-url': 'http://service.yahoo.com',
+ logo: 'yahoo.png',
+ false_ok: false,
+ zero: 0,
+ undef: undefined,
+ links: { home: 'http://www.yahoo.com', mail: 'http://mail.yahoo.com' }
+ };
+
+ 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);
+ },
+
+
+ '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);
+ },
+
+
+ 'get dimensions': function() {
+ var bundle, ycb;
+ bundle = readFixtureFile('dimensions.json');
+ ycb = new libycb.Ycb(Y.clone(bundle, true));
+ cmp(bundle[0].dimensions, ycb.getDimensions());
+ },
+
+
+ 'walk settings': function() {
+ var bundle, ycb;
+ bundle = readFixtureFile('dimensions.json')
+ .concat(readFixtureFile('simple-1.json'))
+ .concat(readFixtureFile('simple-3.json'));
+ ycb = new libycb.Ycb(bundle);
+ var ctxs = {};
+ ycb.walkSettings(function(settings, config) {
+ ctxs[JSON.stringify(settings)] = true;
+ return true;
+ });
+ A.areSame(9, Object.keys(ctxs).length);
+ A.isTrue(ctxs['{}']);
+ A.isTrue(ctxs['{"region":"ca"}']);
+ A.isTrue(ctxs['{"region":"gb"}']);
+ A.isTrue(ctxs['{"lang":"fr"}']);
+ A.isTrue(ctxs['{"region":"fr"}']);
+ A.isTrue(ctxs['{"flavor":"att"}']);
+ A.isTrue(ctxs['{"region":"ca","flavor":"att"}']);
+ A.isTrue(ctxs['{"region":"gb","flavor":"bt"}']);
+ A.isTrue(ctxs['{"region":"fr","flavor":"bt"}']);
+ }
+
+
+}));
+
+
+Y.Test.Runner.add(suite);
+Y.Test.Runner.run();

0 comments on commit 6b6f50a

Please sign in to comment.
Something went wrong with that request. Please try again.