Skip to content
This repository has been archived by the owner on Nov 18, 2023. It is now read-only.

Commit

Permalink
Merge branch 'release/2.0.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
thetutlage committed Apr 2, 2017
2 parents f348df1 + e822a68 commit 6bae56e
Show file tree
Hide file tree
Showing 9 changed files with 221 additions and 20 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ coverage
node_modules
.DS_Store
npm-debug.log
.nyc_output
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
<a name="2.0.1"></a>
## [2.0.1](https://github.com/poppinss/co-compose/compare/v2.0.0...v2.0.1) (2017-04-02)



<a name="2.0.0"></a>
# 2.0.0 (2017-02-24)

Expand Down
28 changes: 25 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,21 @@

[Koa](http://koajs.com/) and [AdonisJs](http://adonisjs.com/) style middleware are super neat since they allow you to create a chain of **Async/Await** functions and write maintainable async code.

**Co compose** makes it easier for you to add the support for same style of middleware inside your applications. It features:
<br />


[![NPM Version][npm-image]][npm-url]
[![Build Status][travis-image]][travis-url]
[![Downloads Stats][npm-downloads]][npm-url]
[![Appveyor][appveyor-image]][appveyor-url]

![](https://img.shields.io/badge/requires-node%20%3E%3D7.0-blue.svg?style=flat-square)
[![](https://img.shields.io/travis/poppinss/co-compose/master.svg?style=flat-square)](https://travis-ci.org/poppinss/co-compose)
<br />

---

<br />

**Co compose** makes it easier for you to add the support for same style of middleware inside your applications. It features:

1. Run middleware in sequence.
2. Pass custom `context (this)` to middleware functions.
Expand Down Expand Up @@ -156,3 +166,15 @@ middleware.tag('ws').register([ws1, ws2])
middleware.tag('http').compose()
middleware.tag('ws').compose()
```


[appveyor-image]: https://ci.appveyor.com/api/projects/status/github/poppinss/co-compose?branch=master&svg=true&passingText=Passing%20On%20Windows
[appveyor-url]: https://ci.appveyor.com/project/thetutlage/co-compose

[npm-image]: https://img.shields.io/npm/v/co-compose.svg?style=flat-square
[npm-url]: https://npmjs.org/package/co-compose

[travis-image]: https://img.shields.io/travis/poppinss/co-compose/master.svg?style=flat-square
[travis-url]: https://travis-ci.org/poppinss/co-compose

[npm-downloads]: https://img.shields.io/npm/dm/co-compose.svg?style=flat-square
22 changes: 22 additions & 0 deletions appveyor.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
environment:
matrix:
- nodejs_version: 'Stable'
- nodejs_version: '7'

init:
git config --global core.autocrlf true

install:
- ps: Install-Product node $env:nodejs_version
- npm install

test_script:
- node --version
- npm --version
- npm run test:win

build: off
clone_depth: 1

matrix:
fast_finish: true
24 changes: 18 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "co-compose",
"version": "2.0.0",
"version": "2.0.1",
"description": "Adonis and koa style middleware layer with ability to run parallel middleware",
"main": "index.js",
"directories": {
Expand All @@ -9,8 +9,11 @@
"scripts": {
"lint": "standard",
"pretest": "npm run lint",
"test": "node --harmony-async-await test/middleware.spec.js",
"coverage": "node --harmony-async-await ./node_modules/.bin/istanbul cover test/middleware.spec.js"
"posttest": "npm run coverage",
"test:local": "node --harmony-async-await ./node_modules/.bin/japa",
"test": "./node_modules/.bin/nyc npm run test:local",
"test:win": "node --harmony-async-await ./node_modules/japa-cli/index.js",
"coverage": "nyc report --reporter=text-lcov | coveralls"
},
"keywords": [
"adonisjs",
Expand All @@ -22,12 +25,21 @@
"author": "amanvirk,adonisjs",
"license": "MIT",
"devDependencies": {
"coveralls": "^2.13.0",
"cz-conventional-changelog": "^2.0.0",
"istanbul": "^1.1.0-alpha.1",
"japa": "^1.0.1",
"standard": "^8.6.0"
"japa": "^1.0.3",
"japa-cli": "^1.0.1",
"nyc": "^10.2.0",
"standard": "^9.0.2"
},
"dependencies": {
"lodash": "^4.17.2",
"lodash": "^4.17.4",
"resetable": "^1.0.0"
},
"config": {
"commitizen": {
"path": "./node_modules/cz-conventional-changelog"
}
}
}
6 changes: 6 additions & 0 deletions lib/Counter.js → src/Counter.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@
* file that was distributed with this source code.
*/

/**
* Counter class to track a counter.
*
* @class Counter
* @constructor
*/
class Counter {
constructor (count) {
this._count = count || 0
Expand Down
48 changes: 39 additions & 9 deletions src/Middleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,15 @@ const _ = require('lodash')
const Pipeline = require('./Pipeline')
const Resetable = require('resetable')

/**
* Middleware class is used to define an array of promises
* to be called one after the other. It follows the hooks
* approach, where each hook is responsible for advancing
* the chain by calling `next`.
*
* @class Middleware
* @constructor
*/
class Middleware {
constructor () {
this._store = {}
Expand All @@ -22,8 +31,22 @@ class Middleware {
}

/**
* Calls a middleware after resolving it from the custom function
* or via the default fn
* Resolves the function/item inside the middleware
* chain based upon it's type. If the function is
* an instance of pipeline, `compose` method
* on pipeline will be called other the
* functon is executed.
*
* @method _resolveListItem
*
* @param {Function|Object} item
* @param {Array} params
* @param {Function} fn Function to be used for resolving each item inside chain
* @param {Function} next
*
* @return {Promise}
*
* @private
*/
_resolveListItem (item, params, fn, next) {
if (item instanceof Pipeline) {
Expand All @@ -34,11 +57,11 @@ class Middleware {

/**
* Set active tag to be used for registering
* or fetching middleware
* or fetching middleware.
*
* @param {String} tag
*
* @return {Object} this for chaining
* @chainable
*/
tag (tag) {
this._activeTag.set(tag)
Expand All @@ -47,7 +70,8 @@ class Middleware {

/**
* Register an array of middleware or a pipeline
* of middleware.
* of middleware. Calling this method for multiple
* times will concat the to existing list.
*
* @param {Array|Pipeline}
*
Expand Down Expand Up @@ -89,8 +113,10 @@ class Middleware {
}

/**
* An optional function to be called when
* resolving middleware.
* An optional function to be called when resolving middleware.
* The callback will be invoked for each function inside the
* middleware chain. This is the best place to convert the
* function/object into something else at runtime.
*
* @param {Function} fn
*
Expand All @@ -108,7 +134,7 @@ class Middleware {
*
* @method compose
*
* @param {Array} [list]
* @param {Array} [list = this.get()]
*
* @return {Function}
*/
Expand Down Expand Up @@ -142,7 +168,11 @@ class Middleware {
}

/**
* Returns a new instance of pipeline
* Returns a new instance of pipeline.
*
* @method pipeline
*
* @param {Array} middleware
*
* @return {Pipeline}
*/
Expand Down
41 changes: 39 additions & 2 deletions src/Pipeline.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,47 @@
* file that was distributed with this source code.
*/

const Counter = require('../lib/Counter')
const Counter = require('./Counter')

/**
* Pipeline is a subset of middleware, where all methods
* are executed in parallel. Still all methods have to
* call `next` so that when the pipeline is over the
* next middleware gets executed.
*
* ## Note
* Pipeline should be used for middleware with no side-effects.
* For example: If your middleware returns the response or
* ends the chain, you should never add it to a pipeline.
*
* Classic example of pipeline middleware are.
* 1. Read cookies/session
* 2. Fetching authenticated user.
* 3. Decorating request object etc.
*
* @class Pipeline
* @constructor
*/
class Pipeline {

constructor (middleware) {
this._middleware = middleware
this._counter = new Counter(0)
}

/**
* Noop is used the next method for the pipeline
* middleware. Calling this method will increment
* a counter telling the pipeline that all methods
* have called next.
*
* @method _noop
*
* @param {Object} counter
*
* @return {AsyncFunction}
*
* @private
*/
_noop (counter) {
return async function () {
counter.inc()
Expand Down Expand Up @@ -45,6 +77,11 @@ class Pipeline {

return async function (next) {
await Promise.all(map)
/**
* Only call next when all middleware inside pipeline
* have called next. Otherwise some middleware has
* intentions of returning early.
*/
if (self._counter.get() === self._middleware.length) {
await next()
}
Expand Down
66 changes: 66 additions & 0 deletions test/middleware.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -591,4 +591,70 @@ test.group('Middleware | Async', () => {
done()
}).catch(done)
})

test('it should call middleware back in reverse order before finishing the chain', (assert, done) => {
assert.plan(1)
const middleware = new Middleware()
const chain = []
async function first (next) {
chain.push('first')
await next()
chain.push('first after')
}

async function second (next) {
chain.push('second')
await next()
chain.push('second after')
}

async function third (next) {
chain.push('third')
await next()
chain.push('third after')
}

middleware.register([first, second, third])
const middlewareChain = middleware.get()
const composedMiddleware = middleware.compose(middlewareChain)

composedMiddleware()
.then(() => {
assert.deepEqual(chain, ['first', 'second', 'third', 'third after', 'second after', 'first after'])
done()
}).catch(done)
})

test('calls after await in pipeline middleware should not await', (assert, done) => {
assert.plan(1)
const middleware = new Middleware()
const chain = []
async function first (next) {
chain.push('first')
await next()
chain.push('first after')
}

async function second (next) {
chain.push('second')
await next()
chain.push('second after')
}

async function third (next) {
chain.push('third')
await next()
chain.push('third after')
}

middleware.register(middleware.pipeline([first, second, third]))
const middlewareChain = middleware.get()
const composedMiddleware = middleware.compose(middlewareChain)

composedMiddleware()
.then(() => {
assert.deepEqual(chain, ['first', 'second', 'third', 'first after', 'second after', 'third after'])
done()
}).catch(done)
})
})

0 comments on commit 6bae56e

Please sign in to comment.