UNPKG

objective

Version:
183 lines (166 loc) 5.44 kB
var generate = require('shortid').generate , pipeline , sequence = require('when/sequence') , promise = require('when').promise , count = 0 , debug = objective.logger.createDebug('pipeline') , TODO = objective.logger.TODO , error = objective.logger.error , warn = objective.logger.warn , info = objective.logger.info function exitWarning() { for (id in pipeline.runs) { var event, payload, depth, runningHandler; event = pipeline.runs[id].event; payload = pipeline.runs[id].payload; depth = pipeline.runs[id].depth; caller = pipeline.runs[id].caller; runningHandler = pipeline.runs[id].runningHandler; warn('still in pipeline'); warn('at %s:%d:%d', caller.file, caller.line, caller.colm); warn('event:%s depth:%s, payload', event, depth, payload); warn(''); //warn('function', runningHandler); } } process.on('exit', function() { if (count == 0) exitWarning(); }); process.on('SIGINT', function() { if (objective.program.attach) return process.exit(0) count++; setTimeout(function(){ count-- }, 2000); if (count > 1) return process.exit(1); exitWarning(); console.log('\nagain to exit'); }); module.exports = pipeline = { pipes: {}, runs: {}, startRun: function(event, payload) { var id = generate(); pipeline.runs[id] = { event: event, payload: payload, depth: 0, runningHandler: null, caller: null } return id; }, createEvent: function(event) { var base; debug("pipeline created event '" + event + "'"); return (base = pipeline.pipes)[event] || (base[event] = []); }, emit: function(event, payload, callback) { var id, cancelled, cancelledReason, fn, pipe; if (!(pipe = pipeline.pipes[event])) { return callback(new Error("No handlers for '" + event + "'.")); } debug("pipeline emitting event '" + event + "'"); id = pipeline.startRun(event, payload); cancelled = false; cancelledReason = 'reason unspecified'; return sequence( pipe.map(function(handler){ return function() { return promise(function(resolve, reject){ pipeline.runs[id].runningHandler = handler.fn; pipeline.runs[id].caller = handler.caller; pipeline.runs[id].depth++; debug("pipeline event handler '%s' running event '%s'", handler.opts.label, event); return objective.injector({ context: handler.opts.context || null, args: [payload], next: function(e) { // TODO, is it error? if (e) return reject(e); resolve(); }, // TODO: handling pipeline injection errors // onInjectionError: reject, cancel: function(reason) { if (reason != null) { cancelledReason = reason; } cancelled = true; return reject(); } }, handler.fn); }); } }) ).then(function(result) { debug("pipeline event '" + event + "' done ok"); delete pipeline.runs[id]; return callback(null, payload); }, function(err) { var depth, fn; depth = pipeline.runs[id].depth; fn = pipeline.runs[id].runningHandler; if (cancelled) { debug("event cancelled at depth " + depth + " by ", fn); info("pipeline event '" + event + "' cancelled because '" + cancelledReason + "'"); delete pipeline.runs[id]; return; } debug("event failed at depth " + depth + " by ", fn, err); // error("pipeline event '" + event + "' failed " + (err.toString())); delete pipeline.runs[id]; return callback(err); }, function(notify) {}); }, // optional // on: function(event, opts, fn, anyway) { // // does not exist yet, subscribe anyway var _arguments = arguments; var args = Object.keys(arguments).map(function(key){return _arguments[key]}); event = args.shift(); opts = args.shift(); if (typeof opts == 'string') opts = {label:opts}; if (typeof opts == 'function') { fn = opts; anyway = args.shift(); opts = {label:'untitled'}; } else { fn = args.shift(); anyway = args.shift(); } if (fn.$$pipeid) return fn.$$pipeid; Object.defineProperty(fn, '$$pipeid', { enumareble: false, configurable: false, writable: false, value: generate() }); var base; if (typeof pipeline.pipes[event] === 'undefined' && !anyway) { error("No such event '" + event + "'"); throw new Error("No such event '" + event + "'"); } debug("pipeline registering handler on event '" + event + "'"); (base = pipeline.pipes)[event] || (base[event] = []); pipeline.pipes[event].push({ opts: opts, fn: fn, caller: objective.getCaller(1) }); return fn.$$pipeid; }, off: function(event, fn) { // deleted handlers may still still be called by any // already (long) running emits (TODO: fix) var deleteId = fn.$$pipeid; pipeline.pipes[event] = pipeline.pipes[event].filter( function(handler) { return handler.fn.$$pipeid != deleteId; } ); delete fn.$$pipeid; } };