Design by Contract (precondition/postcondition) class method decorators for ECMAScript.
npm install contract-decorators



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.
* Installation
* Usage
* Customization
* Configuration
* API
* Tests
* License
Install it via npm:
```
$ npm install --save contract-decorators
`js
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
`
`jsSome 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
class CustomPreconditionError extends Error {
constructor(method, predicate, argument, index) {
super();
}
}
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;
`
`js
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.namepredicate.name || predicate.toString()
*
For performance reasons contract decorators are enabled by default in development environment only (process.env === 'development'). To enable them in other environments use:
`js`
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.
`js
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:
`js``
contract.configure({
enabled: true,
methodNameResolver: customNameResolver,
PreconditionError: CustomPreconditionError,
PostconditionError: CustomPostconditionError,
predicateNameResolver: customNameResolver
});
MIT