UNPKG

dbc

Version:

Design by contract and type checking assertions

534 lines (415 loc) 22.4 kB
<!DOCTYPE html> <html> <head> <title>dbc</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <ul class="sections"> <li id="section-1"> <div class="annotation"> <div class="pilwrap for-h1"> <a class="pilcrow" href="#section-1">&#182;</a> </div> <h1>dbc</h1> <p><code>dbc</code> is a small library for design-by-contract style defensive coding in javascript. </p> </div> <div class="content"><div class='highlight'><pre> (<span class="function"><span class="keyword">function</span> <span class="params">()</span> {</span> <span class="keyword">var</span> mode , messages = []; <span class="keyword">var</span> _ = <span class="keyword">this</span>._ || require(<span class="string">'underscore'</span>); dbc = {</pre></div></div> </li> <li id="section-2"> <div class="annotation"> <div class="pilwrap for-h1"> <a class="pilcrow" href="#section-2">&#182;</a> </div> <h1>Public API</h1> </div> </li> <li id="section-3"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-3">&#182;</a> </div> <h2>check</h2> <p>Check asserts that an object <code>o</code> meets a specification <code>spec</code>. If <code>o</code> does not satisfy <code>spec</code> then an exception is thrown. eg.</p> <pre><code> dbc.check({ name: &#39;John&#39;, age: 22 }, { name: [{validator:&#39;type&#39;,args:[&#39;string&#39;]}], age: [{validator:&#39;type&#39;,args:[&#39;number&#39;]}], });</code></pre> </div> <div class="content"><div class='highlight'><pre> check: <span class="keyword">function</span>(o, spec, message) { message = message || <span class="string">''</span> mode = <span class="string">'check'</span>; <span class="keyword">try</span> { applyValidators.call(<span class="keyword">this</span>, o, spec); } <span class="keyword">catch</span> (e) { <span class="keyword">throw</span> <span class="keyword">new</span> Error(message ? message + <span class="string">': '</span> + e.message : e.message); } },</pre></div></div> </li> <li id="section-4"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-4">&#182;</a> </div> <h2>validate</h2> <p>Validate is the same as check, except that errors are returned as an array of messages instead of throwing an exception.</p> </div> <div class="content"><div class='highlight'><pre> validate: <span class="function"><span class="keyword">function</span> <span class="params">(o, spec)</span> {</span> mode = <span class="string">'validate'</span>; applyValidators.call(<span class="keyword">this</span>, o, spec); <span class="keyword">return</span> messages; },</pre></div></div> </li> <li id="section-5"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-5">&#182;</a> </div> <h2>wrap</h2> <p>Wrap returns a wrapped function that applies &#39;specs&#39; validators to the functions arguments and &#39;returnSpec&#39; validators to the return value. eg</p> <pre><code> var add = dbc.wrap(function (f,s) { return f + s; }, { // validators for the first arg 0: [{validator:&#39;type&#39;, args:[&#39;number&#39;]}], // validators for the second arg 1:[{validator:&#39;type&#39;, args:[&#39;number&#39;]}] }, // validators for the return value [{validator: &#39;type&#39;, args:[&#39;number&#39;]}]);</code></pre> </div> <div class="content"><div class='highlight'><pre> wrap: <span class="keyword">function</span>(original, specs, returnSpec) { <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> <span class="params">()</span> {</span> <span class="keyword">var</span> r; checkArgs(arguments); r = original.apply(<span class="keyword">this</span>,arguments); checkReturn(); <span class="keyword">return</span> r; <span class="function"><span class="keyword">function</span> <span class="title">checkArgs</span><span class="params">(args)</span> {</span> _.each(_.keys(specs || {}), <span class="function"><span class="keyword">function</span> <span class="params">(k,index)</span> {</span> dbc.check( {k: args[index] }, { k: specs[k] }); }); } <span class="function"><span class="keyword">function</span> <span class="title">checkReturn</span><span class="params">()</span> {</span> <span class="keyword">if</span> (returnSpec) { dbc.check({ret: r}, {ret: returnSpec}); } } }; },</pre></div></div> </li> <li id="section-6"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-6">&#182;</a> </div> <h2>makeConstructor</h2> <p>MakeConstructor generates a constructor from a spec. The constructor validates that the created objects meet the spec. ie </p> <pre><code> var Person = dbc.MakeConstructor({ name: [{validator:&#39;type&#39;,args:&#39;string&#39;}], age: [{validator:&#39;type&#39;,args:&#39;number&#39;}] }); Person.prototype.canDrive = dbc.wrap(function () { return this.age &gt; 16; }, null, [{validator:&#39;type&#39;,args:&#39;number&#39;}]); var p = new Person(&#39;John&#39;, 22); p.canDrive(); // true </code></pre> </div> <div class="content"><div class='highlight'><pre> makeConstructor: <span class="keyword">function</span>(spec) { <span class="keyword">var</span> f = <span class="function"><span class="keyword">function</span> <span class="params">(prps)</span> {</span> <span class="keyword">var</span> c = <span class="keyword">this</span>; _.each(_.keys(spec), <span class="function"><span class="keyword">function</span> <span class="params">(key)</span> {</span> c[key] = prps[key]; }); dbc.check(c, c.__spec); }; f.prototype.__spec = spec; <span class="keyword">return</span> f; },</pre></div></div> </li> <li id="section-7"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-7">&#182;</a> </div> <h2>custom</h2> <p>The <code>custom</code> validator applies a custom predicate. eg</p> <pre><code> dbc.check({ number: 7 },{ number: [{validator:&#39;custom&#39;,args:[function isEven(n) { return n % 2 === 0; }]}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> custom: <span class="keyword">function</span>(v, test, message) { <span class="keyword">if</span> (!isExisting(v)) <span class="keyword">return</span>; <span class="keyword">if</span> (!test(v)) { storeMessage(message || <span class="string">'failed custom function condition for value '</span> + v); } },</pre></div></div> </li> <li id="section-8"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-8">&#182;</a> </div> <h2>assert</h2> <p>Assert is for simple boolean assertions. eg</p> <pre><code> dbc.assert(6 === 9, &#39;Six should equal nine&#39;);</code></pre> </div> <div class="content"><div class='highlight'><pre> assert: <span class="keyword">function</span>(condition, message) { <span class="keyword">if</span> (!condition) { storeMessage(message); } },</pre></div></div> </li> <li id="section-9"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-9">&#182;</a> </div> <h2>type</h2> <p>Type asserts the type of a value using JavaScript&#39;s <code>typeof</code> operator. In addition to the JavaScript types (undefined, object, boolean, number, string, function) you can also use <code>array</code>. eg</p> <pre><code> dbc.check({ name:&#39;John&#39; }, { name: [{validator:&#39;type&#39;,args: [&#39;string&#39;]}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> type: <span class="function"><span class="keyword">function</span> <span class="params">(v, type, message)</span> {</span> <span class="keyword">if</span> (type.charAt(type.length-<span class="number">1</span>) == <span class="string">'?'</span>) { <span class="keyword">if</span> (!isExisting(v)) <span class="keyword">return</span>; type = type.substring(<span class="number">0</span>, type.length-<span class="number">1</span>); } message = message || <span class="string">'Expected type of '</span> + type + <span class="string">' but was '</span> + <span class="keyword">typeof</span> v; <span class="keyword">if</span> (type == <span class="string">'array'</span>) { <span class="keyword">if</span> (!_.isArray(v)) { storeMessage(message) } <span class="keyword">return</span>; } <span class="keyword">if</span> (<span class="keyword">typeof</span> v == <span class="string">'undefined'</span> || v == <span class="literal">null</span>) { storeMessage(<span class="string">'Expected type of '</span> + type + <span class="string">' but was null or undefined'</span>); } <span class="keyword">if</span> ((<span class="keyword">typeof</span> v) != type) { storeMessage(message); } },</pre></div></div> </li> <li id="section-10"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-10">&#182;</a> </div> <h2>required</h2> <p>Required asserts that a value is not null or undefined. This validator does not require any args. eg</p> <pre><code> dbc.check({ name:&#39;John&#39; }, { name: [{validator:&#39;required&#39;}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> required: <span class="keyword">function</span>(v, message) { <span class="keyword">if</span> (!isExisting(v)) { storeMessage(message || <span class="string">'expected a defined value'</span>); } },</pre></div></div> </li> <li id="section-11"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-11">&#182;</a> </div> <h2>isArray</h2> <p>IsArray asserts that a value is an array. This validator does not require any args. eg</p> <pre><code> dbc.check({ colours: [&#39;red&#39;,&#39;green&#39;,&#39;blue&#39;] }, { colours: [{validator:&#39;isArray&#39;}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> isArray: <span class="function"><span class="keyword">function</span> <span class="params">(v, message)</span> {</span> <span class="keyword">if</span> (isExisting(v) &amp;&amp; !_.isArray(v)) { storeMessage(message || <span class="string">'expected an array'</span>) } },</pre></div></div> </li> <li id="section-12"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-12">&#182;</a> </div> <h2>isEnumerable</h2> <p>IsEnumerable asserts that a value has a forEach function. This validator does not require any args. eg</p> <pre><code> dbc.check({ colours: [&#39;red&#39;,&#39;green&#39;,&#39;blue&#39;] }, { colours: [{validator:&#39;isEnumerable&#39;}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> isEnumerable: <span class="function"><span class="keyword">function</span> <span class="params">(v, message)</span> {</span> <span class="keyword">if</span> (isExisting(v) &amp;&amp; <span class="keyword">typeof</span> v.forEach !== <span class="string">'function'</span>) { storeMessage(message || <span class="string">'expected an object with a forEach function'</span>); } },</pre></div></div> </li> <li id="section-13"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-13">&#182;</a> </div> <h2>isNonEmptyCollection</h2> <p>isNonEmptyCollection asserts that a value has a length property greater than zero. This validator does not require any args. eg</p> <pre><code> dbc.check({ divs: $(&#39;div&#39;) // assuming jQuery }, { divs: [{validator:&#39;isNonEmptyCollection&#39;}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> isNonEmptyCollection: <span class="function"><span class="keyword">function</span> <span class="params">(v, message)</span> {</span> <span class="keyword">if</span> (!isExisting(v)) <span class="keyword">return</span>; <span class="keyword">if</span> (!(<span class="keyword">typeof</span> v.length === <span class="string">'number'</span> &amp;&amp; v.length &gt; <span class="number">0</span>)) { <span class="keyword">throw</span> <span class="keyword">new</span> Error(message || <span class="string">'expected collection with length &gt; 0'</span>); } },</pre></div></div> </li> <li id="section-14"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-14">&#182;</a> </div> <h2>isFunction</h2> <p>isFunction asserts that a value is a function. This validator does not require any args. eg</p> <pre><code> dbc.check({ square: function (n) { return n * n; } }, { square: [{validator:&#39;isFunction&#39;}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> isFunction: <span class="keyword">function</span>(f, message) { <span class="keyword">this</span>.type(f, <span class="string">'function'</span>, message || <span class="string">'expected a function'</span>); },</pre></div></div> </li> <li id="section-15"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-15">&#182;</a> </div> <h2>isObject</h2> <p>isObject asserts that a value is an object. This validator does not require any args. eg</p> <pre><code> dbc.check({ thing: {} }, { thing: [{validator:&#39;isObject&#39;}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> isObject: <span class="keyword">function</span>(o, message) { <span class="keyword">this</span>.type(o, <span class="string">'object'</span>, message || <span class="string">'expected an object'</span>); },</pre></div></div> </li> <li id="section-16"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-16">&#182;</a> </div> <h2>isInstance</h2> <p>isInstance asserts the constructor of an object. eg</p> <pre><code> dbc.check({ john: new Person(&#39;John&#39;) }, { john: [{validator:&#39;isInstance&#39;,args:[Person]}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> isInstance: <span class="keyword">function</span>(o, type, message) { <span class="keyword">if</span> (isExisting(o) &amp;&amp; !(o <span class="keyword">instanceof</span> type)) { storeMessage(message || <span class="string">"expected "</span> + o + <span class="string">" to be an instance of "</span> + type.name); } },</pre></div></div> </li> <li id="section-17"> <div class="annotation"> <div class="pilwrap for-h2"> <a class="pilcrow" href="#section-17">&#182;</a> </div> <h2>functionArity</h2> <p>functionArity asserts the arity of a function. eg</p> <pre><code> dbc.check({ add: function (f,s) { return f + s; } }, { add: [{validator:&#39;functionArity&#39;,args:[2]}] });</code></pre> </div> <div class="content"><div class='highlight'><pre> functionArity: <span class="function"><span class="keyword">function</span> <span class="params">(f, arity, message)</span> {</span> <span class="keyword">if</span> (!isExisting(f)) <span class="keyword">return</span>; <span class="keyword">this</span>.isFunction(f, <span class="string">'cannot check arity of an object that is not a function'</span>); <span class="keyword">if</span> (f.length !== arity) { storeMessage(message || <span class="string">'Function arity is '</span> + f.length + <span class="string">'. Expected '</span> + arity); } } }; <span class="function"><span class="keyword">function</span> <span class="title">isExisting</span><span class="params">(v)</span> {</span> <span class="keyword">return</span> <span class="keyword">typeof</span> v !== <span class="string">"undefined"</span> &amp;&amp; v !== <span class="literal">null</span>; } <span class="function"><span class="keyword">function</span> <span class="title">applyValidators</span><span class="params">(o, spec)</span> {</span> <span class="keyword">var</span> specKeys = _.keys(spec), dbc = <span class="keyword">this</span>; specKeys.forEach(<span class="function"><span class="keyword">function</span> <span class="params">(key)</span> {</span> <span class="keyword">var</span> validators = spec[key]; validators.forEach(<span class="keyword">function</span>(validator) { dbc[validator.validator].apply(dbc, [o[key]].concat(validator.args || [])) }); }); } <span class="function"><span class="keyword">function</span> <span class="title">storeMessage</span><span class="params">(message)</span> {</span> <span class="keyword">if</span> (mode === <span class="string">'validate'</span>) { messages.push(message); <span class="keyword">return</span>; } <span class="keyword">throw</span> <span class="keyword">new</span> Error(message); } <span class="keyword">if</span> (<span class="keyword">typeof</span> define !== <span class="string">"undefined"</span> &amp;&amp; define !== <span class="literal">null</span>) { define(<span class="string">'dbc'</span>, [], <span class="function"><span class="keyword">function</span> <span class="params">()</span> {</span> <span class="keyword">return</span> dbc; }); } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">typeof</span> window !== <span class="string">"undefined"</span> &amp;&amp; window !== <span class="literal">null</span>) { window.dbc = dbc; } <span class="keyword">if</span> (<span class="keyword">typeof</span> module !== <span class="string">"undefined"</span> &amp;&amp; module !== <span class="literal">null</span>) { module.exports = dbc; } })();</pre></div></div> </li> </ul> </div> </body> </html>