occamsrazor-validator
Advanced tools
Comparing version 5.0.0 to 5.1.0
42
index.js
@@ -0,1 +1,4 @@ | ||
var combineValidators = require('./lib/combine-validators'); | ||
var ValidationResult = require('./lib/validation-result'); | ||
var isAnything = function (obj){ | ||
@@ -8,3 +11,8 @@ return true; | ||
var i, len, out = {}; | ||
if (typeof o === 'string' || typeof o === 'number' || typeof o === 'boolean' || o === null) { | ||
if (typeof o === 'undefined') { | ||
return function () { | ||
return true; | ||
} | ||
} | ||
else if (typeof o === 'string' || typeof o === 'number' || typeof o === 'boolean' || o === null) { | ||
return function (s){ | ||
@@ -23,5 +31,14 @@ return s === o; | ||
else if (Array.isArray(o)){ | ||
return function (arr) { | ||
if (!Array.isArray(arr)) return false; | ||
for (var i = 0; i < o.length; i++) { | ||
if (!shortcut_validators.match(o[i])(arr[i])){ | ||
return false; | ||
} | ||
} | ||
return true; | ||
}; | ||
for(i = 0, len = o.length; i < len;i++){ | ||
if (typeof o[i] !== 'string'){ | ||
throw new Error("Occamsrazor (match): The argument can be a string, number, boolean, null, regular expression, a function, an object or an array of strings"); | ||
throw new Error("Occamsrazor (match): The argument can be a string, number, boolean, undefined, null, regular expression, a function, an object or an array of strings"); | ||
} | ||
@@ -37,8 +54,5 @@ out[o[i]] = undefined; | ||
if (!(k in obj)) return false; | ||
if (typeof o[k] !== 'undefined'){ | ||
if (!shortcut_validators.match(o[k])(obj[k])){ | ||
return false; | ||
} | ||
if (!shortcut_validators.match(o[k])(obj[k])){ | ||
return false; | ||
} | ||
// undefined continue | ||
} | ||
@@ -50,2 +64,13 @@ return true; | ||
}, | ||
has: function () { | ||
var args = Array.prototype.slice.call(arguments); | ||
var i, out = {}; | ||
for(i = 0, len = args.length; i < len;i++){ | ||
if (typeof args[i] !== 'string'){ | ||
throw new Error("Occamsrazor (has): The arguments can only be strings"); | ||
} | ||
out[args[i]] = undefined; | ||
} | ||
return shortcut_validators.match(out); | ||
}, | ||
isPrototypeOf: function (proto){ | ||
@@ -71,3 +96,3 @@ return function (obj){return proto.isPrototypeOf(obj);}; | ||
} | ||
return total + baseScore; | ||
return new ValidationResult(total + baseScore, obj); | ||
}; | ||
@@ -107,3 +132,4 @@ | ||
validator.shortcut_validators = shortcut_validators; | ||
validator.combine = combineValidators; | ||
module.exports = validator; |
{ | ||
"name": "occamsrazor-validator", | ||
"version": "5.0.0", | ||
"version": "5.1.0", | ||
"description": "A duck-typing library", | ||
@@ -33,4 +33,3 @@ "main": "index.js", | ||
}, | ||
"dependencies": { | ||
} | ||
"dependencies": {} | ||
} |
116
README.md
@@ -5,10 +5,11 @@ occamsrazor-validator | ||
This is an helper library that helps to perform variable validation with a simple syntax. | ||
The main goal of the library is to be used for duck-typing. For this reason: | ||
This is a validator library with a very specific goal. Identify if a value matches some criteria using (duck typing)[https://en.wikipedia.org/wiki/Duck_typing] and assigning a score to the match. | ||
* It detects what an object is rather than what it is not | ||
* It returns a "score" (or null). The score is based on how many validation pass and gives you an index of how specific is a validation | ||
Given the goal it is not a full fledged schema validator library. For this reason: | ||
It is a part of occamsrazor (https://github.com/sithmel/occamsrazor.js) that uses this library for picking the right adapter for a specific set of arguments. | ||
* It identifies what an object "has" rather than what it "is" | ||
* An object passing the validation returns a "validationResult". This object has a value, based on how many validation steps has passed. | ||
It is a part of occamsrazor (https://github.com/sithmel/occamsrazor.js) that uses this library for picking the right function for a specific set of arguments. | ||
Importing the library | ||
@@ -22,4 +23,4 @@ ===================== | ||
===================== | ||
Ultimately a validator is a function. When it runs against an object, it returns null or a score. | ||
Null value means that an object doesn't fit in the validator's constraints. | ||
Ultimately a validator is a function. When it runs against an object, it returns null or a score (wrapped in a validationResult for convenience). | ||
Null means that an object doesn't fit in the validator's constraints. | ||
The score represent how well the object fits (its specificity). For example: | ||
@@ -31,6 +32,6 @@ | ||
"validator()"" creates the simplest validator. It matches everything with score 1: | ||
"validator()" creates the simplest validator. It matches everything with score 1: | ||
```js | ||
isAnything('hello'); // returns 1 | ||
isAnything({width: 10}); // returns 1 | ||
isAnything('hello').value(); // 1 | ||
isAnything({width: 10}).value(); // 1 | ||
``` | ||
@@ -43,13 +44,7 @@ You can chain a function to create a more strict validation: | ||
``` | ||
or | ||
So for example, the score of this new validator will be 2: | ||
```js | ||
var hasWheels = validator().match(function (obj){ | ||
return 'wheels' in obj; | ||
}); | ||
hasWheels({wheels: 4}).value(); // 2 | ||
hasWheels({feet: 2}); // returns null | ||
``` | ||
So for example, the score of this new validator will be 2:: | ||
```js | ||
hasWheels({wheels: 4}); // returns 2 | ||
hasWheels({feet: 2}); // returns null | ||
``` | ||
You can go on having a more specific validator: | ||
@@ -94,3 +89,3 @@ ```js | ||
``` | ||
Or numbers:: | ||
Or numbers: | ||
```js | ||
@@ -101,7 +96,7 @@ var is_ten = validator().match(10); | ||
It works for booleans and null in the same way. | ||
If you pass an array of strings it will match with an object containing all the attributes with those names: | ||
If you pass an array it will match with any element of an input array with its content: | ||
```js | ||
var has_width_and_height = validator().match(['width', 'height']); | ||
var has_1_2 = validator().match([1, 2]); | ||
``` | ||
Finally you can perform deep property checking using an object and combining the previous checks:: | ||
Finally you can perform deep property checking using an object and combining the previous checks: | ||
```js | ||
@@ -112,3 +107,3 @@ // match with width and height equal to 10 | ||
// match with a center attribute with x and y subattributes | ||
var has_center = validator().match({center: ['x', 'y']}); | ||
var has_center = validator().match({center: {x: undefined, y: undefined}}); | ||
@@ -121,6 +116,7 @@ // match if obj.recipe.ingredients is a string and match with /nuts/ | ||
``` | ||
There are other 2 helpers available for checking against the prototype or the constructor function:: | ||
There are other 3 helpers available:: | ||
```js | ||
var is_prototype_rect = validator().isPrototypeOf(rect.prototype); | ||
var is_instance_rect = validator().isInstanceOf(Rect); | ||
var has_attr = validator().has('width', 'height'); | ||
``` | ||
@@ -140,6 +136,44 @@ If you need a custom validator you can extend the object validator.shortcut_validators:: | ||
.isInstanceOf(Rect) | ||
.match(['width', 'height']) | ||
.has('width', 'height') | ||
.isSquare(); | ||
``` | ||
Combine validators | ||
------------------ | ||
You might want to match a group of values. You can do it combine as many validators you want: | ||
```js | ||
var isNumber = validator().match(function (n) { | ||
return typeof n === 'number'; | ||
}); | ||
var is5 = isNumber.match(5); | ||
var is8 = isNumber.match(8); | ||
var v = combineValidators(isNumber, is5, is8); | ||
``` | ||
and then trying to make it match: | ||
```js | ||
v(1, 5, 8).value(); // it will return [2, 3, 3] | ||
``` | ||
If all values match, the validator will return a validationResult with value [2, 3, 3]. | ||
The elements of the array are the values of the respective validators. | ||
If one of them doesn't match the result will be null: | ||
```js | ||
v(1, 5, 5); // it will return null | ||
``` | ||
When the value returned is an array it is compared in this way (alphabetically): | ||
```js | ||
[2, 3, 4] > [2, 2, 5] | ||
[1] < [1, 2] | ||
[2] > [1, 9, 5] | ||
``` | ||
Sort and compare results | ||
------------------------ | ||
The result validator object has an useful property. It can be sorted and compared (greater than, lesser than) as it was a basic js type. You can use the output of the toString attribute to compare for equality: | ||
```js | ||
r0 > r1 | ||
var results = [r0, r1, r2, r3]; | ||
r.sort(); | ||
r0.toString() === r1.toString() | ||
``` | ||
Syntax | ||
@@ -159,3 +193,3 @@ ====== | ||
validator().score | ||
----------------------------- | ||
----------------- | ||
@@ -169,3 +203,3 @@ Syntax: | ||
validator().important | ||
--------------------------------- | ||
--------------------- | ||
@@ -179,5 +213,5 @@ Syntax: | ||
validator().match | ||
----------------------------- | ||
----------------- | ||
Add a check to the validator, it uses a special syntax (used by default by .add, .one). | ||
Add a check to the validator, using an expressive syntax. | ||
@@ -199,7 +233,10 @@ Syntax: | ||
// matches if value is equal to the number | ||
var validator = validator().match(number); | ||
// matches if value matches with the regular expression | ||
var validator = validator().match(regular_expression); | ||
// matches if these properties are defined in the object | ||
var validator = validator().match([array of property names]); | ||
// matches if these items matches respectively | ||
var validator = validator().match([items]); | ||
@@ -209,3 +246,3 @@ // deep matching | ||
``` | ||
This last form allows to perform the validation check recursively, walking the properties of the object. | ||
The last two forms allow to perform the validation check recursively, walking the properties of the object/array. | ||
In a property is undefined the value will match any value. | ||
@@ -228,3 +265,3 @@ | ||
validator().isPrototypeOf | ||
------------------------------------- | ||
------------------------- | ||
Check if an object is a prototype of another. | ||
@@ -238,3 +275,3 @@ | ||
validator().instanceOf | ||
------------------------------------- | ||
---------------------- | ||
Check if an object is an instance of a constructor. | ||
@@ -247,2 +284,11 @@ | ||
validator().has | ||
--------------- | ||
Check if an object has attributes with a specific names. | ||
Syntax: | ||
```js | ||
var validator = validator().has(attr1, attr2, ...); | ||
``` | ||
validator.shortcut_validators | ||
@@ -249,0 +295,0 @@ ------------------------------- |
@@ -16,3 +16,3 @@ var assert = require('chai').assert; | ||
var is5 = validator().is5() | ||
assert.equal(is5(5), 2); | ||
assert.equal(is5(5).value(), 2); | ||
assert.isNull(is5(6)); | ||
@@ -34,3 +34,3 @@ }); | ||
var isSquareProto = validator().isPrototypeOf(Square.prototype) | ||
assert.equal(isSquareProto(square), 2); | ||
assert.equal(isSquareProto(square).value(), 2); | ||
}); | ||
@@ -40,4 +40,25 @@ | ||
var isSquareInstance = validator().isInstanceOf(Square) | ||
assert.equal(isSquareInstance(square), 2); | ||
assert.equal(isSquareInstance(square).value(), 2); | ||
}); | ||
}); | ||
describe('has', function () { | ||
var isAnything, hasWidthAndHeight; | ||
before(function () { | ||
isAnything = validator(); | ||
hasWidthAndHeight = isAnything.has('width', 'height'); | ||
}); | ||
it('must set score correctly', function () { | ||
assert.equal(hasWidthAndHeight.score(), 2); | ||
}); | ||
it('must match', function () { | ||
assert.equal(hasWidthAndHeight({width: 8, height: 10}).value(), 2); | ||
}); | ||
it('must not match', function () { | ||
assert.isNull(hasWidthAndHeight({width: 12})); | ||
}); | ||
}); |
@@ -18,2 +18,6 @@ var assert = require('chai').assert; | ||
}); | ||
it('must be clear that is a validator', function () { | ||
assert(typeof is_hello === 'function' && 'score' in is_hello); | ||
}); | ||
}); |
@@ -6,5 +6,11 @@ var assert = require('chai').assert; | ||
it('must match using undefined', function () { | ||
var is_anything = validator().match(undefined); | ||
assert.equal(is_anything('hello').value(), 2); | ||
assert.equal(is_anything('nothello').value(), 2); | ||
}); | ||
it('must match using a string', function () { | ||
var is_hello = validator().match('hello'); | ||
assert.equal(is_hello('hello'), 2); | ||
assert.equal(is_hello('hello').value(), 2); | ||
assert.isNull(is_hello('nothello')); | ||
@@ -15,3 +21,3 @@ }); | ||
var is_hello = validator().match(/hello/); | ||
assert.equal(is_hello('hello world'), 2); | ||
assert.equal(is_hello('hello world').value(), 2); | ||
assert.isNull(is_hello('good morning world')); | ||
@@ -36,4 +42,4 @@ }); | ||
it('must match', function () { | ||
assert.equal(hasWidth({width: 1, height: 2}), 2); | ||
assert.equal(hasHeight_hasWidth({width: 1, height: 2}), 2); | ||
assert.equal(hasWidth({width: 1, height: 2}).value(), 2); | ||
assert.equal(hasHeight_hasWidth({width: 1, height: 2}).value(), 2); | ||
}); | ||
@@ -61,4 +67,4 @@ }); | ||
it('must match', function () { | ||
assert.equal(hasWidth10({width: "10"}), 2); | ||
assert.equal(hasX10({center: {x:"10", y:"1"}}), 2); | ||
assert.equal(hasWidth10({width: "10"}).value(), 2); | ||
assert.equal(hasX10({center: {x:"10", y:"1"}}).value(), 2); | ||
}); | ||
@@ -92,6 +98,6 @@ | ||
it('must match', function () { | ||
assert.equal(hasWidthbetween5and10({width: 8}), 2); | ||
assert.equal(hasWidthbetween5and10({width: 8}).value(), 2); | ||
assert.equal(isNotANumber(NaN), 2); | ||
assert.equal(isArray([1, 2, 3]), 2); | ||
assert.equal(isNotANumber(NaN).value(), 2); | ||
assert.equal(isArray([1, 2, 3]).value(), 2); | ||
}); | ||
@@ -113,2 +119,6 @@ | ||
hasWidthAndHeight = isAnything.match(['width', 'height']); | ||
hasWidthAsSecondField = isAnything.match([undefined, 'width']); | ||
hasSecondNumberOdd = isAnything.match([undefined, function (n) { | ||
return n % 2; | ||
}]); | ||
}); | ||
@@ -121,8 +131,19 @@ | ||
it('must match', function () { | ||
assert.equal(hasWidthAndHeight({width: 8, height: 10}), 2); | ||
assert.equal(hasWidthAndHeight(['width', 'height']).value(), 2); | ||
}); | ||
it('must not match', function () { | ||
assert.isNull(hasWidthAndHeight({width: 12})); | ||
assert.isNull(hasWidthAndHeight(['width'])); | ||
}); | ||
it('must match only second item', function (){ | ||
assert.equal(hasWidthAsSecondField(['xxx', 'width']).value(), 2); | ||
assert.isNull(hasWidthAsSecondField(['width'])); | ||
}); | ||
it('must match only second item with function', function (){ | ||
assert.equal(hasSecondNumberOdd(['xxx', 3]).value(), 2); | ||
assert.isNull(hasSecondNumberOdd(['xxx', 2])); | ||
}); | ||
}); | ||
@@ -143,3 +164,3 @@ | ||
it('must match', function () { | ||
assert.equal(isNull(null), 2); | ||
assert.equal(isNull(null).value(), 2); | ||
}); | ||
@@ -167,4 +188,4 @@ | ||
it('must match', function () { | ||
assert.equal(isTrue(true), 2); | ||
assert.equal(isFalse(false), 2); | ||
assert.equal(isTrue(true).value(), 2); | ||
assert.equal(isFalse(false).value(), 2); | ||
}); | ||
@@ -171,0 +192,0 @@ |
@@ -51,13 +51,13 @@ var assert = require('chai').assert; | ||
it('must pass generic validation', function () { | ||
assert.equal(is_anything(instrument), 1); | ||
assert.equal(is_anything(drum), 1); | ||
assert.equal(is_anything(guitar), 1); | ||
assert.equal(is_anything(electricguitar), 1); | ||
assert.equal(is_anything(instrument).value(), 1); | ||
assert.equal(is_anything(drum).value(), 1); | ||
assert.equal(is_anything(guitar).value(), 1); | ||
assert.equal(is_anything(electricguitar).value(), 1); | ||
}); | ||
it('must sometimes pass validation (1 check)', function () { | ||
assert.equal(is_instrument(instrument), 2); | ||
assert.equal(is_instrument(drum), 2); | ||
assert.equal(is_instrument(guitar), 2); | ||
assert.equal(is_instrument(electricguitar), 2); | ||
assert.equal(is_instrument(instrument).value(), 2); | ||
assert.equal(is_instrument(drum).value(), 2); | ||
assert.equal(is_instrument(guitar).value(), 2); | ||
assert.equal(is_instrument(electricguitar).value(), 2); | ||
}); | ||
@@ -68,4 +68,4 @@ | ||
assert.isNull(is_guitar(drum)); | ||
assert.equal(is_guitar(guitar), 3); | ||
assert.equal(is_guitar(electricguitar), 3); | ||
assert.equal(is_guitar(guitar).value(), 3); | ||
assert.equal(is_guitar(electricguitar).value(), 3); | ||
}); | ||
@@ -77,3 +77,3 @@ | ||
assert.isNull(is_electricguitar(guitar)); | ||
assert.equal(is_electricguitar(electricguitar), 4); | ||
assert.equal(is_electricguitar(electricguitar).value(), 4); | ||
}); | ||
@@ -93,3 +93,3 @@ | ||
it('must not match using important', function () { | ||
assert.equal(is_guitar_important(guitar), 67); | ||
assert.equal(is_guitar_important(guitar).value(), 67); | ||
}); | ||
@@ -96,0 +96,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
29897
17
566
281