Comparing version 2.0.0 to 2.0.1
{ | ||
"name": "ducky", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"description": "Duck-Typed Value Handling for JavaScript", | ||
@@ -5,0 +5,0 @@ "main": "./lib/ducky.js", |
@@ -36,2 +36,3 @@ /* | ||
pkg: grunt.file.readJSON("package.json"), | ||
version: grunt.file.readYAML("VERSION.yml"), | ||
"expand-include": { | ||
@@ -44,5 +45,6 @@ "ducky": { | ||
globalDefines: { | ||
major: "<%= pkg.version.split('.')[0] %>", | ||
minor: "<%= pkg.version.split('.')[1] %>", | ||
micro: "<%= pkg.version.split('.')[2] %>" | ||
major: "<%= version.major %>", | ||
minor: "<%= version.minor %>", | ||
micro: "<%= version.micro %>", | ||
date: "<%= version.date %>" | ||
} | ||
@@ -49,0 +51,0 @@ } |
@@ -38,5 +38,14 @@ /*! | ||
root[name] = factory(root); | ||
}(this, "Ducky", function (/* root */) { | ||
var Ducky = {}; | ||
}(this, "ducky", function (/* root */) { | ||
var ducky = {}; | ||
/* API version */ | ||
ducky.version = { | ||
major: 2, | ||
minor: 0, | ||
micro: 1, | ||
date: 20131230 | ||
}; | ||
/* custom Token class */ | ||
@@ -229,2 +238,3 @@ var Token = function () { | ||
/* the internal compile cache */ | ||
@@ -235,3 +245,3 @@ var select_cache = {}; | ||
and either get the current value or set the new value */ | ||
Ducky.select = function (obj, spec, value) { | ||
ducky.select = function (obj, spec, value) { | ||
/* sanity check arguments */ | ||
@@ -265,3 +275,3 @@ if (arguments.length < 2) | ||
/* compile a path specification into array of dereferencing steps */ | ||
Ducky.select.compile = function (spec) { | ||
ducky.select.compile = function (spec) { | ||
/* sanity check argument */ | ||
@@ -278,3 +288,3 @@ if (arguments.length !== 1) | ||
/* execute object selection */ | ||
Ducky.select.execute = function (obj, path) { | ||
ducky.select.execute = function (obj, path) { | ||
/* sanity check arguments */ | ||
@@ -609,3 +619,3 @@ if (arguments.length < 2) | ||
/* API function: validate an arbitrary value against a validation DSL */ | ||
Ducky.validate = function (value, spec) { | ||
ducky.validate = function (value, spec) { | ||
/* sanity check arguments */ | ||
@@ -623,3 +633,3 @@ if (arguments.length !== 2) | ||
if (typeof ast === "undefined") { | ||
ast = Ducky.validate.compile(spec); | ||
ast = ducky.validate.compile(spec); | ||
validate_cache[spec] = ast; | ||
@@ -629,6 +639,6 @@ } | ||
/* execute validation AST against the value */ | ||
return Ducky.validate.execute(value, ast); | ||
return ducky.validate.execute(value, ast); | ||
}; | ||
Ducky.validate.compile = function (spec) { | ||
ducky.validate.compile = function (spec) { | ||
/* sanity check arguments */ | ||
@@ -651,3 +661,3 @@ if (arguments.length !== 1) | ||
Ducky.validate.execute = function (value, ast) { | ||
ducky.validate.execute = function (value, ast) { | ||
/* sanity check arguments */ | ||
@@ -689,3 +699,3 @@ if (arguments.length !== 2) | ||
return; | ||
if (!Ducky.validate(value, valid)) | ||
if (!ducky.validate(value, valid)) | ||
throw new Error(func + ": parameter \"" + param + "\" has " + | ||
@@ -697,3 +707,3 @@ what + " " + JSON.stringify(value) + ", which does not validate " + | ||
/* API function: flexible parameter handling */ | ||
Ducky.params = function (func, args, spec) { | ||
ducky.params = function (func, args, spec) { | ||
/* start with a fresh parameter object */ | ||
@@ -791,4 +801,4 @@ var params = {}; | ||
return Ducky; | ||
return ducky; | ||
})); | ||
@@ -24,2 +24,2 @@ /*! | ||
*/ | ||
!function(a,b,c){"function"==typeof define&&"undefined"!=typeof define.amd?define(b,function(){return c(a)}):"object"==typeof module&&"object"==typeof module.exports?module.exports=c(a):a[b]=c(a)}(this,"Ducky",function(){var Ducky={},Token=function(){this.name="",this.text="",this.tokens=[],this.pos=0,this.len=0};Token.prototype={setName:function(a){this.name=a},setText:function(a){this.text=a},addToken:function(a,b,c,d,e){this.tokens.push({b1:a,b2:b,e2:c,e1:d,symbol:e}),this.len++},peek:function(a){if("undefined"==typeof a&&(a=0),a>=this.len)throw new Error(this.name+": parse error: not enough tokens");return this.tokens[this.pos+a].symbol},skip:function(a){if("undefined"==typeof a&&(a=1),a>this.len)throw new Error(this.name+": parse error: not enough tokens available to skip: "+this.ctx());this.pos+=a,this.len-=a},consume:function(a){if(this.len<=0)throw new Error(this.name+": parse error: no more tokens available to consume: "+this.ctx());if(this.tokens[this.pos].symbol!==a)throw new Error(this.name+': parse error: expected token symbol "'+a+'": '+this.ctx());this.pos++,this.len--},ctx:function(a){"undefined"==typeof a&&(a=78);var b=this.tokens[this.pos],c="<"+this.text.substr(b.b2,b.e2-b.b2+1)+">";c=this.text.substr(b.b1,b.b2-b.b1)+c,c+=this.text.substr(b.e2+1,b.e1-b.e2);var d=a-c.length;if(d>0){d=Math.floor(d/2);var e,f;if(this.pos>0){var g=0;for(e=this.pos-1;e>=0&&(b=this.tokens[e],f=this.text.substr(b.b1,b.e1-b.b1+1),g+=f.length,!(g>d));e--)c=f+c;e>0&&(c="[...]"+c)}if(this.len>1){var h=0;for(e=this.pos+1;e<this.pos+this.len&&(b=this.tokens[e],f=this.text.substr(b.b1,b.e1-b.b1+1),h+=f.length,!(h>d));e++)c+=f;e<this.pos+this.len&&(c+="[...]")}}return c=c.replace(/\r/,"\\r").replace(/\n/,"\\n").replace(/\t/,"\\t")}};var select_compile=function(a){for(var b,c=[],d=0,e=a;""!==e;){if(null!==(b=e.match(/^\s*(?:\.)?\s*([a-zA-Z$0-9_][a-zA-Z$0-9_:-]*)/)))c.push(b[1]);else if(null!==(b=e.match(/^\s*\[\s*(\d+|\*{1,2})\s*\]/)))c.push(b[1]);else if(null!==(b=e.match(/^\s*\[\s*"((?:\\"|.)*?)"\s*\]/)))c.push(b[1].replace(/\\"/g,'"'));else{if(null===(b=e.match(/^\s*\[\s*'((?:\\'|.)*?)'\s*\]/))){if(null!==(b=e.match(/^\s+$/)))break;throw new Error("select: parse error: invalid character at: "+a.substr(0,d)+"<"+e.substr(0,1)+">"+e.substr(1))}c.push(b[1].replace(/\\'/g,"'"))}d+=b[0].length,e=e.substr(b[0].length)}return c},select_execute=function(a,b){if(0===b.length){if(3===arguments.length)throw new Error("select: cannot set value on empty path");return a}for(var c=0;c<b.length-1;){if("object"!=typeof a)throw new Error("select: cannot further dereference: no more intermediate objects in path");a=a[b[c++]]}if("object"!=typeof a)throw new Error("select: cannot further dereference: no object at end of path");var d=a[b[c]];if(3===arguments.length){var e=arguments[2];void 0===e?a instanceof Array?a.splice(parseInt(b[c],10),1):delete a[b[c]]:a[b[c]]=e}return d},select_cache={};Ducky.select=function(a,b,c){if(arguments.length<2)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (minimum of 2 expected)');if(arguments.length>3)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (maximum of 3 expected)');if("string"!=typeof b)throw new Error('select: invalid specification argument: "'+b+'" (string expected)');var d=select_cache[b];return"undefined"==typeof d&&(d=select_compile(b),select_cache[b]=d),2===arguments.length?select_execute(a,d):select_execute(a,d,c)},Ducky.select.compile=function(a){if(1!==arguments.length)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (exactly 1 expected)');if("string"!=typeof a)throw new Error('select: invalid specification argument: "'+a+'" (string expected)');return select_compile.apply(void 0,arguments)},Ducky.select.execute=function(a,b){if(arguments.length<2)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (minimum of 2 expected)');if(arguments.length>3)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (maximum of 3 expected)');if(!("object"==typeof b&&b instanceof Array))throw new Error('select: invalid path argument: "'+b+'" (array expected)');return select_execute.apply(void 0,arguments)};var validate_tokenize=function(a){var b=new Token;b.setName("validate"),b.setText(a);for(var c,d=0;""!==a;){if(c=a.match(/^(\s*)([^{}\[\]:,?*+()!|\s]+|[{}\[\]:,?*+()!|])(\s*)/),null===c)throw new Error('validate: parse error: cannot further canonicalize: "'+a+'"');b.addToken(d,d+c[1].length,d+c[1].length+c[2].length-1,d+c[0].length-1,c[2]),a=a.substr(c[0].length),d+=c[0].length}return b},validate_parse={parse_spec:function(a){if(a.len<=0)return null;var b,c=a.peek();if("!"===c)b=this.parse_not(a);else if("("===c)b=this.parse_group(a);else if("{"===c)b=this.parse_hash(a);else if("["===c)b=this.parse_array(a);else if(c.match(/^(?:null|undefined|boolean|number|string|function|object)$/))b=this.parse_primary(a);else if("any"===c)b=this.parse_any(a);else{if(!c.match(/^[A-Z][_a-zA-Z$0-9]*$/))throw new Error('validate: parse error: invalid token symbol: "'+a.ctx()+'"');b=this.parse_class(a)}return b},parse_not:function(a){a.consume("!");var b=this.parse_spec(a);return b={type:"not",op:b}},parse_group:function(a){a.consume("(");for(var b=this.parse_spec(a);"|"===a.peek();){a.consume("|");var c=this.parse_spec(a);b={type:"or",op1:b,op2:c}}return a.consume(")"),b},parse_hash:function(a){a.consume("{");for(var b=[];"}"!==a.peek();){var c=this.parse_key(a),d=this.parse_arity(a,"?");a.consume(":");var e=this.parse_spec(a);if(b.push({type:"element",key:c,arity:d,element:e}),","!==a.peek())break;a.skip()}var f={type:"hash",elements:b};return a.consume("}"),f},parse_array:function(a){a.consume("[");for(var b=[];"]"!==a.peek();){var c=this.parse_spec(a),d=this.parse_arity(a,"?*+");if(b.push({type:"element",element:c,arity:d}),","!==a.peek())break;a.skip()}var e={type:"array",elements:b};return a.consume("]"),e},parse_primary:function(a){var b=a.peek();if(!b.match(/^(?:null|undefined|boolean|number|string|function|object)$/))throw new Error('validate: parse error: invalid primary type "'+b+'"');return a.skip(),{type:"primary",name:b}},parse_any:function(a){var b=a.peek();if("any"!==b)throw new Error('validate: parse error: invalid any type "'+b+'"');return a.skip(),{type:"any"}},parse_class:function(a){var b=a.peek();if(!b.match(/^[A-Z][_a-zA-Z$0-9]*$/))throw new Error('validate: parse error: invalid class type "'+b+'"');return a.skip(),{type:"class",name:b}},parse_arity:function(a,b){var c=[1,1];if(a.len>=5&&"{"===a.peek(0)&&a.peek(1).match(/^[0-9]+$/)&&","===a.peek(2)&&a.peek(3).match(/^(?:[0-9]+|oo)$/)&&"}"===a.peek(4))c=[parseInt(a.peek(1),10),"oo"===a.peek(3)?Number.MAX_VALUE:parseInt(a.peek(3),10)],a.skip(5);else if(a.len>=1&&1===a.peek().length&&b.indexOf(a.peek())>=0){var d=a.peek();switch(d){case"?":c=[0,1];break;case"*":c=[0,Number.MAX_VALUE];break;case"+":c=[1,Number.MAX_VALUE]}a.skip()}return c},parse_key:function(a){var b=a.peek();if(!b.match(/^(?:[_a-zA-Z$][_a-zA-Z$0-9]*|@)$/))throw new Error('validate: parse error: invalid key "'+b+'"');return a.skip(),b}},validate_execute={exec_spec:function(a,b){var c=!1;if(null!==b)switch(b.type){case"not":c=this.exec_not(a,b);break;case"or":c=this.exec_or(a,b);break;case"hash":c=this.exec_hash(a,b);break;case"array":c=this.exec_array(a,b);break;case"primary":c=this.exec_primary(a,b);break;case"class":c=this.exec_class(a,b);break;case"any":c=!0;break;default:throw new Error('validate: invalid validation AST: node has unknown type "'+b.type+'"')}return c},exec_not:function(a,b){return!this.exec_spec(a,b.op)},exec_or:function(a,b){return this.exec_spec(a,b.op1)||this.exec_spec(a,b.op2)},exec_hash:function(a,b){var c,d,e,f="object"==typeof a,g={};if(f){var h=!1;for(e in a)if(Object.hasOwnProperty.call(a,e)&&Object.propertyIsEnumerable.call(a,e)&&"constructor"!==e&&"prototype"!==e){h=!0;break}for(c=0;c<b.elements.length;c++)if(d=b.elements[c],g[d.key]=d.element,d.arity[0]>0&&("@"===d.key&&!h||"@"!==d.key&&"undefined"==typeof a[d.key])){f=!1;break}}if(f)for(e in a)if(Object.hasOwnProperty.call(a,e)&&Object.propertyIsEnumerable.call(a,e)&&"constructor"!==e&&"prototype"!==e&&!("undefined"!=typeof g[e]&&this.exec_spec(a[e],g[e])||"undefined"!=typeof g["@"]&&this.exec_spec(a[e],g["@"]))){f=!1;break}return f},exec_array:function(a,b){var c,d,e="object"==typeof a&&a instanceof Array;if(e){var f=0;for(c=0;c<b.elements.length;c++){d=b.elements[c];for(var g=0;g<d.arity[1]&&f<a.length&&this.exec_spec(a[f],d.element);)g++,f++;if(g<d.arity[0]){e=!1;break}}f<a.length&&(e=!1)}return e},exec_primary:function(a,b){return"null"===b.name&&null===a||typeof a===b.name},exec_class:function(value,node){return"object"==typeof value&&Object.prototype.toString.call(value)==="[object "+node.name+"]"||eval("value instanceof "+node.name)}},validate_cache={};Ducky.validate=function(a,b){if(2!==arguments.length)throw new Error('validate: invalid number of arguments: "'+arguments.length+'" (exactly 2 expected)');if("string"!=typeof b)throw new Error('validate: invalid specification argument: "'+b+'" (string expected)');var c=validate_cache[b];return"undefined"==typeof c&&(c=Ducky.validate.compile(b),validate_cache[b]=c),Ducky.validate.execute(a,c)},Ducky.validate.compile=function(a){if(1!==arguments.length)throw new Error('validate: invalid number of arguments: "'+arguments.length+'" (exactly 1 expected)');if("string"!=typeof a)throw new Error('validate: invalid specification argument: "'+a+'" (string expected)');var b=validate_tokenize(a),c=validate_parse.parse_spec(b);return c},Ducky.validate.execute=function(a,b){if(2!==arguments.length)throw new Error('validate: invalid number of arguments: "'+arguments.length+'" (exactly 2 expected)');return validate_execute.exec_spec(a,b)};var params_is_name_based=function(a,b){var c=!1;if(1===a.length&&"object"==typeof a[0]){c=!0;for(var d in a[0])Object.hasOwnProperty.call(a[0],d)||"undefined"==typeof b[d]&&(c=!1)}return c},params_check_validity=function(a,b,c,d,e){if("undefined"!=typeof d&&!Ducky.validate(c,d))throw new Error(a+': parameter "'+b+'" has '+e+" "+JSON.stringify(c)+', which does not validate against "'+d+'"')};return Ducky.params=function(a,b,c){var d,e={};for(d in c)Object.hasOwnProperty.call(c,d)&&"undefined"!=typeof c[d].def&&("undefined"!=typeof c[d].valid&¶ms_check_validity(a,d,c[d].def,c[d].valid,"default value"),e[d]=c[d].def);if(params_is_name_based(b,c)){b=b[0];for(d in b)if(Object.hasOwnProperty.call(b,d)){if("undefined"==typeof c[d])throw new Error(a+': unknown parameter "'+d+'"');params_check_validity(a,d,b[d],c[d].valid,"value"),e[d]=b[d]}for(d in c)if(Object.hasOwnProperty.call(c,d)&&"undefined"!=typeof c[d].req&&c[d].req&&"undefined"==typeof b[d])throw new Error(a+': required parameter "'+d+'" missing')}else{var f=0,g=0,h={};for(d in c)Object.hasOwnProperty.call(c,d)&&"undefined"!=typeof c[d].pos&&(h[c[d].pos]=d,"number"==typeof c[d].pos&&f++,"undefined"!=typeof c[d].req&&c[d].req&&g++);if(b.length<g)throw new Error(a+": invalid number of arguments (at least "+g+" required)");for(var i=0;f>i&&i<b.length;)params_check_validity(a,h[i],b[i],c[h[i]].valid,"value"),e[h[i]]=b[i],i++;if(i<b.length){if("undefined"==typeof h["..."])throw new Error(a+": too many arguments provided");for(var j=[];i<b.length;)j.push(b[i++]);params_check_validity(a,h["..."],j,c[h["..."]].valid,"value"),e[h["..."]]=j}}return e},Ducky}); | ||
!function(a,b,c){"function"==typeof define&&"undefined"!=typeof define.amd?define(b,function(){return c(a)}):"object"==typeof module&&"object"==typeof module.exports?module.exports=c(a):a[b]=c(a)}(this,"ducky",function(){var ducky={};ducky.version={major:2,minor:0,micro:1,date:20131230};var Token=function(){this.name="",this.text="",this.tokens=[],this.pos=0,this.len=0};Token.prototype={setName:function(a){this.name=a},setText:function(a){this.text=a},addToken:function(a,b,c,d,e){this.tokens.push({b1:a,b2:b,e2:c,e1:d,symbol:e}),this.len++},peek:function(a){if("undefined"==typeof a&&(a=0),a>=this.len)throw new Error(this.name+": parse error: not enough tokens");return this.tokens[this.pos+a].symbol},skip:function(a){if("undefined"==typeof a&&(a=1),a>this.len)throw new Error(this.name+": parse error: not enough tokens available to skip: "+this.ctx());this.pos+=a,this.len-=a},consume:function(a){if(this.len<=0)throw new Error(this.name+": parse error: no more tokens available to consume: "+this.ctx());if(this.tokens[this.pos].symbol!==a)throw new Error(this.name+': parse error: expected token symbol "'+a+'": '+this.ctx());this.pos++,this.len--},ctx:function(a){"undefined"==typeof a&&(a=78);var b=this.tokens[this.pos],c="<"+this.text.substr(b.b2,b.e2-b.b2+1)+">";c=this.text.substr(b.b1,b.b2-b.b1)+c,c+=this.text.substr(b.e2+1,b.e1-b.e2);var d=a-c.length;if(d>0){d=Math.floor(d/2);var e,f;if(this.pos>0){var g=0;for(e=this.pos-1;e>=0&&(b=this.tokens[e],f=this.text.substr(b.b1,b.e1-b.b1+1),g+=f.length,!(g>d));e--)c=f+c;e>0&&(c="[...]"+c)}if(this.len>1){var h=0;for(e=this.pos+1;e<this.pos+this.len&&(b=this.tokens[e],f=this.text.substr(b.b1,b.e1-b.b1+1),h+=f.length,!(h>d));e++)c+=f;e<this.pos+this.len&&(c+="[...]")}}return c=c.replace(/\r/,"\\r").replace(/\n/,"\\n").replace(/\t/,"\\t")}};var select_compile=function(a){for(var b,c=[],d=0,e=a;""!==e;){if(null!==(b=e.match(/^\s*(?:\.)?\s*([a-zA-Z$0-9_][a-zA-Z$0-9_:-]*)/)))c.push(b[1]);else if(null!==(b=e.match(/^\s*\[\s*(\d+|\*{1,2})\s*\]/)))c.push(b[1]);else if(null!==(b=e.match(/^\s*\[\s*"((?:\\"|.)*?)"\s*\]/)))c.push(b[1].replace(/\\"/g,'"'));else{if(null===(b=e.match(/^\s*\[\s*'((?:\\'|.)*?)'\s*\]/))){if(null!==(b=e.match(/^\s+$/)))break;throw new Error("select: parse error: invalid character at: "+a.substr(0,d)+"<"+e.substr(0,1)+">"+e.substr(1))}c.push(b[1].replace(/\\'/g,"'"))}d+=b[0].length,e=e.substr(b[0].length)}return c},select_execute=function(a,b){if(0===b.length){if(3===arguments.length)throw new Error("select: cannot set value on empty path");return a}for(var c=0;c<b.length-1;){if("object"!=typeof a)throw new Error("select: cannot further dereference: no more intermediate objects in path");a=a[b[c++]]}if("object"!=typeof a)throw new Error("select: cannot further dereference: no object at end of path");var d=a[b[c]];if(3===arguments.length){var e=arguments[2];void 0===e?a instanceof Array?a.splice(parseInt(b[c],10),1):delete a[b[c]]:a[b[c]]=e}return d},select_cache={};ducky.select=function(a,b,c){if(arguments.length<2)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (minimum of 2 expected)');if(arguments.length>3)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (maximum of 3 expected)');if("string"!=typeof b)throw new Error('select: invalid specification argument: "'+b+'" (string expected)');var d=select_cache[b];return"undefined"==typeof d&&(d=select_compile(b),select_cache[b]=d),2===arguments.length?select_execute(a,d):select_execute(a,d,c)},ducky.select.compile=function(a){if(1!==arguments.length)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (exactly 1 expected)');if("string"!=typeof a)throw new Error('select: invalid specification argument: "'+a+'" (string expected)');return select_compile.apply(void 0,arguments)},ducky.select.execute=function(a,b){if(arguments.length<2)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (minimum of 2 expected)');if(arguments.length>3)throw new Error('select: invalid number of arguments: "'+arguments.length+'" (maximum of 3 expected)');if(!("object"==typeof b&&b instanceof Array))throw new Error('select: invalid path argument: "'+b+'" (array expected)');return select_execute.apply(void 0,arguments)};var validate_tokenize=function(a){var b=new Token;b.setName("validate"),b.setText(a);for(var c,d=0;""!==a;){if(c=a.match(/^(\s*)([^{}\[\]:,?*+()!|\s]+|[{}\[\]:,?*+()!|])(\s*)/),null===c)throw new Error('validate: parse error: cannot further canonicalize: "'+a+'"');b.addToken(d,d+c[1].length,d+c[1].length+c[2].length-1,d+c[0].length-1,c[2]),a=a.substr(c[0].length),d+=c[0].length}return b},validate_parse={parse_spec:function(a){if(a.len<=0)return null;var b,c=a.peek();if("!"===c)b=this.parse_not(a);else if("("===c)b=this.parse_group(a);else if("{"===c)b=this.parse_hash(a);else if("["===c)b=this.parse_array(a);else if(c.match(/^(?:null|undefined|boolean|number|string|function|object)$/))b=this.parse_primary(a);else if("any"===c)b=this.parse_any(a);else{if(!c.match(/^[A-Z][_a-zA-Z$0-9]*$/))throw new Error('validate: parse error: invalid token symbol: "'+a.ctx()+'"');b=this.parse_class(a)}return b},parse_not:function(a){a.consume("!");var b=this.parse_spec(a);return b={type:"not",op:b}},parse_group:function(a){a.consume("(");for(var b=this.parse_spec(a);"|"===a.peek();){a.consume("|");var c=this.parse_spec(a);b={type:"or",op1:b,op2:c}}return a.consume(")"),b},parse_hash:function(a){a.consume("{");for(var b=[];"}"!==a.peek();){var c=this.parse_key(a),d=this.parse_arity(a,"?");a.consume(":");var e=this.parse_spec(a);if(b.push({type:"element",key:c,arity:d,element:e}),","!==a.peek())break;a.skip()}var f={type:"hash",elements:b};return a.consume("}"),f},parse_array:function(a){a.consume("[");for(var b=[];"]"!==a.peek();){var c=this.parse_spec(a),d=this.parse_arity(a,"?*+");if(b.push({type:"element",element:c,arity:d}),","!==a.peek())break;a.skip()}var e={type:"array",elements:b};return a.consume("]"),e},parse_primary:function(a){var b=a.peek();if(!b.match(/^(?:null|undefined|boolean|number|string|function|object)$/))throw new Error('validate: parse error: invalid primary type "'+b+'"');return a.skip(),{type:"primary",name:b}},parse_any:function(a){var b=a.peek();if("any"!==b)throw new Error('validate: parse error: invalid any type "'+b+'"');return a.skip(),{type:"any"}},parse_class:function(a){var b=a.peek();if(!b.match(/^[A-Z][_a-zA-Z$0-9]*$/))throw new Error('validate: parse error: invalid class type "'+b+'"');return a.skip(),{type:"class",name:b}},parse_arity:function(a,b){var c=[1,1];if(a.len>=5&&"{"===a.peek(0)&&a.peek(1).match(/^[0-9]+$/)&&","===a.peek(2)&&a.peek(3).match(/^(?:[0-9]+|oo)$/)&&"}"===a.peek(4))c=[parseInt(a.peek(1),10),"oo"===a.peek(3)?Number.MAX_VALUE:parseInt(a.peek(3),10)],a.skip(5);else if(a.len>=1&&1===a.peek().length&&b.indexOf(a.peek())>=0){var d=a.peek();switch(d){case"?":c=[0,1];break;case"*":c=[0,Number.MAX_VALUE];break;case"+":c=[1,Number.MAX_VALUE]}a.skip()}return c},parse_key:function(a){var b=a.peek();if(!b.match(/^(?:[_a-zA-Z$][_a-zA-Z$0-9]*|@)$/))throw new Error('validate: parse error: invalid key "'+b+'"');return a.skip(),b}},validate_execute={exec_spec:function(a,b){var c=!1;if(null!==b)switch(b.type){case"not":c=this.exec_not(a,b);break;case"or":c=this.exec_or(a,b);break;case"hash":c=this.exec_hash(a,b);break;case"array":c=this.exec_array(a,b);break;case"primary":c=this.exec_primary(a,b);break;case"class":c=this.exec_class(a,b);break;case"any":c=!0;break;default:throw new Error('validate: invalid validation AST: node has unknown type "'+b.type+'"')}return c},exec_not:function(a,b){return!this.exec_spec(a,b.op)},exec_or:function(a,b){return this.exec_spec(a,b.op1)||this.exec_spec(a,b.op2)},exec_hash:function(a,b){var c,d,e,f="object"==typeof a,g={};if(f){var h=!1;for(e in a)if(Object.hasOwnProperty.call(a,e)&&Object.propertyIsEnumerable.call(a,e)&&"constructor"!==e&&"prototype"!==e){h=!0;break}for(c=0;c<b.elements.length;c++)if(d=b.elements[c],g[d.key]=d.element,d.arity[0]>0&&("@"===d.key&&!h||"@"!==d.key&&"undefined"==typeof a[d.key])){f=!1;break}}if(f)for(e in a)if(Object.hasOwnProperty.call(a,e)&&Object.propertyIsEnumerable.call(a,e)&&"constructor"!==e&&"prototype"!==e&&!("undefined"!=typeof g[e]&&this.exec_spec(a[e],g[e])||"undefined"!=typeof g["@"]&&this.exec_spec(a[e],g["@"]))){f=!1;break}return f},exec_array:function(a,b){var c,d,e="object"==typeof a&&a instanceof Array;if(e){var f=0;for(c=0;c<b.elements.length;c++){d=b.elements[c];for(var g=0;g<d.arity[1]&&f<a.length&&this.exec_spec(a[f],d.element);)g++,f++;if(g<d.arity[0]){e=!1;break}}f<a.length&&(e=!1)}return e},exec_primary:function(a,b){return"null"===b.name&&null===a||typeof a===b.name},exec_class:function(value,node){return"object"==typeof value&&Object.prototype.toString.call(value)==="[object "+node.name+"]"||eval("value instanceof "+node.name)}},validate_cache={};ducky.validate=function(a,b){if(2!==arguments.length)throw new Error('validate: invalid number of arguments: "'+arguments.length+'" (exactly 2 expected)');if("string"!=typeof b)throw new Error('validate: invalid specification argument: "'+b+'" (string expected)');var c=validate_cache[b];return"undefined"==typeof c&&(c=ducky.validate.compile(b),validate_cache[b]=c),ducky.validate.execute(a,c)},ducky.validate.compile=function(a){if(1!==arguments.length)throw new Error('validate: invalid number of arguments: "'+arguments.length+'" (exactly 1 expected)');if("string"!=typeof a)throw new Error('validate: invalid specification argument: "'+a+'" (string expected)');var b=validate_tokenize(a),c=validate_parse.parse_spec(b);return c},ducky.validate.execute=function(a,b){if(2!==arguments.length)throw new Error('validate: invalid number of arguments: "'+arguments.length+'" (exactly 2 expected)');return validate_execute.exec_spec(a,b)};var params_is_name_based=function(a,b){var c=!1;if(1===a.length&&"object"==typeof a[0]){c=!0;for(var d in a[0])Object.hasOwnProperty.call(a[0],d)||"undefined"==typeof b[d]&&(c=!1)}return c},params_check_validity=function(a,b,c,d,e){if("undefined"!=typeof d&&!ducky.validate(c,d))throw new Error(a+': parameter "'+b+'" has '+e+" "+JSON.stringify(c)+', which does not validate against "'+d+'"')};return ducky.params=function(a,b,c){var d,e={};for(d in c)Object.hasOwnProperty.call(c,d)&&"undefined"!=typeof c[d].def&&("undefined"!=typeof c[d].valid&¶ms_check_validity(a,d,c[d].def,c[d].valid,"default value"),e[d]=c[d].def);if(params_is_name_based(b,c)){b=b[0];for(d in b)if(Object.hasOwnProperty.call(b,d)){if("undefined"==typeof c[d])throw new Error(a+': unknown parameter "'+d+'"');params_check_validity(a,d,b[d],c[d].valid,"value"),e[d]=b[d]}for(d in c)if(Object.hasOwnProperty.call(c,d)&&"undefined"!=typeof c[d].req&&c[d].req&&"undefined"==typeof b[d])throw new Error(a+': required parameter "'+d+'" missing')}else{var f=0,g=0,h={};for(d in c)Object.hasOwnProperty.call(c,d)&&"undefined"!=typeof c[d].pos&&(h[c[d].pos]=d,"number"==typeof c[d].pos&&f++,"undefined"!=typeof c[d].req&&c[d].req&&g++);if(b.length<g)throw new Error(a+": invalid number of arguments (at least "+g+" required)");for(var i=0;f>i&&i<b.length;)params_check_validity(a,h[i],b[i],c[h[i]].valid,"value"),e[h[i]]=b[i],i++;if(i<b.length){if("undefined"==typeof h["..."])throw new Error(a+": too many arguments provided");for(var j=[];i<b.length;)j.push(b[i++]);params_check_validity(a,h["..."],j,c[h["..."]].valid,"value"),e[h["..."]]=j}}return e},ducky}); |
@@ -5,3 +5,3 @@ { | ||
"description": "Duck-Typed Value Handling for JavaScript", | ||
"version": "2.0.0", | ||
"version": "2.0.1", | ||
"license": "MIT", | ||
@@ -8,0 +8,0 @@ "author": { |
Ducky — http://duckyjs.com/ | ||
================================= | ||
Ducky — [duckyjs.com](http://duckyjs.com/) | ||
================================================ | ||
@@ -16,5 +16,6 @@ **Duck-Typed Value Handling for JavaScript** | ||
This is a JavaScript library for Duck-Testing values, | ||
for use in [Node.js](http://nodejs.org/) based server and browser based client | ||
environments. | ||
Ducky is a small Open-Source JavaScript library, providing Duck-Typed | ||
Value Validation, Value Selection and Flexible Function Parameter | ||
Handling. It can be used in [Node.js](http://nodejs.org/) based server | ||
and browser based client environments. | ||
@@ -26,12 +27,12 @@ Getting Ducky | ||
- Git: directly clone the official repository | ||
- Bower: install as client component via the Bower component manager:<br/> | ||
`$ bower install ducky` | ||
- NPM: install as server component via the Node Package Manager:<br/> | ||
`$ npm install ducky` | ||
- Git: directly clone the official repository:<br/> | ||
`$ git clone https://github.com/rse/ducky.git` | ||
- Bower: install as client component via the Bower component manager: | ||
`$ bower install ducky` | ||
- cURL: downloading only the main file from the repository | ||
- cURL: download only the main file from the repository:<br/> | ||
`$ curl -O https://raw.github.com/rse/ducky/master/lib/ducky.js` | ||
@@ -44,3 +45,3 @@ | ||
#### select(object: Object, path: String, value?: Object): Object | ||
#### ducky.select(object: Object, path: String, value?: Object): Object | ||
@@ -73,2 +74,14 @@ Dereference into (and this way subset) `object` according to the | ||
In case caching of the internally compiled Abstract Syntax Tree (AST) | ||
is not wishes, you can perform the compile and execute steps | ||
of `ducky.select` individually: | ||
##### ducky.select.compile(path: String): Object | ||
Compile the selection specification `path` into an AST. | ||
##### ducky.select.execute(object: Object, ast: Object, value?: Object): Object | ||
Select from `object` a value via `ast` and either return it or set it to the new value `value`. | ||
#### validate(object: Object, spec: String): Boolean | ||
@@ -98,7 +111,19 @@ | ||
validate({ foo: "Foo", bar: "Bar", baz: [ 42, 7, "Quux" ] }, | ||
ducky.validate({ foo: "Foo", bar: "Bar", baz: [ 42, 7, "Quux" ] }, | ||
"{ foo: string, bar: any, baz: [ number+, string* ], quux?: any }") // &arr; true | ||
#### params(name: String, args: Object[], spec: Object): Object | ||
In case caching of the internally compiled Abstract Syntax Tree (AST) | ||
is not wishes, you can perform the compile and execute steps | ||
of `ducky.validate` individually: | ||
##### ducky.validate.compile(spec: String): Object | ||
Compile the validation specification `spec` into an AST. | ||
##### ducky.validate.execute(object: Object, ast: Object): Boolean | ||
Validate `object` against `ast` and return `true` in case it validates. | ||
#### ducky.params(name: String, args: Object[], spec: Object): Object | ||
Handle positional and named function parameters by processing | ||
@@ -105,0 +130,0 @@ a function's `arguments` array. Parameter `name` is the name |
@@ -65,1 +65,2 @@ /* | ||
}; | ||
@@ -30,3 +30,3 @@ /* | ||
and either get the current value or set the new value */ | ||
Ducky.select = function (obj, spec, value) { | ||
ducky.select = function (obj, spec, value) { | ||
/* sanity check arguments */ | ||
@@ -60,3 +60,3 @@ if (arguments.length < 2) | ||
/* compile a path specification into array of dereferencing steps */ | ||
Ducky.select.compile = function (spec) { | ||
ducky.select.compile = function (spec) { | ||
/* sanity check argument */ | ||
@@ -73,3 +73,3 @@ if (arguments.length !== 1) | ||
/* execute object selection */ | ||
Ducky.select.execute = function (obj, path) { | ||
ducky.select.execute = function (obj, path) { | ||
/* sanity check arguments */ | ||
@@ -76,0 +76,0 @@ if (arguments.length < 2) |
@@ -29,3 +29,3 @@ /* | ||
/* API function: validate an arbitrary value against a validation DSL */ | ||
Ducky.validate = function (value, spec) { | ||
ducky.validate = function (value, spec) { | ||
/* sanity check arguments */ | ||
@@ -43,3 +43,3 @@ if (arguments.length !== 2) | ||
if (typeof ast === "undefined") { | ||
ast = Ducky.validate.compile(spec); | ||
ast = ducky.validate.compile(spec); | ||
validate_cache[spec] = ast; | ||
@@ -49,6 +49,6 @@ } | ||
/* execute validation AST against the value */ | ||
return Ducky.validate.execute(value, ast); | ||
return ducky.validate.execute(value, ast); | ||
}; | ||
Ducky.validate.compile = function (spec) { | ||
ducky.validate.compile = function (spec) { | ||
/* sanity check arguments */ | ||
@@ -71,3 +71,3 @@ if (arguments.length !== 1) | ||
Ducky.validate.execute = function (value, ast) { | ||
ducky.validate.execute = function (value, ast) { | ||
/* sanity check arguments */ | ||
@@ -74,0 +74,0 @@ if (arguments.length !== 2) |
@@ -51,3 +51,3 @@ /* | ||
return; | ||
if (!Ducky.validate(value, valid)) | ||
if (!ducky.validate(value, valid)) | ||
throw new Error(func + ": parameter \"" + param + "\" has " + | ||
@@ -59,3 +59,3 @@ what + " " + JSON.stringify(value) + ", which does not validate " + | ||
/* API function: flexible parameter handling */ | ||
Ducky.params = function (func, args, spec) { | ||
ducky.params = function (func, args, spec) { | ||
/* start with a fresh parameter object */ | ||
@@ -62,0 +62,0 @@ var params = {}; |
@@ -25,3 +25,3 @@ /*! | ||
interface Ducky { | ||
interface ducky_api { | ||
validate: { | ||
@@ -51,1 +51,3 @@ (obj: any, spec: string): boolean; | ||
declare var ducky: ducky_api; | ||
@@ -38,4 +38,5 @@ /*! | ||
root[name] = factory(root); | ||
}(this, "Ducky", function (/* root */) { | ||
var Ducky = {}; | ||
}(this, "ducky", function (/* root */) { | ||
var ducky = {}; | ||
include("ducky-0-version.js"); | ||
include("ducky-1-util.js"); | ||
@@ -50,4 +51,4 @@ include("ducky-2-select-1-compile.js"); | ||
include("ducky-4-params.js"); | ||
return Ducky; | ||
return ducky; | ||
})); | ||
@@ -36,4 +36,4 @@ /* | ||
var Ducky = require("../lib/ducky.js") | ||
var select = Ducky.select | ||
var ducky = require("../lib/ducky.js") | ||
var select = ducky.select | ||
@@ -40,0 +40,0 @@ describe("Ducky", function () { |
@@ -36,4 +36,4 @@ /* | ||
var Ducky = require("../lib/ducky.js") | ||
var validate = Ducky.validate | ||
var ducky = require("../lib/ducky.js") | ||
var validate = ducky.validate | ||
@@ -40,0 +40,0 @@ describe("Ducky", function () { |
@@ -36,3 +36,3 @@ /* | ||
var Ducky = require("../lib/ducky.js") | ||
var ducky = require("../lib/ducky.js") | ||
@@ -43,3 +43,3 @@ describe("Ducky", function () { | ||
var foo = function () { | ||
var params = Ducky.params("foo", arguments, { | ||
var params = ducky.params("foo", arguments, { | ||
name: { pos: 0, req: true, valid: "string" }, | ||
@@ -46,0 +46,0 @@ enabled: { pos: 1, def: false, valid: "boolean" }, |
117464
27
2239
179