UNPKG

hubot-async

Version:

A simple helpful robot for your Company

85 lines (73 loc) 3.4 kB
async = require 'async' require('es6-promise').polyfill() class Middleware # We use this recursively, and using nextTick recursively is deprecated in node 0.10. @ticker: if typeof setImmediate is "function" then setImmediate else process.nextTick constructor: (@robot) -> @stack = [] # Public: Execute all middleware in order and call 'next' with the latest # 'done' callback if last middleware calls through. If all middleware is # compliant, 'done' should be called with no arguments when the entire # round trip is complete. # # context - context object that is passed through the middleware stack. # When handling errors, this is assumed to have a `response` property. # # next(context, done) - Called when all middleware is complete (assuming # all continued by calling respective 'next' functions) # # done() - Initial (final) completion callback. May be wrapped by # executed middleware. # # Returns promise - resolves with context when middleware completes # Returns before executing any middleware execute: (context, next, done) -> new Promise (resolve, reject) => done ?= -> # Allow each middleware to resolve the promise early if it calls done() pieceDone = -> done() resolve context # Execute a single piece of middleware and update the completion callback # (each piece of middleware can wrap the 'done' callback with additional # logic). executeSingleMiddleware = (doneFunc, middlewareFunc, cb) => # Match the async.reduce interface nextFunc = (newDoneFunc) -> cb(null, newDoneFunc or doneFunc) # Catch errors in synchronous middleware try middlewareFunc.call(undefined, context, nextFunc, doneFunc) catch err # Maintaining the existing error interface (Response object) @robot.emit('error', err, context.response) # Forcibly fail the middleware and stop executing deeper doneFunc() reject err, context # Executed when the middleware stack is finished allDone = (_, finalDoneFunc) -> next context, finalDoneFunc resolve context # Execute each piece of middleware, collecting the latest 'done' callback # at each step. process.nextTick => async.reduce(@stack, pieceDone, executeSingleMiddleware, allDone) # Public: Registers new middleware # # middleware - A generic pipeline component function that can either # continue the pipeline or interrupt it. The function is called # with (robot, context, next, done). If execution should # continue (next middleware, final callback), the middleware # should call the 'next' function with 'done' as an optional # argument. # If not, the middleware should call the 'done' function with # no arguments. Middleware may wrap the 'done' function in # order to execute logic after the final callback has been # executed. # # Returns nothing. register: (middleware) -> if middleware.length != 3 throw new Error("Incorrect number of arguments for middleware callback (expected 3, got #{middleware.length})") @stack.push middleware return undefined module.exports = Middleware