UNPKG

macgyver

Version:

[![build status](https://secure.travis-ci.org/dominictarr/macgyver.png)](http://travis-ci.org/dominictarr/macgyver) [![browser status](http://ci.testling.com/dominictarr/macgyver.png)](http://ci.testling.com/dominictarr/macgyver) declarative assertion framework for invocation ordering.

119 lines (106 loc) 3.15 kB
'use strict'; var inherits = require('util').inherits var rules = require('./rules') function createId () { return ( Math.random().toString(16).substring(2) + Math.random().toString(16).substring(2) ) } function find (array, id) { if(!id) return for (var k in array) { if(array[k].id == id) return array[k] } } //create a macgyver context var exports = module.exports = function () { var contracts = {} var macgyver = function wrap(funx, name) { //if this is already wrapped don't wrap it. if('string' === typeof funx) name = funx if('function' !== typeof funx) funx = function noOp() {} //default to empty function if(funx.id) return funx var id = createId() var contract = { called: 0, //counter of calls returned: 0, //counter of returns throws: 0, function: funx, name: name, get: function (id) { return contracts[typeof id == 'string' ? id : id.id] } } contract.rules = wrapped.rules = [] contract.wrapped = wrapped contracts[id] = contract function wrapped () { contract.called ++ var args = [].slice.call(arguments) contract.rules.forEach(function (rule) { if(rule.before) rule.before.call(contract, args) }) //actually call the function... var i = contract.rules.length - 1, threw, returned function next () { var args = [].slice.call(arguments) var around while(~i && !(around = contract.rules[i--].around)) ; return ( around ? around.call(contract, next, this, args) : (function () { try { return returned = funx.apply(this, args) } catch (err) { threw = true; throw err } }).call(this) ) } next.apply(this, args) //increment count of returns //(this is useful for asserting when something may happen before a call ends) if(threw) { contract.throws ++; return } contract.returned ++ //after contract.rules.forEach(function (rule) { if(rule.after) rule.after.call(contract, returned) }) return returned //also, have after, have around... } wrapped.id = id //install rules. for (var k in rules) { ;(function (k){ wrapped[k] = function () { var args = [].slice.call(arguments) //some rules don't make sense to duplicate. var rule = find(contract.rules, k) if(rule) return rule.update.apply(contract, args) rule = rules[k].apply(contract, args) if(rule) contract.rules.push(rule) return this } })(k) } return wrapped } macgyver.validate = function () { for (var k in contracts) { var con = contracts[k] con.rules.forEach(function (rule) { if(rule.validate) rule.validate.call(con) }) } } macgyver.autoValidate = function (timeout) { if(process.on) process.on('exit', macgyver.validate) else setTimeout(macgyver.validate, timeout || 10e3) return macgyver } return macgyver }