New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

the-thing-is

Package Overview
Dependencies
Maintainers
2
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

the-thing-is - npm Package Compare versions

Comparing version 0.3.0 to 0.4.0

.jshintrc

9

package.json
{
"name": "the-thing-is",
"version": "0.3.0",
"version": "0.4.0",
"description": "Configuration drivin validation",
"main": "the-thing-is.js",
"scripts": {
"test": "mocha -R spec test/the-thing-is.js"
"test": "mocha -R spec"
},

@@ -22,4 +22,7 @@ "repository": {

"dependencies": {
"is-too": "^2.0.0"
"is-too": "^2.2.0"
},
"devDependencies": {
"mocha": "^2.1.0"
}
}
# the-thing-is
Configuration Driven Validation with a cute name.
> _"You see, the thing is..."_
Uses [`is-it`](https://github.com/mrDarcyMurphy/is) behind the scenes for the comparisons.
...now you can thoroughly and precisely measure your subjects against a series of standards.
`the('16').is(['present', 'numberString', {greaterThanOrEqualTo:0}]) // true`
This thing is _great_ for front-end form validation.
[`is-too`](https://github.com/LoudBit/is-too) does comparisons behind the scenes. `the-thing-is` handles the organization of multiple checks. See `is-too`'s README for a list of what's available.
## Basic usage
``` javascript
var the = require('the-thing-is')
var thing = 16
the('thing').is('present') // true
```
// describe your object with an array to ensure the order of evaluation
### Array of Standards
Use an array to describe your subject with a series of attributes that will be evaluated in order.
``` javascript
var whatYouExpect = ['present', 'integer', {greaterThan:0, lessThan:256}]
if (the(thing).is(whatYouExpect)) {
fuckYeah()
the(16).is(whatYouExpect) // true
the(640).is(whatYouExpect) // false
```
### Tree of Standards
This is where the true value of `the-thing-is` is. Trees can be as deep as you want, `the-thing-is` works through it recursively.
Should the check fail, it'll record the path to the offending branch of the tree and store it as an error.
``` javascript
var userSchema = {
name: ['string'],
address: {
street1: ['present', 'string', {matches: /.*/}],
street2: ['string'],
city: ['present', 'string'],
state: ['present', 'string', {matches: /^[A-Z]{2}$/}],
zip: ['present', 'string', {matches: /^[0-9]{5}$/}]
}
}
if (the(thing).isnt(whatYouExpect)) {
throwSomething(the.last.error)
var user = {
name: "Joe Bob",
address: {
street1: '123 Any St.',
street2: '',
city: 'Anytown',
state: 'NE',
zip: 12345
}
}
the(user).is(userSchema) // false
the.last.error // [{ 'address.zip': ['string'] }]
```
### Negative Standard
If your subject is like an unruly teenager and you expect it to fail to live up to your standards all the time, then just say so.
``` javascript
var teenager = undefined
the(teenager).isnt('present') // true
the.last.error // ['present']
```
## Errors
If your subject fails to live up to the standard you've set then `the-thing-is` will dutifully list all the ways it does so, in a way intended to be useful enough for you to deliver a useful message to your users.
- Failed simple checks will return a string.
- Failed objects will return an object with a key referring to the branch in the tree, and an array of failures.
Altogether, `the-thing-is` will return an array of all your subject's failures.
``` javascript
the.last.error == ['number']
the.last.error == [{greaterThan:0}]
the.last.error == [{'foo.bar': ['number']}]
the.last.error == [{'foo.bar': ['number', {greaterThan:0}]}]
```
Additionally, if you describe your object using standards that don't exist in `is-too` then `the-thing-is` will throw a TypeError.
``` javascript
the('thing').is('gonnaThrowUp')
// => TypeError("`gonnaThrowUp` isn't a valid comparison method.")
```

@@ -0,1 +1,5 @@

/* jshint quotmark:false */
/* global describe, it */
'use strict'
var assert = require('assert')

@@ -39,3 +43,3 @@ var the = require('../the-thing-is')

assert(the.last.thing === 0)
assert(!the.last.error)
assert(!the.last.error.length)
})

@@ -51,3 +55,3 @@ })

assert.equal(the.last.thing, 'thing')
assert(!the.last.error)
assert(!the.last.error.length)
})

@@ -78,3 +82,3 @@ })

it('sets the.last.error', function() {
assert(the.last.error)
assert.deepEqual(the.last.error, ['number', 'integer'])
})

