/
index.js
118 lines (106 loc) · 3.1 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
'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('function' === typeof process.on)
process.on('exit', macgyver.validate)
else
setTimeout(macgyver.validate, timeout || 10e3)
return macgyver
}
return macgyver
}