fix tests, rename option, more style fixes

This commit is contained in:
Naomi Seyfer
2013-10-01 11:57:53 -07:00
parent da6bc1d835
commit 0d8bfbae8a
4 changed files with 109 additions and 177 deletions

View File

@@ -207,15 +207,12 @@ EJSON.fromJSONValue = function (item) {
};
EJSON.stringify = function (item, options) {
var keyOrderSensitive = !!(options && options.keyOrderSensitive);
var indent = options && options.indent || null;
if (indent === true)
indent = 2;
var json = EJSON.toJSONValue(item);
if (keyOrderSensitive)
return JSON.stringify(json, null, indent);
else
return EJSON._canonicalStringify(json, null, indent);
if (options && (options.canonical || options.indent)) {
return EJSON._canonicalStringify(json, options);
} else {
return JSON.stringify(json);
}
};
EJSON.parse = function (item) {
@@ -315,6 +312,7 @@ EJSON.clone = function (v) {
}
return ret;
}
// XXX: Use something better than underscore's isArray
if (_.isArray(v) || _.isArguments(v)) {
// For some reason, _.map doesn't work in this context on Opera (weird test
// failures).

View File

@@ -86,10 +86,10 @@ Tinytest.add("ejson - stringify", function (test) {
test.equal(EJSON.stringify([1, 2, 3], {indent: true}),
"[\n 1,\n 2,\n 3\n]"
);
test.equal(EJSON.stringify([1, 2, 3], {keyOrderSensitive: true}),
test.equal(EJSON.stringify([1, 2, 3], {canonical: false}),
"[1,2,3]"
);
test.equal(EJSON.stringify([1, 2, 3], {indent: true, keyOrderSensitive: true}),
test.equal(EJSON.stringify([1, 2, 3], {indent: true, canonical: false}),
"[\n 1,\n 2,\n 3\n]"
);
@@ -102,14 +102,18 @@ Tinytest.add("ejson - stringify", function (test) {
test.equal(
EJSON.stringify(
{b: [2, {d: 4, c: 3}], a: 1}
{b: [2, {d: 4, c: 3}], a: 1},
{canonical: true}
),
"{\"a\":1,\"b\":[2,{\"c\":3,\"d\":4}]}"
);
test.equal(
EJSON.stringify(
{b: [2, {d: 4, c: 3}], a: 1},
{indent: true}
{
indent: true,
canonical: true
}
),
"{\n" +
" \"a\": 1,\n" +
@@ -125,14 +129,14 @@ Tinytest.add("ejson - stringify", function (test) {
test.equal(
EJSON.stringify(
{b: [2, {d: 4, c: 3}], a: 1},
{keyOrderSensitive: true}
{canonical: false}
),
"{\"b\":[2,{\"d\":4,\"c\":3}],\"a\":1}"
);
test.equal(
EJSON.stringify(
{b: [2, {d: 4, c: 3}], a: 1},
{indent: true, keyOrderSensitive: true}
{indent: true, canonical: false}
),
"{\n" +
" \"b\": [\n" +

View File

@@ -8,8 +8,8 @@ Package.on_use(function (api) {
api.export('EJSON');
api.export('EJSONTest', {testOnly: true});
api.add_files('ejson.js', ['client', 'server']);
api.add_files('base64.js', ['client', 'server']);
api.add_files('stringify.js', ['client', 'server']);
api.add_files('base64.js', ['client', 'server']);
});
Package.on_test(function (api) {

View File

@@ -7,182 +7,112 @@
//
// NO WARRANTY EXPRESSED OR IMPLIED. USE AT YOUR OWN RISK.
EJSON = {}; // Global!
function quote(string) {
return JSON.stringify(string);
}
var rep, gap, indent;
var str = function (key, holder, singleIndent, outerIndent, canonical) {
function str(key, holder) {
// Produce a string from holder[key].
// Produce a string from holder[key].
var i; // The loop counter.
var k; // The member key.
var v; // The member value.
var length;
var innerIndent = outerIndent;
var partial;
var value = holder[key];
var i, // The loop counter.
k, // The member key.
v, // The member value.
length,
mind = gap,
partial,
value = holder[key];
// What happens next depends on the value's type.
// If the value has a toJSON method, call it to obtain a replacement value.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
innerIndent = outerIndent + singleIndent;
partial = [];
if (value && typeof value === 'object' &&
typeof value.toJSON === 'function') {
value = value.toJSON(key);
// Is the value an array?
if (_.isArray(value) || _.isArguments(value)) {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value, singleIndent, innerIndent, canonical) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
if (partial.length === 0) {
v = '[]';
} else if (innerIndent) {
v = '[\n' + innerIndent + partial.join(',\n' + innerIndent) + '\n' + outerIndent + ']';
} else {
v = '[' + partial.join(',') + ']';
}
return v;
}
// If we were called with a replacer function, then call the replacer to
// obtain a replacement value.
if (typeof rep === 'function') {
value = rep.call(holder, key, value);
}
// What happens next depends on the value's type.
switch (typeof value) {
case 'string':
return quote(value);
case 'number':
// JSON numbers must be finite. Encode non-finite numbers as null.
return isFinite(value) ? String(value) : 'null';
case 'boolean':
case 'null':
// If the value is a boolean or null, convert it to a string. Note:
// typeof null does not produce 'null'. The case is included here in
// the remote chance that this gets fixed someday.
return String(value);
// If the type is 'object', we might be dealing with an object or an array or
// null.
case 'object':
// Due to a specification blunder in ECMAScript, typeof null is 'object',
// so watch out for that case.
if (!value) {
return 'null';
}
// Make an array to hold the partial results of stringifying this object value.
gap += indent;
partial = [];
// Is the value an array?
if (Object.prototype.toString.apply(value) === '[object Array]') {
// The value is an array. Stringify every element. Use null as a placeholder
// for non-JSON values.
length = value.length;
for (i = 0; i < length; i += 1) {
partial[i] = str(i, value) || 'null';
}
// Join all of the elements together, separated with commas, and wrap them in
// brackets.
v = partial.length === 0
? '[]'
: gap
? '[\n' + gap + partial.join(',\n' + gap) + '\n' + mind + ']'
: '[' + partial.join(',') + ']';
gap = mind;
return v;
}
// If the replacer is an array, use it to select the members to be stringified.
if (rep && typeof rep === 'object') {
length = rep.length;
for (i = 0; i < length; i += 1) {
if (typeof rep[i] === 'string') {
k = rep[i];
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
}
}
} else {
// Otherwise, iterate through all of the keys in the object.
_.each(_.keys(value).sort(), function (k) {
v = str(k, value);
if (v) {
partial.push(quote(k) + (gap ? ': ' : ':') + v);
}
});
}
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
v = partial.length === 0
? '{}'
: gap
? '{\n' + gap + partial.join(',\n' + gap) + '\n' + mind + '}'
: '{' + partial.join(',') + '}';
gap = mind;
return v;
// Iterate through all of the keys in the object.
var keys = _.keys(value);
if (canonical)
keys = keys.sort();
_.each(keys, function (k) {
v = str(k, value, singleIndent, innerIndent, canonical);
if (v) {
partial.push(quote(k) + (innerIndent ? ': ' : ':') + v);
}
});
// Join all of the member texts together, separated with commas,
// and wrap them in braces.
if (partial.length === 0) {
v = '{}';
} else if (innerIndent) {
v = '{\n' + innerIndent + partial.join(',\n' + innerIndent) + '\n' + outerIndent + '}';
} else {
v = '{' + partial.join(',') + '}';
}
return v;
}
}
// If the JSON object does not yet have a stringify method, give it one.
function stringify(value, replacer, space) {
// The stringify method takes a value and an optional replacer, and an optional
// space parameter, and returns a JSON text. The replacer can be a function
// that can replace values, or an array of strings that will select the keys.
// A default replacer method can be provided. Use of the space parameter can
// produce text that is more easily readable.
var i;
gap = '';
indent = '';
// If the space parameter is a number, make an indent string containing that
// many spaces.
if (typeof space === 'number') {
for (i = 0; i < space; i += 1) {
indent += ' ';
}
// If the space parameter is a string, it will be used as the indent string.
} else if (typeof space === 'string') {
indent = space;
}
// If there is a replacer, it must be a function or an array.
// Otherwise, throw an error.
rep = replacer;
if (replacer && typeof replacer !== 'function' &&
(typeof replacer !== 'object' ||
typeof replacer.length !== 'number')) {
throw new Error('JSON.stringify');
}
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
return str('', {'': value});
}
EJSON._canonicalStringify = stringify;
EJSON._canonicalStringify = function (value, options) {
// Make a fake root object containing our value under the key of ''.
// Return the result of stringifying the value.
options = _.extend({
indent: "",
canonical: false
}, options);
if (options.indent === true) {
options.indent = " ";
} else if (typeof options.indent === 'number') {
var newIndent = "";
for (var i = 0; i < options.indent; i++) {
newIndent += ' ';
}
options.indent = newIndent;
}
return str('', {'': value}, options.indent, "", options.canonical);
};