node-tmatch-3.0.0/000077500000000000000000000000001301136412000137045ustar00rootroot00000000000000node-tmatch-3.0.0/.travis.yml000066400000000000000000000001121301136412000160070ustar00rootroot00000000000000sudo: false language: node_js node_js: - '0.10' - '4' - '5' - '6' node-tmatch-3.0.0/LICENSE000066400000000000000000000013751301136412000147170ustar00rootroot00000000000000The ISC License Copyright (c) Isaac Z. Schlueter and Contributors Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. node-tmatch-3.0.0/README.md000066400000000000000000000071771301136412000151770ustar00rootroot00000000000000# tmatch This module exists to facilitate the `t.match()` method in [`tap`](http://npm.im/tap). It checks whether a value matches a given "pattern". A pattern is an object with a set of fields that must be in the test object, or a regular expression that a test string must match, or any combination thereof. The algorithm is borrowed heavily from [`only-shallow`](http://npm.im/only-shallow), with some notable differences with respect to the handling of missing properties and the way that regular expressions are compared to strings. ## usage ```javascript var matches = require('tmatch') if (!matches(testObject, pattern)) console.log("yay! diversity!"); // somewhat more realistic example.. http.get(someUrl).on('response', function (res) { var expect = { statusCode: 200, headers: { server: /express/ } } if (!tmatch(res, expect)) { throw new Error('Expect 200 status code from express server') } }) ``` ## details Copied from the source, here are the details of `tmatch`'s algorithm: 1. If the object loosely equals the pattern, and either they're both objects or neither objects, then return true. Note that this covers object identity, some type coercion, and matching `null` against `undefined`, and avoids some stuff like `1 == [1]`. 2. If the object is a RegExp and the pattern is also a RegExp, return true if their source, global, multiline, lastIndex, and ignoreCase fields all match. 3. If the pattern is a RegExp, then return true if `pattern.test(object)`, casting the object to a string if it is not already a string. 4. If the object is a string and the pattern is a non-empty string, then return true if the string occurs within the object. 5. If the object and the pattern are both Date objects, then return true if they represent the same date. 6. If the object is a Date object, and the pattern is a string, then return true if the pattern is parseable as a date that is the same date as the object. 7. If the object is an `arguments` object, or the pattern is an `arguments` object, then cast them to arrays and compare their contents. 8. If the pattern is the `Buffer` constructor, then return true if the object is a Buffer. 9. If the pattern is the `Function` constructor, then return true if the object is a function. 10. If the pattern is the String constructor, then return true if the pattern is a string. 11. If the pattern is the Boolean constructor, then return true if the pattern is a boolean. 12. If the pattern is the Array constructor, then return true if the pattern is an array. 13. If the pattern is any function, and then object is an object, then return true if the object is an `instanceof` the pattern. 14. At this point, if the object or the pattern are not objects, then return false (because they would have matched earlier). 15. If the object is a buffer, and the pattern is also a buffer, then return true if they contain the same bytes. 16. At this point, both object and pattern are object type values, so compare their keys: 1. Get list of all iterable keys in pattern and object. If both are zero (two empty objects), return true. 2. Check to see if this pattern and this object have been tested already (to handle cycles). If so, return true, since the check higher up in the stack will catch any mismatch. 3. For each key in the pattern, match it against the corresponding key in object. Missing keys in object will be resolved to `undefined`, so it's possible to use `{foo:null}` as a pattern to ensure that the object *doesn't* have a `foo` property. ## license ISC. Go nuts. node-tmatch-3.0.0/index.js000066400000000000000000000112631301136412000153540ustar00rootroot00000000000000'use strict' function isArguments (obj) { return Object.prototype.toString.call(obj) === '[object Arguments]' } module.exports = match function match (obj, pattern) { return match_(obj, pattern, [], []) } /* istanbul ignore next */ var log = (/\btmatch\b/.test(process.env.NODE_DEBUG || '')) ? console.error : function () {} function match_ (obj, pattern, ca, cb) { log('TMATCH', typeof obj, pattern) if (obj == pattern) { log('TMATCH same object or simple value, or problem') // if one is object, and the other isn't, then this is bogus if (obj === null || pattern === null) { return true } else if (typeof obj === 'object' && typeof pattern === 'object') { return true } else if (typeof obj === 'object' && typeof pattern !== 'object') { return false } else if (typeof obj !== 'object' && typeof pattern === 'object') { return false } else { return true } } else if (obj === null || pattern === null) { log('TMATCH null test, already failed ==') return false } else if (pattern instanceof RegExp) { if (typeof obj === 'string') { log('TMATCH string~=regexp test') return pattern.test(obj) } else if (obj instanceof RegExp) { log('TMATCH regexp~=regexp test') return obj.source === pattern.source && obj.global === pattern.global && obj.multiline === pattern.multiline && obj.lastIndex === pattern.lastIndex && obj.ignoreCase === pattern.ignoreCase } else { log('TMATCH stringify~=regexp test') return pattern.test('' + obj) } } else if (typeof obj === 'string' && typeof pattern === 'string' && pattern) { log('TMATCH string~=string test') return obj.indexOf(pattern) !== -1 } else if (obj instanceof Date && pattern instanceof Date) { log('TMATCH date test') return obj.getTime() === pattern.getTime() } else if (obj instanceof Date && typeof pattern === 'string') { log('TMATCH date~=string test') return obj.getTime() === new Date(pattern).getTime() } else if (isArguments(obj) || isArguments(pattern)) { log('TMATCH arguments test') var slice = Array.prototype.slice return match_(slice.call(obj), slice.call(pattern), ca, cb) } else if (pattern === Buffer) { log('TMATCH Buffer ctor') return Buffer.isBuffer(obj) } else if (pattern === Function) { log('TMATCH Function ctor') return typeof obj === 'function' } else if (pattern === Number) { log('TMATCH Number ctor (finite, not NaN)') return typeof obj === 'number' && obj === obj && isFinite(obj) } else if (pattern !== pattern) { log('TMATCH NaN') return obj !== obj } else if (pattern === String) { log('TMATCH String ctor') return typeof obj === 'string' } else if (pattern === Boolean) { log('TMATCH Boolean ctor') return typeof obj === 'boolean' } else if (pattern === Array) { log('TMATCH Array ctor', pattern, Array.isArray(obj)) return Array.isArray(obj) } else if (typeof pattern === 'function' && typeof obj === 'object') { log('TMATCH object~=function') return obj instanceof pattern } else if (typeof obj !== 'object' || typeof pattern !== 'object') { log('TMATCH obj is not object, pattern is not object, false') return false } else if (Buffer.isBuffer(obj) && Buffer.isBuffer(pattern)) { log('TMATCH buffer test') if (obj.equals) { return obj.equals(pattern) } else { if (obj.length !== pattern.length) return false for (var j = 0; j < obj.length; j++) if (obj[j] != pattern[j]) return false return true } } else { // both are objects. interesting case! log('TMATCH object~=object test') var kobj = Object.keys(obj) var kpat = Object.keys(pattern) log(' TMATCH patternkeys=%j objkeys=%j', kpat, kobj) // don't bother with stack acrobatics if there's nothing there if (kobj.length === 0 && kpat.length === 0) return true // if we've seen this exact pattern and object already, then // it means that pattern and obj have matching cyclicalness // however, non-cyclical patterns can match cyclical objects log(' TMATCH check seen objects...') var cal = ca.length while (cal--) if (ca[cal] === obj && cb[cal] === pattern) return true ca.push(obj); cb.push(pattern) log(' TMATCH not seen previously') var key for (var l = kpat.length - 1; l >= 0; l--) { key = kpat[l] log(' TMATCH test obj[%j]', key, obj[key], pattern[key]) if (!match_(obj[key], pattern[key], ca, cb)) return false } ca.pop() cb.pop() log(' TMATCH object pass') return true } /* istanbul ignore next */ throw new Error('impossible to reach this point') } node-tmatch-3.0.0/package.json000066400000000000000000000011121301136412000161650ustar00rootroot00000000000000{ "name": "tmatch", "version": "3.0.0", "description": "This module exists to facilitate the `t.match()` method in [`tap`](http://npm.im/tap).", "main": "index.js", "scripts": { "test": "tap test/*.js --100" }, "repository": { "type": "git", "url": "git+https://github.com/isaacs/tmatch.git" }, "author": "Isaac Z. Schlueter (http://blog.izs.me/)", "license": "ISC", "bugs": { "url": "https://github.com/isaacs/tmatch/issues" }, "homepage": "https://github.com/isaacs/tmatch#readme", "devDependencies": { "tap": "^7.1.2" } } node-tmatch-3.0.0/test/000077500000000000000000000000001301136412000146635ustar00rootroot00000000000000node-tmatch-3.0.0/test/deep.js000066400000000000000000000137601301136412000161450ustar00rootroot00000000000000var tap = require('tap') var test = tap.test var match = require('../') test("shouldn't care about key order and types", function (t) { t.ok(match({ a: 1, b: 2 }, { b: 2, a: '1' })) t.end() }) test('should notice objects with different shapes', function (t) { t.notOk(match( { a: 1, b: 'a thing' }, { a: 1, b: undefined } )) t.ok(match( { a: 1 }, { a: 1, b: undefined } )) t.end() }) test('extra keys in object are ok', function (t) { t.ok(match( { a: 1, b: null, c: 'ok' }, { a: 1, b: undefined } )) t.end() }) test('should notice objects with different keys', function (t) { t.notOk(match( { a: 1, b: 2 }, { a: 1, c: 2 } )) t.end() }) test('should handle dates', function (t) { t.notOk(match(new Date('1972-08-01'), null)) t.notOk(match(new Date('1972-08-01'), undefined)) t.ok(match(new Date('1972-08-01'), new Date('1972-08-01'))) t.ok(match({ x: new Date('1972-08-01') }, { x: new Date('1972-08-01') })) t.end() }) test('should handle RegExps', function (t) { t.notOk(match(/[b]/, /[a]/)) t.notOk(match(/[a]/i, /[a]/g)) t.ok(match(/[a]/, /[a]/)) t.ok(match(/ab?[a-z]{,6}/g, /ab?[a-z]{,6}/g)) t.notOk(match([1, 2, 3], /asdf/)) t.ok(match([1, 2, 3], /,2,/)) t.ok(match({ x: 123 }, { x: /^\w+/ })) t.ok(match({ toString: function () { return 'FooBar' }}, /^FooBar$/)) t.notOk(match({ toString: function () { return 'x' }}, /^FooBar$/)) t.end() }) test('should handle functions', function (t) { var fnA = function (a) { return a } var fnB = function (a) { return a } t.notOk(match( function a () {}, function a () {} // but is it the _same_ a tho )) t.notOk(match(fnA, fnB)) t.ok(match(fnA, fnA)) t.ok(match(fnB, fnB)) t.end() }) test('should handle arguments', function (t) { var outer = arguments ;(function (tt) { var inner = arguments t.ok(match(outer, outer)) t.ok(match(outer, inner)) t.ok(match(outer, [t])) }(t)) t.end() }) test('same arrays match', function (t) { t.ok(match([1, 2, 3], [1, 2, 3])) t.end() }) test("different arrays don't match", function (t) { t.notOk(match([1, 2, 3], [1, 2, 3, 4])) t.notOk(match([1, 2, 3], [1, 2, 4])) t.end() }) test('empty arrays match', function (t) { t.ok(match([], [])) t.ok(match({ x: [] }, { x: [] })) t.end() }) test("shallower shouldn't care about key order recursively and types", function (t) { t.ok(match( { x: { a: 1, b: 2 }, y: { c: 3, d: 4 } }, { y: { d: 4, c: 3 }, x: { b: '2', a: '1' } } )) t.end() }) test('undefined is the same as itself', function (t) { t.ok(match(undefined, undefined)) t.ok(match({ x: undefined }, { x: undefined })) t.ok(match({ x: [undefined] }, { x: [undefined] })) t.end() }) test('undefined and null are Close Enough', function (t) { t.ok(match(undefined, null)) t.ok(match({ x: null }, { x: undefined })) t.ok(match({ x: [undefined] }, { x: [null] })) t.end() }) test("null is as shallow as you'd expect", function (t) { t.ok(match(null, null)) t.ok(match({ x: null }, { x: null })) t.ok(match({ x: [null] }, { x: [null] })) t.end() }) test('the same number matches', function (t) { t.ok(match(0, 0)) t.ok(match(1, 1)) t.ok(match(3.14, 3.14)) t.end() }) test("different numbers don't match", function (t) { t.notOk(match(0, 1)) t.notOk(match(1, -1)) t.notOk(match(3.14, 2.72)) t.end() }) test("tmatch shouldn't care about key order (but still might) and types", function (t) { t.ok(match( [ { foo: { z: 100, y: 200, x: 300 } }, 'bar', 11, { baz: { d: 4, a: 1, b: 2, c: 3 } } ], [ { foo: { z: 100, y: 200, x: 300 } }, 'bar', 11, { baz: { a: '1', b: '2', c: '3', d: '4' } } ] )) t.end() }) test("match shouldn't blow up on circular data structures", function (t) { var x1 = { z: 4 } var y1 = { x: x1 } x1.y = y1 var x2 = { z: 4 } var y2 = { x: x2 } x2.y = y2 t.ok(match(x1, x2)) t.end() }) test('regexps match strings', function (t) { var x = { one: 'String' } var y = { one: /.ring$/ } t.ok(match(x, y)) t.ok(match(x.one, y.one)) x = [ 'String', 'String' ] y = [ /.ring$/, /.ring$/ ] t.ok(match(x, y)) x = [ 'Ring', /.ring$/ ] y = [ /.ring$/ ] t.notOk(match(x, y)) t.end() }) test('partial strings match on indexOf', function (t) { var x = { one: 'String' } var y = { one: 'rin' } t.ok(match(x, y)) t.notOk(match(y, x)) t.end() }) test('ctors and other fun things', function (t) { function Foo () { this._isFoo = 'foo' } if (!Buffer.prototype.equals) { Buffer.prototype.equals = function (pattern) { var obj = this if (obj.length !== pattern.length) return false for (var j = 0; j < obj.length; j++) if (obj[j] != pattern[j]) return false return true } } t.notOk(match(new Buffer('asdf'), new Buffer('asdff'))) var d = new Date().toISOString() var obj = { buffer: new Buffer('x'), date: new Date(d), fn: function () {}, foo: new Foo(), num: 1.2, nan: NaN, bool: true, array: [], str: 'asdf', inf: Infinity, neginf: -Infinity } t.ok(match(obj, { buffer: Buffer, date: Date, fn: Function, foo: Foo, num: Number, nan: NaN, bool: Boolean, array: Array, str: String })) t.ok(match(obj, { buffer: new Buffer('x'), date: d, foo: new Foo(), str: 'sd' })) var buf = new Buffer('x') buf.equals = null t.ok(match(obj, { buffer: buf })) var buf2 = new Buffer('y') buf2.equals = null t.notOk(match(buf, buf2)) var buf3 = new Buffer('xy') buf3.equals = null t.notOk(match(buf, buf3)) var buf4 = new Buffer('xy') buf4.equals = null t.ok(match(buf4, buf3)) t.notOk(match(obj, { inf: Number })) t.notOk(match(obj, { neginf: Number })) t.notOk(match(obj, { nan: Number })) t.end() }) test('js WAT! array/string stuff', function (t) { t.notOk(match([1], 1)) t.notOk(match(1, [1])) t.ok(match([1], [1])) var o = {} t.ok(match(o, o)) t.ok(match(1, '1')) t.end() })