contract-decorators

0.2.2 • Public • Published

contract-decorators

NPM

Build Status Code Climate Test Coverage

Class method decorators to ensure that class method is called with valid arguments (precondition) and/or returns expected result (postcondition).

To get more information about preconditions and postconditions see Design by contract article on Wikipedia.

ECMAScript decorators are currently in proposal state. Use babeljs to transpile them into executable code.

Contents

Installation

Install it via npm:

$ npm install --save contract-decorators

Usage

const contract = require('contract-decorators');
const { precondition, postcondition } = contract;
contract.enabled = true;
 
const test = new class Test {
  @precondition(a => a < 9, b => b > 1)
  @postcondition(result => result % 2)
  method(a, b) {
    return a + b;
  }
}
 
test.method(1, 2);
// 3
test.method(9, 0);
// Uncaught PreconditionError: Precondition failed. Argument #0 of method "method" must satisfy predicate "a => a < 9" but it does not: 9.
test.method(0, 0);
// Uncaught PreconditionError: Precondition failed. Argument #1 of method "method" must satisfy predicate "b => b > 1" but it does not: 0.
test.method(2, 4);
// Uncaught PostconditionError: Postcondition failed. Result of method "method" must satisfy predicate "result => result % 2" but it does not: 6.
 
contract.enabled = false;
 
test.method(9, 0);
// 9
test.method(0, 0);
// 0
test.method(2, 4);
// 6

Customization

Custom errors

class CustomPreconditionError extends Error {
  constructor(method, predicate, argument, index) {
    super(`Some friendly message containing name of ${method} that is called with contract violation, value of ${argument} causing the violation, its ${index} and name of ${predicate} that proves it`);
  }
}
 
contract.PreconditionError = CustomPreconditionError;
 
class CustomPostconditionError extends Error {
  constructor(method, predicate, result) {
    super(`Some friendly message containing name of ${method} that returns ${result} causing contract violation and name of ${predicate} that proves it.`);
  }
}
 
contract.PostconditionError = CustomPostconditionError;

Custom method/predicate name resolvers

const customNameResolver = func => func.name;
 
contract.methodNameResolver = customNameResolver;
contract.predicateNameResolver = customNameResolver;

Decorated method is always wrapped into function with the same name that method has with Contract suffix (${method.name}Contract). To avoid multiple Contract suffixes, original name of the method is stored as .originalName property of wrapper function.

By default the following algorithms are used for method and predicate name resolution:

  • method.originalName || method.name
  • predicate.name || predicate.toString()

Configuration

For performance reasons contract decorators are enabled by default in development environment only (process.env === 'development'). To enable them in other environments use:

contract.enabled = true;

If contract decorators are disabled at the moment of decorator application, no decoration occurs and succeeding enabling won't have any effect. This behavior is intended to gain maximal performance in production.

contract.enabled = false;
 
const test = new class WillNotBeDecorated {
  @precondition(() => false)
  method() {
    return 'ok';
  }
}
 
contract.enabled = true;
 
test.method();
// ok

Configuration and customization of contract decorators can be performed with one call:

contract.configure({
  enabled: true,
  methodNameResolver: customNameResolver,
  PreconditionError: CustomPreconditionError,
  PostconditionError: CustomPostconditionError,
  predicateNameResolver: customNameResolver
});

License

MIT

Dependents (0)

Package Sidebar

Install

npm i contract-decorators

Weekly Downloads

1

Version

0.2.2

License

MIT

Last publish

Collaborators

  • vladen