From 1b5444c81d93fa81a342937848e5226c72f0f979 Mon Sep 17 00:00:00 2001 From: Gleb Bahmutov Date: Thu, 17 Dec 2015 16:41:51 -0500 Subject: [PATCH] feat(api): check.schema is curried --- README.md | 25 +++++++++++++++++++++++++ check-more-types.js | 8 ++++---- check-more-types.min.js | 2 +- docs/use.md | 25 +++++++++++++++++++++++++ test/unit-tests.js | 7 +++++++ 5 files changed, 62 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 6721c71..8a99aa9 100644 --- a/README.md +++ b/README.md @@ -425,6 +425,13 @@ check.badItems(check.unemptyString, ['foo', '', 'bar']); // [''] This makes it very convenient to create new validator functions using partial argument application +The method is curried, thus you can easily create predicate function + +```js +var hasName = check.schema({ name: check.unemptyString }); +hasName({ name: 'joe' }); // true +``` + #### check.schema bind var personSchema = { @@ -443,6 +450,24 @@ argument application isValidPerson(h1); // true isValidPerson(h2); // false +If you want you can manually bind `check.schema` to first argument + + var personSchema = { + name: check.unemptyString, + age: check.positiveNumber + }; + var isValidPerson = check.schema.bind(null, personSchema); + var h1 = { + name: 'joe', + age: 10 + }; + var h2 = { + name: 'ann' + // missing age property + }; + isValidPerson(h1); // true + isValidPerson(h2); // false + You can use `Function.prototype.bind` or any partial application method, for example `_.partial(check.schema, personSchema);`. Because bound schema parameter generates a valid function, you can nest checks using diff --git a/check-more-types.js b/check-more-types.js index df36139..7b8de6c 100644 --- a/check-more-types.js +++ b/check-more-types.js @@ -15,9 +15,9 @@ } // utility method - function curry2(fn) { + function curry2(fn, strict2) { return function curried(a) { - if (arguments.length > 2) { + if (strict2 && arguments.length > 2) { throw new Error('Curry2 function ' + fn.name + ' called with too many arguments ' + arguments.length); } @@ -747,7 +747,7 @@ arrayOfStrings: arrayOfStrings, arrayOfArraysOfStrings: arrayOfArraysOfStrings, all: all, - schema: schema, + schema: curry2(schema), raises: raises, empty: empty, found: found, @@ -762,7 +762,7 @@ git: git, arrayOf: arrayOf, badItems: badItems, - oneOf: curry2(oneOf), + oneOf: curry2(oneOf, true), promise: isPromise, validDate: validDate, equal: curry2(equal), diff --git a/check-more-types.min.js b/check-more-types.min.js index aca01a0..d6cbdb3 100644 --- a/check-more-types.min.js +++ b/check-more-types.min.js @@ -2,4 +2,4 @@ homepage: https://github.com/kensho/check-more-types Copyright @ 2014 Kensho license: MIT */ -!function(){"use strict";function a(a){return function(b){if(arguments.length>2)throw new Error("Curry2 function "+a.name+" called with too many arguments "+arguments.length);return 2===arguments.length?a(arguments[0],arguments[1]):function(c){return a(b,c)}}}function b(a){return"function"==typeof a}function c(a){return"string"==typeof a}function d(a){return c(a)&&a}function e(a){return"object"==typeof a&&!Array.isArray(a)&&!j(a)&&!m(a)}function f(a){return e(a)&&0===Object.keys(a).length}function g(a){return"number"==typeof a&&!isNaN(a)&&a!==1/0&&a!==-(1/0)}function h(a){return g(a)&&a%1===0}function i(a){return g(a)&&a%1!==0}function j(a){return null===a}function k(a){return g(a)&&a>0}function l(a){return g(a)&&0>a}function m(a){return a instanceof Date}function n(a){return a instanceof RegExp}function o(a,b){return a instanceof b}function p(a,b){return(Array.isArray(a)||c(a))&&a.length===b}function q(a){return a>=0}function r(a,b){return c(a)&&c(b)&&0===b.indexOf(a)}function s(a,b){return typeof b===a}function t(a){return c(a)&&(da(a)||ea(a))}function u(a){var b,c;for(b in a)if(a.hasOwnProperty(b)){if(c=a[b],e(c)&&u(c)===!1)return!1;if(c===!1)return!1}return!0}function v(a,c){var d,f,g={};for(d in c)c.hasOwnProperty(d)&&(f=c[d],b(f)?g[d]=f(a[d]):e(f)&&(g[d]=v(a[d],f)));return g}function w(a){return"undefined"!=typeof a}function x(a){return fa.date(a)&&fa.number(Number(a))}function y(a){return fa.unemptyString(a)&&/^\d+\.\d+\.\d+$/.test(a)}function z(a){var b=typeof a;return"number"===b||"boolean"===b||"string"===b}function A(a){return"number"==typeof a&&0===a}function B(a,b){return a===b}function C(a,b){return w(a)&&J(a,"length")&&b>=0&&b0}function M(a,b){return fa.array(b)&&b.every(a)}function N(a,b){return fa.verify.array(b,"expected array to find bad items"),b.filter(aa(a))}function O(a,b){var c=fa.array(a)&&a.every(fa.string);return c&&fa.bool(b)&&b?a.every(fa.lowerCase):c}function P(a,b){return fa.array(a)&&a.every(function(a){return fa.arrayOfStrings(a,b)})}function Q(a,b){return fa.verify.fn(fa.every,"missing check.every method"),fa.verify.fn(fa.map,"missing check.map method"),fa.verify.object(a,"missing object to check"),fa.verify.object(b,"missing predicates object"),Object.keys(b).forEach(function(a){if(!fa.fn(b[a]))throw new Error("not a predicate function for "+a+" but "+b[a])}),fa.every(fa.map(a,b))}function R(a,b){return Q(b,a)}function S(a,b){fa.verify.fn(a,"expected function that raises");try{a()}catch(c){return"undefined"==typeof b?!0:"function"==typeof b?b(c):!1}return!1}function T(a){return""===a}function U(a){var b="string"==typeof a||Array.isArray(a);return b?!a.length:a instanceof Object?!Object.keys(a).length:!1}function V(a){var b="string"==typeof a||Array.isArray(a);return b?a.length:a instanceof Object?Object.keys(a).length:!0}function W(a){return fa.number(a)&&a>=0&&1>=a}function X(a){return fa.string(a)&&ga.test(a)}function Y(a){return fa.string(a)&&40===a.length&&ha.test(a)}function Z(a){return fa.string(a)&&7===a.length&&ia.test(a)}function $(){var a=Array.prototype.slice.call(arguments,0);if(!a.length)throw new Error("empty list of arguments to or");return function(){var b=Array.prototype.slice.call(arguments,0);return a.some(function(a){try{return fa.fn(a)?a.apply(null,b):Boolean(a)}catch(c){return!1}})}}function _(){var a=Array.prototype.slice.call(arguments,0);if(!a.length)throw new Error("empty list of arguments to or");return function(){var b=Array.prototype.slice.call(arguments,0);return a.every(function(a){return fa.fn(a)?a.apply(null,b):Boolean(a)})}}function aa(a){return function(){return!a.apply(null,arguments)}}function ba(a){return fa.object(a)&&la(a)}function ca(a,b){return a===b}if("function"!=typeof Function.prototype.bind)throw new Error("Missing Function.prototype.bind, please load es5-shim first");var da=r.bind(null,"http://"),ea=r.bind(null,"https://"),fa={maybe:{},verify:{},not:{},every:u,map:v},ga=/^#(?:[0-9a-fA-F]{3}){1,2}$/,ha=/^[0-9a-f]{40}$/,ia=/^[0-9a-f]{7}$/;if(!fa.defend){var ja=function(a,b,c){fa.verify.fn(a,"expected a function"),fa.verify.array(b,"expected list of predicates"),fa.verify.defined(c,"missing args");var d=0,e=0,f=b.length;for(d=0;f>d;d+=1){var g=b[d];if(fa.fn(g)){if(!g.call(null,c[e])){var h="Argument "+(e+1)+": "+c[e]+" does not pass predicate";throw fa.unemptyString(b[d+1])&&(h+=": "+b[d+1]),new Error(h)}e+=1}}return a.apply(null,c)};fa.defend=function(a){var b=Array.prototype.slice.call(arguments,1);return function(){return ja(a,b,arguments)}}}fa.mixin||(fa.mixin=function(a,f){function g(a,c,f){if(!e(a))throw new Error("missing object "+a);if(!d(c))throw new Error("missing name");if(!b(f))throw new Error("missing function");a[c]||(a[c]=f)}function h(a){return function(){return!fa.defined(arguments[0])||fa.nulled(arguments[0])?!0:a.apply(null,arguments)}}function i(a,b){return function(){var c;if(a.apply(null,arguments)===!1)throw c=arguments[arguments.length-1],new Error(fa.unemptyString(c)?c:b)}}if(c(a)&&b(f)){var j=a;a=f,f=j}if(!b(a))throw new Error("expected predicate function");if(d(f)||(f=a.name),!d(f))throw new Error("predicate function missing name\n"+a.toString());g(fa,f,a),g(fa.maybe,f,h(a)),g(fa.not,f,aa(a)),g(fa.verify,f,i(a,f+" failed"))}),fa.then||(fa.then=function(a,b){return function(){var c="function"==typeof a?a.apply(null,arguments):a;return c?b.apply(null,arguments):void 0}});var ka={then:b};ka["catch"]=b,ka["finally"]=b;var la=R.bind(null,ka),ma={nulled:j,fn:b,string:c,unemptyString:d,object:e,number:g,array:Array.isArray,positiveNumber:k,negativeNumber:l,defined:w,same:B,allSame:E,bit:H,bool:I,has:J,lowerCase:K,unemptyArray:L,arrayOfStrings:O,arrayOfArraysOfStrings:P,all:Q,schema:R,raises:S,empty:U,found:q,emptyString:T,unempty:V,unit:W,hexRgb:X,sameLength:D,commitId:Y,shortCommitId:Z,index:C,git:G,arrayOf:M,badItems:N,oneOf:a(F),promise:ba,validDate:x,equal:a(ca),or:$,and:_,primitive:z,zero:A,date:m,regexp:n,instance:o,emptyObject:f,length:p,floatNumber:i,intNumber:h,startsWith:r,webUrl:t,semver:y,type:a(s)};Object.keys(ma).forEach(function(a){fa.mixin(ma[a],a)}),"object"==typeof module&&(module.exports=fa),"object"==typeof window&&(window.check=fa),"object"==typeof global&&(global.check=fa)}(); \ No newline at end of file +!function(){"use strict";function a(a,b){return function(c){if(b&&arguments.length>2)throw new Error("Curry2 function "+a.name+" called with too many arguments "+arguments.length);return 2===arguments.length?a(arguments[0],arguments[1]):function(b){return a(c,b)}}}function b(a){return"function"==typeof a}function c(a){return"string"==typeof a}function d(a){return c(a)&&a}function e(a){return"object"==typeof a&&!Array.isArray(a)&&!j(a)&&!m(a)}function f(a){return e(a)&&0===Object.keys(a).length}function g(a){return"number"==typeof a&&!isNaN(a)&&a!==1/0&&a!==-(1/0)}function h(a){return g(a)&&a%1===0}function i(a){return g(a)&&a%1!==0}function j(a){return null===a}function k(a){return g(a)&&a>0}function l(a){return g(a)&&0>a}function m(a){return a instanceof Date}function n(a){return a instanceof RegExp}function o(a,b){return a instanceof b}function p(a,b){return(Array.isArray(a)||c(a))&&a.length===b}function q(a){return a>=0}function r(a,b){return c(a)&&c(b)&&0===b.indexOf(a)}function s(a,b){return typeof b===a}function t(a){return c(a)&&(da(a)||ea(a))}function u(a){var b,c;for(b in a)if(a.hasOwnProperty(b)){if(c=a[b],e(c)&&u(c)===!1)return!1;if(c===!1)return!1}return!0}function v(a,c){var d,f,g={};for(d in c)c.hasOwnProperty(d)&&(f=c[d],b(f)?g[d]=f(a[d]):e(f)&&(g[d]=v(a[d],f)));return g}function w(a){return"undefined"!=typeof a}function x(a){return fa.date(a)&&fa.number(Number(a))}function y(a){return fa.unemptyString(a)&&/^\d+\.\d+\.\d+$/.test(a)}function z(a){var b=typeof a;return"number"===b||"boolean"===b||"string"===b}function A(a){return"number"==typeof a&&0===a}function B(a,b){return a===b}function C(a,b){return w(a)&&J(a,"length")&&b>=0&&b0}function M(a,b){return fa.array(b)&&b.every(a)}function N(a,b){return fa.verify.array(b,"expected array to find bad items"),b.filter(aa(a))}function O(a,b){var c=fa.array(a)&&a.every(fa.string);return c&&fa.bool(b)&&b?a.every(fa.lowerCase):c}function P(a,b){return fa.array(a)&&a.every(function(a){return fa.arrayOfStrings(a,b)})}function Q(a,b){return fa.verify.fn(fa.every,"missing check.every method"),fa.verify.fn(fa.map,"missing check.map method"),fa.verify.object(a,"missing object to check"),fa.verify.object(b,"missing predicates object"),Object.keys(b).forEach(function(a){if(!fa.fn(b[a]))throw new Error("not a predicate function for "+a+" but "+b[a])}),fa.every(fa.map(a,b))}function R(a,b){return Q(b,a)}function S(a,b){fa.verify.fn(a,"expected function that raises");try{a()}catch(c){return"undefined"==typeof b?!0:"function"==typeof b?b(c):!1}return!1}function T(a){return""===a}function U(a){var b="string"==typeof a||Array.isArray(a);return b?!a.length:a instanceof Object?!Object.keys(a).length:!1}function V(a){var b="string"==typeof a||Array.isArray(a);return b?a.length:a instanceof Object?Object.keys(a).length:!0}function W(a){return fa.number(a)&&a>=0&&1>=a}function X(a){return fa.string(a)&&ga.test(a)}function Y(a){return fa.string(a)&&40===a.length&&ha.test(a)}function Z(a){return fa.string(a)&&7===a.length&&ia.test(a)}function $(){var a=Array.prototype.slice.call(arguments,0);if(!a.length)throw new Error("empty list of arguments to or");return function(){var b=Array.prototype.slice.call(arguments,0);return a.some(function(a){try{return fa.fn(a)?a.apply(null,b):Boolean(a)}catch(c){return!1}})}}function _(){var a=Array.prototype.slice.call(arguments,0);if(!a.length)throw new Error("empty list of arguments to or");return function(){var b=Array.prototype.slice.call(arguments,0);return a.every(function(a){return fa.fn(a)?a.apply(null,b):Boolean(a)})}}function aa(a){return function(){return!a.apply(null,arguments)}}function ba(a){return fa.object(a)&&la(a)}function ca(a,b){return a===b}if("function"!=typeof Function.prototype.bind)throw new Error("Missing Function.prototype.bind, please load es5-shim first");var da=r.bind(null,"http://"),ea=r.bind(null,"https://"),fa={maybe:{},verify:{},not:{},every:u,map:v},ga=/^#(?:[0-9a-fA-F]{3}){1,2}$/,ha=/^[0-9a-f]{40}$/,ia=/^[0-9a-f]{7}$/;if(!fa.defend){var ja=function(a,b,c){fa.verify.fn(a,"expected a function"),fa.verify.array(b,"expected list of predicates"),fa.verify.defined(c,"missing args");var d=0,e=0,f=b.length;for(d=0;f>d;d+=1){var g=b[d];if(fa.fn(g)){if(!g.call(null,c[e])){var h="Argument "+(e+1)+": "+c[e]+" does not pass predicate";throw fa.unemptyString(b[d+1])&&(h+=": "+b[d+1]),new Error(h)}e+=1}}return a.apply(null,c)};fa.defend=function(a){var b=Array.prototype.slice.call(arguments,1);return function(){return ja(a,b,arguments)}}}fa.mixin||(fa.mixin=function(a,f){function g(a,c,f){if(!e(a))throw new Error("missing object "+a);if(!d(c))throw new Error("missing name");if(!b(f))throw new Error("missing function");a[c]||(a[c]=f)}function h(a){return function(){return!fa.defined(arguments[0])||fa.nulled(arguments[0])?!0:a.apply(null,arguments)}}function i(a,b){return function(){var c;if(a.apply(null,arguments)===!1)throw c=arguments[arguments.length-1],new Error(fa.unemptyString(c)?c:b)}}if(c(a)&&b(f)){var j=a;a=f,f=j}if(!b(a))throw new Error("expected predicate function");if(d(f)||(f=a.name),!d(f))throw new Error("predicate function missing name\n"+a.toString());g(fa,f,a),g(fa.maybe,f,h(a)),g(fa.not,f,aa(a)),g(fa.verify,f,i(a,f+" failed"))}),fa.then||(fa.then=function(a,b){return function(){var c="function"==typeof a?a.apply(null,arguments):a;return c?b.apply(null,arguments):void 0}});var ka={then:b};ka["catch"]=b,ka["finally"]=b;var la=R.bind(null,ka),ma={nulled:j,fn:b,string:c,unemptyString:d,object:e,number:g,array:Array.isArray,positiveNumber:k,negativeNumber:l,defined:w,same:B,allSame:E,bit:H,bool:I,has:J,lowerCase:K,unemptyArray:L,arrayOfStrings:O,arrayOfArraysOfStrings:P,all:Q,schema:a(R),raises:S,empty:U,found:q,emptyString:T,unempty:V,unit:W,hexRgb:X,sameLength:D,commitId:Y,shortCommitId:Z,index:C,git:G,arrayOf:M,badItems:N,oneOf:a(F,!0),promise:ba,validDate:x,equal:a(ca),or:$,and:_,primitive:z,zero:A,date:m,regexp:n,instance:o,emptyObject:f,length:p,floatNumber:i,intNumber:h,startsWith:r,webUrl:t,semver:y,type:a(s)};Object.keys(ma).forEach(function(a){fa.mixin(ma[a],a)}),"object"==typeof module&&(module.exports=fa),"object"==typeof window&&(window.check=fa),"object"==typeof global&&(global.check=fa)}(); \ No newline at end of file diff --git a/docs/use.md b/docs/use.md index 2e28a3a..b49563a 100644 --- a/docs/use.md +++ b/docs/use.md @@ -318,6 +318,13 @@ check.badItems(check.unemptyString, ['foo', '', 'bar']); // [''] This makes it very convenient to create new validator functions using partial argument application +The method is curried, thus you can easily create predicate function + +```js +var hasName = check.schema({ name: check.unemptyString }); +hasName({ name: 'joe' }); // true +``` + ### check.schema bind var personSchema = { @@ -336,6 +343,24 @@ argument application isValidPerson(h1); // true isValidPerson(h2); // false +If you want you can manually bind `check.schema` to first argument + + var personSchema = { + name: check.unemptyString, + age: check.positiveNumber + }; + var isValidPerson = check.schema.bind(null, personSchema); + var h1 = { + name: 'joe', + age: 10 + }; + var h2 = { + name: 'ann' + // missing age property + }; + isValidPerson(h1); // true + isValidPerson(h2); // false + You can use `Function.prototype.bind` or any partial application method, for example `_.partial(check.schema, personSchema);`. Because bound schema parameter generates a valid function, you can nest checks using diff --git a/test/unit-tests.js b/test/unit-tests.js index 8402845..83b1243 100644 --- a/test/unit-tests.js +++ b/test/unit-tests.js @@ -744,6 +744,13 @@ describe('check-more-types', function () { describe('check.schema', function () { la(check.fn(check.schema)); + it('is curried', function () { + var hasName = check.schema({ name: check.unemptyString }); + la(check.fn(hasName), 'returned a function'); + la(hasName({ name: 'my name' })); + la(!hasName({ age: 42 })); + }); + it('check.schema', function () { var obj = { foo: 'foo',