hoek
Advanced tools
Comparing version 2.7.0 to 2.8.0
116
lib/index.js
@@ -258,3 +258,3 @@ // Load modules | ||
for (var i = 0, il = obj.length; i < il; ++i) { | ||
if (obj[i] !== ref[i]) { | ||
if (!exports.deepEqual(obj[i], ref[i])) { | ||
return false; | ||
@@ -385,2 +385,112 @@ } | ||
// Test if the reference contains the values | ||
exports.contain = function (ref, values, options) { | ||
/* | ||
string -> string(s) | ||
array -> item(s) | ||
object -> key(s) | ||
object -> object (key:value) | ||
*/ | ||
var valuePairs = null; | ||
if (typeof ref === 'object' && | ||
typeof values === 'object' && | ||
!Array.isArray(ref) && | ||
!Array.isArray(values)) { | ||
valuePairs = values; | ||
values = Object.keys(values); | ||
} | ||
else { | ||
values = [].concat(values); | ||
} | ||
options = options || {}; // deep, once, only, part | ||
exports.assert(arguments.length >= 2, 'Insufficient arguments'); | ||
exports.assert(typeof ref === 'string' || typeof ref === 'object', 'Reference must be string or an object'); | ||
exports.assert(values.length, 'Values array cannot be empty'); | ||
var compare = options.deep ? exports.deepEqual : function (a, b) { return a === b; }; | ||
var misses = false; | ||
var matches = new Array(values.length); | ||
for (var i = 0, il = matches.length; i < il; ++i) { | ||
matches[i] = 0; | ||
} | ||
if (typeof ref === 'string') { | ||
var pattern = '('; | ||
for (i = 0, il = values.length; i < il; ++i) { | ||
var value = values[i]; | ||
exports.assert(typeof value === 'string', 'Cannot compare string reference to non-string value'); | ||
pattern += (i ? '|' : '') + exports.escapeRegex(value); | ||
} | ||
var regex = new RegExp(pattern + ')', 'g'); | ||
var leftovers = ref.replace(regex, function ($0, $1) { | ||
var index = values.indexOf($1); | ||
++matches[index]; | ||
return ''; // Remove from string | ||
}); | ||
misses = !!leftovers; | ||
} | ||
else if (Array.isArray(ref)) { | ||
for (i = 0, il = ref.length; i < il; ++i) { | ||
for (var j = 0, jl = values.length, found = false; j < jl && found === false; ++j) { | ||
found = compare(ref[i], values[j]) && j; | ||
} | ||
if (found !== false) { | ||
++matches[found] | ||
} | ||
else { | ||
misses = true; | ||
} | ||
} | ||
} | ||
else { | ||
var keys = Object.keys(ref); | ||
for (i = 0, il = keys.length; i < il; ++i) { | ||
var key = keys[i]; | ||
var found = values.indexOf(key); | ||
if (found !== -1) { | ||
if (valuePairs && | ||
!compare(ref[key], valuePairs[key])) { | ||
return false; | ||
} | ||
++matches[found] | ||
} | ||
else { | ||
misses = true; | ||
} | ||
} | ||
} | ||
var result = false; | ||
for (var i = 0, il = matches.length; i < il; ++i) { | ||
result = result || !!matches[i]; | ||
if ((options.once && matches[i] > 1) || | ||
(!options.part && !matches[i])) { | ||
return false; | ||
} | ||
} | ||
if (options.only && | ||
misses) { | ||
return false; | ||
} | ||
return result; | ||
}; | ||
// Flatten array | ||
@@ -519,3 +629,5 @@ | ||
for (var i = 1, il = arguments.length; i < il; ++i) { | ||
msgs.push(arguments[i]); // Avoids Array.slice arguments leak, allowing for V8 optimizations | ||
if (arguments[i] !== '') { | ||
msgs.push(arguments[i]); // Avoids Array.slice arguments leak, allowing for V8 optimizations | ||
} | ||
} | ||
@@ -522,0 +634,0 @@ |
{ | ||
"name": "hoek", | ||
"description": "General purpose node utilities", | ||
"version": "2.7.0", | ||
"version": "2.8.0", | ||
"repository": "git://github.com/hapijs/hoek", | ||
@@ -6,0 +6,0 @@ "main": "index", |
![hoek Logo](https://raw.github.com/hapijs/hoek/master/images/hoek.png) | ||
General purpose node utilities | ||
Utility methods for the hapi ecosystem. This module is not intended to solve every problem for everyone, but rather as a central place to store hapi-specific methods. If you're looking for a general purpose utility module, check out [lodash](https://github.com/lodash/lodash) or [underscore](https://github.com/jashkenas/underscore). | ||
@@ -22,2 +22,3 @@ [![Build Status](https://secure.travis-ci.org/hapijs/hoek.png)](http://travis-ci.org/hapijs/hoek) | ||
* [intersect](#intersectarray1-array2 "intersect") | ||
* [contain](#containref-values-options "contain") | ||
* [flatten](#flattenarray-target "flatten") | ||
@@ -227,2 +228,24 @@ * [reach](#reachobj-chain-options "reach") | ||
### contain(ref, values, [options]) | ||
Tests if the reference value contains the provided values where: | ||
- `ref` - the reference string, array, or object. | ||
- `values` - a single or array of values to find within the `ref` value. If `ref` is an object, `values` can be a key name, | ||
an array of key names, or an object with key-value pairs to compare. | ||
- `options` - an optional object with the following optional settings: | ||
- `deep` - if `true`, performed a deep comparison of the values. | ||
- `once` - if `true`, allows only one occurrence of each value. | ||
- `only` - if `true`, does not allow values not explicitly listed. | ||
- `part` - if `true`, allows partial match of the values (at least one must always match). | ||
Note: comparing a string to overlapping values will result in failed comparison (e.g. `contain('abc', ['ab', 'bc'])`). | ||
Also, if an object key's value does not match the provided value, `false` is returned even when `part` is specified. | ||
```javascript | ||
Hoek.contain('aaa', 'a', { only: true }); // true | ||
Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true }); // true | ||
Hoek.contain([1, 2, 2], [1, 2], { once: true }); // false | ||
Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true }); // true | ||
``` | ||
### flatten(array, [target]) | ||
@@ -229,0 +252,0 @@ |
@@ -436,3 +436,3 @@ // Load modules | ||
it('should combine an empty object with a non-empty object', function (done) { | ||
it('combines an empty object with a non-empty object', function (done) { | ||
@@ -448,3 +448,3 @@ var a = {}; | ||
it('should override values in target', function (done) { | ||
it('overrides values in target', function (done) { | ||
@@ -464,3 +464,3 @@ var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' }; | ||
it('should override values in target (flip)', function (done) { | ||
it('overrides values in target (flip)', function (done) { | ||
@@ -762,5 +762,7 @@ var a = { x: 1, y: 2, z: 3, v: 5, t: 'test', m: 'abc' }; | ||
expect(Hoek.deepEqual([[1]], [[1]])).to.be.true; | ||
expect(Hoek.deepEqual([1, 2, 3], [1, 2, 3])).to.be.true; | ||
expect(Hoek.deepEqual([1, 2, 3], [1, 3, 2])).to.be.false; | ||
expect(Hoek.deepEqual([1, 2, 3], [1, 2])).to.be.false; | ||
expect(Hoek.deepEqual([1], [1])).to.be.true; | ||
done(); | ||
@@ -793,3 +795,3 @@ }); | ||
var b = Hoek.clone(a); | ||
expect(Hoek.deepEqual(a, b)).to.equal.true; | ||
expect(Hoek.deepEqual(a, b)).to.be.true; | ||
done(); | ||
@@ -815,3 +817,3 @@ }); | ||
var copy = Hoek.clone(obj); | ||
expect(Hoek.deepEqual(obj, copy)).to.equal.true; | ||
expect(Hoek.deepEqual(obj, copy)).to.be.true; | ||
expect(execCount).to.equal(0); | ||
@@ -839,3 +841,3 @@ expect(copy.test).to.equal(1); | ||
expect(Hoek.deepEqual(obj, ref)).to.equal.false; | ||
expect(Hoek.deepEqual(obj, ref)).to.be.false; | ||
done(); | ||
@@ -860,5 +862,5 @@ }); | ||
expect(Hoek.deepEqual(new Obj(), new Ref())).to.equal.false; | ||
expect(Hoek.deepEqual(new Obj(), new Obj())).to.equal.true; | ||
expect(Hoek.deepEqual(new Ref(), new Ref())).to.equal.true; | ||
expect(Hoek.deepEqual(new Obj(), new Ref())).to.be.false; | ||
expect(Hoek.deepEqual(new Obj(), new Obj())).to.be.true; | ||
expect(Hoek.deepEqual(new Ref(), new Ref())).to.be.true; | ||
done(); | ||
@@ -870,3 +872,3 @@ }); | ||
it('should ensure uniqueness within array of objects based on subkey', function (done) { | ||
it('ensures uniqueness within array of objects based on subkey', function (done) { | ||
@@ -894,3 +896,3 @@ var a = Hoek.unique(dupsArray, 'x'); | ||
it('should convert basic array to existential object', function (done) { | ||
it('converts basic array to existential object', function (done) { | ||
@@ -905,3 +907,3 @@ var keys = [1, 2, 3, 4]; | ||
it('should convert array of objects to existential object', function (done) { | ||
it('converts array of objects to existential object', function (done) { | ||
@@ -962,2 +964,70 @@ var keys = [{ x: 1 }, { x: 2 }, { x: 3 }, { y: 4 }]; | ||
describe('contain()', function () { | ||
it('tests strings', function (done) { | ||
expect(Hoek.contain('abc', 'ab')).to.be.true; | ||
expect(Hoek.contain('abc', 'abc', { only: true })).to.be.true; | ||
expect(Hoek.contain('aaa', 'a', { only: true })).to.be.true; | ||
expect(Hoek.contain('abc', 'b', { once: true })).to.be.true; | ||
expect(Hoek.contain('abc', ['a', 'c'])).to.be.true; | ||
expect(Hoek.contain('abc', ['a', 'd'], { part: true })).to.be.true; | ||
expect(Hoek.contain('abc', 'ac')).to.be.false; | ||
expect(Hoek.contain('abcd', 'abc', { only: true })).to.be.false; | ||
expect(Hoek.contain('aab', 'a', { only: true })).to.be.false; | ||
expect(Hoek.contain('abb', 'b', { once: true })).to.be.false; | ||
expect(Hoek.contain('abc', ['a', 'd'])).to.be.false; | ||
expect(Hoek.contain('abc', ['ab', 'bc'])).to.be.false; // Overlapping values not supported | ||
done(); | ||
}); | ||
it('tests arrays', function (done) { | ||
expect(Hoek.contain([1, 2, 3], 1)).to.be.true; | ||
expect(Hoek.contain([{ a: 1 }], { a: 1 }, { deep: true })).to.be.true; | ||
expect(Hoek.contain([1, 2, 3], [1, 2])).to.be.true; | ||
expect(Hoek.contain([{ a: 1 }], [{ a: 1 }], { deep: true })).to.be.true; | ||
expect(Hoek.contain([1, 1, 2], [1, 2], { only: true })).to.be.true; | ||
expect(Hoek.contain([1, 2], [1, 2], { once: true })).to.be.true; | ||
expect(Hoek.contain([1, 2, 3], [1, 4], { part: true })).to.be.true; | ||
expect(Hoek.contain([[1], [2]], [[1]], { deep: true })).to.be.true; | ||
expect(Hoek.contain([1, 2, 3], 4)).to.be.false; | ||
expect(Hoek.contain([{ a: 1 }], { a: 2 }, { deep: true })).to.be.false; | ||
expect(Hoek.contain([{ a: 1 }], { a: 1 })).to.be.false; | ||
expect(Hoek.contain([1, 2, 3], [4, 5])).to.be.false; | ||
expect(Hoek.contain([[3], [2]], [[1]])).to.be.false; | ||
expect(Hoek.contain([[1], [2]], [[1]])).to.be.false; | ||
expect(Hoek.contain([{ a: 1 }], [{ a: 2 }], { deep: true })).to.be.false; | ||
expect(Hoek.contain([1, 3, 2], [1, 2], { only: true })).to.be.false; | ||
expect(Hoek.contain([1, 2, 2], [1, 2], { once: true })).to.be.false; | ||
expect(Hoek.contain([0, 2, 3], [1, 4], { part: true })).to.be.false; | ||
done(); | ||
}); | ||
it('tests objects', function (done) { | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'a')).to.be.true; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'c'])).to.be.true; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'b', 'c'], { only: true })).to.be.true; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1 })).to.be.true; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, c: 3 })).to.be.true; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 }, { part: true })).to.be.true; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2, c: 3 }, { only: true })).to.be.true; | ||
expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] }, { deep: true })).to.be.true; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, 'd')).to.be.false; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, ['a', 'd'])).to.be.false; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3, d: 4 }, ['a', 'b', 'c'], { only: true })).to.be.false; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2 })).to.be.false; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 2, b: 2 }, { part: true })).to.be.false; // part does not ignore bad value | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 3 })).to.be.false; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, d: 4 })).to.be.false; | ||
expect(Hoek.contain({ a: 1, b: 2, c: 3 }, { a: 1, b: 2 }, { only: true })).to.be.false; | ||
expect(Hoek.contain({ a: [1], b: [2], c: [3] }, { a: [1], c: [3] })).to.be.false; | ||
done(); | ||
}); | ||
}); | ||
describe('flatten()', function () { | ||
@@ -1108,3 +1178,3 @@ | ||
it('should include constructor functions correctly', function (done) { | ||
it('includes constructor functions correctly', function (done) { | ||
@@ -1127,3 +1197,3 @@ var Something = function (next) { | ||
it('should exit process when not in test mode', function (done) { | ||
it('exits process when not in test mode', function (done) { | ||
@@ -1149,3 +1219,3 @@ var env = process.env.NODE_ENV; | ||
it('should throw when not in test mode and abortThrow is true', function (done) { | ||
it('throws when not in test mode and abortThrow is true', function (done) { | ||
@@ -1168,3 +1238,3 @@ var env = process.env.NODE_ENV; | ||
it('should respect hideStack argument', function (done) { | ||
it('respects hideStack argument', function (done) { | ||
@@ -1222,3 +1292,3 @@ var env = process.env.NODE_ENV; | ||
it('should default to showing stack', function (done) { | ||
it('defaults to showing stack', function (done) { | ||
@@ -1251,3 +1321,3 @@ var env = process.env.NODE_ENV; | ||
it('should throw an Error when using assert in a test', function (done) { | ||
it('throws an Error when using assert in a test', function (done) { | ||
@@ -1263,3 +1333,3 @@ var fn = function () { | ||
it('should throw an Error when using assert in a test with no message', function (done) { | ||
it('throws an Error when using assert in a test with no message', function (done) { | ||
@@ -1275,3 +1345,3 @@ var fn = function () { | ||
it('should throw an Error when using assert in a test with multipart message', function (done) { | ||
it('throws an Error when using assert in a test with multipart message', function (done) { | ||
@@ -1287,6 +1357,17 @@ var fn = function () { | ||
it('should throw an Error when using assert in a test with object message', function (done) { | ||
it('throws an Error when using assert in a test with multipart message (empty)', function (done) { | ||
var fn = function () { | ||
Hoek.assert(false, 'This', 'is', '', 'my message'); | ||
}; | ||
expect(fn).to.throw('This is my message'); | ||
done(); | ||
}); | ||
it('throws an Error when using assert in a test with object message', function (done) { | ||
var fn = function () { | ||
Hoek.assert(false, 'This', 'is', { spinal: 'tap' }); | ||
@@ -1299,3 +1380,3 @@ }; | ||
it('should throw an Error when using assert in a test with error object message', function (done) { | ||
it('throws an Error when using assert in a test with error object message', function (done) { | ||
@@ -1340,3 +1421,3 @@ var fn = function () { | ||
it('should escape all special regular expression characters', function (done) { | ||
it('escapes all special regular expression characters', function (done) { | ||
@@ -1435,3 +1516,3 @@ var a = Hoek.escapeRegex('4^f$s.4*5+-_?%=#!:@|~\\/`"(>)[<]d{}s,'); | ||
it('should escape all special HTTP header attribute characters', function (done) { | ||
it('escapes all special HTTP header attribute characters', function (done) { | ||
@@ -1443,3 +1524,3 @@ var a = Hoek.escapeHeaderAttribute('I said go!!!#"' + String.fromCharCode(92)); | ||
it('should throw on large unicode characters', function (done) { | ||
it('throws on large unicode characters', function (done) { | ||
@@ -1455,3 +1536,3 @@ var fn = function () { | ||
it('should throw on CRLF to prevent response splitting', function (done) { | ||
it('throws on CRLF to prevent response splitting', function (done) { | ||
@@ -1470,3 +1551,3 @@ var fn = function () { | ||
it('should escape all special HTML characters', function (done) { | ||
it('escapes all special HTML characters', function (done) { | ||
@@ -1473,0 +1554,0 @@ var a = Hoek.escapeHtml('&<>"\'`'); |
131380
2135
495