@@ -86,7 +90,7 @@ })

describe("a test of a standard that doesn't exist", function(){
describe("the('thing').is([{gonnaGetThrown:true}])", function(){
it("should throw an error", function(){
var whatIExpect = [{gonnaGetThrown:true}]
assert.throws(function(){
the(0).is(whatIExpect)
the('thing').is(whatIExpect)
}, TypeError)

@@ -96,3 +100,3 @@ })

describe("an incorrect test of a standard", function(){
describe("the('0').is([{number:'wrong'}])", function(){
it("shouldn't throw an error", function(){

@@ -106,26 +110,37 @@ var whatIExpect = [{number:'wrong'}]

describe("a complex expectation that passes", function(){
it('passes all the conditions', function(){
describe("the('0')", function(){
it(".is(['string', 'integerString', {gte: 0, lte:100}])", function(){
var whatIExpect = [
'string', 'integerString',
{
// this is a trick - `number:false` is true
// because `number` isn't a check against a standard
number: false,
integerString: true,
'string', 'integerString', {
gte: 0,
lte: 100
}
]
}]
assert( the('0').is(whatIExpect) )
assert( the.last.error.length )
})
it(".is([{ equal:0 }])", function(){
assert( the('0').is([{ equal: 0 }]) )
})
it(".is([{ exactly: '0' }])", function(){
assert( the('0').is([{ exactly: '0' }]) )
})
it(".isnt([{ equal: 1 }])", function(){
assert( the('0').isnt([{ equal: 1 }]) )
assert.deepEqual( the.last.error, [{equal:'1'}])
})
it(".isnt([{ equal: '1' }])", function(){
assert( the('0').isnt([{ equal: '1' }]) )
assert.deepEqual( the.last.error, [{equal:'1'}])
})
})
describe("a complex expectation that's not met", function(){
it("doesn't meet the expectations", function(){
the('0').is(['string', 'integerString', {gte:0, lte:100}])
assert( the.last.error.length )
describe("the('101')", function(){
it(".isnt(['string', 'integerString', {gte: 0, lte:100}])", function(){
var whatIExpect = [
'string', 'integerString', {
gte: 0,
lte: 100
}]
assert( the('101').isnt(whatIExpect), false )
})
it("sets a meaningful (enough) error message", function(){
assert.equal( the.last.error[0], "See, the thing is, 0 (string) isn't gte 0.")
it("creates a meaningful error array", function(){
assert.deepEqual( the.last.error, [{lte:100}])
})

@@ -167,5 +182,163 @@ })

assert.equal(the.last.thing, 'thing')
assert(!the.last.error)
assert(!the.last.error.length)
})
})
})
describe('testing objects', function() {
describe('{foo:"bar"}', function() {
var subject = { foo: "bar" }
it('is("plainObject")', function() {
assert.equal(the(subject).is('plainObject'), true)
})
it('is({ foo: "string" })', function() {
assert.equal(the(subject).is({ foo: "string" }), true)
})
it('is({foo:["string"]})', function() {
assert.equal(the(subject).is({ foo: ["string"] }), true)
})
it('is({bar:["string"]}) == false', function() {
assert.equal(the(subject).is({ bar:["string"] }), false)
})
it('isnt({bar:["string"]})', function() {
assert.equal(the(subject).isnt({ bar:["string"] }), true)
})
})
describe('{ foo:"bar", bar:"fizz", baz:666 }', function() {
var subject = {
foo: 'bar',
bar: 'fizz',
baz: 666
}
it('is("plainObject")', function() {
assert.equal(the(subject).is('plainObject'), true)
})
it('is({ foo:"string", bar:"string", baz:["number", {greaterThan:0}, {lessThan:777}] })', function() {
assert.equal(the(subject).is({
foo: 'string',
bar: 'string',
baz: ['number', {greaterThan:0}, {lessThan:777}]
}), true)
})
it('isnt({ flop:"string", bar:"string", baz:["number", {greaterThan:0}, {lessThan:777}] })', function() {
assert.equal(the(subject).isnt({
flop: 'string',
bar: 'string',
baz: ['number', {greaterThan:0}, {lessThan:777}]
}), true)
})
it('isnt({ foo:"string", bar:"string", baz:["number", {greaterThan:666}] })', function() {
assert.equal(the(subject).isnt({
foo: 'string',
bar: 'string',
baz: ['number', {greaterThan:666}]
}), true)
})
})
describe('{foo: {bar: {baz: {buz: "fiz"}}}}', function() {
var subject = {
foo: {
bar: {
baz: {
buz: 'fiz'
}
}
}
}
it('is("plainObject")', function() {
assert.equal(the(subject).is('plainObject'), true)
})
it('is({foo: {bar: {baz: {buz: ["string"]}}}})', function() {
assert.equal(the(subject).is({foo: {bar: {baz: {buz: ['string']}}}}), true)
})
it('isnt({foo: {bar: {baz: {buz: ["number"]}}}}) - wrong type', function() {
assert.equal(the(subject).isnt({foo: {bar: {baz: {buz: ['number']}}}}), true)
})
it('isnt({foo: {bar: {baz: {buz: {fiz: ["string"]}}}}}) - nested too deep', function() {
assert.throws(function(){
the(subject).isnt({foo: {bar: {baz: {buz: {fiz: ['string']}}}}})
}, TypeError)
})
})
describe('{foo: {bar: {baz: {buz: "fiz"}}}}', function() {
var subject = {
fo: {
foo: {
fooo: 'fooo'
},
oof: {
ooof: 'ooof'
}
},
bar: {
baz: {
buz: 'buz'
},
rab: {
zub: 'zub'
},
zab: 'zab'
}
}
it('is("plainObject")', function() {
assert.equal(the(subject).is('plainObject'), true)
})
it('is({foo: {bar: {baz: {buz: ["string"]}}}})', function() {
var whatIExpect = {
fo: {
foo: {
fooo: 'string'
},
oof: {
ooof: 'string'
}
},
bar: {
baz: {
buz: 'string'
}
}
}
assert.equal(the(subject).is(whatIExpect), true)
})
it('isnt({foo: {bar: {baz: {buz: ["number"]}}}}) - foo not at top level', function() {
assert.equal(the(subject).isnt({foo: {bar: {baz: {buz: ['number']}}}}), true)
assert.deepEqual(the.last.error, [{'foo':['present']}])
})
})
describe('things with multiple errors', function() {
it('report multiple errors', function(){
the('thing').is(['number', {greaterThan:0}, {lessThan:1000}])
assert.deepEqual( the.last.error, ['number', {greaterThan:0}, {lessThan:1000}])
})
it('objects report multiple errors too', function(){
the({
foo: 1,
bar: 2,
fizz: {
buzz: 'fizzbuzz'
}
}).is({
foo: ['number'],
bar: ['string'],
fizz: {
buzz: ['number', {greaterThan:0}]
}
})
assert.deepEqual( the.last.error, [{bar:['string']}, {'fizz.buzz': ['number', {greaterThan:0}]}] )
})
})
})
// The Thing Is
// Configuration driven validation
// with the goal of a near-english syntax (for consumers)
//
// Detailed Object Descriptions
// ...with a sort of _artistic_ quality of code
//
// Usage:

@@ -10,7 +11,6 @@ //

// var whatYouExpect = ['present', 'integer', {greaterThan:0, lessThan:256}] -- combo v2, maintains order, groups comparisons that are chaotic
// var whatYouExpect = {present:true, number:true, greaterThan:0} -- object (verbose, unreliable order)
// var whatYouExpect = [{present:true}, {number:true}, {greaterThan:0}] -- array of objects (very verbose, reliable order)
// var whatYouExpect = [{present:true}, {number:true}, {greaterThan:0}] -- array of objects (very verbose, reliable order, overkill)
//
// if (the(thing).is(whatYouExpect)) {
// fuckYeah()
// ohYeah()
// }

@@ -22,88 +22,132 @@ //

'use strict'
// Dependencies
var is = require('is-too')
// the -- function you care about
function the(thing) {
the.past.push({
thing: thing,
errors: []
})
the.last = the.past[the.past.length-1 || 0]
return comparisons
function the (thing) {
the.path = []
the.last = {
thing: thing,
error: []
}
return {
is: what,
isnt: function (expected, thing) {
return !this.is(expected, thing)
}
}
}
the.past = []
the.last = null
// is, isnt
var comparisons = {
is: function(whatYouExpect) {
// will recursively see if the(thing).is(whatYouExpect)
function what (expected, thing) {
var its = true
var expectation = null
thing = thing || the.last.thing
// the(thing).is()
// whatYouExpect is undefined or null -- so check mere presence of a thing
if ( is.not.present(whatYouExpect) )
its = is.present(the.last.thing)
// the(thing).is()
// expected is undefined or null -- so check mere presence of a thing
if ( is.not.present(expected) )
return see('present', thing)
// 'present' -- single boolean check
// the(thing).is('integer') // true/false
// the(thing).is('borkborkbork') // throw
else
if ( is.string(whatYouExpect) )
its = booleanCheck(whatYouExpect)
// 'present' -- single boolean check
// the(thing).is('integer') // true/false
// the(thing).is('borkborkbork') // throw
if ( is.string(expected) )
return see(expected, thing)
// ['present', 'number'] -- array of boolean
// the(thing).is(['present', 'integer'])
// ['present', 'number'] -- array of boolean comparisons
// ['present', 'number', {greaterThan:0}, {lessThanorEqualTo:100}] -- separated objects
// ['present', 'number', {greaterThan:0, lessThanorEqualTo:100}] -- combined object
// { foo: ['present', { bar: ['present'] }] }
// the(thing).is(['present', 'integer'])
if ( is.array(expected) )
expected.forEach(function(expected){
return what(expected, thing)
})
// { foo: ['bar'] } -- dictionary describing complex or deep objects
// the(thing).is({
// name: ['present', 'string'],
// address: {
// street: 'string',
// city: 'string',
// state: 'string',
// zip: 'string'
// }
// })
if ( is.plainObject(expected) )
if (is.plainObject(thing)) {
// stash the path to branch the tree
var pathStash = the.path.slice()
Object.keys(expected).forEach(function(key, i){
var nexThing = thing[key],
nexPectation = expected[key]
if (i > 0)
the.path.pop()
the.path.push(key)
if ( is.present(nexPectation) && is.present(nexThing) )
return what(nexPectation, nexThing)
else
return see('present', nexThing)
})
the.path = pathStash.slice();
}
else
if ( is.array(whatYouExpect) )
for (var i = 0; i < whatYouExpect.length && its == true; i++) {
expectation = whatYouExpect[i]
if (is.string(expectation))
its = booleanCheck(expectation)
else
its = subjectCheck(expectation)
Object.keys(expected).forEach(function(key, i, arr){
var standard = expected[key];
return see(key, thing, standard);
})
if (!its)
the.last.error = '' + the.last.thing + ' is not ' + whatYouExpect[i];
}
return !the.last.error.length;
// {greaterThan:0} -- configuration object with single condition
// {present:true, integer:true, greaterThan:0} -- unreliable due to unreliable hash key order
// [{present:true}, {integer:true}, {greaterThan:0}] -- reliable, but verbose
// ['present', 'integer', {greaterThanOrEqualTo:0}] -- preferred
return its
},
isnt: function(whatYouExpect) {
return !this.is(whatYouExpect)
}
}
// simple yes/no type comparisons against an expectation
function booleanCheck(expectation) {
if (is[expectation])
return is[expectation](the.last.thing)
function see (expected, thing, standard) {
var err = {},
fail = {},
failPath = the.path.join('.')
if ( is.not.string(expected) || is.not.present(is[expected]) )
throw new TypeError('`' + expected + '` isn\'t a valid comparison method.')
// good to go, so go
if ( is[expected](thing, standard) )
return true
// needs to be stored as an object
if ( is.present(standard) )
fail[expected] = standard // eg. {greaterThan:0}
else
throw new TypeError("`"+expectation+"` isn't a valid comparison method.")
}
fail = expected // eg. 'present'
function subjectCheck(expectation) {
the.last.error = []
// needs to be stored as the value of the path
if (the.path.length)
err[failPath] = [fail] // eg. {'foo.bar': ['present']}
else
err = fail // eg. 'present'
// loop through the keys in the object to
Object.keys(expectation).forEach(function(key, value){
if ( !is[key] )
throw new TypeError("`"+key+"` isn't a valid comparison method.")
if ( is[key](the.last.thing, expectation[key]) ) {
the.last.error.push(['See, the thing is, ', the.last.thing, ' (', typeof the.last.thing, ') isn\'t ', key, ' ', value, '.'].join(''));
// need to group the fails for a key in the same array
var isExisting = the.last.error.some(function something (last) {
if (is.plainObject(last) && failPath in last) {
last[failPath].push(fail)
return true
}
})
return !!the.last.error.length
if (!isExisting)
the.last.error.push(err)
return false
}

@@ -110,0 +154,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc