Commit 819db320 authored by fxa's avatar fxa

Bugfix #10 UriTemplate.parse('/foo{?a,b,c}').expand({}); should produce /foo

parent aa5b2ecf
......@@ -70,7 +70,7 @@
return all.toArray();
}());
function closeTask (err) {
function closeAsyncJakeTask (err) {
if (err) {
fail(JSON.stringify(err, null, 4));
}
......@@ -86,7 +86,13 @@
callback(err && err.code !== 'ENOENT' ? err : undefined);
});
}
async.forEach([TMP_UNTESTED_UNCOMPRESSED, TMP_UNTESTED_COMPRESSED, TARGET_UNCOMPRESSED, TARGET_COMPRESSED], unlinkWhenExists, closeTask);
async.forEach([
TMP_UNTESTED_UNCOMPRESSED,
TMP_UNTESTED_COMPRESSED,
TARGET_UNCOMPRESSED,
TARGET_COMPRESSED],
unlinkWhenExists,
closeAsyncJakeTask);
}, ASYNC);
file(TARGET_UNCOMPRESSED, TARGET_UNCOMPRESSED_DEPENDENCIES, function () {
......@@ -121,7 +127,7 @@
jake.logger.log('move uncompressed version to target directory');
fs.rename(TMP_UNTESTED_UNCOMPRESSED, TARGET_UNCOMPRESSED, callback);
}
], closeTask);
], closeAsyncJakeTask);
}, ASYNC);
file(TARGET_COMPRESSED, [TARGET_UNCOMPRESSED], function () {
......@@ -140,19 +146,20 @@
jake.logger.log('move compressed version to target ... ');
fs.rename(TMP_UNTESTED_COMPRESSED, TARGET_COMPRESSED, callback);
}
], closeTask);
], closeAsyncJakeTask);
}, ASYNC);
// for short test only
desc('unit tests (without jshint)');
desc('unit tests (without jshint, only a shortcut for development)');
task('unit', [], function () {
nodeunit.reporters['default'].run(UNIT_TESTS, NODEUNIT_OPTIONS, complete);
}, ASYNC);
desc('build');
desc('build and test all artifacts');
task('build', [TARGET_COMPRESSED], function () {
jake.logger.log('done.');
});
desc('default -- called, if you call jake without parameters');
task('default', ['clean', 'build']);
}());
\ No newline at end of file
......@@ -62,6 +62,7 @@ MIT License, see http://mit-license.org/
Release Notes
-------------
* 0.3.1 fixed https://github.com/fxa/uritemplate-js/issues/10 thank you, Paul-Martin!
* 0.3.0 introduced UriTemplateError as exception, so the interface changed from string to UriTemplateError (as the rfc suggested)
* 0.2.4 fixed double encoding according [RubenVerborgh] and some Prefix modifiers bugs
* 0.2.3 fixed bug with empty objects ('{?empty}' with '{empty:{}}' shall expand to '?empty=')
......
This diff is collapsed.
This diff is collapsed.
......@@ -33,7 +33,7 @@
"uritemplate-test/spec-examples-by-sections.json",
"uritemplate-test/spec-examples.json"
],
"version": "0.3.0",
"version": "0.3.1",
"readmeFilename": "README.md",
"gitHead": "901b85201a821427dfb4591b56aea3a70d45c67c",
"devDependencies": {
......
This diff is collapsed.
......@@ -25,6 +25,16 @@ var encodingHelper = (function () {
return encode(text, true);
}
function encodeLiteralCharacter (literal, index) {
var chr = pctEncoder.pctCharAt(literal, index);
if (chr.length > 1) {
return chr;
}
else {
return rfcCharHelper.isReserved(chr) || rfcCharHelper.isUnreserved(chr) ? chr : pctEncoder.encodeCharacter(chr);
}
}
function encodeLiteral (literal) {
var
result = '',
......@@ -45,7 +55,8 @@ var encodingHelper = (function () {
return {
encode: encode,
encodePassReserved: encodePassReserved,
encodeLiteral: encodeLiteral
encodeLiteral: encodeLiteral,
encodeLiteralCharacter: encodeLiteralCharacter
};
}());
......@@ -6,26 +6,21 @@
* Section 2.3 of the RFC makes clear defintions:
* * undefined and null are not defined.
* * the empty string is defined
* * an array ("list") is defined, if it contains at least one defined element
* * an object ("map") is defined, if it contains at least one defined property
* * an array ("list") is defined, if it is not empty (even if all elements are not defined)
* * an object ("map") is defined, if it contains at least one property with defined value
* @param object
* @return {Boolean}
*/
function isDefined (object) {
"use strict";
var
index,
propertyName;
if (object === null || object === undefined) {
return false;
}
if (objectHelper.isArray(object)) {
for (index = 0; index < object.length; index += 1) {
if (isDefined(object[index])) {
return true;
}
}
return false;
// Section 2.3: A variable defined as a list value is considered undefined if the list contains zero members
return object.length > 0;
}
if (typeof object === "string" || typeof object === "number" || typeof object === "boolean") {
// falsy values like empty strings, false or 0 are "defined"
......
......@@ -5,34 +5,43 @@ var objectHelper = (function () {
return Object.prototype.toString.apply(value) === '[object Array]';
}
// performs an array.reduce for objects
// TODO handling if initialValue is undefined
function objectReduce (object, callback, initialValue) {
function join (arr, separator) {
var
propertyName,
currentValue = initialValue;
for (propertyName in object) {
if (object.hasOwnProperty(propertyName)) {
currentValue = callback(currentValue, object[propertyName], propertyName, object);
result = '',
first = true,
index;
for (index = 0; index < arr.length; index += 1) {
if (first) {
first = false;
}
else {
result += separator;
}
result += arr[index];
}
return currentValue;
return result;
}
// performs an array.reduce, if reduce is not present (older browser...)
// TODO handling if initialValue is undefined
function arrayReduce (array, callback, initialValue) {
function map (arr, mapper) {
var
index,
currentValue = initialValue;
for (index = 0; index < array.length; index += 1) {
currentValue = callback(currentValue, array[index], index, array);
result = [],
index = 0;
for (; index < arr.length; index += 1) {
result.push(mapper(arr[index]));
}
return currentValue;
return result;
}
function reduce (arrayOrObject, callback, initialValue) {
return isArray(arrayOrObject) ? arrayReduce(arrayOrObject, callback, initialValue) : objectReduce(arrayOrObject, callback, initialValue);
function filter (arr, predicate) {
var
result = [],
index = 0;
for (; index < arr.length; index += 1) {
if (predicate(arr[index])) {
result.push(arr[index]);
}
}
return result;
}
function deepFreezeUsingObjectFreeze (object) {
......@@ -63,7 +72,9 @@ var objectHelper = (function () {
return {
isArray: isArray,
reduce: reduce,
join: join,
map: map,
filter: filter,
deepFreeze: deepFreeze
};
}());
......@@ -14,7 +14,7 @@ module.exports = (function () {
}
function loadUriTemplate () {
var context = {module: {}};
var context = {module: {}, console: console};
sandbox(global.URI_TEMPLATE_FILE, context);
return context.module.exports;
}
......
module.exports = (function () {
"use strict";
var isDefined = (function () {
var context = {};
require('nodeunit').utils.sandbox('src/objectHelper.js', context);
require('nodeunit').utils.sandbox('src/isDefined.js', context);
return context.isDefined;
}());
return {
'special "undefined" value, such as undef or null are not "defined"': function (test) {
test.ok(!isDefined(null));
test.ok(!isDefined(undefined));
test.done();
},
'A variable value that is a string of length zero is not considered undefined': function (test) {
test.ok(isDefined(''));
test.done();
},
'A variable defined as a list value is considered undefined if the list contains zero members': {
/*
'empty array is undefined': function (test) {
test.ok(!isDefined([]));
test.done();
},
*/
'array containing only null is defined': function (test) {
test.ok(isDefined([null]));
test.done();
},
'array containing only undefined is defined': function (test) {
test.ok(isDefined([undefined]));
test.done();
},
'array containing only empty array is defined': function (test) {
test.ok(isDefined([
[]
]));
test.done();
},
'array containing only empty object is defined': function (test) {
test.ok(isDefined([
{}
]));
test.done();
},
'array containing empty string is defined': function (test) {
test.ok(isDefined(['']));
test.done();
}
},
// propably the longest id I ever wrote. The text is from the RFC
'A variable defined as an associative array of (name, value) pairs is considered undefined if the array contains zero members or if all member names in the array are associated with undefined values': {
'the empty object is not "defined"': function (test) {
test.ok(!isDefined({}));
test.done();
},
'null is a pretty member name': function (test) {
test.ok(isDefined({null: 1}));
test.done();
},
'not defined if all values are not "defined': function (test) {
test.ok(!isDefined({a: null, b: undefined, c: {}}));
test.done();
}
}
};
}());
......@@ -6,30 +6,6 @@ module.exports = (function () {
var objectHelper = context.objectHelper;
return {
'reduce works with initial value': function (test) {
var callNum = 0;
var result = objectHelper.reduce({a: 1, b: 2, c: 17}, function (current, value, name) {
if (callNum === 0) {
test.equal(current, -1);
test.equal(value, 1);
test.equal(name, 'a');
}
else if (callNum === 1) {
test.equal(current, 1);
test.equal(value, 2);
test.equal(name, 'b');
}
else {
test.equal(current, 2);
test.equal(value, 17);
test.equal(name, 'c');
}
callNum += 1;
return Math.max(current, value);
}, -1);
test.equal(result, 17);
test.done();
},
'deepFreeze': {
'deepFreeze freezes an object': function (test) {
var object = {};
......
module.exports = (function () {
"use strict";
var
sandbox = require('nodeunit').utils.sandbox,
context = {};
context = {console: console};
sandbox('src/objectHelper.js', context);
sandbox('src/charHelper.js', context);
sandbox('src/pctEncoder.js', context);
......@@ -18,6 +17,65 @@ module.exports = (function () {
var VariableExpression = context.VariableExpression;
return {
'unexploded': {
'empty is in list': function (test) {
var ve = new VariableExpression("{x,empty}", operators.valueOf(''), [
{varname: 'x', exploded: false, maxLength: null},
{varname: 'empty', exploded: false, maxLength: null}
]);
test.equal(ve.expand({x: 'x', empty:''}), 'x,');
test.done();
},
'null is not in list': function (test) {
var ve = new VariableExpression("{x,undef}", operators.valueOf(''), [
{varname: 'x', exploded: false, maxLength: null},
{varname: 'empty', exploded: false, maxLength: null}
]);
test.equal(ve.expand({x: 'x', undef: null}), 'x');
test.done();
},
'when empty list and not named, the operator is not printed': function (test) {
var ve = new VariableExpression("{.empty_list}", operators.valueOf('.'), [
{varname: 'empty_list', exploded: false, maxLength: null}
]);
test.equal(ve.expand({empty_list: []}), '');
test.done();
},
'when empty list and named, the operator is printed': function (test) {
var ve = new VariableExpression("{?empty_list}", operators.valueOf('?'), [
{varname: 'empty_list', exploded: false, maxLength: null}
]);
test.equal(ve.expand({empty_list: []}), '?empty_list=');
test.done();
}
},
'exploded': {
'unnamed': {
'a map shows a key-val list': function (test) {
var ve = new VariableExpression("{keys*}", operators.valueOf(''), [
{varname: 'keys', exploded: true, maxLength: null}
]);
test.equal(ve.expand({keys: {a: 'a', b: 'b', c: 'c'}}), 'a=a,b=b,c=c');
test.done();
},
'a empty map prints no operator': function (test) {
var ve = new VariableExpression("{.empty_map*}", operators.valueOf('.'), [
{varname: 'empty_map', exploded: true, maxLength: null}
]);
test.equal(ve.expand({empty_map: {}}), '');
test.done();
}
},
'named': {
'a named, exploded list repeats the name': function (test) {
var ve = new VariableExpression("{;count*}", operators.valueOf(';'), [
{varname: 'count', exploded: true, maxLength: null}
]);
test.equal(ve.expand({count: ['one', 'two', 'three']}), ';count=one;count=two;count=three');
test.done();
}
}
},
"there must be no separator at the end of the level3 list": function (test) {
var ve = new VariableExpression("{+x,y}", operators.valueOf('+'), [
{varname: 'x', exploded: false, maxLength: null},
......@@ -28,17 +86,17 @@ module.exports = (function () {
test.done();
},
"empty lists with ? must show the name": function (test) {
var ve = new VariableExpression("{?empty}", operators.valueOf('?'), [
{varname: 'empty', exploded: false, maxLength: null}
var ve = new VariableExpression("{?empty_list}", operators.valueOf('?'), [
{varname: 'empty_list', exploded: false, maxLength: null}
]);
test.equal(ve.expand({empty: {}}), '?empty=');
test.equal(ve.expand({empty_list: {}}), '?empty_list=');
test.done();
},
"exploded empty lists with ? must not show the name": function (test) {
var ve = new VariableExpression("{?empty*}", operators.valueOf('?'), [
{varname: 'empty', exploded: true, maxLength: null}
var ve = new VariableExpression("{?empty_list*}", operators.valueOf('?'), [
{varname: 'empty_list', exploded: true, maxLength: null}
]);
test.equal(ve.expand({empty: {}}), '');
test.equal(ve.expand({empty_list: []}), '');
test.done();
},
"double encode if ?": function (test) {
......@@ -61,6 +119,23 @@ module.exports = (function () {
]);
test.equal(ve.expand({one: 'two'}), 't');
test.done();
},
'query expression with 1 varname will expand to empty, if data is undef': function (test) {
var ve = new VariableExpression("{?a}", operators.valueOf('?'), [
{varname: 'a', exploded: false}
]);
test.equal(ve.expand({}), '');
test.done();
},
'query expression with more than 1 varname will expand to empty, if data is undef': function (test) {
var ve = new VariableExpression("{?a,b,c}", operators.valueOf('?'), [
{varname: 'a', exploded: false},
{varname: 'b', exploded: false},
{varname: 'c', exploded: false}
]);
test.equal(ve.expand({}), '');
test.done();
}
};
}());
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment