Comparar commits
175 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| a512d9b47d | |||
| 62234cc106 | |||
| ab61837885 | |||
| 7a5041bf9c | |||
| 4d219135b2 | |||
| 26eeb64640 | |||
| 5426eb0b62 | |||
| b9e32ec2c4 | |||
| 32b8613708 | |||
| 90eddb3439 | |||
| accd6180c1 | |||
| e770b674ff | |||
| 4f7c4d1051 | |||
| 78845e7d23 | |||
| 93f1cecc92 | |||
| 8ccd89f6ad | |||
| 0a210cce7a | |||
| f110248462 | |||
| 96e4014a70 | |||
| 2173d00829 | |||
| 3827f5ef8b | |||
| 73bed61afa | |||
| ee89ff5026 | |||
| 8df9e745d5 | |||
| c76e954504 | |||
| 136f054614 | |||
| e9a4b4f8ff | |||
| 5415c98ff9 | |||
| 39efa452fc | |||
| bddcdee3fe | |||
| 2e8d44b444 | |||
| 70d68419b0 | |||
| 26fab2a27d | |||
| 0f5560eebd | |||
| 170dcc2907 | |||
| 18d6c78ef4 | |||
| 1248f0338b | |||
| b400814d00 | |||
| a934929bb3 | |||
| 140efb574c | |||
| 3c0c96114f | |||
| 362c96c8c1 | |||
| 5db3d0f9fc | |||
| 80c814c393 | |||
| bf66465937 | |||
| 72eea7e6cd | |||
| 8f4cd13c89 | |||
| 6556cefc71 | |||
| 7bfb58920a | |||
| 2e324ccf5f | |||
| 1a10ee76b3 | |||
| 619e6349f6 | |||
| b1ff68548f | |||
| 376b6c3bad | |||
| f4c8a59b17 | |||
| e6129d8ba5 | |||
| d2baf11b8a | |||
| 82b5b12ca7 | |||
| c145ab9b81 | |||
| c10223b803 | |||
| 1e09b54ad2 | |||
| d073e0aeb5 | |||
| ce7293de13 | |||
| 108e66c24b | |||
| f90401b8c0 | |||
| a32e705d49 | |||
| 640cf4ca21 | |||
| 442e782692 | |||
| 0a874ad8b3 | |||
| 18083f0c13 | |||
| f25aaf11e9 | |||
| 9b09257b28 | |||
| f895516a2c | |||
| 76aa718b75 | |||
| 0acee67339 | |||
| 4475e335ef | |||
| 895673141d | |||
| e4cd99ae1c | |||
| c39a398d83 | |||
| 2787bd5bf0 | |||
| 6aaa7dc26d | |||
| ebf60d2340 | |||
| 02d43846f6 | |||
| 8930cd563c | |||
| 2b90cd7d51 | |||
| d5fde6a4b9 | |||
| 99b2e0fa08 | |||
| ebcb1ca90e | |||
| 1763b073f9 | |||
| 908e467548 | |||
| 3c6ad5350b | |||
| 0ff3aa4b20 | |||
| fd42b5c42c | |||
| 1311f2ac25 | |||
| 82a9817061 | |||
| 685eec0149 | |||
| 910dae16ab | |||
| fd53197b46 | |||
| d84d0b69ef | |||
| b694ba27be | |||
| e3cbac2d77 | |||
| bbaa295ee2 | |||
| 1150a88001 | |||
| 58cfd60000 | |||
| fcf268742d | |||
| 30d71c8f8f | |||
| c3f9398b12 | |||
| f1ac6ab764 | |||
| d6ca5f71bc | |||
| cdca9cf88f | |||
| 5840b42f4a | |||
| a5be68b5b2 | |||
| 9fda13bc25 | |||
| 125dd7a594 | |||
| df2584cc3b | |||
| 4de95c0e7b | |||
| 9ed1f2a446 | |||
| 833a4873a4 | |||
| 6ca1807372 | |||
| 9da3e9ccc7 | |||
| 5f65c36171 | |||
| d64bb2f886 | |||
| fc179c8fc3 | |||
| 8235af47fe | |||
| 908f3da3da | |||
| 64aac199de | |||
| 503c45840f | |||
| 6f102ff40f | |||
| 6e26a8d366 | |||
| d9aea70ccc | |||
| dd33ef2eb6 | |||
| f3a32f2e29 | |||
| 1a3e40d818 | |||
| 18cdb3d845 | |||
| 0fca62c037 | |||
| e9cd82b72d | |||
| 4d9ad21047 | |||
| 1cc2dc7150 | |||
| 3dc88b2c0c | |||
| 53f1ffb4e7 | |||
| 579a1be7c8 | |||
| c0a68fcd0d | |||
| b79853e9bd | |||
| f86838ceab | |||
| 809e0b8c92 | |||
| a58e3deac2 | |||
| 430699c082 | |||
| b04233981f | |||
| de10194f33 | |||
| 7c2e1ad0ed | |||
| 67ddb429e3 | |||
| a169cc7119 | |||
| 9719b58575 | |||
| 13c18fa363 | |||
| c17ad6ef65 | |||
| 2c14d0c966 | |||
| 2cd1783613 | |||
| 06db11cd61 | |||
| 125421ec45 | |||
| cc84f40d61 | |||
| c130918135 | |||
| babeb4633e | |||
| 30167356f2 | |||
| 441b309959 | |||
| b7b032f8e0 | |||
| 50f43462ae | |||
| bd2a972dba | |||
| 3c162ae030 | |||
| 3c12757d9e | |||
| fa746cc027 | |||
| 1e85178c73 | |||
| d0bc0ad2ca | |||
| c33f1bac08 | |||
| 58d522d824 | |||
| 858e4dccdc |
+87
-1
@@ -1,8 +1,94 @@
|
||||
|
||||
3.0.0beta6 / 2012-07-13
|
||||
==================
|
||||
|
||||
* add `err.view` property for view errors. Closes #1226
|
||||
* add "jsonp callback name" setting
|
||||
* add support for "/foo/:bar*" non-greedy matches
|
||||
* change `res.sendfile()` to use `send()` module
|
||||
* change `res.send` to use "response-send" module
|
||||
* remove `app.locals.use` and `res.locals.use`, use regular middleware
|
||||
|
||||
3.0.0beta5 / 2012-07-03
|
||||
==================
|
||||
|
||||
* add "make check" support
|
||||
* add route-map example
|
||||
* add `res.json(obj, status)` support back for BC
|
||||
* add "methods" dep, remove internal methods module
|
||||
* update connect dep
|
||||
* update auth example to utilize cores pbkdf2
|
||||
* updated tests to use "supertest"
|
||||
|
||||
3.0.0beta4 / 2012-06-25
|
||||
==================
|
||||
|
||||
* Added `req.auth`
|
||||
* Added `req.range(size)`
|
||||
* Added `res.links(obj)`
|
||||
* Added `res.send(body, status)` support back for backwards compat
|
||||
* Added `.default()` support to `res.format()`
|
||||
* Added 2xx / 304 check to `req.fresh`
|
||||
* Revert "Added + support to the router"
|
||||
* Fixed `res.send()` freshness check, respect res.statusCode
|
||||
|
||||
3.0.0beta3 / 2012-06-15
|
||||
==================
|
||||
|
||||
* Added hogan `--hjs` to express(1) [nullfirm]
|
||||
* Added another example to content-negotiation
|
||||
* Added `fresh` dep
|
||||
* Changed: `res.send()` always checks freshness
|
||||
* Fixed: expose connects mime module. Cloases #1165
|
||||
|
||||
3.0.0beta2 / 2012-06-06
|
||||
==================
|
||||
|
||||
* Added `+` support to the router
|
||||
* Added `req.host`
|
||||
* Changed `req.param()` to check route first
|
||||
* Update connect dep
|
||||
|
||||
3.0.0beta1 / 2012-06-01
|
||||
==================
|
||||
|
||||
* Added `res.format()` callback to override default 406 behaviour
|
||||
* Fixed `res.redirect()` 406. Closes #1154
|
||||
|
||||
3.0.0alpha5 / 2012-05-30
|
||||
==================
|
||||
|
||||
* Added `req.ip`
|
||||
* Added `{ signed: true }` option to `res.cookie()`
|
||||
* Removed `res.signedCookie()`
|
||||
* Changed: dont reverse `req.ips`
|
||||
* Fixed "trust proxy" setting check for `req.ips`
|
||||
|
||||
3.0.0alpha4 / 2012-05-09
|
||||
==================
|
||||
|
||||
* Added: allow `[]` in jsonp callback. Closes #1128
|
||||
* Added `PORT` env var support in generated template. Closes #1118 [benatkin]
|
||||
* Updated: connect 2.2.2
|
||||
|
||||
3.0.0alpha3 / 2012-05-04
|
||||
==================
|
||||
|
||||
* Added public `app.routes`. Closes #887
|
||||
* Added _view-locals_ example
|
||||
* Added _mvc_ example
|
||||
* Added `res.locals.use()`. Closes #1120
|
||||
* Added conditional-GET support to `res.send()`
|
||||
* Added: coerce `res.set()` values to strings
|
||||
* Changed: moved `static()` in generated apps below router
|
||||
* Changed: `res.send()` only set ETag when not previously set
|
||||
* Changed connect 2.2.1 dep
|
||||
* Changed: `make test` now runs unit / acceptance tests
|
||||
* Fixed req/res proto inheritance
|
||||
|
||||
3.0.0alpha2 / 2012-04-26
|
||||
==================
|
||||
|
||||
|
||||
* Added `make benchmark` back
|
||||
* Added `res.send()` support for `String` objects
|
||||
* Added client-side data exposing example
|
||||
|
||||
+11
-4
@@ -1,4 +1,5 @@
|
||||
|
||||
MOCHA_OPTS=
|
||||
REPORTER = dot
|
||||
|
||||
docs: docs/express.md
|
||||
@@ -10,13 +11,19 @@ docs/%.md: lib/%.js
|
||||
@mkdir -p docs
|
||||
dox --raw < $< | ./support/docs > $@
|
||||
|
||||
test:
|
||||
check: test
|
||||
|
||||
test: test-unit test-acceptance
|
||||
|
||||
test-unit:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER)
|
||||
--reporter $(REPORTER) \
|
||||
$(MOCHA_OPTS)
|
||||
|
||||
test-acceptance:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter spec \
|
||||
--reporter $(REPORTER) \
|
||||
--bail \
|
||||
test/acceptance/*.js
|
||||
|
||||
test-cov: lib-cov
|
||||
@@ -31,4 +38,4 @@ docclean:
|
||||
benchmark:
|
||||
@./support/bench
|
||||
|
||||
.PHONY: docs docclean test test-acceptance benchmark
|
||||
.PHONY: docs docclean test test-unit test-acceptance benchmark
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
|
||||

|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org). [](http://travis-ci.org/visionmedia/express)
|
||||
|
||||
```js
|
||||
var express = require('express');
|
||||
@@ -77,7 +77,7 @@ app.listen(3000);
|
||||
First install the dev dependencies to install all the example / test suite deps:
|
||||
|
||||
$ cd express
|
||||
$ npm install -d
|
||||
$ npm install
|
||||
|
||||
then run whichever tests you want:
|
||||
|
||||
|
||||
+31
-8
@@ -19,6 +19,7 @@ program
|
||||
.option('-s, --sessions', 'add session support')
|
||||
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
|
||||
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
|
||||
.option('-h, --hogan', 'add hogan.js engine support')
|
||||
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
|
||||
.option('-f, --force', 'force on non-empty directory')
|
||||
.parse(process.argv);
|
||||
@@ -36,6 +37,7 @@ var eol = 'win32' == os.platform() ? '\r\n' : '\n'
|
||||
program.template = 'jade';
|
||||
if (program.ejs) program.template = 'ejs';
|
||||
if (program.jshtml) program.template = 'jshtml';
|
||||
if (program.hogan) program.template = 'hjs';
|
||||
|
||||
/**
|
||||
* Routes index template.
|
||||
@@ -57,7 +59,7 @@ var index = [
|
||||
*/
|
||||
|
||||
var jadeLayout = [
|
||||
'!!!'
|
||||
'doctype 5'
|
||||
, 'html'
|
||||
, ' head'
|
||||
, ' title= title'
|
||||
@@ -122,6 +124,23 @@ var jshtmlIndex = [
|
||||
, '<p>Welcome to @write(title)</p>'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Hogan.js index template.
|
||||
*/
|
||||
var hoganIndex = [
|
||||
'<!DOCTYPE html>'
|
||||
, '<html>'
|
||||
, ' <head>'
|
||||
, ' <title>{{ title }}</title>'
|
||||
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
|
||||
, ' </head>'
|
||||
, ' <body>'
|
||||
, ' <h1>{{ title }}</h1>'
|
||||
, ' <p>Welcome to {{ title }}</p>'
|
||||
, ' </body>'
|
||||
, '</html>'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Default css template.
|
||||
*/
|
||||
@@ -181,14 +200,15 @@ var app = [
|
||||
, 'var app = express();'
|
||||
, ''
|
||||
, 'app.configure(function(){'
|
||||
, ' app.set(\'port\', process.env.PORT || 3000);'
|
||||
, ' app.set(\'views\', __dirname + \'/views\');'
|
||||
, ' app.set(\'view engine\', \':TEMPLATE\');'
|
||||
, ' app.use(express.favicon());'
|
||||
, ' app.use(express.logger(\'dev\'));{css}'
|
||||
, ' app.use(express.static(__dirname + \'/public\'));'
|
||||
, ' app.use(express.logger(\'dev\'));'
|
||||
, ' app.use(express.bodyParser());'
|
||||
, ' app.use(express.methodOverride());{sess}'
|
||||
, ' app.use(app.router);'
|
||||
, ' app.use(app.router);{css}'
|
||||
, ' app.use(express.static(__dirname + \'/public\'));'
|
||||
, '});'
|
||||
, ''
|
||||
, 'app.configure(\'development\', function(){'
|
||||
@@ -197,9 +217,9 @@ var app = [
|
||||
, ''
|
||||
, 'app.get(\'/\', routes.index);'
|
||||
, ''
|
||||
, 'http.createServer(app).listen(3000);'
|
||||
, ''
|
||||
, 'console.log("Express server listening on port 3000");'
|
||||
, 'http.createServer(app).listen(app.get(\'port\'), function(){'
|
||||
, ' console.log("Express server listening on port " + app.get(\'port\'));'
|
||||
, '});'
|
||||
, ''
|
||||
].join(eol);
|
||||
|
||||
@@ -274,6 +294,9 @@ function createApplicationAt(path) {
|
||||
write(path + '/views/layout.jshtml', jshtmlLayout);
|
||||
write(path + '/views/index.jshtml', jshtmlIndex);
|
||||
break;
|
||||
case 'hjs':
|
||||
write(path + '/views/index.hjs', hoganIndex);
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
@@ -284,7 +307,7 @@ function createApplicationAt(path) {
|
||||
app = app.replace('{css}', eol + ' app.use(require(\'less-middleware\')({ src: __dirname + \'/public\' }));');
|
||||
break;
|
||||
case 'stylus':
|
||||
app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
|
||||
app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware(__dirname + \'/public\'));');
|
||||
break;
|
||||
default:
|
||||
app = app.replace('{css}', '');
|
||||
|
||||
+17
-22
@@ -220,7 +220,7 @@
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
// => "html"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('html');
|
||||
@@ -256,8 +256,8 @@
|
||||
|
||||
Return the value of param `name` when present or `defaultValue`.
|
||||
|
||||
- Checks body params, ex: id=12, {"id":12}
|
||||
- Checks route placeholders, ex: _/user/:id_
|
||||
- Checks body params, ex: id=12, {"id":12}
|
||||
- Checks query string params, ex: ?id=12
|
||||
|
||||
To utilize request bodies, `req.body`
|
||||
@@ -285,17 +285,6 @@
|
||||
|
||||
req.is('html');
|
||||
// => false
|
||||
|
||||
Now within our route callbacks, we can use to to assert content types
|
||||
such as "image/jpeg", "image/png", etc.
|
||||
|
||||
app.post('/image/upload', function(req, res, next){
|
||||
if (req.is('image/*')) {
|
||||
// do something
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
# res
|
||||
@@ -372,6 +361,8 @@
|
||||
and optional callback `fn(err)`. The callback is invoked
|
||||
when the data transfer is complete, or when an error has
|
||||
ocurred. Be sure to check `res.headerSent` if you plan to respond.
|
||||
|
||||
This method uses `res.sendfile()`.
|
||||
|
||||
# res.format()
|
||||
|
||||
@@ -404,7 +395,7 @@
|
||||
});
|
||||
|
||||
In addition to canonicalized MIME types you may
|
||||
## also use extnames mapped to these types
|
||||
also use extnames mapped to these types:
|
||||
|
||||
res.format({
|
||||
text: function(){
|
||||
@@ -417,22 +408,30 @@
|
||||
|
||||
json: function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
By default Express passes an `Error`
|
||||
with a `.status` of 406 to `next(err)`
|
||||
if a match is not made, however you may
|
||||
provide an optional callback `fn` to
|
||||
be invoked instead.
|
||||
|
||||
# res.attachment()
|
||||
|
||||
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
|
||||
|
||||
# res.set()
|
||||
# res.set
|
||||
|
||||
Set header `field` to `val`, or pass
|
||||
an object of of header fields.
|
||||
an object of header fields.
|
||||
|
||||
## Examples
|
||||
|
||||
res.set('Accept', 'application/json');
|
||||
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
||||
|
||||
Aliased as `res.header()`.
|
||||
|
||||
# res.get()
|
||||
|
||||
@@ -442,11 +441,6 @@
|
||||
|
||||
Clear cookie `name`.
|
||||
|
||||
# res.signedCookie()
|
||||
|
||||
Set a signed cookie with the given `name` and `val`.
|
||||
See `res.cookie()` for details.
|
||||
|
||||
# res.cookie()
|
||||
|
||||
Set cookie `name` to `val`, with the given `options`.
|
||||
@@ -454,6 +448,7 @@
|
||||
## Options
|
||||
|
||||
- `maxAge` max-age in milliseconds, converted to `expires`
|
||||
- `signed` sign the cookie
|
||||
- `path` defaults to "/"
|
||||
|
||||
## Examples
|
||||
|
||||
+2
-13
@@ -39,7 +39,7 @@
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
// => "html"
|
||||
// => "html"
|
||||
|
||||
// Accept: text/*, application/json
|
||||
req.accepts('html');
|
||||
@@ -75,8 +75,8 @@
|
||||
|
||||
Return the value of param `name` when present or `defaultValue`.
|
||||
|
||||
- Checks body params, ex: id=12, {"id":12}
|
||||
- Checks route placeholders, ex: _/user/:id_
|
||||
- Checks body params, ex: id=12, {"id":12}
|
||||
- Checks query string params, ex: ?id=12
|
||||
|
||||
To utilize request bodies, `req.body`
|
||||
@@ -104,15 +104,4 @@
|
||||
|
||||
req.is('html');
|
||||
// => false
|
||||
|
||||
Now within our route callbacks, we can use to to assert content types
|
||||
such as "image/jpeg", "image/png", etc.
|
||||
|
||||
app.post('/image/upload', function(req, res, next){
|
||||
if (req.is('image/*')) {
|
||||
// do something
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
+15
-9
@@ -73,6 +73,8 @@
|
||||
and optional callback `fn(err)`. The callback is invoked
|
||||
when the data transfer is complete, or when an error has
|
||||
ocurred. Be sure to check `res.headerSent` if you plan to respond.
|
||||
|
||||
This method uses `res.sendfile()`.
|
||||
|
||||
# res.format()
|
||||
|
||||
@@ -105,7 +107,7 @@
|
||||
});
|
||||
|
||||
In addition to canonicalized MIME types you may
|
||||
## also use extnames mapped to these types
|
||||
also use extnames mapped to these types:
|
||||
|
||||
res.format({
|
||||
text: function(){
|
||||
@@ -118,22 +120,30 @@
|
||||
|
||||
json: function(){
|
||||
res.send({ message: 'hey' });
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
By default Express passes an `Error`
|
||||
with a `.status` of 406 to `next(err)`
|
||||
if a match is not made, however you may
|
||||
provide an optional callback `fn` to
|
||||
be invoked instead.
|
||||
|
||||
# res.attachment()
|
||||
|
||||
Set _Content-Disposition_ header to _attachment_ with optional `filename`.
|
||||
|
||||
# res.set()
|
||||
# res.set
|
||||
|
||||
Set header `field` to `val`, or pass
|
||||
an object of of header fields.
|
||||
an object of header fields.
|
||||
|
||||
## Examples
|
||||
|
||||
res.set('Accept', 'application/json');
|
||||
res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
||||
|
||||
Aliased as `res.header()`.
|
||||
|
||||
# res.get()
|
||||
|
||||
@@ -143,11 +153,6 @@
|
||||
|
||||
Clear cookie `name`.
|
||||
|
||||
# res.signedCookie()
|
||||
|
||||
Set a signed cookie with the given `name` and `val`.
|
||||
See `res.cookie()` for details.
|
||||
|
||||
# res.cookie()
|
||||
|
||||
Set cookie `name` to `val`, with the given `options`.
|
||||
@@ -155,6 +160,7 @@
|
||||
## Options
|
||||
|
||||
- `maxAge` max-age in milliseconds, converted to `expires`
|
||||
- `signed` sign the cookie
|
||||
- `path` defaults to "/"
|
||||
|
||||
## Examples
|
||||
|
||||
+30
-25
@@ -4,20 +4,24 @@
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, crypto = require('crypto');
|
||||
, hash = require('./pass').hash;
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// config
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// middleware
|
||||
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.cookieParser('shhhh, very secret'));
|
||||
app.use(express.session());
|
||||
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Session-persisted message middleware
|
||||
|
||||
app.locals.use(function(req,res){
|
||||
app.use(function(req, res, next){
|
||||
var err = req.session.error
|
||||
, msg = req.session.success;
|
||||
delete req.session.error;
|
||||
@@ -25,39 +29,40 @@ app.locals.use(function(req,res){
|
||||
res.locals.message = '';
|
||||
if (err) res.locals.message = '<p class="msg error">' + err + '</p>';
|
||||
if (msg) res.locals.message = '<p class="msg success">' + msg + '</p>';
|
||||
})
|
||||
next();
|
||||
});
|
||||
|
||||
// dummy database
|
||||
|
||||
// Generate a salt for the user to prevent rainbow table attacks
|
||||
// for better security take a look at the bcrypt c++ addon:
|
||||
// https://github.com/ncb000gt/node.bcrypt.js
|
||||
var users = {
|
||||
tj: {
|
||||
name: 'tj'
|
||||
, salt: 'randomly-generated-salt'
|
||||
, pass: hash('foobar', 'randomly-generated-salt')
|
||||
}
|
||||
tj: { name: 'tj' }
|
||||
};
|
||||
|
||||
// Used to generate a hash of the plain-text password + salt
|
||||
function hash(msg, key) {
|
||||
return crypto
|
||||
.createHmac('sha256', key)
|
||||
.update(msg)
|
||||
.digest('hex');
|
||||
}
|
||||
// when you create a user, generate a salt
|
||||
// and hash the password ('foobar' is the pass here)
|
||||
|
||||
hash('foobar', function(err, salt, hash){
|
||||
if (err) throw err;
|
||||
// store the salt & hash in the "db"
|
||||
users.tj.salt = salt;
|
||||
users.tj.hash = hash;
|
||||
});
|
||||
|
||||
|
||||
// Authenticate using our plain-object database of doom!
|
||||
function authenticate(name, pass, fn) {
|
||||
console.log('authenticating %s:%s', name, pass);
|
||||
if (!module.parent) console.log('authenticating %s:%s', name, pass);
|
||||
var user = users[name];
|
||||
// query the db for the given username
|
||||
if (!user) return fn(new Error('cannot find user'));
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the hash against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
if (user.pass == hash(pass, user.salt)) return fn(null, user);
|
||||
// Otherwise password is invalid
|
||||
fn(new Error('invalid password'));
|
||||
hash(pass, user.salt, function(err, hash){
|
||||
if (err) return fn(err);
|
||||
if (hash == user.hash) return fn(null, user);
|
||||
fn(new Error('invalid password'));
|
||||
})
|
||||
}
|
||||
|
||||
function restrict(req, res, next) {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
// check out https://github.com/visionmedia/node-pwd
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var crypto = require('crypto');
|
||||
|
||||
/**
|
||||
* Bytesize.
|
||||
*/
|
||||
|
||||
var len = 128;
|
||||
|
||||
/**
|
||||
* Iterations. ~300ms
|
||||
*/
|
||||
|
||||
var iterations = 12000;
|
||||
|
||||
/**
|
||||
* Hashes a password with optional `salt`, otherwise
|
||||
* generate a salt for `pass` and invoke `fn(err, salt, hash)`.
|
||||
*
|
||||
* @param {String} password to hash
|
||||
* @param {String} optional salt
|
||||
* @param {Function} callback
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.hash = function (pwd, salt, fn) {
|
||||
if (3 == arguments.length) {
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, fn);
|
||||
} else {
|
||||
fn = salt;
|
||||
crypto.randomBytes(len, function(err, salt){
|
||||
if (err) return fn(err);
|
||||
salt = salt.toString('base64');
|
||||
crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
|
||||
if (err) return fn(err);
|
||||
fn(null, salt, hash);
|
||||
});
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
var express = require('../..')
|
||||
, app = express();
|
||||
|
||||
app.set('views', __dirname);
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
var pets = [];
|
||||
|
||||
var n = 1000;
|
||||
while (n--) {
|
||||
pets.push({ name: 'Tobi', age: 2, species: 'ferret' });
|
||||
pets.push({ name: 'Loki', age: 1, species: 'ferret' });
|
||||
pets.push({ name: 'Jane', age: 6, species: 'ferret' });
|
||||
}
|
||||
|
||||
app.use(express.logger('dev'));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('pets', { pets: pets });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express listening on port 3000');
|
||||
@@ -0,0 +1,12 @@
|
||||
style
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px "Helvetica Neue", Helvetica;
|
||||
}
|
||||
|
||||
table
|
||||
for pet in pets
|
||||
tr
|
||||
td= pet.name
|
||||
td= pet.age
|
||||
td= pet.species
|
||||
@@ -1,50 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
// config
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// middleware
|
||||
|
||||
app.configure('development',function(){
|
||||
app.use(express.logger('dev'));
|
||||
})
|
||||
|
||||
app.configure(function(){
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieParser('keyboard cat'));
|
||||
app.use(express.session());
|
||||
app.use(app.router);
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
// Locals
|
||||
|
||||
app.locals.use(function(req, res){
|
||||
// expose "error" and "message" to all
|
||||
// views that are rendered.
|
||||
res.locals.error = req.session.error || '';
|
||||
res.locals.message = req.session.message || '';
|
||||
// remove them so they're not displayed on subsequent renders
|
||||
delete req.session.error;
|
||||
delete req.session.message;
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
require('./routes/site')(app);
|
||||
require('./routes/post')(app);
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
|
||||
// Fake data store
|
||||
|
||||
var ids = 0
|
||||
, db = {};
|
||||
|
||||
var Post = exports = module.exports = function Post(title, body) {
|
||||
this.id = ++ids;
|
||||
this.title = title;
|
||||
this.body = body;
|
||||
this.createdAt = new Date;
|
||||
};
|
||||
|
||||
Post.prototype.save = function(fn){
|
||||
db[this.id] = this;
|
||||
fn();
|
||||
};
|
||||
|
||||
Post.prototype.validate = function(fn){
|
||||
if (!this.title) return fn(new Error('title required'));
|
||||
if (!this.body) return fn(new Error('body required'));
|
||||
if (this.body.length < 10) {
|
||||
return fn(new Error(
|
||||
'body should be at least 10 characters long, was only ' + this.body.length));
|
||||
}
|
||||
fn();
|
||||
};
|
||||
|
||||
|
||||
Post.prototype.update = function(data, fn){
|
||||
this.updatedAt = new Date;
|
||||
for (var key in data) {
|
||||
if (undefined != data[key]) {
|
||||
this[key] = data[key];
|
||||
}
|
||||
}
|
||||
this.save(fn);
|
||||
};
|
||||
|
||||
Post.prototype.destroy = function(fn){
|
||||
exports.destroy(this.id, fn);
|
||||
};
|
||||
|
||||
exports.count = function(fn){
|
||||
fn(null, Object.keys(db).length);
|
||||
};
|
||||
|
||||
exports.all = function(fn){
|
||||
var arr = Object.keys(db).reduce(function(arr, id){
|
||||
arr.push(db[id]);
|
||||
return arr;
|
||||
}, []);
|
||||
fn(null, arr);
|
||||
};
|
||||
|
||||
exports.get = function(id, fn){
|
||||
fn(null, db[id]);
|
||||
};
|
||||
|
||||
exports.destroy = function(id, fn) {
|
||||
if (db[id]) {
|
||||
delete db[id];
|
||||
fn();
|
||||
} else {
|
||||
fn(new Error('post ' + id + ' does not exist'));
|
||||
}
|
||||
};
|
||||
@@ -1,56 +0,0 @@
|
||||
body {
|
||||
font: 13px "Helvetica Neue", Arial, sans-serif;
|
||||
color: #111;
|
||||
padding: 60px 80px;
|
||||
}
|
||||
h1, h2 {
|
||||
color: #c00;
|
||||
}
|
||||
a {
|
||||
color: #c00;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
label {
|
||||
padding: 6px 0;
|
||||
display: block;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
textarea,
|
||||
input {
|
||||
outline: none;
|
||||
padding: 5px;
|
||||
background: #f1f1f1;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
textarea:focus,
|
||||
input:focus {
|
||||
background: #fff;
|
||||
}
|
||||
textarea {
|
||||
width: 500px;
|
||||
height: 200px;
|
||||
}
|
||||
a.edit {
|
||||
margin-left: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.date {
|
||||
font-size: 11px;
|
||||
}
|
||||
p.error,
|
||||
p.message {
|
||||
padding: 10px;
|
||||
border: 1px solid;
|
||||
}
|
||||
p.error {
|
||||
background: #FDEAE7;
|
||||
color: #E4250C;
|
||||
}
|
||||
p.message {
|
||||
color: #2EBBE6;
|
||||
background: #F7FBFD;
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var basicAuth = require('../../../lib/express').basicAuth
|
||||
, Post = require('../models/post');
|
||||
|
||||
module.exports = function(app){
|
||||
/**
|
||||
* Apply basic auth to all post related routes
|
||||
*/
|
||||
|
||||
app.all('/post(/*)?', basicAuth(function(user, pass){
|
||||
return 'admin' == user && 'express' == pass;
|
||||
}));
|
||||
|
||||
/**
|
||||
* Map :post to the database, loading
|
||||
* every time :post is present.
|
||||
*/
|
||||
|
||||
app.param('post', function(req, res, next, id){
|
||||
Post.get(id, function(err, post){
|
||||
if (err) return next(err);
|
||||
if (!post) return next(new Error('failed to load post ' + id));
|
||||
req.post = post;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a post.
|
||||
*/
|
||||
|
||||
app.get('/post/add', function(req, res){
|
||||
res.render('post/form', { post: {}});
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a post.
|
||||
*/
|
||||
|
||||
app.post('/post', function(req, res){
|
||||
var data = req.body.post || {}
|
||||
, post = new Post(data.title, data.body);
|
||||
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.session.error = err.message;
|
||||
return res.redirect('back');
|
||||
}
|
||||
|
||||
post.save(function(err){
|
||||
req.session.message = 'Successfully created the post.';
|
||||
res.redirect('/post/' + post.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Display the post.
|
||||
*/
|
||||
|
||||
app.get('/post/:post', function(req, res){
|
||||
res.render('post', { post: req.post });
|
||||
});
|
||||
|
||||
/**
|
||||
* Display the post edit form.
|
||||
*/
|
||||
|
||||
app.get('/post/:post/edit', function(req, res){
|
||||
res.render('post/form', { post: req.post });
|
||||
});
|
||||
|
||||
/**
|
||||
* Update post. Typically a data layer would handle this stuff.
|
||||
*/
|
||||
|
||||
app.put('/post/:post', function(req, res, next){
|
||||
var post = req.post;
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.session.error = err.message;
|
||||
return res.redirect('back');
|
||||
}
|
||||
|
||||
post.update(req.body.post, function(err){
|
||||
if (err) return next(err);
|
||||
req.session.message = 'Successfully updated post';
|
||||
res.redirect('back');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,19 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Post = require('../models/post');
|
||||
|
||||
module.exports = function(app){
|
||||
app.get('/', function(req, res){
|
||||
Post.count(function(err, count){
|
||||
Post.all(function(err, posts){
|
||||
res.render('index', {
|
||||
count: count
|
||||
, posts: posts
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -1,22 +0,0 @@
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h1 Blog
|
||||
|
||||
if count
|
||||
p Display all #{count} post(s)
|
||||
#posts
|
||||
each post in posts
|
||||
include post/index
|
||||
else
|
||||
p
|
||||
| It looks like you have no posts!
|
||||
p
|
||||
| Click
|
||||
a(href='/post/add') here
|
||||
| to create a post. Login
|
||||
| as
|
||||
em "admin"
|
||||
| and
|
||||
em "express"
|
||||
| .
|
||||
@@ -1,8 +0,0 @@
|
||||
!!! 5
|
||||
html
|
||||
head
|
||||
title Blog
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
body
|
||||
#container
|
||||
block content
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
if error
|
||||
p.error= error
|
||||
|
||||
if message
|
||||
p.message= message
|
||||
@@ -1,22 +0,0 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
if post.title
|
||||
h1 Editing #{post.title}
|
||||
else
|
||||
h1 New Post
|
||||
|
||||
include ../messages
|
||||
|
||||
form#post(action='/post' + (post.title ? '/' + post.id : ''), method='post')
|
||||
if post.title
|
||||
input(type='hidden', name='_method', value='put')
|
||||
p
|
||||
label(for='post[title]') Title:
|
||||
input(type='text', name='post[title]', value=post.title)
|
||||
p
|
||||
label(for='post[body]') Body:
|
||||
textarea(name='post[body]')= post.body || ''
|
||||
p
|
||||
input(type='submit', value=post.title ? 'Update' : 'Create')
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
.post
|
||||
// title
|
||||
h2
|
||||
= post.title
|
||||
a.edit(href='/post/' + post.id + '/edit') Edit
|
||||
|
||||
include ../messages
|
||||
|
||||
// dates
|
||||
p.date.created Created at #{post.createdAt}
|
||||
if post.updatedAt
|
||||
p.date.updated Updated at #{post.updatedAt}
|
||||
|
||||
// body
|
||||
pre.body= post.body
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
users.push({ name: 'Loki' });
|
||||
users.push({ name: 'Jane' });
|
||||
|
||||
module.exports = users;
|
||||
@@ -1,12 +1,7 @@
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
users.push({ name: 'Loki' });
|
||||
users.push({ name: 'Jane' });
|
||||
, app = module.exports = express()
|
||||
, users = require('./db');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.format({
|
||||
@@ -28,6 +23,18 @@ app.get('/', function(req, res){
|
||||
})
|
||||
});
|
||||
|
||||
// or you could write a tiny middleware like
|
||||
// this to abstract make things a bit more declarative:
|
||||
|
||||
function format(mod) {
|
||||
var obj = require(mod);
|
||||
return function(req, res){
|
||||
res.format(obj);
|
||||
}
|
||||
}
|
||||
|
||||
app.get('/users', format('./users'));
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
var users = require('./db');
|
||||
|
||||
exports.html = function(req, res){
|
||||
res.send('<ul>' + users.map(function(user){
|
||||
return '<li>' + user.name + '</li>';
|
||||
}).join('') + '</ul>');
|
||||
};
|
||||
|
||||
exports.text = function(req, res){
|
||||
res.send(users.map(function(user){
|
||||
return ' - ' + user.name + '\n';
|
||||
}).join(''));
|
||||
};
|
||||
|
||||
exports.json = function(req, res){
|
||||
res.json(users);
|
||||
};
|
||||
@@ -4,10 +4,10 @@
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
, app = module.exports = express()
|
||||
, test = app.get('env') == 'test';
|
||||
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
app.use(express.logger('dev'));
|
||||
if (!test) app.use(express.logger('dev'));
|
||||
app.use(app.router);
|
||||
|
||||
// the error handler is strategically
|
||||
@@ -24,8 +24,7 @@ app.use(error);
|
||||
|
||||
function error(err, req, res, next) {
|
||||
// log it
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
console.error(err.stack);
|
||||
if (!test) console.error(err.stack);
|
||||
|
||||
// respond with 500 "Internal Server Error".
|
||||
res.send(500);
|
||||
@@ -45,14 +45,6 @@ app.use(function(req, res, next){
|
||||
next();
|
||||
});
|
||||
|
||||
// if you wanted to _always_ expose
|
||||
// the user you might do something like this:
|
||||
/*
|
||||
app.locals.use(function(req, res){
|
||||
if (req.user) res.locals.expose.user = req.user;
|
||||
})
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/user');
|
||||
});
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// Here we use the bodyDecoder middleware
|
||||
// to parse urlencoded request bodies
|
||||
// which populates req.body
|
||||
app.use(express.bodyParser());
|
||||
|
||||
// The methodOverride middleware allows us
|
||||
// to set a hidden input of _method to an arbitrary
|
||||
// HTTP method to support app.put(), app.del() etc
|
||||
app.use(express.methodOverride());
|
||||
|
||||
// Required by session
|
||||
app.use(express.cookieParser());
|
||||
|
||||
// Required by req.flash() for persistent
|
||||
// notifications
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// get ?name=foo
|
||||
var name = req.param('name') || '';
|
||||
|
||||
// Switch the button label based if we have a name
|
||||
var label = name ? 'Update' : 'Save';
|
||||
|
||||
// Buffer all flash messages.
|
||||
// Typically this would all be done in a template
|
||||
// however for illustration purposes we iterate
|
||||
// here.
|
||||
|
||||
// The messages in req.flash() persist until called,
|
||||
// at which time they are flushed from the session
|
||||
var msgs = '<ul>',
|
||||
flash = req.flash();
|
||||
Object.keys(flash).forEach(function(type){
|
||||
flash[type].forEach(function(msg){
|
||||
msgs += '<li class="' + type + '">' + msg + '</li>';
|
||||
});
|
||||
});
|
||||
msgs += '</ul>';
|
||||
|
||||
// If we have a name, we are updating,
|
||||
// so add the hidden _method input
|
||||
res.send(msgs
|
||||
+ '<form method="post">'
|
||||
+ (name ? '<input type="hidden" value="put" name="_method" />' : '')
|
||||
+ 'Name: <input type="text" name="name" value="' + name + '" />'
|
||||
+ '<input type="submit" value="' + label + '" />'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
if (req.body.name) {
|
||||
// Typically here we would create a resource
|
||||
req.flash('info', 'Saved ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
} else {
|
||||
req.flash('error', 'Error: name required');
|
||||
res.redirect('/');
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/', function(req, res){
|
||||
// Typically here we would update a resource
|
||||
req.flash('info', 'Updated ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,113 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, http = require('http');
|
||||
|
||||
var app = express();
|
||||
|
||||
// Expose our views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
/**
|
||||
* Request github json api `path`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function request(path, fn){
|
||||
var client = http.createClient(80, 'github.com')
|
||||
, req = client.request('GET', '/api/v2/json' + path, { Host: 'github.com' });
|
||||
req.on('response', function(res){
|
||||
res.body = '';
|
||||
res.on('data', function(chunk){ res.body += chunk; });
|
||||
res.on('end', function(){
|
||||
try {
|
||||
fn(null, JSON.parse(res.body));
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
});
|
||||
req.end();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sort repositories by watchers desc.
|
||||
*
|
||||
* @param {Array} repos
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function sort(repos){
|
||||
return repos.sort(function(a, b){
|
||||
if (a.watchers == b.watchers) return 0;
|
||||
if (a.watchers > b.watchers) return -1;
|
||||
if (a.watchers < b.watchers) return 1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tally up total watchers.
|
||||
*
|
||||
* @param {Array} repos
|
||||
* @return {Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function totalWatchers(repos) {
|
||||
return repos.reduce(function(sum, repo){
|
||||
return sum + repo.watchers;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Default to my user name :)
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/repos/visionmedia');
|
||||
});
|
||||
|
||||
/**
|
||||
* Display repos.
|
||||
*/
|
||||
|
||||
app.get('/repos/*', function(req, res, next){
|
||||
var names = req.params[0].split('/')
|
||||
, users = [];
|
||||
(function fetchData(name){
|
||||
// We have a user name
|
||||
if (name) {
|
||||
console.log('... fetching \x1b[33m%s\x1b[0m', name);
|
||||
request('/repos/show/' + name, function(err, user){
|
||||
if (err) {
|
||||
next(err)
|
||||
} else {
|
||||
user.totalWatchers = totalWatchers(user.repositories);
|
||||
user.repos = sort(user.repositories);
|
||||
user.name = name;
|
||||
users.push(user);
|
||||
fetchData(names.shift());
|
||||
}
|
||||
});
|
||||
// No more users
|
||||
} else {
|
||||
console.log('... done');
|
||||
res.render('index', { users: users });
|
||||
}
|
||||
})(names.shift());
|
||||
});
|
||||
|
||||
// Serve statics from ./public
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// Listen on port 3000
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,19 +0,0 @@
|
||||
body {
|
||||
padding: 30px 50px;
|
||||
font: 12px/1.4 "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #00AAFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.user {
|
||||
margin: 0 10px;
|
||||
float: left;
|
||||
width: 300px;
|
||||
}
|
||||
table td:nth-child(2) {
|
||||
padding: 0 5px;
|
||||
}
|
||||
@@ -1,14 +0,0 @@
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
each user in users
|
||||
.user
|
||||
h2= user.name
|
||||
p.summary
|
||||
| <a href="http://github.com/#{user.name}">#{user.name}</a> has
|
||||
| <strong>#{user.repos.length}</strong> repositories
|
||||
| with a total of <strong>#{user.totalWatchers}</strong> watchers.
|
||||
table#repos
|
||||
for repo in user.repos
|
||||
include repo
|
||||
@@ -1,8 +0,0 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Github Example
|
||||
link(rel="stylesheet", href="/style.css")
|
||||
body
|
||||
#container
|
||||
block content
|
||||
@@ -1,5 +0,0 @@
|
||||
tr.repo
|
||||
td.name
|
||||
a(href: repo.homepage || repo.url)= repo.name
|
||||
td.watchers
|
||||
= repo.watchers
|
||||
@@ -1,8 +1,4 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = express();
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
exports.index = function(req, res){
|
||||
res.redirect('/users');
|
||||
};
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.engine = 'jade';
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var pet = db.pets[req.params.pet_id];
|
||||
if (!pet) return next(new Error('Pet not found'));
|
||||
req.pet = pet;
|
||||
next();
|
||||
};
|
||||
|
||||
exports.show = function(req, res, next){
|
||||
res.render('show', { pet: req.pet });
|
||||
};
|
||||
|
||||
exports.edit = function(req, res, next){
|
||||
res.render('edit', { pet: req.pet });
|
||||
};
|
||||
|
||||
exports.update = function(req, res, next){
|
||||
var body = req.body;
|
||||
req.pet.name = body.user.name;
|
||||
res.message('Information updated!');
|
||||
res.redirect('/pet/' + req.pet.id);
|
||||
};
|
||||
@@ -0,0 +1,7 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
h1= pet.name
|
||||
form(action='/pet/#{pet.id}', method='post')
|
||||
input(type='hidden', name='_method', value='put')
|
||||
label Name:
|
||||
input(type='text', name='user[name]', value=pet.name)
|
||||
input(type='submit', value='Update')
|
||||
@@ -0,0 +1,6 @@
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
|
||||
h1= pet.name
|
||||
a(href='/pet/#{pet.id}/edit') edit
|
||||
|
||||
p You are viewing #{pet.name}
|
||||
@@ -0,0 +1,17 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.name = 'pet';
|
||||
exports.prefix = '/user/:user_id';
|
||||
|
||||
exports.create = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
var user = db.users[id];
|
||||
var body = req.body;
|
||||
if (!user) return next(new Error('User not found'));
|
||||
var pet = { name: body.pet.name };
|
||||
pet.id = db.pets.push(pet) - 1;
|
||||
user.pets.push(pet);
|
||||
res.message('Added pet ' + body.pet.name);
|
||||
res.redirect('/user/' + id);
|
||||
};
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
var db = require('../../db');
|
||||
|
||||
exports.before = function(req, res, next){
|
||||
var id = req.params.user_id;
|
||||
if (!id) return next();
|
||||
// pretend to query a database...
|
||||
process.nextTick(function(){
|
||||
req.user = db.users[id];
|
||||
// cant find that user
|
||||
if (!req.user) return next(new Error('User not found'));
|
||||
// found it, move on to the routes
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
exports.list = function(req, res, next){
|
||||
res.render('list', { users: db.users });
|
||||
};
|
||||
|
||||
exports.edit = function(req, res, next){
|
||||
res.render('edit', { user: req.user });
|
||||
};
|
||||
|
||||
exports.show = function(req, res, next){
|
||||
res.render('show', { user: req.user });
|
||||
};
|
||||
|
||||
exports.update = function(req, res, next){
|
||||
var body = req.body;
|
||||
req.user.name = body.user.name;
|
||||
res.message('Information updated!');
|
||||
res.redirect('/user/' + req.user.id);
|
||||
};
|
||||
@@ -0,0 +1,12 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1><%= user.name %></h1>
|
||||
<form action='/user/<%= user.id %>' method='post'>
|
||||
<input type="hidden" name="_method" value="put" />
|
||||
<label>Name: <input type="text" name="user[name]" value="<%= user.name %>" /></label>
|
||||
<input type="submit" value="Update" />
|
||||
</form>
|
||||
|
||||
<form action='/user/<%= user.id %>/pet' method='post'>
|
||||
<label>Pet: <input type="text" name="pet[name]" placeholder="name" /></label>
|
||||
<input type="submit" value="Add" />
|
||||
</form>
|
||||
@@ -0,0 +1,8 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>Users</h1>
|
||||
<p>Click a user below to view their pets.</p>
|
||||
<ul>
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><a href="/user/<%= user.id %>"><%= user.name %></a></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
@@ -0,0 +1,21 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1><%= user.name %> <a href="/user/<%= user.id %>/edit">edit</a></h1>
|
||||
|
||||
<% if (hasMessages) { %>
|
||||
<ul id="messages">
|
||||
<% messages.forEach(function(msg){ %>
|
||||
<li><%= msg %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } %>
|
||||
|
||||
<% if (user.pets.length) { %>
|
||||
<p>View <%= user.name %>s pets:</p>
|
||||
<ul>
|
||||
<% user.pets.forEach(function(pet){ %>
|
||||
<li><a href="/pet/<%= pet.id %>"><%= pet.name %></a></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
<% } else { %>
|
||||
<p>No pets!</p>
|
||||
<% } %>
|
||||
@@ -0,0 +1,15 @@
|
||||
|
||||
// faux database
|
||||
|
||||
var pets = exports.pets = [];
|
||||
|
||||
pets.push({ name: 'Tobi', id: 0 });
|
||||
pets.push({ name: 'Loki', id: 1 });
|
||||
pets.push({ name: 'Jane', id: 2 });
|
||||
pets.push({ name: 'Raul', id: 3 });
|
||||
|
||||
var users = exports.users = [];
|
||||
|
||||
users.push({ name: 'TJ', pets: [pets[0], pets[1], pets[2]], id: 0 });
|
||||
users.push({ name: 'Guillermo', pets: [pets[3]], id: 1 });
|
||||
users.push({ name: 'Nathan', pets: [], id: 2 });
|
||||
@@ -0,0 +1,93 @@
|
||||
|
||||
var express = require('../..');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// settings
|
||||
|
||||
// map .renderFile to ".html" files
|
||||
app.engine('html', require('ejs').renderFile);
|
||||
|
||||
// make ".html" the default
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// set views for error and 404 pages
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// define a custom res.message() method
|
||||
// which stores messages in the session
|
||||
app.response.message = function(msg){
|
||||
// reference `req.session` via the `this.req` reference
|
||||
var sess = this.req.session;
|
||||
// simply add the msg to an array for later
|
||||
sess.messages = sess.messages || [];
|
||||
sess.messages.push(msg);
|
||||
return this;
|
||||
};
|
||||
|
||||
// log
|
||||
if (!module.parent) app.use(express.logger('dev'));
|
||||
|
||||
// serve static files
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// session support
|
||||
app.use(express.cookieParser('some secret here'));
|
||||
app.use(express.session());
|
||||
|
||||
// parse request bodies (req.body)
|
||||
app.use(express.bodyParser());
|
||||
|
||||
// support _method (PUT in forms etc)
|
||||
app.use(express.methodOverride());
|
||||
|
||||
// expose the "messages" local variable when views are rendered
|
||||
app.use(function(req, res, next){
|
||||
var msgs = req.session.messages || [];
|
||||
|
||||
// expose "messages" local variable
|
||||
res.locals.messages = msgs;
|
||||
|
||||
// expose "hasMessages"
|
||||
res.locals.hasMessages = !! msgs.length;
|
||||
|
||||
/* This is equivalent:
|
||||
res.locals({
|
||||
messages: msgs,
|
||||
hasMessages: !! msgs.length
|
||||
});
|
||||
*/
|
||||
|
||||
// empty or "flush" the messages so they
|
||||
// don't build up
|
||||
req.session.messages = [];
|
||||
next();
|
||||
});
|
||||
|
||||
// load controllers
|
||||
require('./lib/boot')(app, { verbose: !module.parent });
|
||||
|
||||
// assume "not found" in the error msgs
|
||||
// is a 404. this is somewhat silly, but
|
||||
// valid, you can do whatever you like, set
|
||||
// properties, use instanceof etc.
|
||||
app.use(function(err, req, res, next){
|
||||
// treat as 404
|
||||
if (~err.message.indexOf('not found')) return next();
|
||||
|
||||
// log it
|
||||
console.error(err.stack);
|
||||
|
||||
// error page
|
||||
res.status(500).render('5xx');
|
||||
});
|
||||
|
||||
// assume 404 since no middleware responded
|
||||
app.use(function(req, res, next){
|
||||
res.status(404).render('404', { url: req.originalUrl });
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('\n listening on port 3000\n');
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
|
||||
var express = require('../../..')
|
||||
, fs = require('fs');
|
||||
|
||||
module.exports = function(parent, options){
|
||||
var verbose = options.verbose;
|
||||
fs.readdirSync(__dirname + '/../controllers').forEach(function(name){
|
||||
verbose && console.log('\n %s:', name);
|
||||
var obj = require('./../controllers/' + name)
|
||||
, name = obj.name || name
|
||||
, prefix = obj.prefix || ''
|
||||
, app = express()
|
||||
, method
|
||||
, path;
|
||||
|
||||
// allow specifying the view engine
|
||||
if (obj.engine) app.set('view engine', obj.engine);
|
||||
app.set('views', __dirname + '/../controllers/' + name + '/views');
|
||||
|
||||
// before middleware support
|
||||
if (obj.before) {
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
app.all(path, obj.before);
|
||||
verbose && console.log(' ALL %s -> before', path);
|
||||
path = '/' + name + '/:' + name + '_id/*';
|
||||
app.all(path, obj.before);
|
||||
verbose && console.log(' ALL %s -> before', path);
|
||||
}
|
||||
|
||||
// generate routes based
|
||||
// on the exported methods
|
||||
for (var key in obj) {
|
||||
// "reserved" exports
|
||||
if (~['name', 'prefix', 'engine', 'before'].indexOf(key)) continue;
|
||||
// route exports
|
||||
switch (key) {
|
||||
case 'show':
|
||||
method = 'get';
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
app[method](path, obj[key]);
|
||||
break;
|
||||
case 'list':
|
||||
method = 'get';
|
||||
path = '/' + name + 's';
|
||||
break;
|
||||
case 'edit':
|
||||
method = 'get';
|
||||
path = '/' + name + '/:' + name + '_id/edit';
|
||||
break;
|
||||
case 'update':
|
||||
method = 'put';
|
||||
path = '/' + name + '/:' + name + '_id';
|
||||
break;
|
||||
case 'create':
|
||||
method = 'post';
|
||||
path = '/' + name;
|
||||
break;
|
||||
case 'index':
|
||||
method = 'get';
|
||||
path = '/';
|
||||
break;
|
||||
default:
|
||||
throw new Error('unrecognized route: ' + name + '.' + key);
|
||||
}
|
||||
|
||||
path = prefix + path;
|
||||
app[method](path, obj[key]);
|
||||
verbose && console.log(' %s %s -> %s', method.toUpperCase(), path, key);
|
||||
}
|
||||
|
||||
// mount the app
|
||||
parent.use(app);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,14 @@
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px "Helvetica Neue", Helvetica, Arial;
|
||||
}
|
||||
a {
|
||||
color: #107aff;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
h1 a {
|
||||
font-size: 16px;
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>404: Not Found</h1>
|
||||
<p>Sorry we can't find <%= url %></p>
|
||||
@@ -0,0 +1,3 @@
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
<h1>500: Internal Server Error</h1>
|
||||
<p>Looks like something blew up!</p>
|
||||
@@ -0,0 +1,63 @@
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, verbose = process.env.NODE_ENV != 'test'
|
||||
, app = module.exports = express();
|
||||
|
||||
app.map = function(a, route){
|
||||
route = route || '';
|
||||
for (var key in a) {
|
||||
switch (typeof a[key]) {
|
||||
// { '/path': { ... }}
|
||||
case 'object':
|
||||
app.map(a[key], route + key);
|
||||
break;
|
||||
// get: function(){ ... }
|
||||
case 'function':
|
||||
if (verbose) console.log('%s %s', key, route);
|
||||
app[key](route, a[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var users = {
|
||||
list: function(req, res){
|
||||
res.send('user list');
|
||||
},
|
||||
|
||||
get: function(req, res){
|
||||
res.send('user ' + req.params.uid);
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
res.send('delete users');
|
||||
}
|
||||
};
|
||||
|
||||
var pets = {
|
||||
list: function(req, res){
|
||||
res.send('user ' + req.params.uid + '\'s pets');
|
||||
},
|
||||
|
||||
del: function(req, res){
|
||||
res.send('delete ' + req.params.uid + '\'s pet ' + req.params.pid);
|
||||
}
|
||||
};
|
||||
|
||||
app.map({
|
||||
'/users': {
|
||||
get: users.list,
|
||||
del: users.del,
|
||||
'/:uid': {
|
||||
get: users.get,
|
||||
'/pets': {
|
||||
get: pets.list,
|
||||
'/:pid': {
|
||||
del: pets.del
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,110 @@
|
||||
|
||||
var express = require('../..')
|
||||
, User = require('./user')
|
||||
, app = express();
|
||||
|
||||
app.set('views', __dirname);
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// filter ferrets only
|
||||
|
||||
function ferrets(user) {
|
||||
return user.species == 'ferret';
|
||||
}
|
||||
|
||||
// naive nesting approach,
|
||||
// delegating errors to next(err)
|
||||
// in order to expose the "count"
|
||||
// and "users" locals
|
||||
|
||||
app.get('/', function(req, res, next){
|
||||
User.count(function(err, count){
|
||||
if (err) return next(err);
|
||||
User.all(function(err, users){
|
||||
if (err) return next(err);
|
||||
res.render('user', {
|
||||
title: 'Users',
|
||||
count: count,
|
||||
users: users.filter(ferrets)
|
||||
});
|
||||
})
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// this approach is cleaner,
|
||||
// less nesting and we have
|
||||
// the variables available
|
||||
// on the request object
|
||||
|
||||
function count(req, res, next) {
|
||||
User.count(function(err, count){
|
||||
if (err) return next(err);
|
||||
req.count = count;
|
||||
next();
|
||||
})
|
||||
}
|
||||
|
||||
function users(req, res, next) {
|
||||
User.all(function(err, users){
|
||||
if (err) return next(err);
|
||||
req.users = users;
|
||||
next();
|
||||
})
|
||||
}
|
||||
|
||||
app.get('/middleware', count, users, function(req, res, next){
|
||||
res.render('user', {
|
||||
title: 'Users',
|
||||
count: req.count,
|
||||
users: req.users.filter(ferrets)
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
// this approach is much like the last
|
||||
// however we're explicitly exposing
|
||||
// the locals within each middleware
|
||||
//
|
||||
// note that this may not always work
|
||||
// well, for example here we filter
|
||||
// the users in the middleware, which
|
||||
// may not be ideal for our application.
|
||||
// so in that sense the previous example
|
||||
// is more flexible with `req.users`.
|
||||
|
||||
function count2(req, res, next) {
|
||||
User.count(function(err, count){
|
||||
if (err) return next(err);
|
||||
res.locals.count = count;
|
||||
next();
|
||||
})
|
||||
}
|
||||
|
||||
function users2(req, res, next) {
|
||||
User.all(function(err, users){
|
||||
if (err) return next(err);
|
||||
res.locals.users = users.filter(ferrets);
|
||||
next();
|
||||
})
|
||||
}
|
||||
|
||||
app.get('/middleware-locals', count2, users2, function(req, res, next){
|
||||
// you can see now how we have much less
|
||||
// to pass to res.render(). If we have
|
||||
// several routes related to users this
|
||||
// can be a great productivity booster
|
||||
res.render('user', { title: 'Users' });
|
||||
});
|
||||
|
||||
|
||||
app.get('/locals', function(req, res){
|
||||
res.render('user', { title: 'Users' });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Application listening on port 3000');
|
||||
@@ -0,0 +1,12 @@
|
||||
doctype 5
|
||||
html
|
||||
head
|
||||
title= title
|
||||
style
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 16px Helvetica, Arial;
|
||||
}
|
||||
body
|
||||
h2= title
|
||||
block content
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
for user in users
|
||||
.user
|
||||
h3= user.name
|
||||
p #{user.name} is a #{user.age} year old #{user.species}.
|
||||
@@ -0,0 +1,32 @@
|
||||
|
||||
module.exports = User;
|
||||
|
||||
// faux model
|
||||
|
||||
function User(name, age, species) {
|
||||
this.name = name;
|
||||
this.age = age;
|
||||
this.species = species;
|
||||
}
|
||||
|
||||
User.all = function(fn){
|
||||
process.nextTick(function(){
|
||||
fn(null, users);
|
||||
});
|
||||
};
|
||||
|
||||
User.count = function(fn){
|
||||
process.nextTick(function(){
|
||||
fn(null, users.length);
|
||||
});
|
||||
};
|
||||
|
||||
// faux database
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push(new User('Tobi', 2, 'ferret'));
|
||||
users.push(new User('Loki', 1, 'ferret'));
|
||||
users.push(new User('Jane', 6, 'ferret'));
|
||||
users.push(new User('Luna', 1, 'cat'));
|
||||
users.push(new User('Manny', 1, 'cat'));
|
||||
+18
-29
@@ -1,19 +1,13 @@
|
||||
|
||||
/*!
|
||||
* Express - application
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect')
|
||||
, Router = require('./router')
|
||||
, methods = Router.methods.concat('del', 'all')
|
||||
, methods = require('methods')
|
||||
, middleware = require('./middleware')
|
||||
, debug = require('debug')('express:application')
|
||||
, locals = require('./utils').locals
|
||||
, View = require('./view')
|
||||
, url = require('url')
|
||||
, utils = connect.utils
|
||||
@@ -64,28 +58,16 @@ app.defaultConfiguration = function(){
|
||||
this.use(connect.query());
|
||||
this.use(middleware.init(this));
|
||||
|
||||
// app locals
|
||||
this.locals = function(obj){
|
||||
for (var key in obj) self.locals[key] = obj[key];
|
||||
return self;
|
||||
};
|
||||
|
||||
// response locals
|
||||
this.locals.use = function(fn){
|
||||
if (3 == fn.length) {
|
||||
self.viewCallbacks.push(fn);
|
||||
} else {
|
||||
self.viewCallbacks.push(function(req, res, done){
|
||||
fn(req, res);
|
||||
done();
|
||||
});
|
||||
}
|
||||
return this;
|
||||
};
|
||||
// inherit protos
|
||||
this.on('mount', function(parent){
|
||||
this.request.__proto__ = parent.request;
|
||||
this.response.__proto__ = parent.response;
|
||||
this.engines.__proto__ = parent.engines;
|
||||
});
|
||||
|
||||
// router
|
||||
this._router = new Router(this);
|
||||
this.routes = this._router.routes;
|
||||
this.routes = this._router.map;
|
||||
this.__defineGetter__('router', function(){
|
||||
this._usedRouter = true;
|
||||
this._router.caseSensitive = this.enabled('case sensitive routing');
|
||||
@@ -93,11 +75,15 @@ app.defaultConfiguration = function(){
|
||||
return this._router.middleware;
|
||||
});
|
||||
|
||||
// setup locals
|
||||
this.locals = locals(this);
|
||||
|
||||
// default locals
|
||||
this.locals.settings = this.settings;
|
||||
|
||||
// default configuration
|
||||
this.enable('jsonp callback');
|
||||
this.set('jsonp callback name', 'callback');
|
||||
|
||||
this.configure('development', function(){
|
||||
this.set('json spaces', 2);
|
||||
@@ -134,6 +120,8 @@ app.use = function(route, fn){
|
||||
var orig = req.app;
|
||||
app.handle(req, res, function(err){
|
||||
req.app = res.app = orig;
|
||||
req.__proto__ = orig.request;
|
||||
res.__proto__ = orig.response;
|
||||
next(err);
|
||||
});
|
||||
};
|
||||
@@ -438,7 +426,6 @@ methods.forEach(function(method){
|
||||
app.all = function(path){
|
||||
var args = arguments;
|
||||
methods.forEach(function(method){
|
||||
if ('all' == method || 'del' == method) return;
|
||||
app[method].apply(this, args);
|
||||
}, this);
|
||||
return this;
|
||||
@@ -503,7 +490,9 @@ app.render = function(name, options, fn){
|
||||
});
|
||||
|
||||
if (!view.path) {
|
||||
return fn(new Error('Failed to lookup view "' + name + '"'));
|
||||
var err = new Error('Failed to lookup view "' + name + '"');
|
||||
err.view = view;
|
||||
return fn(err);
|
||||
}
|
||||
|
||||
// prime the cache
|
||||
|
||||
+8
-13
@@ -1,10 +1,4 @@
|
||||
|
||||
/*!
|
||||
* Express
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -16,6 +10,7 @@ var http = require('http')
|
||||
, Router = require('./router')
|
||||
, req = require('./request')
|
||||
, res = require('./response')
|
||||
, send = require('send')
|
||||
, utils = connect.utils;
|
||||
|
||||
/**
|
||||
@@ -28,7 +23,13 @@ exports = module.exports = createApplication;
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
exports.version = '3.0.0alpha2';
|
||||
exports.version = '3.0.0beta6';
|
||||
|
||||
/**
|
||||
* Expose mime.
|
||||
*/
|
||||
|
||||
exports.mime = send.mime;
|
||||
|
||||
/**
|
||||
* Create an express application.
|
||||
@@ -79,12 +80,6 @@ exports.response = res;
|
||||
exports.Route = Route;
|
||||
exports.Router = Router;
|
||||
|
||||
/**
|
||||
* Expose HTTP methods.
|
||||
*/
|
||||
|
||||
exports.methods = require('./router/methods');
|
||||
|
||||
// Error handler title
|
||||
|
||||
exports.errorHandler.title = 'Express';
|
||||
|
||||
+5
-10
@@ -1,10 +1,10 @@
|
||||
|
||||
/*!
|
||||
* Express - middleware
|
||||
* Copyright(c) 2010-2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('./utils');
|
||||
|
||||
/**
|
||||
* Initialization middleware, exposing the
|
||||
* request and response to eachother, as well
|
||||
@@ -18,8 +18,6 @@
|
||||
exports.init = function(app){
|
||||
return function expressInit(req, res, next){
|
||||
req.app = res.app = app;
|
||||
if (req.next) return next();
|
||||
|
||||
res.setHeader('X-Powered-By', 'Express');
|
||||
req.res = res;
|
||||
res.req = req;
|
||||
@@ -28,10 +26,7 @@ exports.init = function(app){
|
||||
req.__proto__ = app.request;
|
||||
res.__proto__ = app.response;
|
||||
|
||||
res.locals = function(obj){
|
||||
for (var key in obj) res.locals[key] = obj[key];
|
||||
return res;
|
||||
};
|
||||
res.locals = res.locals || utils.locals(res);
|
||||
|
||||
next();
|
||||
}
|
||||
|
||||
+104
-37
@@ -1,10 +1,4 @@
|
||||
|
||||
/*!
|
||||
* Express - request
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -12,8 +6,10 @@
|
||||
var http = require('http')
|
||||
, utils = require('./utils')
|
||||
, connect = require('connect')
|
||||
, fresh = require('fresh')
|
||||
, parseRange = require('range-parser')
|
||||
, parse = connect.utils.parseUrl
|
||||
, mime = require('mime');
|
||||
, mime = connect.mime;
|
||||
|
||||
/**
|
||||
* Request prototype.
|
||||
@@ -137,6 +133,32 @@ req.acceptsLanguage = function(lang){
|
||||
: true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse Range header field,
|
||||
* capping to the given `size`.
|
||||
*
|
||||
* Unspecified ranges such as "0-" require
|
||||
* knowledge of your resource length. In
|
||||
* the case of a byte range this is of course
|
||||
* the total number of bytes. If the Range
|
||||
* header field is not given `null` is returned,
|
||||
* `-1` when unsatisfiable, `-2` when syntactically invalid.
|
||||
*
|
||||
* NOTE: remember that ranges are inclusive, so
|
||||
* for example "Range: users=0-3" should respond
|
||||
* with 4 users when available, not 3.
|
||||
*
|
||||
* @param {Number} size
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.range = function(size){
|
||||
var range = this.get('Range');
|
||||
if (!range) return;
|
||||
return parseRange(size, range);
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an array of Accepted media types
|
||||
* ordered from highest quality to lowest.
|
||||
@@ -214,8 +236,8 @@ req.__defineGetter__('acceptedCharsets', function(){
|
||||
/**
|
||||
* Return the value of param `name` when present or `defaultValue`.
|
||||
*
|
||||
* - Checks body params, ex: id=12, {"id":12}
|
||||
* - Checks route placeholders, ex: _/user/:id_
|
||||
* - Checks body params, ex: id=12, {"id":12}
|
||||
* - Checks query string params, ex: ?id=12
|
||||
*
|
||||
* To utilize request bodies, `req.body`
|
||||
@@ -229,19 +251,12 @@ req.__defineGetter__('acceptedCharsets', function(){
|
||||
*/
|
||||
|
||||
req.param = function(name, defaultValue){
|
||||
// req.body
|
||||
if (this.body && undefined !== this.body[name]) return this.body[name];
|
||||
|
||||
// route params
|
||||
if (this.params
|
||||
&& this.params.hasOwnProperty(name)
|
||||
&& undefined !== this.params[name]) {
|
||||
return this.params[name];
|
||||
}
|
||||
|
||||
// query-string
|
||||
if (undefined !== this.query[name]) return this.query[name];
|
||||
|
||||
var params = this.params || {};
|
||||
var body = this.body || {};
|
||||
var query = this.query || {};
|
||||
if (null != params[name] && params.hasOwnProperty(name)) return params[name];
|
||||
if (null != body[name]) return body[name];
|
||||
if (null != query[name]) return query[name];
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
@@ -266,17 +281,6 @@ req.param = function(name, defaultValue){
|
||||
* req.is('html');
|
||||
* // => false
|
||||
*
|
||||
* Now within our route callbacks, we can use to to assert content types
|
||||
* such as "image/jpeg", "image/png" as shown here:
|
||||
*
|
||||
* app.post('/image/upload', function(req, res, next){
|
||||
* if (req.is('image/*')) {
|
||||
* // do something
|
||||
* } else {
|
||||
* next();
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
@@ -331,12 +335,25 @@ req.__defineGetter__('secure', function(){
|
||||
return 'https' == this.protocol;
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the remote address, or when
|
||||
* "trust proxy" is `true` return
|
||||
* the upstream addr.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ip', function(){
|
||||
return this.ips[0] || this.connection.remoteAddress;
|
||||
});
|
||||
|
||||
/**
|
||||
* When "trust proxy" is `true`, parse
|
||||
* the "X-Forwarded-For" ip address list.
|
||||
*
|
||||
* For example if the value were "client, proxy1, proxy2"
|
||||
* you would receive the array `["proxy2", "proxy1", "client"]`
|
||||
* you would receive the array `["client", "proxy1", "proxy2"]`
|
||||
* where "proxy2" is the furthest down-stream.
|
||||
*
|
||||
* @return {Array}
|
||||
@@ -344,12 +361,40 @@ req.__defineGetter__('secure', function(){
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ips', function(){
|
||||
var trustProxy = this.app.get('trust proxy');
|
||||
var val = this.get('X-Forwarded-For');
|
||||
return val
|
||||
? val.split(/ *, */).reverse()
|
||||
return trustProxy && val
|
||||
? val.split(/ *, */)
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return basic auth credentials.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* // http://tobi:hello@example.com
|
||||
* req.auth
|
||||
* // => { username: 'tobi', password: 'hello' }
|
||||
*
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('auth', function(){
|
||||
// missing
|
||||
var auth = this.get('Authorization');
|
||||
if (!auth) return {};
|
||||
|
||||
// malformed
|
||||
auth = auth.split(' ')[1];
|
||||
if (!auth) return {};
|
||||
|
||||
// credentials
|
||||
auth = new Buffer(auth, 'base64').toString().split(':');
|
||||
return { username: auth[0], password: auth[1] };
|
||||
});
|
||||
|
||||
/**
|
||||
* Return subdomains as an array.
|
||||
*
|
||||
@@ -378,6 +423,17 @@ req.__defineGetter__('path', function(){
|
||||
return parse(this).pathname;
|
||||
});
|
||||
|
||||
/**
|
||||
* Parse the "Host" header field hostname.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('host', function(){
|
||||
return this.get('Host').split(':')[0];
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the request is fresh, aka
|
||||
* Last-Modified and/or the ETag
|
||||
@@ -388,7 +444,18 @@ req.__defineGetter__('path', function(){
|
||||
*/
|
||||
|
||||
req.__defineGetter__('fresh', function(){
|
||||
return ! this.stale;
|
||||
var method = this.method;
|
||||
var s = this.res.statusCode;
|
||||
|
||||
// GET or HEAD for weak freshness validation only
|
||||
if ('GET' != method && 'HEAD' != method) return false;
|
||||
|
||||
// 2xx or 304 as per rfc2616 14.26
|
||||
if ((s >= 200 && s < 300) || 304 == s) {
|
||||
return fresh(this.headers, this.res._headers);
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -401,7 +468,7 @@ req.__defineGetter__('fresh', function(){
|
||||
*/
|
||||
|
||||
req.__defineGetter__('stale', function(){
|
||||
return connect.utils.modified(this, this.res);
|
||||
return !this.fresh;
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
+113
-152
@@ -1,10 +1,3 @@
|
||||
|
||||
/*!
|
||||
* Express - response
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -18,7 +11,9 @@ var fs = require('fs')
|
||||
, normalizeTypes = require('./utils').normalizeTypes
|
||||
, statusCodes = http.STATUS_CODES
|
||||
, send = connect.static.send
|
||||
, mime = require('mime')
|
||||
, cookie = require('cookie')
|
||||
, send = require('send')
|
||||
, mime = connect.mime
|
||||
, basename = path.basename
|
||||
, extname = path.extname
|
||||
, join = path.join;
|
||||
@@ -44,6 +39,27 @@ res.status = function(code){
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set Link header field with the given `links`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.links({
|
||||
* next: 'http://api.example.com/users?page=2',
|
||||
* last: 'http://api.example.com/users?page=5'
|
||||
* });
|
||||
*
|
||||
* @param {Object} links
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.links = function(links){
|
||||
return this.set('Link', Object.keys(links).map(function(rel){
|
||||
return '<' + links[rel] + '>; rel="' + rel + '"';
|
||||
}).join(', '));
|
||||
};
|
||||
|
||||
/**
|
||||
* Send a response.
|
||||
*
|
||||
@@ -61,63 +77,7 @@ res.status = function(code){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.send = function(body){
|
||||
var req = this.req
|
||||
, head = 'HEAD' == req.method;
|
||||
|
||||
// allow status / body
|
||||
if (2 == arguments.length) {
|
||||
this.statusCode = body;
|
||||
body = arguments[1];
|
||||
}
|
||||
|
||||
// convert string objects to primitives
|
||||
if (body instanceof String) body = body.toString();
|
||||
|
||||
switch (typeof body) {
|
||||
// response status
|
||||
case 'number':
|
||||
this.get('Content-Type') || this.contentType('.txt');
|
||||
this.statusCode = body;
|
||||
body = http.STATUS_CODES[body];
|
||||
break;
|
||||
// string defaulting to html
|
||||
case 'string':
|
||||
if (!this.get('Content-Type')) {
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.contentType('.html');
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
if (null == body) {
|
||||
body = '';
|
||||
} else if (Buffer.isBuffer(body)) {
|
||||
this.get('Content-Type') || this.contentType('.bin');
|
||||
} else {
|
||||
return this.json(body);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// populate Content-Length
|
||||
if (undefined !== body && !this.get('Content-Length')) {
|
||||
this.set('Content-Length', Buffer.isBuffer(body)
|
||||
? body.length
|
||||
: Buffer.byteLength(body));
|
||||
}
|
||||
|
||||
// strip irrelevant headers
|
||||
if (204 == this.statusCode || 304 == this.statusCode) {
|
||||
this.removeHeader('Content-Type');
|
||||
this.removeHeader('Content-Length');
|
||||
body = '';
|
||||
}
|
||||
|
||||
// respond
|
||||
this.end(head ? null : body);
|
||||
return this;
|
||||
};
|
||||
res.send = require('response-send');
|
||||
|
||||
/**
|
||||
* Send JSON response.
|
||||
@@ -138,23 +98,31 @@ res.send = function(body){
|
||||
res.json = function(obj){
|
||||
// allow status / body
|
||||
if (2 == arguments.length) {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
// res.json(body, status) backwards compat
|
||||
if ('number' == typeof arguments[1]) {
|
||||
this.statusCode = arguments[1];
|
||||
} else {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
}
|
||||
}
|
||||
|
||||
// settings
|
||||
var app = this.app
|
||||
, jsonp = app.get('jsonp callback')
|
||||
, replacer = app.get('json replacer')
|
||||
, spaces = app.get('json spaces')
|
||||
, body = JSON.stringify(obj, replacer, spaces)
|
||||
, callback = this.req.query.callback;
|
||||
, callback = this.req.query[app.get('jsonp callback name')];
|
||||
|
||||
// content-type
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.set('Content-Type', 'application/json');
|
||||
|
||||
|
||||
// jsonp
|
||||
if (callback && jsonp) {
|
||||
this.set('Content-Type', 'text/javascript');
|
||||
body = callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
|
||||
body = callback.replace(/[^[]\w$.]/g, '') + '(' + body + ');';
|
||||
}
|
||||
|
||||
return this.send(body);
|
||||
@@ -204,7 +172,8 @@ res.sendfile = function(path, options, fn){
|
||||
var self = this
|
||||
, req = self.req
|
||||
, next = this.req.next
|
||||
, options = options || {};
|
||||
, options = options || {}
|
||||
, done;
|
||||
|
||||
// support function as second arg
|
||||
if ('function' == typeof options) {
|
||||
@@ -212,30 +181,43 @@ res.sendfile = function(path, options, fn){
|
||||
options = {};
|
||||
}
|
||||
|
||||
// callback
|
||||
options.callback = function(err){
|
||||
if (err) {
|
||||
// cast ENOENT
|
||||
if ('ENOENT' == err.code) err = utils.error(404);
|
||||
// socket errors
|
||||
req.socket.on('error', error);
|
||||
|
||||
// ditch content-disposition to prevent funky responses
|
||||
if (!self.headerSent) self.removeHeader('Content-Disposition');
|
||||
// errors
|
||||
function error(err) {
|
||||
if (done) return;
|
||||
done = true;
|
||||
|
||||
// woot! callback available
|
||||
if (fn) return fn(err);
|
||||
// clean up
|
||||
req.socket.removeListener('error', error);
|
||||
if (!self.headerSent) self.removeHeader('Content-Disposition');
|
||||
|
||||
// lost in limbo if there's no callback
|
||||
if (self.headerSent) return;
|
||||
// callback available
|
||||
if (fn) return fn(err);
|
||||
|
||||
return req.next(err);
|
||||
}
|
||||
// list in limbo if there's no callback
|
||||
if (self.headerSent) return;
|
||||
|
||||
fn && fn();
|
||||
};
|
||||
// delegate
|
||||
next(err);
|
||||
}
|
||||
|
||||
// streaming
|
||||
function stream() {
|
||||
if (done) return;
|
||||
req.socket.removeListener('error', error);
|
||||
if (fn) self.on('finish', fn);
|
||||
}
|
||||
|
||||
// transfer
|
||||
options.path = encodeURIComponent(path);
|
||||
send(this.req, this, next, options);
|
||||
var file = send(req, path);
|
||||
if (options.root) file.root(options.root);
|
||||
file.maxage(options.maxAge || 0);
|
||||
file.on('error', error);
|
||||
file.on('directory', next);
|
||||
file.on('stream', stream);
|
||||
file.pipe(this);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -246,7 +228,7 @@ res.sendfile = function(path, options, fn){
|
||||
* when the data transfer is complete, or when an error has
|
||||
* ocurred. Be sure to check `res.headerSent` if you plan to respond.
|
||||
*
|
||||
* This method uses `res.attachment()` and `res.sendfile()`.
|
||||
* This method uses `res.sendfile()`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String|Function} filename or fn
|
||||
@@ -261,7 +243,9 @@ res.download = function(path, filename, fn){
|
||||
filename = null;
|
||||
}
|
||||
|
||||
return this.attachment(filename || path).sendfile(path, fn);
|
||||
filename = filename || path;
|
||||
this.set('Content-Disposition', 'attachment; filename="' + basename(filename) + '"');
|
||||
return this.sendfile(path, fn);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -334,6 +318,12 @@ res.type = function(type){
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* By default Express passes an `Error`
|
||||
* with a `.status` of 406 to `next(err)`
|
||||
* if a match is not made. If you provide
|
||||
* a `.default` callback it will be invoked
|
||||
* instead.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
@@ -342,14 +332,20 @@ res.type = function(type){
|
||||
res.format = function(obj){
|
||||
var keys = Object.keys(obj)
|
||||
, req = this.req
|
||||
, next = req.next
|
||||
, key = req.accepts(keys);
|
||||
, next = req.next;
|
||||
|
||||
var fn = obj.default;
|
||||
if (fn) delete obj.default;
|
||||
|
||||
var key = req.accepts(keys);
|
||||
|
||||
this.set('Vary', 'Accept');
|
||||
|
||||
if (key) {
|
||||
this.set('Content-Type', normalizeType(key));
|
||||
obj[key](req, this, next);
|
||||
} else if (fn) {
|
||||
fn();
|
||||
} else {
|
||||
var err = new Error('Not Acceptable');
|
||||
err.status = 406;
|
||||
@@ -378,7 +374,7 @@ res.attachment = function(filename){
|
||||
|
||||
/**
|
||||
* Set header `field` to `val`, or pass
|
||||
* an object of of header fields.
|
||||
* an object of header fields.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -396,10 +392,10 @@ res.attachment = function(filename){
|
||||
res.set =
|
||||
res.header = function(field, val){
|
||||
if (2 == arguments.length) {
|
||||
this.setHeader(field, val);
|
||||
this.setHeader(field, '' + val);
|
||||
} else {
|
||||
for (var key in field) {
|
||||
this.setHeader(key, field[key]);
|
||||
this.setHeader(key, '' + field[key]);
|
||||
}
|
||||
}
|
||||
return this;
|
||||
@@ -433,30 +429,13 @@ res.clearCookie = function(name, options){
|
||||
: opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a signed cookie with the given `name` and `val`.
|
||||
* See `res.cookie()` for details.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String|Object} val
|
||||
* @param {Object} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.signedCookie = function(name, val, options){
|
||||
var secret = this.req.secret;
|
||||
if (!secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
|
||||
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
|
||||
val = utils.sign(val, secret);
|
||||
return this.cookie(name, val, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set cookie `name` to `val`, with the given `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` max-age in milliseconds, converted to `expires`
|
||||
* - `signed` sign the cookie
|
||||
* - `path` defaults to "/"
|
||||
*
|
||||
* Examples:
|
||||
@@ -475,11 +454,14 @@ res.signedCookie = function(name, val, options){
|
||||
|
||||
res.cookie = function(name, val, options){
|
||||
options = options || {};
|
||||
var secret = this.req.secret;
|
||||
var signed = options.signed;
|
||||
if (signed && !secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
|
||||
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
|
||||
if (signed) val = utils.sign(val, secret);
|
||||
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
|
||||
if (null == options.path) options.path = '/';
|
||||
var cookie = utils.serializeCookie(name, val, options);
|
||||
this.set('Set-Cookie', cookie);
|
||||
this.set('Set-Cookie', cookie.serialize(name, String(val), options));
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -540,7 +522,7 @@ res.redirect = function(url){
|
||||
var path = app.path();
|
||||
|
||||
// relative to path
|
||||
if (0 == url.indexOf('./') || 0 == url.indexOf('..')) {
|
||||
if ('.' == url[0]) {
|
||||
url = req.path + '/' + url;
|
||||
// relative to mount-point
|
||||
} else if ('/' != url[0]) {
|
||||
@@ -560,12 +542,17 @@ res.redirect = function(url){
|
||||
|
||||
html: function(){
|
||||
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
|
||||
},
|
||||
|
||||
default: function(){
|
||||
body = '';
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
// Respond
|
||||
this.statusCode = status;
|
||||
this.set('Location', url);
|
||||
this.set('Content-Length', Buffer.byteLength(body));
|
||||
this.end(head ? null : body);
|
||||
};
|
||||
|
||||
@@ -601,41 +588,15 @@ res.render = function(view, options, fn){
|
||||
fn = options, options = {};
|
||||
}
|
||||
|
||||
function render() {
|
||||
// merge res.locals
|
||||
options.locals = self.locals;
|
||||
// merge res.locals
|
||||
options.locals = self.locals;
|
||||
|
||||
// default callback to respond
|
||||
fn = fn || function(err, str){
|
||||
if (err) return req.next(err);
|
||||
self.send(str);
|
||||
};
|
||||
// default callback to respond
|
||||
fn = fn || function(err, str){
|
||||
if (err) return req.next(err);
|
||||
self.send(str);
|
||||
};
|
||||
|
||||
// render
|
||||
app.render(view, options, fn);
|
||||
}
|
||||
|
||||
// invoke view callbacks
|
||||
var callbacks = app.viewCallbacks
|
||||
, pending = callbacks.length
|
||||
, len = pending
|
||||
, done;
|
||||
|
||||
if (len) {
|
||||
for (var i = 0; i < len; ++i) {
|
||||
callbacks[i](req, self, function(err){
|
||||
if (done) return;
|
||||
|
||||
if (err) {
|
||||
req.next(err);
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
--pending || render();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
render();
|
||||
}
|
||||
// render
|
||||
app.render(view, options, fn);
|
||||
};
|
||||
+23
-24
@@ -1,10 +1,3 @@
|
||||
|
||||
/*!
|
||||
* Express - Router
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -12,7 +5,8 @@
|
||||
var Route = require('./route')
|
||||
, utils = require('../utils')
|
||||
, debug = require('debug')('express:router')
|
||||
, parse = require('connect').utils.parseUrl;
|
||||
, parse = require('connect').utils.parseUrl
|
||||
, methods = require('methods');
|
||||
|
||||
/**
|
||||
* Expose `Router` constructor.
|
||||
@@ -20,12 +14,6 @@ var Route = require('./route')
|
||||
|
||||
exports = module.exports = Router;
|
||||
|
||||
/**
|
||||
* Expose HTTP methods.
|
||||
*/
|
||||
|
||||
var methods = exports.methods = require('./methods');
|
||||
|
||||
/**
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
*
|
||||
@@ -41,7 +29,6 @@ function Router(options) {
|
||||
this._params = [];
|
||||
this.caseSensitive = options.caseSensitive;
|
||||
this.strict = options.strict;
|
||||
|
||||
this.middleware = function router(req, res, next){
|
||||
self._dispatch(req, res, next);
|
||||
};
|
||||
@@ -97,7 +84,7 @@ Router.prototype._dispatch = function(req, res, next){
|
||||
var params = this.params
|
||||
, self = this;
|
||||
|
||||
debug('dispatching %s %s', req.method, req.url);
|
||||
debug('dispatching %s %s (%s)', req.method, req.url, req.originalUrl);
|
||||
|
||||
// route dispatch
|
||||
(function pass(i, err){
|
||||
@@ -115,7 +102,7 @@ Router.prototype._dispatch = function(req, res, next){
|
||||
}
|
||||
|
||||
// match route
|
||||
req.route = route = self.match(req, i);
|
||||
req.route = route = self.matchRequest(req, i);
|
||||
|
||||
// implied OPTIONS
|
||||
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
|
||||
@@ -232,7 +219,7 @@ Router.prototype._optionsFor = function(path){
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype.match = function(req, i, head){
|
||||
Router.prototype.matchRequest = function(req, i, head){
|
||||
var method = req.method.toLowerCase()
|
||||
, url = parse(req)
|
||||
, path = url.pathname
|
||||
@@ -241,14 +228,9 @@ Router.prototype.match = function(req, i, head){
|
||||
, route;
|
||||
|
||||
// HEAD support
|
||||
// TODO: clean this up
|
||||
if (!head && 'head' == method) {
|
||||
// attempt lookup
|
||||
route = this.match(req, i, true);
|
||||
route = this.matchRequest(req, i, true);
|
||||
if (route) return route;
|
||||
|
||||
// default to GET as res.render() / res.send()
|
||||
// etc support HEAD
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
@@ -266,6 +248,23 @@ Router.prototype.match = function(req, i, head){
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt to match a route for `method`
|
||||
* and `url` with optional starting
|
||||
* index of `i` defaulting to 0.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} url
|
||||
* @param {Number} i
|
||||
* @return {Route}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype.match = function(method, url, i, head){
|
||||
var req = { method: method, url: url };
|
||||
return this.matchRequest(req, i, head);
|
||||
};
|
||||
|
||||
/**
|
||||
* Route `method`, `path`, and one or more callbacks.
|
||||
*
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
/*!
|
||||
* Express - router - methods
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* HTTP methods supported by node.
|
||||
*/
|
||||
|
||||
module.exports = [
|
||||
'get'
|
||||
, 'post'
|
||||
, 'put'
|
||||
, 'head'
|
||||
, 'delete'
|
||||
, 'options'
|
||||
, 'trace'
|
||||
, 'copy'
|
||||
, 'lock'
|
||||
, 'mkcol'
|
||||
, 'move'
|
||||
, 'propfind'
|
||||
, 'proppatch'
|
||||
, 'unlock'
|
||||
, 'report'
|
||||
, 'mkactivity'
|
||||
, 'checkout'
|
||||
, 'merge'
|
||||
, 'm-search'
|
||||
, 'notify'
|
||||
, 'subscribe'
|
||||
, 'unsubscribe'
|
||||
, 'patch'
|
||||
];
|
||||
@@ -1,10 +1,4 @@
|
||||
|
||||
/*!
|
||||
* Express - router - Route
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
+26
-11
@@ -1,15 +1,30 @@
|
||||
|
||||
/*!
|
||||
* Express - utils
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mime = require('mime');
|
||||
var mime = require('connect').mime;
|
||||
|
||||
/**
|
||||
* Make `locals()` bound to the given `obj`.
|
||||
*
|
||||
* This is used for `app.locals` and `res.locals`.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.locals = function(obj){
|
||||
obj.viewCallbacks = obj.viewCallbacks || [];
|
||||
|
||||
function locals(obj){
|
||||
for (var key in obj) locals[key] = obj[key];
|
||||
return obj;
|
||||
};
|
||||
|
||||
return locals;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `path` looks absolute.
|
||||
@@ -233,12 +248,11 @@ exports.escape = function(html) {
|
||||
|
||||
exports.pathRegexp = function(path, keys, sensitive, strict) {
|
||||
if (path instanceof RegExp) return path;
|
||||
if (path instanceof Array)
|
||||
path = '(' + path.join('|') + ')';
|
||||
if (Array.isArray(path)) path = '(' + path.join('|') + ')';
|
||||
path = path
|
||||
.concat(strict ? '' : '/?')
|
||||
.replace(/\/\(/g, '(?:/')
|
||||
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
|
||||
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?(\*)?/g, function(_, slash, format, key, capture, optional, star){
|
||||
keys.push({ name: key, optional: !! optional });
|
||||
slash = slash || '';
|
||||
return ''
|
||||
@@ -246,7 +260,8 @@ exports.pathRegexp = function(path, keys, sensitive, strict) {
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
|
||||
+ (optional || '');
|
||||
+ (optional || '')
|
||||
+ (star ? '(/*)?' : '');
|
||||
})
|
||||
.replace(/([\/.])/g, '\\$1')
|
||||
.replace(/\*/g, '(.*)');
|
||||
|
||||
+2
-8
@@ -1,20 +1,14 @@
|
||||
|
||||
/*!
|
||||
* Express - View
|
||||
* Copyright(c) 2010-2012 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var path = require('path')
|
||||
, fs = require('fs')
|
||||
, utils = require('./utils')
|
||||
, dirname = path.dirname
|
||||
, basename = path.basename
|
||||
, extname = path.extname
|
||||
, exists = path.existsSync
|
||||
, exists = fs.existsSync || path.existsSync
|
||||
, join = path.join;
|
||||
|
||||
/**
|
||||
|
||||
+25
-8
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Sinatra inspired web development framework",
|
||||
"version": "3.0.0alpha2",
|
||||
"version": "3.0.0beta6",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
|
||||
@@ -10,23 +10,40 @@
|
||||
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
|
||||
],
|
||||
"dependencies": {
|
||||
"connect": "2.2.0",
|
||||
"commander": "0.5.2",
|
||||
"mime": "1.2.5",
|
||||
"mkdirp": "0.3.1",
|
||||
"connect": "2.3.8",
|
||||
"commander": "0.6.1",
|
||||
"range-parser": "0.0.4",
|
||||
"response-send": "0.0.1",
|
||||
"mkdirp": "0.3.3",
|
||||
"cookie": "0.0.3",
|
||||
"fresh": "0.1.0",
|
||||
"methods": "0.0.1",
|
||||
"send": "0.0.2",
|
||||
"debug": "*"
|
||||
},
|
||||
"devDependencies": {
|
||||
"ejs": "*",
|
||||
"mocha": "*",
|
||||
"jade": "*",
|
||||
"hjs": "*",
|
||||
"stylus": "*",
|
||||
"should": "*",
|
||||
"connect-redis": "*",
|
||||
"github-flavored-markdown": "*"
|
||||
"github-flavored-markdown": "*",
|
||||
"supertest": "0.0.1"
|
||||
},
|
||||
"keywords": [
|
||||
"express",
|
||||
"framework",
|
||||
"sinatra",
|
||||
"web",
|
||||
"rest",
|
||||
"restful",
|
||||
"router",
|
||||
"app",
|
||||
"api"
|
||||
],
|
||||
"publishConfig": { "tag": "3.0" },
|
||||
"keywords": ["express", "framework", "sinatra", "web", "rest", "restful", "router"],
|
||||
"repository": "git://github.com/visionmedia/express",
|
||||
"main": "index",
|
||||
"bin": { "express": "./bin/express" },
|
||||
@@ -34,6 +51,6 @@
|
||||
"prepublish" : "npm prune",
|
||||
"test": "make test"
|
||||
},
|
||||
"engines": { "node":">= 0.5.0 < 0.7.0" }
|
||||
"engines": { "node": "*" }
|
||||
}
|
||||
|
||||
|
||||
+32
-1
@@ -5,7 +5,30 @@
|
||||
|
||||
var express = require('../');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = express()
|
||||
, blog = express()
|
||||
, admin = express();
|
||||
|
||||
// app.use(express.logger('dev'))
|
||||
blog.use('/admin', admin);
|
||||
app.use('/blog', blog);
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
app.locals.self = true;
|
||||
|
||||
var repo = require('../package.json');
|
||||
|
||||
app.get('/render', function(req, res){
|
||||
res.render('hello');
|
||||
});
|
||||
|
||||
admin.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
blog.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World\n');
|
||||
@@ -15,6 +38,14 @@ app.get('/json', function(req, res){
|
||||
res.send({ name: 'Tobi', role: 'admin' });
|
||||
});
|
||||
|
||||
app.get('/json/:n', function(req, res){
|
||||
var n = ~~req.params.n;
|
||||
var arr = [];
|
||||
var obj = { name: 'Tobi', role: 'admin' };
|
||||
while (n--) arr.push(obj);
|
||||
res.send(arr);
|
||||
});
|
||||
|
||||
function foo(req, res, next) {
|
||||
next();
|
||||
}
|
||||
|
||||
+15
-2
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
node ./support/app &
|
||||
NODE_ENV=production node ./support/app &
|
||||
pid=$!
|
||||
|
||||
bench() {
|
||||
@@ -10,10 +10,23 @@ bench() {
|
||||
| xargs echo "$2:"
|
||||
}
|
||||
|
||||
bench_conditional() {
|
||||
ab -n 5000 -c 50 -H "If-None-Match: $3" -k -q http://127.0.0.1:8000$1 \
|
||||
| grep "Requests per" \
|
||||
| cut -d ' ' -f 7 \
|
||||
| xargs echo "$2:"
|
||||
}
|
||||
|
||||
sleep .5
|
||||
bench / "Hello World"
|
||||
bench /json "JSON"
|
||||
bench /blog "Mounted Hello World"
|
||||
bench /blog/admin "Mounted 2 Hello World"
|
||||
bench /middleware "Middleware"
|
||||
bench /match "Router"
|
||||
bench /render "Render"
|
||||
bench /json "JSON tiny"
|
||||
bench /json/15 "JSON small"
|
||||
bench /json/50 "JSON medium"
|
||||
bench /json/150 "JSON large"
|
||||
|
||||
kill -9 $pid
|
||||
@@ -0,0 +1 @@
|
||||
p Hello
|
||||
+31
-5
@@ -12,27 +12,53 @@ describe('Router', function(){
|
||||
app = express();
|
||||
})
|
||||
|
||||
describe('.match(req, i)', function(){
|
||||
describe('.match(method, url, i)', function(){
|
||||
it('should match based on index', function(){
|
||||
router.route('get', '/foo', function(){});
|
||||
router.route('get', '/foob?', function(){});
|
||||
router.route('get', '/bar', function(){});
|
||||
|
||||
var method = 'GET';
|
||||
var url = '/foo?bar=baz';
|
||||
|
||||
var route = router.match(method, url, 0);
|
||||
route.constructor.name.should.equal('Route');
|
||||
route.method.should.equal('get');
|
||||
route.path.should.equal('/foo');
|
||||
|
||||
var route = router.match(method, url, 1);
|
||||
route.path.should.equal('/foob?');
|
||||
|
||||
var route = router.match(method, url, 2);
|
||||
assert(!route);
|
||||
|
||||
url = '/bar';
|
||||
var route = router.match(method, url);
|
||||
route.path.should.equal('/bar');
|
||||
})
|
||||
})
|
||||
|
||||
describe('.matchRequest(req, i)', function(){
|
||||
it('should match based on index', function(){
|
||||
router.route('get', '/foo', function(){});
|
||||
router.route('get', '/foob?', function(){});
|
||||
router.route('get', '/bar', function(){});
|
||||
var req = { method: 'GET', url: '/foo?bar=baz' };
|
||||
|
||||
var route = router.match(req, 0);
|
||||
var route = router.matchRequest(req, 0);
|
||||
route.constructor.name.should.equal('Route');
|
||||
route.method.should.equal('get');
|
||||
route.path.should.equal('/foo');
|
||||
|
||||
var route = router.match(req, 1);
|
||||
var route = router.matchRequest(req, 1);
|
||||
req._route_index.should.equal(1);
|
||||
route.path.should.equal('/foob?');
|
||||
|
||||
var route = router.match(req, 2);
|
||||
var route = router.matchRequest(req, 2);
|
||||
assert(!route);
|
||||
|
||||
req.url = '/bar';
|
||||
var route = router.match(req);
|
||||
var route = router.matchRequest(req);
|
||||
route.path.should.equal('/bar');
|
||||
})
|
||||
})
|
||||
|
||||
+10
-61
@@ -1,8 +1,8 @@
|
||||
var app = require('../../examples/auth/app')
|
||||
, request = require('../support/http');
|
||||
|
||||
function redirects(to,fn){
|
||||
return function(res){
|
||||
function redirects(to, fn){
|
||||
return function(err, res){
|
||||
res.statusCode.should.equal(302)
|
||||
res.headers.should.have.property('location').match(to);
|
||||
fn()
|
||||
@@ -14,80 +14,29 @@ function getCookie(res) {
|
||||
}
|
||||
|
||||
describe('auth', function(){
|
||||
var cookie;
|
||||
|
||||
describe('GET /',function(){
|
||||
it('should redirect to /login', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(redirects(/\/login$/,done))
|
||||
.get('/')
|
||||
.end(redirects(/\/login$/, done))
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /restricted (w/o cookie)',function(){
|
||||
it('should redirect to /login', function(done){
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.end(redirects(/\/login$/,done))
|
||||
.get('/restricted')
|
||||
.end(redirects(/\/login$/,done))
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /login', function(){
|
||||
it('should fail without proper credentials', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.set('content-type','application/x-www-form-urlencoded')
|
||||
.write('&username=not-tj&password=foobar')
|
||||
.end(redirects(/\/login$/,done))
|
||||
})
|
||||
|
||||
it('should authenticate', function(done){
|
||||
request(app)
|
||||
.post('/login')
|
||||
.set('content-type', 'application/x-www-form-urlencoded')
|
||||
.write('username=tj&password=foobar')
|
||||
.end(function(res){
|
||||
res.statusCode.should.equal(302);
|
||||
cookie = getCookie(res);
|
||||
request(app)
|
||||
.get('/login')
|
||||
.set('Cookie', cookie)
|
||||
.end(function(res){
|
||||
res.body.should.include('Authenticated as tj');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /restricted (w. cookie)',function(){
|
||||
it('should respond with 200', function(done){
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.set('Cookie', cookie)
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /logout',function(){
|
||||
it('should respond with 302 and clear cookie',function(done){
|
||||
request(app)
|
||||
.get('/logout')
|
||||
.set('Cookie', cookie)
|
||||
.end(function(res){
|
||||
res.statusCode.should.equal(302);
|
||||
res.headers.should.not.have.property('set-cookie')
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /restricted (w. expired cookie)',function(){
|
||||
it('should respond with 302',function(done){
|
||||
request(app)
|
||||
.get('/restricted')
|
||||
.set('Cookie', cookie)
|
||||
.expect(302, done)
|
||||
.post('/login')
|
||||
.type('urlencoded')
|
||||
.send('username=not-tj&password=foobar')
|
||||
.end(redirects(/\/login$/, done))
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,79 +0,0 @@
|
||||
|
||||
var app = require('../../examples/blog')
|
||||
, request = require('../support/http');
|
||||
|
||||
var authorization = 'Basic ' + Buffer('admin:express').toString('base64');
|
||||
|
||||
function redirects(to,fn){
|
||||
return function(res){
|
||||
res.statusCode.should.equal(302)
|
||||
res.headers.should.have.property('location').match(to);
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
describe('blog', function(){
|
||||
describe('GET /', function(){
|
||||
it('should have no posts', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(/you have no posts/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /post/add', function(){
|
||||
it('should require auth', function(done){
|
||||
request(app)
|
||||
.get('/post/add')
|
||||
.expect(401, done)
|
||||
})
|
||||
|
||||
it('should login', function(done){
|
||||
request(app)
|
||||
.get('/post/add')
|
||||
.set('Authorization', authorization)
|
||||
.expect(/<h1>New Post<\/h1>/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /post', function(){
|
||||
it('should require auth', function(done){
|
||||
request(app)
|
||||
.post('/post')
|
||||
.expect(401, done)
|
||||
})
|
||||
|
||||
it('should redirect to / with no title or body', function(done){
|
||||
request(app)
|
||||
.post('/post')
|
||||
.set('Authorization', authorization)
|
||||
.end(redirects(/\/$/, done))
|
||||
})
|
||||
|
||||
it('should redirect to / with no body', function(done){
|
||||
request(app)
|
||||
.post('/post')
|
||||
.set('Authorization', authorization)
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.write('post[title]=Kittens')
|
||||
.end(redirects(/\/$/, done))
|
||||
})
|
||||
|
||||
it('should redirect to /post/:post when successful', function(done){
|
||||
request(app)
|
||||
.post('/post')
|
||||
.set('Authorization', authorization)
|
||||
.set('Content-Type', 'application/x-www-form-urlencoded')
|
||||
.write('post[title]=Kittens&post[body]=In+very+large+baskets')
|
||||
.end(redirects(/\/post\/\d+$/, done))
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /', function(){
|
||||
it('should now list 1 post',function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(/Display all 1 post/, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -7,20 +7,16 @@ describe('content-negotiation', function(){
|
||||
it('should default to text/html', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
res.body.should.equal('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>');
|
||||
done();
|
||||
})
|
||||
.expect('<ul><li>Tobi</li><li>Loki</li><li>Jane</li></ul>')
|
||||
.end(done);
|
||||
})
|
||||
|
||||
it('should accept to text/plain', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('Accept', 'text/plain')
|
||||
.end(function(res){
|
||||
res.body.should.equal(' - Tobi\n - Loki\n - Jane\n');
|
||||
done();
|
||||
})
|
||||
.expect(' - Tobi\n - Loki\n - Jane\n')
|
||||
.end(done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
var app = require('../../examples/cookies/app')
|
||||
, request = require('../support/http');
|
||||
|
||||
@@ -5,29 +6,29 @@ describe('cookies', function(){
|
||||
describe('GET /', function(){
|
||||
it('should have a form', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(/<form/,done)
|
||||
.get('/')
|
||||
.expect(/<form/, done);
|
||||
})
|
||||
|
||||
it('should respond with no cookies', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
res.headers.should.not.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.headers.should.not.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /', function(){
|
||||
it('should set a cookie', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type','application/x-www-form-urlencoded')
|
||||
.write('remember=1')
|
||||
.end(function(res){
|
||||
res.headers.should.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
.post('/')
|
||||
.send({ remember: 1 })
|
||||
.end(function(err, res){
|
||||
res.headers.should.have.property('set-cookie')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -1,3 +1,4 @@
|
||||
|
||||
var app = require('../../examples/downloads/app')
|
||||
, request = require('../support/http');
|
||||
|
||||
@@ -5,28 +6,28 @@ describe('downloads', function(){
|
||||
describe('GET /', function(){
|
||||
it('should have a link to amazing.txt', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(/href="\/files\/amazing.txt"/,done)
|
||||
.get('/')
|
||||
.expect(/href="\/files\/amazing.txt"/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /files/amazing.txt', function(){
|
||||
it('should have a download header', function(done){
|
||||
request(app)
|
||||
.get('/files/amazing.txt')
|
||||
.end(function(res){
|
||||
res.should.have.property('statusCode',200)
|
||||
res.headers.should.have.property('content-disposition', 'attachment; filename="amazing.txt"')
|
||||
done()
|
||||
})
|
||||
.get('/files/amazing.txt')
|
||||
.end(function(err, res){
|
||||
res.status.should.equal(200);
|
||||
res.headers.should.have.property('content-disposition', 'attachment; filename="amazing.txt"')
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /files/missing.txt', function(){
|
||||
it('should respond with 404', function(done){
|
||||
request(app)
|
||||
.get('/files/missing.txt')
|
||||
.expect(404,done)
|
||||
.get('/files/missing.txt')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -7,12 +7,12 @@ describe('ejs', function(){
|
||||
it('should respond with html', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
res.should.have.status(200);
|
||||
res.should.have.header('Content-Type', 'text/html; charset=utf-8');
|
||||
res.body.should.include('<li>tobi tobi@learnboost.com</li>');
|
||||
res.body.should.include('<li>loki loki@learnboost.com</li>');
|
||||
res.body.should.include('<li>jane jane@learnboost.com</li>');
|
||||
res.text.should.include('<li>tobi tobi@learnboost.com</li>');
|
||||
res.text.should.include('<li>loki loki@learnboost.com</li>');
|
||||
res.text.should.include('<li>jane jane@learnboost.com</li>');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
var app = require('../../examples/error-pages/app')
|
||||
|
||||
var app = require('../../examples/error-pages')
|
||||
, request = require('../support/http');
|
||||
|
||||
describe('error-pages', function(){
|
||||
describe('GET /', function(){
|
||||
it('should respond with page list', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(/Pages Example/,done)
|
||||
.get('/')
|
||||
.expect(/Pages Example/, done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -14,54 +15,56 @@ describe('error-pages', function(){
|
||||
describe('GET /403', function(){
|
||||
it('should respond with 403', function(done){
|
||||
request(app)
|
||||
.get('/403')
|
||||
.expect(403,done)
|
||||
.get('/403')
|
||||
.expect(403, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /404', function(){
|
||||
it('should respond with 404', function(done){
|
||||
request(app)
|
||||
.get('/404')
|
||||
.expect(404,done)
|
||||
.get('/404')
|
||||
.expect(404, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /500', function(){
|
||||
it('should respond with 500', function(done){
|
||||
request(app)
|
||||
.get('/500')
|
||||
.expect(500,done)
|
||||
.get('/500')
|
||||
.expect(500, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
describe('Accept: application/json',function(){
|
||||
describe('GET /403', function(){
|
||||
it('should respond with 403', function(done){
|
||||
request(app)
|
||||
.get('/403')
|
||||
.set('Accept','application/json')
|
||||
.expect(403,done)
|
||||
.get('/403')
|
||||
.set('Accept','application/json')
|
||||
.expect(403, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /404', function(){
|
||||
it('should respond with 404', function(done){
|
||||
request(app)
|
||||
.get('/404')
|
||||
.set('Accept','application/json')
|
||||
.end(function(res){
|
||||
res.should.have.property('statusCode',200)
|
||||
res.should.have.property('body',JSON.stringify({error:'Not found'}))
|
||||
done()
|
||||
})
|
||||
.get('/404')
|
||||
.set('Accept','application/json')
|
||||
.end(function(err, res){
|
||||
res.body.should.eql({ error: 'Not found' });
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /500', function(){
|
||||
it('should respond with 500', function(done){
|
||||
request(app)
|
||||
.get('/500')
|
||||
.set('Accept','application/json')
|
||||
.expect(500,done)
|
||||
.get('/500')
|
||||
.set('Accept', 'application/json')
|
||||
.expect(500, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -71,31 +74,29 @@ describe('error-pages', function(){
|
||||
describe('GET /403', function(){
|
||||
it('should respond with 403', function(done){
|
||||
request(app)
|
||||
.get('/403')
|
||||
.set('Accept','text/plain')
|
||||
.expect(403,done)
|
||||
.get('/403')
|
||||
.set('Accept','text/plain')
|
||||
.expect(403, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /404', function(){
|
||||
it('should respond with 404', function(done){
|
||||
request(app)
|
||||
.get('/404')
|
||||
.set('Accept','text/plain')
|
||||
.end(function(res){
|
||||
res.should.have.property('statusCode',200)
|
||||
res.should.have.property('body','Not found')
|
||||
done()
|
||||
})
|
||||
.get('/404')
|
||||
.set('Accept', 'text/plain')
|
||||
.expect(200)
|
||||
.expect('Not found', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /500', function(){
|
||||
it('should respond with 500', function(done){
|
||||
request(app)
|
||||
.get('/500')
|
||||
.set('Accept','text/plain')
|
||||
.expect(500,done)
|
||||
.get('/500')
|
||||
.set('Accept','text/plain')
|
||||
.expect(500, done)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
})
|
||||
@@ -1,4 +1,5 @@
|
||||
var app = require('../../examples/error/app')
|
||||
|
||||
var app = require('../../examples/error')
|
||||
, request = require('../support/http');
|
||||
|
||||
describe('error', function(){
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
var app = require('../../examples/multipart/app')
|
||||
, request = require('../support/http')
|
||||
, path = 'test/acceptance/fixtures/grey.png'
|
||||
, fs = require('fs')
|
||||
|
||||
var logo = fs.readFileSync(path)
|
||||
, boundary = '------expressmultipart';
|
||||
|
||||
describe('multipart', function(){
|
||||
describe('GET /', function(){
|
||||
it('should respond with a form', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect(/<form/, done)
|
||||
})
|
||||
})
|
||||
|
||||
describe('POST /', function(){
|
||||
it('should upload logo as multipart', function(done){
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('content-type','multipart/form-data; boundary='+boundary.slice(2))
|
||||
.write(boundary + '\r\n')
|
||||
.write('Content-Disposition: form-data; name="title"\r\n')
|
||||
.write('\r\n')
|
||||
.write('grey\r\n')
|
||||
.write(boundary + '\r\n')
|
||||
.write('Content-Disposition: form-data; name="image"; filename="grey.png"\r\n')
|
||||
.write('Content-Type: image/png\r\n')
|
||||
.write('\r\n')
|
||||
.write(logo+'\r\n')
|
||||
.write(boundary+'--\r\n')
|
||||
.end(function(res){
|
||||
res.body.should.match(/uploaded grey.png/)
|
||||
res.body.should.match(/\(224 Kb\)/)
|
||||
res.body.should.match(/as grey/)
|
||||
done()
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,91 @@
|
||||
|
||||
var request = require('../support/http')
|
||||
, app = require('../../examples/mvc');
|
||||
|
||||
describe('mvc', function(){
|
||||
describe('GET /', function(){
|
||||
it('should redirect to /users', function(done){
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(err, res){
|
||||
res.should.have.status(302);
|
||||
res.headers.location.should.include('/users');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users', function(){
|
||||
it('should display a list of users', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>Users</h1>');
|
||||
res.text.should.include('>TJ<');
|
||||
res.text.should.include('>Guillermo<');
|
||||
res.text.should.include('>Nathan<');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/:id', function(){
|
||||
describe('when present', function(){
|
||||
it('should display the user', function(done){
|
||||
request(app)
|
||||
.get('/user/0')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>TJ <a href="/user/0/edit">edit');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
it('should display the users pets', function(done){
|
||||
request(app)
|
||||
.get('/user/0')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('/pet/0">Tobi');
|
||||
res.text.should.include('/pet/1">Loki');
|
||||
res.text.should.include('/pet/2">Jane');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when not present', function(){
|
||||
it('should 404', function(done){
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.expect(404, done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /user/:id/edit', function(){
|
||||
it('should display the edit form', function(done){
|
||||
request(app)
|
||||
.get('/user/1/edit')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>Guillermo</h1>');
|
||||
res.text.should.include('value="put"');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('PUT /user/:id', function(){
|
||||
it('should update the user', function(done){
|
||||
request(app)
|
||||
.put('/user/1')
|
||||
.send({ user: { name: 'Tobo' }})
|
||||
.end(function(err, res){
|
||||
request(app)
|
||||
.get('/user/1/edit')
|
||||
.end(function(err, res){
|
||||
res.text.should.include('<h1>Tobo</h1>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -37,7 +37,7 @@ describe('resource', function(){
|
||||
describe('DELETE /users/1', function(){
|
||||
it('should respond with users 1 through 3', function(done){
|
||||
request(app)
|
||||
.delete('/users/1')
|
||||
.del('/users/1')
|
||||
.expect(/^destroyed/,done)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
var request = require('supertest')
|
||||
, app = require('../../examples/route-map');
|
||||
|
||||
describe('route-map', function(){
|
||||
describe('GET /users', function(){
|
||||
it('should respond with users', function(done){
|
||||
request(app)
|
||||
.get('/users')
|
||||
.expect('user list', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('DELETE /users', function(){
|
||||
it('should delete users', function(done){
|
||||
request(app)
|
||||
.del('/users')
|
||||
.expect('delete users', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/:id', function(){
|
||||
it('should get a user', function(done){
|
||||
request(app)
|
||||
.get('/users/12')
|
||||
.expect('user 12', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/:id/pets', function(){
|
||||
it('should get a users pets', function(done){
|
||||
request(app)
|
||||
.get('/users/12/pets')
|
||||
.expect('user 12\'s pets', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('GET /users/:id/pets/:pid', function(){
|
||||
it('should get a users pet', function(done){
|
||||
request(app)
|
||||
.del('/users/12/pets/2')
|
||||
.expect('delete 12\'s pet 2', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -24,9 +24,9 @@ describe('web-service', function(){
|
||||
it('should respond users json', function(done){
|
||||
request(app)
|
||||
.get('/api/users?api-key=foo')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
res.should.be.json;
|
||||
res.body.should.equal('[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]');
|
||||
res.text.should.equal('[{"name":"tobi"},{"name":"loki"},{"name":"jane"}]');
|
||||
done();
|
||||
});
|
||||
})
|
||||
@@ -37,10 +37,10 @@ describe('web-service', function(){
|
||||
it('should respond with 404 json', function(done){
|
||||
request(app)
|
||||
.get('/api/something?api-key=bar')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
res.should.have.status(404);
|
||||
res.should.be.json;
|
||||
res.body.should.equal('{"error":"Lame, can\'t find that"}');
|
||||
res.text.should.equal('{"error":"Lame, can\'t find that"}');
|
||||
done();
|
||||
});
|
||||
})
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@ describe('app.all()', function(){
|
||||
});
|
||||
|
||||
request(app)
|
||||
.delete('/tobi')
|
||||
.del('/tobi')
|
||||
.expect(404, done);
|
||||
})
|
||||
})
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ describe('app.del()', function(){
|
||||
});
|
||||
|
||||
request(app)
|
||||
.delete('/tobi')
|
||||
.del('/tobi')
|
||||
.expect('deleted tobi!', done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,10 +6,10 @@ describe('app', function(){
|
||||
describe('.locals(obj)', function(){
|
||||
it('should merge locals', function(){
|
||||
var app = express();
|
||||
Object.keys(app.locals).should.eql(['use', 'settings']);
|
||||
Object.keys(app.locals).should.eql(['settings']);
|
||||
app.locals({ user: 'tobi', age: 1 });
|
||||
app.locals({ age: 2 });
|
||||
Object.keys(app.locals).should.eql(['use', 'settings', 'user', 'age']);
|
||||
Object.keys(app.locals).should.eql(['settings', 'user', 'age']);
|
||||
app.locals.user.should.equal('tobi');
|
||||
app.locals.age.should.equal(2);
|
||||
})
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http');
|
||||
|
||||
describe('app', function(){
|
||||
describe('.locals.use(fn)', function(){
|
||||
it('should run in parallel on res.render()', function(done){
|
||||
var app = express();
|
||||
var calls = [];
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
|
||||
app.locals.first = 'tobi';
|
||||
|
||||
app.locals.use(function(req, res, done){
|
||||
process.nextTick(function(){
|
||||
calls.push('one');
|
||||
res.locals.last = 'holowaychuk';
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
app.locals.use(function(req, res, done){
|
||||
process.nextTick(function(){
|
||||
calls.push('two');
|
||||
res.locals.species = 'ferret';
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
app.use(function(req, res){
|
||||
calls.push('use');
|
||||
res.render('pet.jade');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
calls.should.eql(['use', 'one', 'two']);
|
||||
res.body.should.equal('<p>tobi holowaychuk is a ferret</p>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
|
||||
describe('with arity < 3', function(){
|
||||
it('should done() for you', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.first = 'tobi';
|
||||
|
||||
app.locals.use(function(req, res){
|
||||
res.locals.last = 'holowaychuk';
|
||||
res.locals.species = 'ferret';
|
||||
});
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('pet.jade');
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
res.body.should.equal('<p>tobi holowaychuk is a ferret</p>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should not override res.render() locals', function(done){
|
||||
var app = express();
|
||||
|
||||
app.set('views', __dirname + '/fixtures');
|
||||
app.locals.first = 'tobi';
|
||||
|
||||
app.locals.use(function(req, res){
|
||||
res.locals.last = 'holowaychuk';
|
||||
res.locals.species = 'ferret';
|
||||
});
|
||||
|
||||
app.use(function(req, res){
|
||||
res.render('pet.jade', { last: 'ibot' });
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
res.body.should.equal('<p>tobi ibot is a ferret</p>');
|
||||
done();
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
+4
-11
@@ -12,12 +12,8 @@ describe('OPTIONS', function(){
|
||||
|
||||
request(app)
|
||||
.options('/users')
|
||||
.end(function(res){
|
||||
res.body.should.equal('GET,PUT');
|
||||
res.headers.should.have.property('content-type');
|
||||
res.headers.should.have.property('allow', 'GET,PUT');
|
||||
done();
|
||||
});
|
||||
.expect('GET,PUT')
|
||||
.expect('Allow', 'GET,PUT', done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -35,10 +31,7 @@ describe('app.options()', function(){
|
||||
|
||||
request(app)
|
||||
.options('/users')
|
||||
.end(function(res){
|
||||
res.body.should.equal('GET');
|
||||
res.headers.should.have.property('allow', 'GET');
|
||||
done();
|
||||
});
|
||||
.expect('GET')
|
||||
.expect('Allow', 'GET', done);
|
||||
})
|
||||
})
|
||||
+7
-16
@@ -29,14 +29,11 @@ describe('app', function(){
|
||||
|
||||
request(app)
|
||||
.get('/user/tj')
|
||||
.end(function(res){
|
||||
res.body.should.equal('tj');
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('tj');
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.end(function(res){
|
||||
res.should.have.status(404);
|
||||
done();
|
||||
});
|
||||
.expect(404, done);
|
||||
});
|
||||
|
||||
})
|
||||
@@ -67,15 +64,12 @@ describe('app', function(){
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.end(function(res){
|
||||
res.body.should.equal('123');
|
||||
.end(function(err, res){
|
||||
res.text.should.equal('123');
|
||||
|
||||
request(app)
|
||||
.get('/post/123')
|
||||
.end(function(res){
|
||||
res.body.should.equal('123');
|
||||
done();
|
||||
})
|
||||
.expect('123', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -99,10 +93,7 @@ describe('app', function(){
|
||||
|
||||
request(app)
|
||||
.get('/user/123')
|
||||
.end(function(res){
|
||||
res.body.should.equal('123');
|
||||
done();
|
||||
})
|
||||
.expect('123', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,10 +17,7 @@ describe('app', function(){
|
||||
|
||||
request(app)
|
||||
.get('/foo?name=tobi')
|
||||
.end(function(res){
|
||||
res.body.should.equal('name=tobi');
|
||||
done();
|
||||
});
|
||||
.expect('name=tobi', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -17,10 +17,7 @@ describe('app', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
res.body.should.equal('HEY');
|
||||
done();
|
||||
});
|
||||
.expect('HEY', done);
|
||||
})
|
||||
|
||||
it('should not be influenced by other app protos', function(done){
|
||||
@@ -41,10 +38,7 @@ describe('app', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
res.body.should.equal('HEY');
|
||||
done();
|
||||
});
|
||||
.expect('HEY', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+67
-6
@@ -1,11 +1,14 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http')
|
||||
, assert = require('assert');
|
||||
, assert = require('assert')
|
||||
, methods = require('methods');
|
||||
|
||||
describe('app.router', function(){
|
||||
describe('methods supported', function(){
|
||||
express.methods.forEach(function(method){
|
||||
methods.forEach(function(method){
|
||||
it('should include ' + method.toUpperCase(), function(done){
|
||||
if (method == 'delete') method = 'del';
|
||||
var app = express();
|
||||
var calls = [];
|
||||
|
||||
@@ -265,7 +268,7 @@ describe('app.router', function(){
|
||||
|
||||
request(app)
|
||||
.get('/user/10')
|
||||
.end(function(res){
|
||||
.end(function(err, res){
|
||||
res.statusCode.should.equal(200);
|
||||
request(app)
|
||||
.get('/user/tj')
|
||||
@@ -311,10 +314,26 @@ describe('app.router', function(){
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/api/users/0.json')
|
||||
.expect('users/0 as json', done);
|
||||
.get('/api/users/foo.bar.json')
|
||||
.expect('users/foo.bar as json', done);
|
||||
})
|
||||
|
||||
|
||||
it('should work cross-segment', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/api*', function(req, res){
|
||||
res.send(req.params[0]);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/api')
|
||||
.expect('', function(){
|
||||
request(app)
|
||||
.get('/api/hey')
|
||||
.expect('/hey', done);
|
||||
});
|
||||
})
|
||||
|
||||
it('should allow naming', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -328,6 +347,30 @@ describe('app.router', function(){
|
||||
.expect('users/0.json', done);
|
||||
})
|
||||
|
||||
it('should not be greedy immediately after param', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/user/:user*', function(req, res){
|
||||
res.end(req.params.user);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/122')
|
||||
.expect('122', done);
|
||||
})
|
||||
|
||||
it('should eat everything after /', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/user/:user*', function(req, res){
|
||||
res.end(req.params.user);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/122/aaa')
|
||||
.expect('122', done);
|
||||
})
|
||||
|
||||
it('should span multiple segments', function(done){
|
||||
var app = express();
|
||||
|
||||
@@ -535,4 +578,22 @@ describe('app.router', function(){
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('should allow rewriting of the url', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/account/edit', function(req, res, next){
|
||||
req.user = { id: 12 }; // faux authenticated user
|
||||
req.url = '/user/' + req.user.id + '/edit';
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user/:id/edit', function(req, res){
|
||||
res.send('editing user ' + req.params.id);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/account/edit')
|
||||
.expect('editing user 12', done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
|
||||
var express = require('../')
|
||||
, assert = require('assert')
|
||||
, request = require('./support/http');
|
||||
|
||||
describe('app.routes', function(){
|
||||
it('should be initialized', function(){
|
||||
var app = express();
|
||||
app.routes.should.eql({});
|
||||
})
|
||||
|
||||
it('should be populated with routes', function(){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){});
|
||||
app.get('/user/:id', function(req, res){});
|
||||
|
||||
var get = app.routes.get;
|
||||
get.should.have.length(2);
|
||||
|
||||
get[0].path.should.equal('/');
|
||||
get[0].method.should.equal('get');
|
||||
get[0].regexp.toString().should.equal('/^\\/\\/?$/i');
|
||||
|
||||
get[1].path.should.equal('/user/:id');
|
||||
get[1].method.should.equal('get');
|
||||
})
|
||||
|
||||
it('should be mutable', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){});
|
||||
app.get('/user/:id', function(req, res){});
|
||||
|
||||
var get = app.routes.get;
|
||||
get.should.have.length(2);
|
||||
|
||||
get[0].path.should.equal('/');
|
||||
get[0].method.should.equal('get');
|
||||
get[0].regexp.toString().should.equal('/^\\/\\/?$/i');
|
||||
|
||||
get.splice(1);
|
||||
|
||||
request(app)
|
||||
.get('/user/12')
|
||||
.expect(404, done);
|
||||
})
|
||||
})
|
||||
+4
-7
@@ -12,14 +12,11 @@ describe('exports', function(){
|
||||
express.should.have.property('session');
|
||||
express.should.have.property('static');
|
||||
})
|
||||
|
||||
it('should expose HTTP methods', function(){
|
||||
express.methods.should.be.an.instanceof(Array);
|
||||
express.methods.should.include('get');
|
||||
express.methods.should.include('put');
|
||||
express.methods.should.include('post');
|
||||
|
||||
it('should expose .mime', function(){
|
||||
express.mime.should.equal(require('connect').mime);
|
||||
})
|
||||
|
||||
|
||||
it('should expose Router', function(){
|
||||
express.Router.should.be.a('function');
|
||||
})
|
||||
|
||||
@@ -13,10 +13,7 @@ describe('throw after .end()', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
res.should.have.status(200);
|
||||
res.body.should.equal('yay');
|
||||
done();
|
||||
});
|
||||
.expect('yay')
|
||||
.expect(200, done);
|
||||
})
|
||||
})
|
||||
|
||||
+2
-8
@@ -15,10 +15,7 @@ describe('req', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('If-None-Match', '12345')
|
||||
.end(function(res){
|
||||
res.body.should.equal('true');
|
||||
done();
|
||||
});
|
||||
.expect(304, done);
|
||||
})
|
||||
|
||||
it('should return false when the resource is modified', function(done){
|
||||
@@ -32,10 +29,7 @@ describe('req', function(){
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('If-None-Match', '12345')
|
||||
.end(function(res){
|
||||
res.body.should.equal('false');
|
||||
done();
|
||||
});
|
||||
.expect('false', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
+5
-9
@@ -1,6 +1,7 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http');
|
||||
, request = require('./support/http')
|
||||
, assert = require('assert');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.get(field)', function(){
|
||||
@@ -8,16 +9,14 @@ describe('req', function(){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res){
|
||||
assert(req.get('Something-Else') === undefined);
|
||||
res.end(req.get('Content-Type'));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.end(function(res){
|
||||
res.body.should.equal('application/json');
|
||||
done();
|
||||
});
|
||||
.expect('application/json', done);
|
||||
})
|
||||
|
||||
it('should special-case Referer', function(done){
|
||||
@@ -30,10 +29,7 @@ describe('req', function(){
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Referrer', 'http://foobar.com')
|
||||
.end(function(res){
|
||||
res.body.should.equal('http://foobar.com');
|
||||
done();
|
||||
});
|
||||
.expect('http://foobar.com', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
var express = require('../');
|
||||
|
||||
function req(ret) {
|
||||
return {
|
||||
get: function(){ return ret }
|
||||
, __proto__: express.request
|
||||
};
|
||||
}
|
||||
|
||||
describe('req', function(){
|
||||
describe('.host', function(){
|
||||
it('should return hostname', function(){
|
||||
req('example.com:3000').host.should.equal('example.com');
|
||||
req('example.com').host.should.equal('example.com');
|
||||
})
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,57 @@
|
||||
|
||||
var express = require('../')
|
||||
, request = require('./support/http');
|
||||
|
||||
describe('req', function(){
|
||||
describe('.ip', function(){
|
||||
describe('when X-Forwarded-For is present', function(){
|
||||
describe('when "trust proxy" is enabled', function(){
|
||||
it('should return the client addr', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ip);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('client', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
it('should return the remote address', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ip);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('127.0.0.1', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('when X-Forwarded-For is not present', function(){
|
||||
it('should return the remote address', function(done){
|
||||
var app = express();
|
||||
|
||||
app.enable('trust proxy');
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ip);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.expect('127.0.0.1', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
+28
-9
@@ -5,17 +5,36 @@ var express = require('../')
|
||||
describe('req', function(){
|
||||
describe('.ips', function(){
|
||||
describe('when X-Forwarded-For is present', function(){
|
||||
it('should return an array of the specified addresses', function(done){
|
||||
var app = express();
|
||||
describe('when "trust proxy" is enabled', function(){
|
||||
it('should return an array of the specified addresses', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ips);
|
||||
});
|
||||
app.enable('trust proxy');
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('["p2","p1","client"]', done);
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ips);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('["client","p1","p2"]', done);
|
||||
})
|
||||
})
|
||||
|
||||
describe('when "trust proxy" is disabled', function(){
|
||||
it('should return an empty array', function(done){
|
||||
var app = express();
|
||||
|
||||
app.use(function(req, res, next){
|
||||
res.send(req.ips);
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.set('X-Forwarded-For', 'client, p1, p2')
|
||||
.expect('[]', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
+6
-19
@@ -13,10 +13,7 @@ describe('req', function(){
|
||||
|
||||
request(app)
|
||||
.get('/')
|
||||
.end(function(res){
|
||||
res.body.should.equal('tj');
|
||||
done();
|
||||
})
|
||||
.expect('tj', done);
|
||||
})
|
||||
})
|
||||
|
||||
@@ -30,10 +27,7 @@ describe('req', function(){
|
||||
|
||||
request(app)
|
||||
.get('/?name=tj')
|
||||
.end(function(res){
|
||||
res.body.should.equal('tj');
|
||||
done();
|
||||
})
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
it('should check req.body', function(done){
|
||||
@@ -47,27 +41,20 @@ describe('req', function(){
|
||||
|
||||
request(app)
|
||||
.post('/')
|
||||
.set('Content-Type', 'application/json')
|
||||
.write('{"name":"tj"}')
|
||||
.end(function(res){
|
||||
res.body.should.equal('tj');
|
||||
done();
|
||||
})
|
||||
.send({ name: 'tj' })
|
||||
.expect('tj', done);
|
||||
})
|
||||
|
||||
it('should check req.params', function(done){
|
||||
var app = express();
|
||||
|
||||
app.get('/user/:name', function(req, res){
|
||||
res.end(req.param('name'));
|
||||
res.end(req.param('filter') + req.param('name'));
|
||||
});
|
||||
|
||||
request(app)
|
||||
.get('/user/tj')
|
||||
.end(function(res){
|
||||
res.body.should.equal('tj');
|
||||
done();
|
||||
})
|
||||
.expect('undefinedtj', done);
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário