UNPKG

atbar

Version:

Async callback manager for javascript in nodejs and browser

416 lines 12.8 kB
/* coffee -cb /opt/node/bb/dev/node_modules/atbar/atbar.cof */var AtbarNode, atbarVersion, _; var __hasProp = Object.prototype.hasOwnProperty, __slice = Array.prototype.slice; atbarVersion = "0.4.1"; _ = require('underscore'); _.mixin(require('underscore.string')); _.mixin(require('underscore.inspector')); AtbarNode = (function() { function AtbarNode(parent, label, options, func) { var attr, _ref, _ref2, _ref3; this.parent = parent; this.label = label; this.options = options; this.func = func; this.root = (_ref = (_ref2 = this.parent) != null ? _ref2.root : void 0) != null ? _ref : this; if (this.parent) { _ref3 = this.parent; for (attr in _ref3) { if (!__hasProp.call(_ref3, attr)) continue; if (attr[0] === '_') { this[attr] = this.parent[attr]; } } } this.children = []; this.activeCallbacks = {}; this.callbackResults = []; if (this.parent) { if (this.parent.id !== 'root') { this.id = this.parent.id + '-' + label; } else { this.id = label; } } else { this.id = 'root'; } } AtbarNode.prototype._ = function() { var arg1, args, func, label, options; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; label = this.children.length; options = null; switch (args.length) { case 1: func = args[0]; break; case 2: arg1 = args[0], func = args[1]; switch (typeof arg1) { case 'string': label = arg1; break; case 'object': options = arg1; if (options.label) { label = options.label; } break; default: this.execErr('first @_ arg has invalid type'); return; } break; default: this.execErr('@_ has wrong number of arguments'); return; } if (typeof func !== 'function') { this.execErr('last @_ arg not a function'); return; } if (label === 'atbar' || label === 'root' || label === 'first' || label === 'loop' || label === 'next' || label === 'throw' || label === 'enter' || label === 'exit') { this.execErr(label + ' is a reserved word'); return; } return this.children.push(new AtbarNode(this, label, options, func)); }; AtbarNode.prototype.$ = function() { var arg, args, callbackInfo, data, destNode, destSpec, firstChild, i, level, levelNode, node, seqNum, srcLabel, strings, _i, _len, _ref; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; level = 0; strings = []; data = null; for (_i = 0, _len = args.length; _i < _len; _i++) { arg = args[_i]; switch (typeof arg) { case 'number': level = arg; break; case 'string': strings.unshift(arg); break; case 'object': data = arg; break; default: this.execErr('invalid arg type for callback generator: ' + typeof arg); return function() {}; } } destSpec = strings[0], srcLabel = strings[1]; if (destSpec === 'first') { level++; } levelNode = this; for (i = 0; 0 <= level ? i < level : i > level; 0 <= level ? i++ : i--) { levelNode = levelNode != null ? levelNode.parent : void 0; } if (!levelNode) { this.execErr('callback level param above top level'); return function() {}; } destNode = levelNode; if (destSpec != null) { destSpec; } else { destSpec = 'next'; }; if (destSpec === 'throw') { destSpec = 'catch'; } if (destSpec !== 'loop') { switch (destSpec) { case 'root': destNode = this.root.children[0]; break; case 'enter': case 'first': if (!(firstChild = destNode.children[0])) { return function() {}; } destNode = firstChild; break; case 'next': destNode = destNode.nextNode(); break; case 'exit': destNode = this.root; break; default: while ((destNode = destNode.nextNode())) { if (destNode.label === destSpec || destNode === this.root) { break; } } } } if (destNode === this.root) { node = this; if (destSpec === 'catch') { return (function(node) { return function(err) { return node.exit(err, null); }; })(node); } else if (destSpec !== 'next' && destSpec !== 'root' && destSpec !== 'first' && destSpec !== 'loop' && destSpec !== 'enter' && destSpec !== 'exit') { this.execErr('Destination for ' + destSpec + ' not found'); return function() {}; } } seqNum = this.root.nextCbSeqNum++; callbackInfo = { seqNum: seqNum, srcNode: this, destNode: destNode, level: level, srcLabel: srcLabel, destSpec: destSpec, data: data }; if (!((_ref = destNode.options) != null ? _ref.nojoin : void 0)) { destNode.activeCallbacks[seqNum] = callbackInfo; this.root.totalActiveCallbacks++; } return (function(callbackInfo) { return function() { var cbArgs; cbArgs = 1 <= arguments.length ? __slice.call(arguments, 0) : []; callbackInfo.cbArgs = cbArgs; return callbackInfo.destNode.call(callbackInfo); }; })(callbackInfo); }; AtbarNode.prototype.call = function(callbackInfo) { var cbArgs, data, destNode, destSpec, idx, param, seqNum, srcLabel, srcNode, tpi, _i, _len, _len2, _ref, _ref2, _ref3; seqNum = callbackInfo.seqNum, srcNode = callbackInfo.srcNode, srcLabel = callbackInfo.srcLabel, destNode = callbackInfo.destNode, destSpec = callbackInfo.destSpec, cbArgs = callbackInfo.cbArgs, data = callbackInfo.data; if (this.root.finished) { if (this._trace) { this.printNode(srcNode.id, destSpec, true, false, 'callback after atbar finished'); } return; } if (!((_ref = this.options) != null ? _ref.nojoin : void 0)) { if (!this.activeCallbacks[seqNum]) { if (this._trace) { this.printNode(srcNode.id, destSpec, true, false, 'unregistered or duplicate callback'); } return; } this.root.totalActiveCallbacks--; delete this.activeCallbacks[seqNum]; } if (this.callbacksComplete) { this.callbackResults = []; } this.data = data; this.callbackResults.push(callbackInfo); this.callbacksComplete = _.size(this.activeCallbacks) === 0; if (this === this.root && destSpec === 'exit') { this.exit(null, this.callbackResults); return; } if (cbArgs.length) { if (!this.throwParamIndexes) { this.throwParamIndexes = []; _ref2 = this.func.toString().match(/^\s*function\s*\(([^)]*)\)/i)[1].split(','); for (idx = 0, _len = _ref2.length; idx < _len; idx++) { param = _ref2[idx]; if (_.trim(param).toLowerCase() === '_throw') { this.throwParamIndexes.push(idx); } } } _ref3 = this.throwParamIndexes; for (_i = 0, _len2 = _ref3.length; _i < _len2; _i++) { tpi = _ref3[_i]; if (cbArgs[tpi]) { this["throw"](cbArgs[tpi]); return; } } } if (!this.callbacksComplete) { if (this._trace) { this.printNode(srcNode.id, destSpec, true, false, 'Callback ignored, waiting for ' + _.size(this.activeCallbacks) + ' more'); } return; } if (this === this.root && destSpec !== 'loop') { this.exitIfIdle(null, cbArgs, srcNode.id); return; } if (this._trace) { if (srcLabel === 'atbar') { console.log("Starting atbar trace (Version " + atbarVersion + ") ..."); } else { this.printNode(srcNode.id, destSpec, true, false); } } try { this.func.apply(this, cbArgs); } catch (e) { if (this.root.finished) { throw e; } this.$('throw')(e); } if (this.children.length) { return this.$('enter')(); } }; AtbarNode.prototype.nextNode = function() { var idx, nextNode, node, parent, sibling, siblings, _len, _ref; node = this; while (node) { siblings = (_ref = node.parent) != null ? _ref.children : void 0; if (siblings) { for (idx = 0, _len = siblings.length; idx < _len; idx++) { sibling = siblings[idx]; if (sibling === node && (nextNode = siblings[idx + 1])) { return nextNode; } } } if ((parent = node.parent)) { return parent.nextNode(); } else { break; } } return this.root; }; AtbarNode.prototype.exit = function(err, resp) { this.root.finished = true; this.listing(); if (this.root.exitCallback) { return this.root.exitCallback(err, resp); } else if (err) { throw err; } }; AtbarNode.prototype.exitIfIdle = function(err, resp, id) { if (this.root.totalActiveCallbacks === 0) { if (this._trace && id) { this.printNode(id, null, true, false, 'Nothing to do, exiting ...'); } return this.exit(err, resp); } }; AtbarNode.prototype["throw"] = function(e) { return this.$('throw')(e); }; AtbarNode.prototype.execErr = function(msg) { msg = "atbar execution error at " + this.id + ": " + msg; return this.$('throw')(msg); }; AtbarNode.prototype.log = function() { var arg, args, dump, msg, _i, _len; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; if (this._debug) { msg = ''; if (typeof args[0] === 'string') { msg = args.shift(); } dump = ''; for (_i = 0, _len = args.length; _i < _len; _i++) { arg = args[_i]; dump += '\n' + _.inspect(arg, 1); } return console.log(("\natbar log at " + this.id + ": ") + msg + dump); } }; AtbarNode.prototype.logx = function() {}; AtbarNode.prototype.printNode = function(src, label, trace, returnText, comment) { var code, line, time; if (src == null) { src = ''; } if (comment) { code = comment; } else { code = this.func.toString().replace(/\s+/g, ' ').replace(/^\s*function\s*/, ''); if (code.length > 80) { code = code.slice(0, 80) + ' ...'; } } src = src + (label ? '(' + label + ')' : ''); if (trace) { time = (+(new Date) - this.root.startTime) / 1e3; line = _.sprintf('%8.3f %-3d %12s -> %-7s %s', time, this.root.totalActiveCallbacks, src, this.id, code); } else { line = _.sprintf('%-10s %s', this.id, code); } if (returnText) { return line + '\n'; } else { return console.log(line); } }; AtbarNode.prototype.treeText = function() { var text, treeTextRecurse; text = ''; treeTextRecurse = function(node, depth) { var child, i, pad, _i, _len, _ref; if (node.label !== 'root') { pad = ''; for (i = 0; 0 <= depth ? i < depth : i > depth; 0 <= depth ? i++ : i--) { pad = ' ' + pad; } text += pad + node.printNode(null, null, false, true); } _ref = node.children; for (_i = 0, _len = _ref.length; _i < _len; _i++) { child = _ref[_i]; treeTextRecurse(child, depth + 1); } return null; }; treeTextRecurse(this.root, 1); return text; }; AtbarNode.prototype.listing = function() { if (this._listing) { console.log('\nListing of visited atbar functions ...'); return console.log(this.treeText()); } }; return AtbarNode; })(); exports.run = function() { var arg, args, exitCallback, func, funcs, opt, options, root, _i, _j, _len, _len2, _ref; args = 1 <= arguments.length ? __slice.call(arguments, 0) : []; options = _.trim((_ref = exports.options) != null ? _ref : '').split(/\W+/); funcs = []; for (_i = 0, _len = args.length; _i < _len; _i++) { arg = args[_i]; switch (typeof arg) { case 'function': funcs.unshift(arg); break; case 'string': options.push(arg); } } if (funcs.length > 1) { func = funcs[1]; exitCallback = funcs[0]; } else { func = funcs[0]; } if (func) { root = new AtbarNode(null, 'root', null, func); root.nextCbSeqNum = 0; root.totalActiveCallbacks = 0; root.exitCallback = exitCallback; for (_j = 0, _len2 = options.length; _j < _len2; _j++) { opt = options[_j]; if (opt) { root['_' + opt] = true; } } root.startTime = +(new Date); return root.$('atbar', 'loop')(); } };