├── .npmignore ├── .gitignore ├── .travis.yml ├── test ├── mocha.opts └── index_test.js ├── index.js ├── package.json ├── LICENSE └── Readme.md /.npmignore: -------------------------------------------------------------------------------- 1 | .git* -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.11" -------------------------------------------------------------------------------- /test/mocha.opts: -------------------------------------------------------------------------------- 1 | --reporter spec 2 | --bail 3 | --check-leaks 4 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var co = require('co'); 2 | 3 | module.exports = function wrap(gen) { 4 | var fn = co.wrap(gen); 5 | 6 | if (gen.length === 4) { 7 | return function(err, req, res, next) { 8 | return fn(err, req, res, next).catch(next); 9 | } 10 | } 11 | 12 | return function(req, res, next) { 13 | return fn(req, res, next).catch(next); 14 | }; 15 | }; 16 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "co-express", 3 | "description": "An express wrapper that enables generators to be used as middlewares", 4 | "version": "1.2.1", 5 | "author": { 6 | "name": "Martín Ciparelli", 7 | "email": "mciparelli@gmail.com" 8 | }, 9 | "contributors": [ 10 | { 11 | "name": "Martín Ciparelli", 12 | "email": "mciparelli@gmail.com" 13 | }, 14 | { 15 | "name": "Alexey Simonenko", 16 | "email": "alexey@simonenko.su" 17 | } 18 | ], 19 | "keywords": [ 20 | "web", 21 | "express", 22 | "route", 23 | "middleware", 24 | "generator", 25 | "harmony", 26 | "continuable" 27 | ], 28 | "repository": { 29 | "type": "git", 30 | "url": "git://github.com/mciparelli/co-express" 31 | }, 32 | "main": "index", 33 | "scripts": { 34 | "test": "mocha --harmony" 35 | }, 36 | "engines": { 37 | "node": ">=v0.11.4" 38 | }, 39 | "license": "MIT", 40 | "dependencies": { 41 | "co": "^4.0.1" 42 | }, 43 | "devDependencies": { 44 | "express": "^4.10.6", 45 | "mocha": "^2.0.1", 46 | "should": "^7.0.2", 47 | "supertest": "^1.0.1" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2013 Martín Ciparelli 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | **co-express** is an express wrapper that enables generators to be used as middlewares. 2 | 3 | [![NPM version](https://badge.fury.io/js/co-express.png)](http://badge.fury.io/js/co-express) [![Build Status](https://travis-ci.org/mciparelli/co-express.png?branch=master)](https://travis-ci.org/mciparelli/co-express) [![Dependency Status](https://david-dm.org/mciparelli/co-express.png)](https://david-dm.org/mciparelli/co-express) [![devDependency Status](https://david-dm.org/mciparelli/co-express/dev-status.png)](https://david-dm.org/mciparelli/co-express#info=devDependencies) 4 | 5 | ## Usage 6 | 7 | **co-express** lets you write your express routes by using generator functions as middlewares. 8 | Like this: 9 | 10 | ```js 11 | var fs = require('co-fs'); 12 | var express = require('express'); 13 | var wrap = require('co-express'); 14 | 15 | var app = express(); 16 | 17 | app.get('/', wrap(function* (req, res) { 18 | var packageContents = yield fs.readFile('./package.json', 'utf8'); 19 | res.send(packageContents); 20 | })); 21 | 22 | app.listen(8000); 23 | ``` 24 | 25 | You can also define multiple generator functions just the [express](https://github.com/visionmedia/express) way, as long as you wrap them and call `next()`: 26 | 27 | ```js 28 | app.get('/users', wrap(function* (req, res, next) { 29 | req.users = yield db.getUsers(); 30 | next(); 31 | }), wrap(function* (req, res) { 32 | res.send(req.users); 33 | })); 34 | ``` 35 | 36 | ## Installation 37 | 38 | ```bash 39 | $ npm install co-express 40 | ``` 41 | 42 | ## License 43 | 44 | (The MIT License) 45 | 46 | Copyright (c) 2013 Martín Ciparelli <mciparelli@gmail.com> 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining 49 | a copy of this software and associated documentation files (the 50 | 'Software'), to deal in the Software without restriction, including 51 | without limitation the rights to use, copy, modify, merge, publish, 52 | distribute, sublicense, and/or sell copies of the Software, and to 53 | permit persons to whom the Software is furnished to do so, subject to 54 | the following conditions: 55 | 56 | The above copyright notice and this permission notice shall be 57 | included in all copies or substantial portions of the Software. 58 | 59 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 60 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 61 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 62 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 63 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 64 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 65 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 66 | -------------------------------------------------------------------------------- /test/index_test.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | wrap = require('../index'), 3 | should = require('should'), 4 | request = require('supertest'); 5 | 6 | function asyncText(err, text, cb) { 7 | setImmediate(function() { 8 | cb(err, text); 9 | }); 10 | } 11 | 12 | function thunk(err, text) { 13 | return function(cb) { 14 | asyncText(err, text, cb); 15 | }; 16 | } 17 | 18 | describe('co-express', function() { 19 | it('supports a single generator route', function(done) { 20 | var app = express(); 21 | 22 | app.get('/', wrap(function* (req, res, next) { 23 | res.send('it works!'); 24 | })); 25 | 26 | request(app) 27 | .get('/') 28 | .end(function(err, res) { 29 | should.not.exist(err); 30 | res.text.should.equal('it works!'); 31 | done(); 32 | }); 33 | }); 34 | 35 | it('supports multiple generator routes', function(done) { 36 | var app = express(); 37 | 38 | app.get('/', wrap(function* (req, res, next) { 39 | req.val = yield thunk(null, 'thunk'); 40 | next(); 41 | }), wrap(function* (req, res, next) { 42 | req.val += yield thunk(null, 'thunk'); 43 | next(); 44 | }), wrap(function* (req, res) { 45 | res.send(req.val + 'func'); 46 | })); 47 | 48 | request(app) 49 | .get('/') 50 | .end(function(err, res) { 51 | should.not.exist(err); 52 | res.text.should.equal('thunkthunkfunc'); 53 | done(); 54 | }); 55 | }); 56 | 57 | it('doesn\'t alter application object', function(done) { 58 | var app = express(); 59 | 60 | app.get('/', wrap(function* (req, res, next) { 61 | res.send('it works!'); 62 | })); 63 | 64 | app.set('it', 'works!'); 65 | 66 | request(app) 67 | .get('/') 68 | .end(function(err, res) { 69 | should.not.exist(err); 70 | res.text.should.equal('it works!'); 71 | app.get('it').should.equal('works!'); 72 | done(); 73 | }); 74 | }); 75 | 76 | it('passes uncaught exceptions', function(done) { 77 | var app = express(); 78 | 79 | app.get('/', wrap(function* (req, res, next) { 80 | var val = yield thunk(new Error('thunk error')); 81 | res.send(val); 82 | })); 83 | 84 | app.use(function(err, req, res, next) { 85 | if (err && err.message === 'thunk error') { 86 | res.send('caught'); 87 | } else { 88 | next(err); 89 | } 90 | }); 91 | 92 | request(app) 93 | .get('/') 94 | .end(function(err, res) { 95 | should.not.exist(err); 96 | res.text.should.equal('caught'); 97 | done(); 98 | }); 99 | }); 100 | 101 | it('supports error routes', function(done) { 102 | var app = express(); 103 | 104 | app.get('/', wrap(function* (req, res, next) { 105 | var val = yield thunk(new Error('thunk error')); 106 | res.send(val); 107 | })); 108 | 109 | app.use(wrap(function* (err, req, res, next) { 110 | if (err && err.message === 'thunk error') { 111 | res.send('caught'); 112 | } else { 113 | next(err); 114 | } 115 | })); 116 | 117 | request(app) 118 | .get('/') 119 | .end(function(err, res) { 120 | should.not.exist(err); 121 | res.text.should.equal('caught'); 122 | done(); 123 | }); 124 | }); 125 | 126 | it('supports app.route()', function(done) { 127 | var app = express(); 128 | 129 | var books = app.route('/books'); 130 | 131 | books.get(wrap(function* (req, res, next) { 132 | req.val = yield thunk(null, 'thunk'); 133 | next(); 134 | }), wrap(function* (req, res, next) { 135 | req.val += yield thunk(null, 'thunk'); 136 | next(); 137 | }), wrap(function* (req, res) { 138 | res.send(req.val + 'func'); 139 | })); 140 | 141 | 142 | request(app) 143 | .get('/books') 144 | .end(function(err, res) { 145 | should.not.exist(err); 146 | res.text.should.equal('thunkthunkfunc'); 147 | done(); 148 | }); 149 | }); 150 | }); 151 | --------------------------------------------------------------------------------