contract-decorators
v0.2.2
Published
Design by Contract (precondition/postcondition) class method decorators for ECMAScript.
Downloads
4
Maintainers
Readme
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.
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 multipleContract
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