Naughty Programmer's Madness
Share your code. npm Orgs help your team discover, share, and reuse code. Create a free org »

bycontract

0.0.7 • Public • Published

ByContract

NPM Build Status Join the chat at https://gitter.im/dsheiko/bycontract

byContract is a small validation library (1,1 KB gzip) that allows you to benefit from Design by Contract programming in your JavaScript code. The lib uses JSDoc expression for a contract. Therefore you likely already familiar with the syntax. The library is implemented as a UMD-compatible module, so you can use as CommonJs and AMD. Besides, it exposes byContract function globally when window object available, meaning you can still use it in non-modular programming.

Getting Started

Test value against a contract
byContract( value, "JSDOC-EXPRESSION" ); // ok or exception
// or
byContract( value, "JSDOC-EXPRESSION", "text" ); // exception message prefixed with `text`
Test set of values against a contract list
byContract( [ value, value ], [ "JSDOC-EXPRESSION", "JSDOC-EXPRESSION" ] );  // ok or exception
// e.g.
byContract( arguments, [ "JSDOC-EXPRESSION", "JSDOC-EXPRESSION" ] );  // ok or exception
Validate value against a contract
byContract.validate( value, "JSDOC-EXPRESSION" );  // true or false
Usage example: ensure the contract
/**
 * @param {number|string} sum 
 * @param {Object.<string, string>} payload 
 * @param {function} cb 
 * @returns {HTMLElement} 
 */
function foo( sum, payload, cb ) {
  // Test if the contract is respected at entry point
  byContract( arguments, [ "number|string", "Object.<string, string>", "function" ] );
  // ..
  var res = document.createElement( "div" );
  // Test if the contract is respected at exit point
  return byContract( res, HTMLElement );
}
// Test it
foo( 100, { foo: "foo" }, function(){}); // ok
foo( 100, { foo: 100 }, function(){}); // exception - ByContractError: Value of index 1 violates the contract `Object.<string, string>`
Usage example: validate the value
class MyModel extends Backbone.Model {
  validate( attrs ) {
    var errors = [];
    if ( !byContract.validate( attrs.id, "!number" ) ) {
      errors.push({ name: "id", message: "Id must be a number" });
    }
    return errors.length > 0 ? errors : false;
  }
}
 

Contract Expressions

Primitive Types

You can use one of primitive types: array, string, undefined, boolean, function, nan, null, number, object, regexp

byContract( true, "boolean" );
// or
byContract( true, "Boolean" );

Union Types

byContract( 100, "string|number|boolean" ); // ok
byContract( "foo", "string|number|boolean" ); // ok
byContract( true, "string|number|boolean" ); // ok
byContract( [], "string|number|boolean" ); // Exception!

Optional Parameters

function foo( bar, baz ) {
  byContract( arguments, [ "number=", "string=" ] );
}
foo(); // ok
foo( 100 ); // ok
foo( 100, "baz" ); // ok
foo( 100, 100 ); // Exception!
foo( "bar", "baz" ); // Exception!

Array/Object Expressions

byContract( [ 1, 1 ], "Array.<number>" ); // ok
byContract( [ 1, "1" ], "Array.<number>" ); // Exception!
 
byContract( { foo: "foo", bar: "bar" }, "Object.<string, string>" ); // ok
byContract( { foo: "foo", bar: 100 }, "Object.<string, string>" ); // Exception!

Interface validation

You can validate if a supplied value is an instance of a declared interface:

var MyClass = function(){},
    instance = new MyClass();
 
byContract( instance, MyClass ); // ok

When the interface is globally available you can set contract as a string:

var instance = new Date();
byContract( instance, "Date" ); // ok
//..
byContract( view, "Backbone.NativeView" ); // ok
//..
byContract( node, "HTMLElement" ); // ok
//..
byContract( ev, "Event" ); // ok

Globally available interfaces can also be used in Array/Object expressions:

byContract( [ new Date(), new Date(), new Date() ], "Array.<Date>" ); // ok

Nullable/Non-nullable Type

byContract( 100, "?number" ); // ok
byContract( null, "?number" ); // ok
byContract( 100, "!number" ); // ok
byContract( null, "!number" ); // Exception!

Validation Exceptions

try {
  byContract( 1, "NaN" );
} catch( err ) {
  console.log( err instanceof Error ); // true
  console.log( err instanceof TypeError ); // true
  console.log( err instanceof byContract.Exception ); // true
  console.log( err.name ); // ByContractError
  console.log( err.message ); // Value violates the contract `NaN`
}
Output in NodeJS
function bar(){
  byContract( 1, "NaN" );
}
function foo() {
  bar();
}

foo();

ByContractError
    at bar (/private/tmp/demo.js:6:3)
    at foo (/private/tmp/demo.js:9:3)
    at Object.<anonymous> (/private/tmp/demo.js:12:1)
    ..

Custom Types

Pretty much like with JSDoc @typedef one can declare a custom type and use it as a contract.

Validating against a Union Type

Here we define a union type for values that can contain either numbers or strings that represent numbers.

byContract.typedef( "NumberLike", "number|string" );
byContract( 10, "NumberLike" ); // OK
byContract( null, "NumberLike" ); // throws Value incorrectly implements interface `NumberLike`

Validating against a Complex Type

This example defines a type Hero that represents an object/namespace required to have properties hasSuperhumanStrength and hasWaterbreathing both of boolean type.

byContract.typedef( "Hero", {
  hasSuperhumanStrength: "boolean",
  hasWaterbreathing: "boolean"
});
var superman = {
  hasSuperhumanStrength: true,
  hasWaterbreathing: false
};
byContract( superman, "Hero" ); // OK

When any of properties violates the specified contract an exception thrown

var superman = {
  hasSuperhumanStrength: 42,
  hasWaterbreathing: null
};
byContract( superman, "Hero" ); // throws Value incorrectly implements interface `Hero`

If value mises a property of the complex type an exception thrown

var auqaman = {
  hasWaterbreathing: true
};
byContract( superman, "Hero" ); // throws Value incorrectly implements interface `Hero`

Custom Validators

Basic type validators exposed publicly in byContract.is namespace. so you can extend it:

byContract.is.email = function( val ){
  var re = /^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
  return re.test( val );
}
byContract( "me@dsheiko.com", "email" ); // ok
byContract( "bla-bla", "email" ); // Exception!

Disable Validation on Production Environment

if ( env === "production" ) {
  byContract.isEnabled = false;
}

Analytics

install

npm i bycontract

Downloadsweekly downloads

37

version

0.0.7

license

none

homepage

github.com

repository

Gitgithub

last publish

collaborators

  • avatar