Compare commits

...

98 Commits
3.2.3 ... 2.5.9

Author SHA1 Message Date
TJ Holowaychuk
e3e7d0def6 Release 2.5.9 2012-04-02 19:21:18 -07:00
TJ Holowaychuk
585039f487 Merge pull request #1037 from pbuyle/2.x
Support PURGE request method.
2012-03-05 13:48:21 -08:00
Pierre Buyle
de4bc808a4 Support PURGE request method. 2012-03-05 16:35:14 -05:00
TJ Holowaychuk
1515d53f55 Merge pull request #1020 from papandreou/2.x
req.accepts: */* wildcard fix
2012-02-22 08:57:49 -08:00
Andreas Lind Petersen
c92c10d8ff req.accepts: */* wildcard fix.
Look for '*/*' anywhere in the Accept header instead of only supporting it when it's the exact value of the header.

IE8 sends:

    Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, application/x-shockwave-flash, application/x-ms-application, application/x-ms-xbap, application/vnd.ms-xpsdocument, application/xaml+xml, */*

for which req.accepts('html') returned false.
2012-02-22 11:04:12 +01:00
TJ Holowaychuk
bc81e7441c Merge pull request #1007 from mmalecki/fix-boilerplate
[2.x] Do not to call `app.address()` before `listening` event
2012-02-13 10:29:14 -08:00
Maciej Małecki
78b874e881 Do not to call app.address() before listening event
According to joyent/node@d3f6b094c7,
call to `net.Server.address()` should happen only after `net.Server`
emits `listening` event.
2012-02-12 16:53:17 +01:00
Tj Holowaychuk
5c50e3a58c Release 2.5.8 2012-02-08 12:05:54 -08:00
Tj Holowaychuk
508e8cc64e upgrade deps. Closes #991 2012-02-08 12:04:51 -08:00
Tj Holowaychuk
5c962ba27b Release 2.5.7 2012-02-06 10:04:33 -08:00
TJ Holowaychuk
9bcb46848b Merge pull request #992 from mscdex/patch-1
Fixed app.all to not execute twice for the same incoming DELETE request
2012-02-06 10:05:07 -08:00
Brian White
4363b20186 Add test to check for multiple DELETEs in app.all 2012-02-04 23:13:36 -05:00
Brian White
77645bcc93 Fix app.all to not execute twice for the same incoming DELETE method request 2012-02-04 02:08:41 -05:00
TJ Holowaychuk
b0a66fe6c9 Merge pull request #985 from Turbo87/patch-2
bin/express: Removed whitespace / Added missing semicolon
2012-01-30 15:09:56 -08:00
Tobias Bieniek
e8dd0a1267 bin/express: Removed whitespace / Added missing semicolon 2012-01-30 23:29:57 +01:00
Tj Holowaychuk
9dd7b7b63a Release 2.5.6 2012-01-13 15:37:56 -08:00
Tj Holowaychuk
ba80b7ba97 updated hamljs dev dep. Closes #953 2012-01-13 14:06:30 -08:00
TJ Holowaychuk
45bb4aff09 Release 2.5.5 2012-01-08 12:31:48 -08:00
TJ Holowaychuk
bb14a86e17 Merge pull request #954 from matthewleon/filename_bug
In express 2.x, filename should be set on cached templates
2012-01-08 12:30:35 -08:00
Matthew Leon Grinshpun
1773681ef8 set filename on cached templates 2012-01-08 12:44:27 +01:00
Tj Holowaychuk
2ac612f14d Release 2.5.4 2012-01-02 08:33:41 -08:00
Tj Holowaychuk
20570fc80d Fixed express(1) eol on 0.4.x. Closes #947 2012-01-02 08:33:05 -08:00
TJ Holowaychuk
92c1c953dc Release 2.5.3 2011-12-30 15:31:10 -08:00
TJ Holowaychuk
8b372b3faa fixed a require 2011-12-30 15:30:00 -08:00
TJ Holowaychuk
da3a743c1b backported req.is() charset fix 2011-12-30 15:29:21 -08:00
TJ Holowaychuk
09c9537381 tweak eol 2011-12-28 10:15:18 -07:00
TJ Holowaychuk
9cf1800731 Merge pull request #942 from atsuya/fix_eol
Use os.platform to determine End-of-line
2011-12-28 09:14:56 -08:00
Atsuya Takagi
0d0fd347ab determine enf of line based on os.platform. 2011-12-28 01:26:53 -08:00
Tj Holowaychuk
5cf8023dc3 connect 1.x dep 2011-12-16 08:51:55 -08:00
Tj Holowaychuk
380a6c5363 Release 2.5.2 2011-12-10 11:07:21 -08:00
Tj Holowaychuk
c047b64ab5 removed less support since compiler() has gone away 2011-12-10 11:05:49 -08:00
Tj Holowaychuk
ae2bcb2615 express(1) LF -> CRLF for windows 2011-12-10 11:05:20 -08:00
Tj Holowaychuk
02cdf0c72b fixed express(1) --version 2011-11-18 08:03:16 -08:00
Tj Holowaychuk
a7520ad00c Release 2.5.1 2011-11-17 11:36:13 -08:00
Tj Holowaychuk
bf7807619d updated connect to 1.8.x 2011-11-17 11:32:56 -08:00
Tj Holowaychuk
b5346005af typo 2011-11-04 08:55:57 -07:00
Tj Holowaychuk
334a8d3fa2 Removed sass.js support from express(1)
sass.js is lame, use stylus
2011-11-04 08:35:10 -07:00
Tj Holowaychuk
7c6c07497a "node": ">= 0.4.1 < 0.7.0" 2011-10-24 16:01:46 -07:00
Tj Holowaychuk
95f6cda9c4 Release 2.5.0 2011-10-24 16:00:46 -07:00
Tj Holowaychuk
e2de941d09 Release 0.5.0 2011-10-24 16:00:35 -07:00
Tj Holowaychuk
f7d67ce766 Added ./routes dir for generated app by default 2011-10-24 15:58:22 -07:00
Tj Holowaychuk
b63f2ca903 Added npm install reminder to express(1) app gen 2011-10-24 15:46:42 -07:00
Tj Holowaychuk
77c2d89be6 fixing tests 2011-10-24 14:59:21 -07:00
Tj Holowaychuk
458097fe7b fixing tests 2011-10-24 14:51:09 -07:00
Tj Holowaychuk
1b25240d36 update expresso/should 2011-10-24 14:47:06 -07:00
Tj Holowaychuk
1cce4d98ff Removed make test-cov since it wont work with node 0.5.x 2011-10-24 14:37:09 -07:00
Tj Holowaychuk
09b5c79073 OCD 2011-10-24 14:36:45 -07:00
Tj Holowaychuk
9348500366 readme 2011-10-24 14:36:30 -07:00
Tj Holowaychuk
0f6a044d98 Fixed express(1) public dir for windows. Closes #866 2011-10-14 08:38:21 -07:00
Tj Holowaychuk
049a557341 remove test stuff 2011-10-07 08:30:31 -07:00
Tj Holowaychuk
94a6f42efd clean up jade example 2011-10-07 08:28:57 -07:00
Tj Holowaychuk
a4aed5f51a jade example using template inheritance 2011-10-07 08:25:43 -07:00
Tj Holowaychuk
3e6f45cb72 update jade dev dep 2011-10-07 08:19:11 -07:00
Tj Holowaychuk
3bd4de7f73 bump express(1) version 2011-10-06 12:05:44 -07:00
Tj Holowaychuk
5c04f85f93 Release 2.4.7 2011-10-05 15:41:50 -07:00
Tj Holowaychuk
77f885d4a0 connect 1.7.x to fix npm issue... 2011-10-05 15:34:22 -07:00
Tj Holowaychuk
c2fc9a83e8 Added mkdirp to express(1). Closes #795 2011-09-21 08:24:37 -07:00
Tj Holowaychuk
d8b20bd2d5 Added simple json-config example
for some who might prefer this. there are benefits to both
2011-09-13 09:00:09 -07:00
Tj Holowaychuk
d3c4fd91c9 typo 2011-09-12 10:39:22 -07:00
Tj Holowaychuk
00e44f6fc9 Fixed res.redirect() HEAD support. [reported by xerox] 2011-09-07 10:01:28 -07:00
Tj Holowaychuk
6692f911ab Fixed req.flash(), only escape args 2011-09-06 15:14:09 -07:00
Tj Holowaychuk
4f0b3f4684 Added shorthand for the parsed request's pathname 2011-09-02 16:41:58 -07:00
Tj Holowaychuk
cbf330c3db Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie] 2011-08-29 08:40:30 -07:00
Tj Holowaychuk
13010987b0 logger for jade example 2011-08-24 03:14:21 -07:00
Tj Holowaychuk
b078481cdb Release 2.5.6 2011-08-22 10:20:04 -07:00
Tj Holowaychuk
3888468a96 Fixed multiple param callback regression. Closes #824 [reported by TroyGoode] 2011-08-22 10:04:46 -07:00
Tj Holowaychuk
a6b70ceca4 fixed tests 2011-08-22 09:47:43 -07:00
Tj Holowaychuk
45757a0f1a bump express(1) version 2011-08-20 04:33:51 -07:00
Tj Holowaychuk
24a4a80ccd Release 2.4.5 2011-08-19 10:12:38 -07:00
Tj Holowaychuk
2fdd906a41 docs 2011-08-19 08:53:02 -07:00
Tj Holowaychuk
71cc6bac22 google analytics 2011-08-19 08:51:54 -07:00
Tj Holowaychuk
3a46660932 typo. Closes #815 2011-08-18 09:03:46 -07:00
Tj Holowaychuk
adca07a858 Refactored Route to use a single array of callbacks 2011-08-17 15:48:27 -07:00
Tj Holowaychuk
6b924bf1df fixed route error handlers when errors are thrown 2011-08-17 14:39:27 -07:00
Tj Holowaychuk
eb88f160b6 Added support for routes to handle errors. Closes #809
currently only the route end-point callbacks
support this, however this will change in the near future
to support route middleware etc
2011-08-17 14:33:11 -07:00
Tj Holowaychuk
01e6df759e Added "basepath" setting to work in conjunction with reverse proxies etc. Closes #805
this will allow you to essentially "trick" the express
app into thinking it is mounted when it is not.
2011-08-17 09:27:28 -07:00
Tj Holowaychuk
1dc4e6fcb4 qs >= 0.3.1 2011-08-17 07:53:16 -07:00
Tj Holowaychuk
fff6951d94 use nextRoute() internally 2011-08-16 18:29:57 -07:00
Tj Holowaychuk
1ebd49af75 Changed: removed .call(self) for route callbacks
not sure why we had this, ive never even used it
and the tests dont cover it, and its slower
2011-08-16 18:28:41 -07:00
Tj Holowaychuk
8c2107e337 Added app.routes.all(). Closes #803
not a huge fan of this API-wise, but at least it is something for now
2011-08-16 17:51:20 -07:00
Tj Holowaychuk
cddc5442f1 typo 2011-08-15 16:31:11 -07:00
Tj Holowaychuk
ac8cb270fe Fixed res.redirect() on windows due to join() usage. Closes #808 2011-08-15 13:46:58 -07:00
Tj Holowaychuk
d54ee58f93 Added support for multiple callbacks for app.param(). Closes #801
you can also make several calls to `app.param()` for the same
param name, which is equivalent to passing multiple in
a single call
2011-08-11 11:07:04 -07:00
Tj Holowaychuk
ce0fa0a3b2 Added test for multiple app.param() calls for the same param 2011-08-11 10:17:23 -07:00
Tj Holowaychuk
e2f41147ed added another test 2011-08-11 10:12:16 -07:00
Tj Holowaychuk
af3f7f0a65 Added test for app.param(fn) 2011-08-11 10:11:00 -07:00
Tj Holowaychuk
a37003945b docs 2011-08-05 19:35:11 -07:00
Tj Holowaychuk
75361fb177 Release 2.4.4 2011-08-05 04:29:52 -07:00
Tj Holowaychuk
21f9b386db empty string 2011-08-05 04:28:31 -07:00
Tj Holowaychuk
271cb16ecd Fixed res.send(204) (again?) 2011-08-05 04:28:08 -07:00
Tj Holowaychuk
803ec213d7 Fixed res.header() intention of a set, even when undefined 2011-08-03 19:59:40 -07:00
Tj Holowaychuk
9e9042f1eb added header.jade to jade example 2011-08-02 08:59:14 -07:00
Tj Holowaychuk
ec4c86f46d fixed * consumption 2011-07-29 09:51:09 -07:00
Arpad Borsos
45f22ec602 specialcase .:format routing to not include a dot in the capture group 2011-07-29 09:43:34 -07:00
Tj Holowaychuk
0e1eb72058 fixed a jade test 2011-07-29 09:32:36 -07:00
Tj Holowaychuk
4690b6cdf2 tweak generated stylus 2011-07-25 11:15:10 -07:00
Tj Holowaychuk
22204a5ce1 Fixed res.send(204) support. Closes #771 2011-07-22 08:34:13 -07:00
Tj Holowaychuk
1ec16c0450 qs >= 0.3.0 2011-07-19 12:08:50 -07:00
40 changed files with 1185 additions and 380 deletions

1
.gitignore vendored
View File

@@ -11,3 +11,4 @@ lib-cov
benchmarks/graphs
testing.js
node_modules/
testing

View File

@@ -1,4 +1,96 @@
2.5.9/ 2012-04-02
==================
* Added support for PURGE request method [pbuyle]
* Fixed `express(1)` generated app `app.address()` before `listening` [mmalecki]
2.5.8 / 2012-02-08
==================
* Update mkdirp dep. Closes #991
2.5.7 / 2012-02-06
==================
* Fixed `app.all` duplicate DELETE requests [mscdex]
2.5.6 / 2012-01-13
==================
* Updated hamljs dev dep. Closes #953
2.5.5 / 2012-01-08
==================
* Fixed: set `filename` on cached templates [matthewleon]
2.5.4 / 2012-01-02
==================
* Fixed `express(1)` eol on 0.4.x. Closes #947
2.5.3 / 2011-12-30
==================
* Fixed `req.is()` when a charset is present
2.5.2 / 2011-12-10
==================
* Fixed: express(1) LF -> CRLF for windows
2.5.1 / 2011-11-17
==================
* Changed: updated connect to 1.8.x
* Removed sass.js support from express(1)
2.5.0 / 2011-10-24
==================
* Added ./routes dir for generated app by default
* Added npm install reminder to express(1) app gen
* Added 0.5.x support
* Removed `make test-cov` since it wont work with node 0.5.x
* Fixed express(1) public dir for windows. Closes #866
2.4.7 / 2011-10-05
==================
* Added mkdirp to express(1). Closes #795
* Added simple _json-config_ example
* Added shorthand for the parsed request's pathname via `req.path`
* Changed connect dep to 1.7.x to fix npm issue...
* Fixed `res.redirect()` __HEAD__ support. [reported by xerox]
* Fixed `req.flash()`, only escape args
* Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie]
2.4.6 / 2011-08-22
==================
* Fixed multiple param callback regression. Closes #824 [reported by TroyGoode]
2.4.5 / 2011-08-19
==================
* Added support for routes to handle errors. Closes #809
* Added `app.routes.all()`. Closes #803
* Added "basepath" setting to work in conjunction with reverse proxies etc.
* Refactored `Route` to use a single array of callbacks
* Added support for multiple callbacks for `app.param()`. Closes #801
Closes #805
* Changed: removed .call(self) for route callbacks
* Dependency: `qs >= 0.3.1`
* Fixed `res.redirect()` on windows due to `join()` usage. Closes #808
2.4.4 / 2011-08-05
==================
* Fixed `res.header()` intention of a set, even when `undefined`
* Fixed `*`, value no longer required
* Fixed `res.send(204)` support. Closes #771
2.4.3 / 2011-07-14
==================

View File

@@ -1,16 +1,10 @@
DOCS = $(shell find docs/*.md)
HTMLDOCS =$(DOCS:.md=.html)
HTMLDOCS = $(DOCS:.md=.html)
TESTS = $(shell find test/*.test.js)
test:
@NODE_ENV=test ./node_modules/.bin/expresso \
-I lib \
$(TESTFLAGS) \
$(TESTS)
test-cov:
@TESTFLAGS=--cov $(MAKE) test
@NODE_ENV=test ./node_modules/.bin/expresso $(TESTS)
docs: $(HTMLDOCS)
@ echo "... generating TOC"
@@ -32,4 +26,4 @@ site:
docclean:
rm -f docs/*.{1,html}
.PHONY: site test test-cov docs docclean
.PHONY: site test docs docclean

View File

@@ -95,7 +95,9 @@ The following are the major contributors of Express (in no specific order).
Express 1.x is compatible with node 0.2.x and connect < 1.0.
Express 2.x is compatible with node 0.4.x and connect 1.x
Express 2.x is compatible with node 0.4.x or 0.6.x, and connect 1.x
Express 3.x (master) will be compatible with node 0.6.x and connect 2.x
## Viewing Examples

View File

@@ -5,13 +5,15 @@
*/
var fs = require('fs')
, exec = require('child_process').exec;
, os = require('os')
, exec = require('child_process').exec
, mkdirp = require('mkdirp');
/**
* Framework version.
*/
var version = '2.4.3';
var version = '2.5.8';
/**
* Add session support.
@@ -25,6 +27,14 @@ var sessions = false;
var cssEngine;
/**
* End-of-line code.
*/
var eol = os.platform
? ('win32' == os.platform() ? '\r\n' : '\n')
: '\n';
/**
* Template engine to utilize.
*/
@@ -42,11 +52,26 @@ var usage = ''
+ ' Options:\n'
+ ' -s, --sessions add session support\n'
+ ' -t, --template <engine> add template <engine> support (jade|ejs). default=jade\n'
+ ' -c, --css <engine> add stylesheet <engine> support (less|sass|stylus). default=plain css\n'
+ ' -c, --css <engine> add stylesheet <engine> support (stylus). default=plain css\n'
+ ' -v, --version output framework version\n'
+ ' -h, --help output help information\n'
;
/**
* Routes index template.
*/
var index = [
''
, '/*'
, ' * GET home page.'
, ' */'
, ''
, 'exports.index = function(req, res){'
, ' res.render(\'index\', { title: \'Express\' })'
, '};'
].join(eol);
/**
* Jade layout template.
*/
@@ -58,7 +83,7 @@ var jadeLayout = [
, ' title= title'
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
, ' body!= body'
].join('\n');
].join(eol);
/**
* Jade index template.
@@ -67,7 +92,7 @@ var jadeLayout = [
var jadeIndex = [
'h1= title'
, 'p Welcome to #{title}'
].join('\n');
].join(eol);
/**
* EJS layout template.
@@ -84,7 +109,7 @@ var ejsLayout = [
, ' <%- body %>'
, ' </body>'
, '</html>'
].join('\n');
].join(eol);
/**
* EJS index template.
@@ -93,7 +118,7 @@ var ejsLayout = [
var ejsIndex = [
'<h1><%= title %></h1>'
, '<p>Welcome to <%= title %></p>'
].join('\n');
].join(eol);
/**
* Default css template.
@@ -108,34 +133,7 @@ var css = [
, 'a {'
, ' color: #00B7FF;'
, '}'
].join('\n');
/**
* Default less template.
*/
var less = [
'body {'
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join('\n');
/**
* Default sass template.
*/
var sass = [
'body'
, ' :padding 50px'
, ' :font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, 'a'
, ' :color #00B7FF'
].join('\n');
].join(eol);
/**
* Default stylus template.
@@ -143,11 +141,11 @@ var sass = [
var stylus = [
'body'
, ' padding 50px'
, ' font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, ' padding: 50px'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, 'a'
, ' color #00B7FF'
].join('\n');
, ' color: #00B7FF'
].join(eol);
/**
* App template.
@@ -159,7 +157,8 @@ var app = [
, ' * Module dependencies.'
, ' */'
, ''
, 'var express = require(\'express\');'
, 'var express = require(\'express\')'
, ' , routes = require(\'./routes\');'
, ''
, 'var app = module.exports = express.createServer();'
, ''
@@ -175,25 +174,22 @@ var app = [
, '});'
, ''
, 'app.configure(\'development\', function(){'
, ' app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); '
, ' app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));'
, '});'
, ''
, 'app.configure(\'production\', function(){'
, ' app.use(express.errorHandler()); '
, ' app.use(express.errorHandler());'
, '});'
, ''
, '// Routes'
, ''
, 'app.get(\'/\', function(req, res){'
, ' res.render(\'index\', {'
, ' title: \'Express\''
, ' });'
, 'app.get(\'/\', routes.index);'
, ''
, 'app.listen(3000, function(){'
, ' console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);'
, '});'
, ''
, 'app.listen(3000);'
, 'console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);'
, ''
].join('\n');
].join(eol);
// Parse arguments
@@ -259,7 +255,16 @@ while (args.length) {
*/
function createApplicationAt(path) {
console.log();
process.on('exit', function(){
console.log();
console.log(' dont forget to install dependencies:');
console.log(' $ cd %s && npm install', path);
console.log();
});
mkdir(path, function(){
mkdir(path + '/public');
mkdir(path + '/public/javascripts');
mkdir(path + '/public/images');
mkdir(path + '/public/stylesheets', function(){
@@ -267,16 +272,15 @@ function createApplicationAt(path) {
case 'stylus':
write(path + '/public/stylesheets/style.styl', stylus);
break;
case 'less':
write(path + '/public/stylesheets/style.less', less);
break;
case 'sass':
write(path + '/public/stylesheets/style.sass', sass);
break;
default:
write(path + '/public/stylesheets/style.css', css);
}
});
mkdir(path + '/routes', function(){
write(path + '/routes/index.js', index);
});
mkdir(path + '/views', function(){
switch (templateEngine) {
case 'ejs':
@@ -292,12 +296,8 @@ function createApplicationAt(path) {
// CSS Engine support
switch (cssEngine) {
case 'sass':
case 'less':
app = app.replace('{css}', '\n app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'' + cssEngine + '\'] }));');
break;
case 'stylus':
app = app.replace('{css}', '\n app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
app = app.replace('{css}', eol + ' app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
break;
default:
app = app.replace('{css}', '');
@@ -305,22 +305,22 @@ function createApplicationAt(path) {
// Session support
app = app.replace('{sess}', sessions
? '\n app.use(express.cookieParser());\n app.use(express.session({ secret: \'your secret here\' }));'
? eol + ' app.use(express.cookieParser());' + eol + ' app.use(express.session({ secret: \'your secret here\' }));'
: '');
// Template support
app = app.replace(':TEMPLATE', templateEngine);
// package.json
var json = '{\n';
json += ' "name": "application-name"\n';
json += ' , "version": "0.0.1"\n';
json += ' , "private": true\n';
json += ' , "dependencies": {\n';
json += ' "express": "' + version + '"\n';
if (cssEngine) json += ' , "' + cssEngine + '": ">= 0.0.1"\n';
if (templateEngine) json += ' , "' + templateEngine + '": ">= 0.0.1"\n';
json += ' }\n';
var json = '{' + eol;
json += ' "name": "application-name"' + eol;
json += ' , "version": "0.0.1"' + eol;
json += ' , "private": true' + eol;
json += ' , "dependencies": {' + eol;
json += ' "express": "' + version + '"' + eol;
if (cssEngine) json += ' , "' + cssEngine + '": ">= 0.0.1"' + eol;
if (templateEngine) json += ' , "' + templateEngine + '": ">= 0.0.1"' + eol;
json += ' }' + eol;
json += '}';
@@ -398,9 +398,9 @@ function prompt(msg, fn) {
*/
function mkdir(path, fn) {
exec('mkdir -p ' + path, function(err){
mkdirp(path, 0755, function(err){
if (err) throw err;
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
console.log(' \033[36mcreate\033[0m : ' + path);
fn && fn();
});
}

View File

@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;

View File

@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;

View File

@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;

View File

@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
@@ -180,8 +193,6 @@
</a>
<div id="wrapper">
<div id="container"><ul id="toc">
</ul><ul id="toc">
<li><a href="#installation">Installation</a></li>
<li><a href="#creating-a server">Creating A Server</a></li>
<li><a href="#creating-an https server">Creating An HTTPS Server</a></li>
@@ -217,6 +228,7 @@
<li><a href="#res.sendfile()">sendfile()</a></li>
<li><a href="#res.download()">download()</a></li>
<li><a href="#res.send()">send()</a></li>
<li><a href="#res.json()">json()</a></li>
<li><a href="#res.redirect()">redirect()</a></li>
<li><a href="#res.cookie()">cookie()</a></li>
<li><a href="#res.clearcookie()">clearCookie()</a></li>
@@ -335,6 +347,13 @@ app.configure('production', function(){
});
</code></pre>
<p>For similar environments you may also pass several env strings:</p>
<pre><code>app.configure('stage', 'prod', function(){
// config
});
</code></pre>
<p>For internal and arbitrary settings Express provides the <em>set(key[, val])</em>, <em>enable(key)</em>, <em>disable(key)</em> methods:</p>
<pre><code> app.configure(function(){
@@ -365,12 +384,14 @@ app.configure('production', function(){
<p>Express supports the following settings out of the box:</p>
<ul>
<li><em>home</em> Application base path used for <em>res.redirect()</em> and transparently handling mounted apps.</li>
<li><em>basepath</em> Application base path used for <em>res.redirect()</em> and transparently handling mounted apps.</li>
<li><em>views</em> Root views directory defaulting to <strong>CWD/views</strong></li>
<li><em>view engine</em> Default view engine name for views rendered without extensions</li>
<li><em>view options</em> An object specifying global view options</li>
<li><em>view cache</em> Enable view caching (enabled in production)</li>
<li><em>case sensitive routes</em> Enable case-sensitive routing</li>
<li><em>strict routing</em> When enabled trailing slashes are no longer ignored</li>
<li><em>jsonp callback</em> Enable <em>res.send()</em> / <em>res.json()</em> transparent jsonp support</li>
</ul>
@@ -1268,6 +1289,19 @@ it will not be set again.</p>
<p>Note that this method <em>end()</em>s the response, so you will want to use node&rsquo;s <em>res.write()</em> for multiple writes or streaming.</p>
<h3 id="res.json()">res.json(obj[, headers|status[, status]])</h3>
<p> Send a JSON response with optional <em>headers</em> and <em>status</em>. This method
is ideal for JSON-only APIs, however <em>res.send(obj)</em> will send JSON as
well, though not ideal for cases when you want to send for example a string
as JSON, since the default for <em>res.send(string)</em> is text/html.</p>
<pre><code>res.json(null);
res.json({ user: 'tj' });
res.json('oh noes!', 500);
res.json('I dont have that', 404);
</code></pre>
<h3 id="res.redirect()">res.redirect(url[, status])</h3>
<p>Redirect to the given <em>url</em> with a default response <em>status</em> of 302.</p>
@@ -1281,11 +1315,12 @@ res.redirect('back');
<p>Express supports &ldquo;redirect mapping&rdquo;, which by default provides <em>home</em>, and <em>back</em>.
The <em>back</em> map checks the <em>Referrer</em> and <em>Referer</em> headers, while <em>home</em> utilizes
the &ldquo;home&rdquo; setting and defaults to &ldquo;/&rdquo;.</p>
the &ldquo;basepath&rdquo; setting and defaults to &ldquo;/&rdquo;.</p>
<h3 id="res.cookie()">res.cookie(name, val[, options])</h3>
<p>Sets the given cookie <em>name</em> to <em>val</em>, with options <em>httpOnly</em>, <em>secure</em>, <em>expires</em> etc.</p>
<p>Sets the given cookie <em>name</em> to <em>val</em>, with options <em>httpOnly</em>, <em>secure</em>, <em>expires</em> etc. The <em>path</em> option defaults to the app&rsquo;s &ldquo;basepath&rdquo; setting, which
is typically &ldquo;/&rdquo;.</p>
<pre><code>// "Remember me" for 15 minutes
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000), httpOnly: true });
@@ -1307,7 +1342,8 @@ app.get('/', function(req, res){
<h3 id="res.clearcookie()">res.clearCookie(name[, options])</h3>
<p>Clear cookie <em>name</em> by setting &ldquo;expires&rdquo; far in the past.</p>
<p>Clear cookie <em>name</em> by setting &ldquo;expires&rdquo; far in the past. Much like
<em>res.cookie()</em> the <em>path</em> option also defaults to the &ldquo;basepath&rdquo; setting.</p>
<pre><code>res.clearCookie('rememberme');
</code></pre>
@@ -1324,6 +1360,16 @@ automatically, however otherwise a response of <em>200</em> and <em>text/html</e
res.render('index', { layout: false, user: user });
</code></pre>
<p>This <em>options</em> object is also considered an &ldquo;options&rdquo; object. For example
when you pass the <em>status</em> local, it&rsquo;s not only available to the view, it
sets the response status to this number. This is also useful if a template
engine accepts specific options, such as <em>debug</em>, or <em>compress</em>. Below
is an example of how one might render an error page, passing the <em>status</em> for
display, as well as it setting <em>res.statusCode</em>.</p>
<pre><code> res.render('error', { status: 500, message: 'Internal Server Error' });
</code></pre>
<h3 id="res.partial()">res.partial(view[, options])</h3>
<p>Render <em>view</em> partial with the given <em>options</em>. This method is always available
@@ -1596,7 +1642,6 @@ as well as the <em>name()</em> function exposed.</p>
<p>Express also provides a few locals by default:</p>
<pre><code>- `settings` the app's settings object
- `filename` the view's filename
- `layout(path)` specify the layout from within a view
</code></pre>

View File

@@ -104,7 +104,7 @@ This is _very_ important, as many caching mechanisms are _only enabled_ when in
Express supports the following settings out of the box:
* _home_ Application base path used for _res.redirect()_ and transparently handling mounted apps.
* _basepath_ Application base path used for _res.redirect()_ and transparently handling mounted apps.
* _views_ Root views directory defaulting to **CWD/views**
* _view engine_ Default view engine name for views rendered without extensions
* _view options_ An object specifying global view options
@@ -947,11 +947,11 @@ Redirect to the given _url_ with a default response _status_ of 302.
Express supports "redirect mapping", which by default provides _home_, and _back_.
The _back_ map checks the _Referrer_ and _Referer_ headers, while _home_ utilizes
the "home" setting and defaults to "/".
the "basepath" setting and defaults to "/".
### res.cookie(name, val[, options])
Sets the given cookie _name_ to _val_, with options _httpOnly_, _secure_, _expires_ etc. The _path_ option defaults to the app's "home" setting, which
Sets the given cookie _name_ to _val_, with options _httpOnly_, _secure_, _expires_ etc. The _path_ option defaults to the app's "basepath" setting, which
is typically "/".
// "Remember me" for 15 minutes
@@ -972,7 +972,7 @@ To parse incoming _Cookie_ headers, use the _cookieParser_ middleware, which pro
### res.clearCookie(name[, options])
Clear cookie _name_ by setting "expires" far in the past. Much like
_res.cookie()_ the _path_ option also defaults to the "home" setting.
_res.cookie()_ the _path_ option also defaults to the "basepath" setting.
res.clearCookie('rememberme');

View File

@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;
@@ -233,7 +246,7 @@ app.listen(3000);
<h2>Third-Party Modules</h2>
<p>The following modules compliment or extend Express directly:</p>
<p>The following modules complement or extend Express directly:</p>
<ul>
<li><a href="http://github.com/visionmedia/express-resource">express-resource</a> provides resourceful routing</li>
@@ -254,10 +267,6 @@ app.listen(3000);
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
<li>Visit the <a href="http://github.com/visionmedia/express/wiki">Wiki</a></li>
<li><a href="http://hideyukisaito.com/doc/expressjs/">日本語ドキュメンテーション</a> by <a href="https://github.com/hideyukisaito">hideyukisaito</a></li>
<li>Screencast &ndash; <a href="http://bit.ly/eRYu0O">Introduction</a></li>
<li>Screencast &ndash; <a href="http://bit.ly/dU13Fx">View Partials</a></li>
<li>Screencast &ndash; <a href="http://bit.ly/hX4IaH">Route Specific Middleware</a></li>
<li>Screencast &ndash; <a href="http://bit.ly/eNqmVs">Route Path Placeholder Preconditions</a></li>
</ul>
</div>

View File

@@ -34,7 +34,7 @@ The following are the major contributors of Express (in no specific order).
## Third-Party Modules
The following modules compliment or extend Express directly:
The following modules complement or extend Express directly:
* [express-resource](http://github.com/visionmedia/express-resource) provides resourceful routing
* [express-messages](http://github.com/visionmedia/express-messages) flash message notification rendering

View File

@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;

View File

@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;

View File

@@ -3,6 +3,19 @@
<title>Express - node web framework</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script type="text/javascript">
var _gaq = _gaq || [];
_gaq.push(['_setAccount', 'UA-25235225-1']);
_gaq.push(['_trackPageview']);
(function() {
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
})();
</script>
<style>
#tagline {
margin-left: 75px;

View File

@@ -13,13 +13,17 @@ var pub = __dirname + '/public';
// and then serve with connect's staticProvider
var app = express.createServer();
app.use(express.logger('dev'));
app.use(express.compiler({ src: pub, enable: ['sass'] }));
app.use(app.router);
app.use(express.static(pub));
app.use(express.errorHandler({ dump: true, stack: true }));
// Optional since express defaults to CWD/views
// we're using jade's template inheritance, so we dont need
// the express layout concept
app.set('view options', { layout: false });
// Optional since express defaults to CWD/views
app.set('views', __dirname + '/views');
// Set our default template engine to "jade"
@@ -34,30 +38,15 @@ function User(name, email) {
// Dummy users
var users = [
new User('tj', 'tj@vision-media.ca')
, new User('ciaran', 'ciaranj@gmail.com')
, new User('aaron', 'aaron.heckmann+github@gmail.com')
new User('Tobi', 'tobi@learnboost.com')
, new User('Loki', 'loki@learnboost.com')
, new User('Jane', 'jane@learnboost.com')
];
app.get('/', function(req, res){
res.render('users', { users: users });
});
app.get('/users/callback', function(req, res){
// a callback is also accepted
res.partial('users/user', users, function(err, html){
if (err) throw err;
res.send(html);
});
});
app.get('/users', function(req, res){
// we can use res.partial() as if
// we were in a view, utilizing the same api
// to render a fragment
res.partial('users/user', users);
});
app.get('/users/list', function(req, res){
// use "object" to utilize the name deduced from
// the view filename. The examples below are equivalent

View File

@@ -1,6 +1,8 @@
!!!
doctype 5
html
head
title Jade Example
block title
title Jade Example
link(rel="stylesheet", href="/stylesheets/style.css")
body!= body
body
block content

View File

@@ -1,3 +1,12 @@
- if (users.length)
h1 Users
#users!= partial('user', users)
extends ../layout
block title
title Users
block content
if users.length
h1 Users
#users
for user in users
include ./user

View File

@@ -1,3 +1,4 @@
ul#users
- each user in list
li!= partial('user', user)
for user in list
li
include ./user

View File

@@ -0,0 +1,26 @@
/**
* Module dependencies.
*/
var express = require('../../');
app = express.createServer();
// load the config for this environment (NODE_ENV)
var config = require('./config')[app.settings.env];
// apply settings
for (var key in config) app.set(key, config[key]);
// apply middleware
config.middleware.forEach(app.use.bind(app));
app.get('/', function(req, res){
res.render('index', { layout: false });
});
app.listen(3000);

View File

@@ -0,0 +1,19 @@
// ok so it's not JSON, but close enough :)
var express = require('../../');
exports.development = {
'view engine': 'jade'
, 'views': __dirname + '/views'
, 'title': 'My Site'
, 'middleware': [
express.logger('dev')
, app.router
, express.static(__dirname + '/public')
]
};
exports.production = {
};

View File

@@ -0,0 +1,4 @@
html
body
h1 #{settings.title}
p Simple example

View File

@@ -28,7 +28,7 @@ var exports = module.exports = connect.middleware;
* Framework version.
*/
exports.version = '2.4.3';
exports.version = '2.5.9';
/**
* Shortcut for `new Server(...)`.

View File

@@ -1,4 +1,3 @@
/*!
* Express - HTTPServer
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
@@ -66,7 +65,6 @@ app.init = function(middleware){
this.dynamicViewHelpers = {};
this.errorHandlers = [];
this.set('home', '/');
this.set('env', process.env.NODE_ENV || 'development');
// expose objects to each other
@@ -208,7 +206,7 @@ app.registerErrorHandlers = function(){
*/
app.use = function(route, middleware){
var app, home, handle;
var app, base, handle;
if ('string' != typeof route) {
middleware = route, route = '/';
@@ -234,9 +232,10 @@ app.use = function(route, middleware){
// mounted an app, invoke the hook
// and adjust some settings
if (app) {
home = app.set('home');
if ('/' == home) home = '';
app.set('home', app.route + home);
base = this.set('basepath') || this.route;
if ('/' == base) base = '';
base = base + (app.set('basepath') || app.route);
app.set('basepath', base);
app.parent = this;
if (app.__mounted) app.__mounted.call(app, this);
}
@@ -312,7 +311,7 @@ app.dynamicHelpers = function(obj){
};
/**
* Map the given param placeholder `name`(s) to the given callback `fn`.
* Map the given param placeholder `name`(s) to the given callback(s).
*
* Param mapping is used to provide pre-conditions to routes
* which us normalized placeholders. This callback has the same
@@ -332,6 +331,38 @@ app.dynamicHelpers = function(obj){
* });
* });
*
* Passing a single function allows you to map logic
* to the values passed to `app.param()`, for example
* this is useful to provide coercion support in a concise manner.
*
* The following example maps regular expressions to param values
* ensuring that they match, otherwise passing control to the next
* route:
*
* app.param(function(name, regexp){
* if (regexp instanceof RegExp) {
* return function(req, res, next, val){
* var captures;
* if (captures = regexp.exec(String(val))) {
* req.params[name] = captures;
* next();
* } else {
* next('route');
* }
* }
* }
* });
*
* We can now use it as shown below, where "/commit/:commit" expects
* that the value for ":commit" is at 5 or more digits. The capture
* groups are then available as `req.params.commit` as we defined
* in the function above.
*
* app.param('commit', /^\d{5,}$/);
*
* For more of this useful functionality take a look
* at [express-params](http://github.com/visionmedia/express-params).
*
* @param {String|Array|Function} name
* @param {Function} fn
* @return {Server} for chaining
@@ -339,18 +370,25 @@ app.dynamicHelpers = function(obj){
*/
app.param = function(name, fn){
var self = this
, fns = [].slice.call(arguments, 1);
// array
if (Array.isArray(name)) {
name.forEach(function(name){
this.param(name, fn);
}, this);
fns.forEach(function(fn){
self.param(name, fn);
});
});
// param logic
} else if ('function' == typeof name) {
this.routes.param(name);
// single
} else {
if (':' == name[0]) name = name.substr(1);
this.routes.param(name, fn);
fns.forEach(function(fn){
self.routes.param(name, fn);
});
}
return this;
@@ -532,7 +570,7 @@ app.all = function(path){
var args = arguments;
if (1 == args.length) return this.routes.lookup('all', path);
methods.forEach(function(method){
if ('all' == method) return;
if ('all' == method || 'del' == method) return;
app[method].apply(this, args);
}, this);
return this;

View File

@@ -12,6 +12,7 @@
var http = require('http')
, req = http.IncomingMessage.prototype
, utils = require('./utils')
, parse = require('url').parse
, mime = require('mime');
/**
@@ -87,6 +88,17 @@ req.get = function(field, param){
return RegExp.$1 || RegExp.$2;
};
/**
* Short-hand for `require('url').parse(req.url).pathname`.
*
* @return {String}
* @api public
*/
req.__defineGetter__('path', function(){
return parse(this.url).pathname;
});
/**
* Check if the _Accept_ header is present, and includes the given `type`.
*
@@ -123,8 +135,8 @@ req.accepts = function(type){
// normalize extensions ".json" -> "json"
if (type && '.' == type[0]) type = type.substr(1);
// when Accept does not exist, or is '*/*' return true
if (!accept || '*/*' == accept) {
// when Accept does not exist, or contains '*/*' return true
if (!accept || ~accept.indexOf('*/*')) {
return true;
} else if (type) {
// allow "html" vs "text/html" etc
@@ -217,10 +229,10 @@ req.flash = function(type, msg){
, args = arguments
, formatters = this.app.flashFormatters || {};
formatters.__proto__ = flashFormatters;
msg = utils.miniMarkdown(utils.escape(msg));
msg = utils.miniMarkdown(msg);
msg = msg.replace(/%([a-zA-Z])/g, function(_, format){
var formatter = formatters[format];
if (formatter) return formatter(args[i++]);
if (formatter) return formatter(utils.escape(args[i++]));
});
return (msgs[type] = msgs[type] || []).push(msg);
} else if (type) {
@@ -280,16 +292,18 @@ req.flash = function(type, msg){
req.is = function(type){
var fn = this.app.is(type);
if (fn) return fn(this);
var contentType = this.headers['content-type'];
if (!contentType) return;
var ct = this.headers['content-type'];
if (!ct) return false;
ct = ct.split(';')[0];
if (!~type.indexOf('/')) type = mime.lookup(type);
if (~type.indexOf('*')) {
type = type.split('/')
contentType = contentType.split('/');
if ('*' == type[0] && type[1] == contentType[1]) return true;
if ('*' == type[1] && type[0] == contentType[0]) return true;
type = type.split('/');
ct = ct.split('/');
if ('*' == type[0] && type[1] == ct[1]) return true;
if ('*' == type[1] && type[0] == ct[0]) return true;
return false;
}
return !! ~contentType.indexOf(type);
return !! ~ct.indexOf(type);
};
// Callback for isXMLHttpRequest / xhr

View File

@@ -52,7 +52,7 @@ res.send = function(body, headers, status){
status = status || this.statusCode;
// allow 0 args as 204
if (!arguments.length || undefined === body) body = status = 204;
if (!arguments.length || undefined === body) status = 204;
// determine content type
switch (typeof body) {
@@ -81,7 +81,7 @@ res.send = function(body, headers, status){
}
// populate Content-Length
if (!this.header('Content-Length')) {
if (undefined !== body && !this.header('Content-Length')) {
this.header('Content-Length', Buffer.isBuffer(body)
? body.length
: Buffer.byteLength(body));
@@ -100,11 +100,12 @@ res.send = function(body, headers, status){
if (204 == status || 304 == status) {
this.removeHeader('Content-Type');
this.removeHeader('Content-Length');
body = '';
}
// respond
this.statusCode = status;
this.end('HEAD' == this.req.method ? undefined : body);
this.end('HEAD' == this.req.method ? null : body);
return this;
};
@@ -270,17 +271,14 @@ res.download = function(path, filename, fn, fn2){
*
* @param {String} name
* @param {String} val
* @return {String}
* @return {ServerResponse} for chaining
* @api public
*/
res.header = function(name, val){
if (val === undefined) {
return this.getHeader(name);
} else {
this.setHeader(name, val);
return val;
}
if (1 == arguments.length) return this.getHeader(name);
this.setHeader(name, val);
return this;
};
/**
@@ -304,7 +302,7 @@ res.clearCookie = function(name, options){
* Options:
*
* - `maxAge` max-age in milliseconds, converted to `expires`
* - `path` defaults to the "home" setting which is typically "/"
* - `path` defaults to the "basepath" setting which is typically "/"
*
* Examples:
*
@@ -323,7 +321,7 @@ res.clearCookie = function(name, options){
res.cookie = function(name, val, options){
options = options || {};
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
if (undefined === options.path) options.path = this.app.set('home');
if (undefined === options.path) options.path = this.app.set('basepath');
var cookie = utils.serializeCookie(name, val, options);
this.header('Set-Cookie', cookie);
};
@@ -335,8 +333,8 @@ res.cookie = function(name, val, options){
* The given `url` can also be the name of a mapped url, for
* example by default express supports "back" which redirects
* to the _Referrer_ or _Referer_ headers or the application's
* "home" setting. Express also supports "home" out of the box,
* which can be set via `app.set('home', '/blog');`, and defaults
* "basepath" setting. Express also supports "basepath" out of the box,
* which can be set via `app.set('basepath', '/blog');`, and defaults
* to '/'.
*
* Redirect Mapping:
@@ -376,8 +374,9 @@ res.cookie = function(name, val, options){
res.redirect = function(url, status){
var app = this.app
, req = this.req
, base = app.set('home') || '/'
, base = app.set('basepath') || app.route
, status = status || 302
, head = 'HEAD' == req.method
, body;
// Setup redirect map
@@ -400,14 +399,13 @@ res.redirect = function(url, status){
// Relative
if (!~url.indexOf('://')) {
// Respect mount-point
if (app.route) url = join(app.route, url);
if ('/' != base && 0 != url.indexOf(base)) url = base + url;
// Absolute
var host = req.headers.host
, tls = req.connection.encrypted;
url = 'http' + (tls ? 's' : '') + '://' + host + url;
}
// Support text/{plain,html} by default
if (req.accepts('html')) {
@@ -421,7 +419,7 @@ res.redirect = function(url, status){
// Respond
this.statusCode = status;
this.header('Location', url);
this.end(body);
this.end(head ? null : body);
};
/**

View File

@@ -79,10 +79,23 @@ Router.prototype.param = function(name, fn){
throw new Error('invalid param() call for ' + name + ', got ' + fn);
}
this.params[name] = fn;
(this.params[name] = this.params[name] || []).push(fn);
return this;
};
/**
* Return a `Collection` of all routes defined.
*
* @return {Collection}
* @api public
*/
Router.prototype.all = function(){
return this.find(function(){
return true;
});
};
/**
* Remove the given `route`, returns
* a bool indicating if the route was present
@@ -182,14 +195,18 @@ Router.prototype._dispatch = function(req, res, next){
, self = this;
// route dispatch
(function pass(i){
var route
(function pass(i, err){
var paramCallbacks
, paramIndex = 0
, paramVal
, route
, keys
, key
, ret;
// match next route
function nextRoute() {
pass(req._route_index + 1);
function nextRoute(err) {
pass(req._route_index + 1, err);
}
// match route
@@ -199,7 +216,7 @@ Router.prototype._dispatch = function(req, res, next){
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
// no route
if (!route) return next();
if (!route) return next(err);
// we have a route
// start at param 0
@@ -207,50 +224,58 @@ Router.prototype._dispatch = function(req, res, next){
keys = route.keys;
i = 0;
(function param(err) {
var key = keys[i++]
, val = key && req.params[key.name]
, fn = key && params[key.name]
, ret;
// param callbacks
function param(err) {
paramIndex = 0;
key = keys[i++];
paramVal = key && req.params[key.name];
paramCallbacks = key && params[key.name];
try {
if ('route' == err) {
nextRoute();
} else if (err) {
next(err);
} else if (fn && undefined !== val) {
fn(req, res, param, val);
i = 0;
callbacks(err);
} else if (paramCallbacks && undefined !== paramVal) {
paramCallback();
} else if (key) {
param();
} else {
i = 0;
middleware();
callbacks();
}
} catch (err) {
next(err);
}
})();
// invoke route middleware
function middleware(err) {
var fn = route.middleware[i++];
if ('route' == err) {
nextRoute();
} else if (err) {
next(err);
} else if (fn) {
fn(req, res, middleware);
} else {
done();
param(err);
}
};
// invoke middleware callback
function done() {
route.callback.call(self, req, res, function(err){
if (err) return next(err);
pass(req._route_index + 1);
});
param(err);
// single param callbacks
function paramCallback(err) {
var fn = paramCallbacks[paramIndex++];
if (err || !fn) return param(err);
fn(req, res, paramCallback, paramVal, key.name);
}
// invoke route callbacks
function callbacks(err) {
var fn = route.callbacks[i++];
try {
if ('route' == err) {
nextRoute();
} else if (err && fn) {
if (fn.length < 4) return callbacks(err);
fn(err, req, res, callbacks);
} else if (fn) {
fn(req, res, callbacks);
} else {
nextRoute(err);
}
} catch (err) {
callbacks(err);
}
}
})(0);
};
@@ -344,37 +369,26 @@ Router.prototype._match = function(req, i){
};
/**
* Route `method`, `path`, and optional middleware
* to the callback `fn`.
* Route `method`, `path`, and one or more callbacks.
*
* @param {String} method
* @param {String} path
* @param {Function} ...
* @param {Function} fn
* @param {Function} callback...
* @return {Router} for chaining
* @api private
*/
Router.prototype._route = function(method, path, fn){
Router.prototype._route = function(method, path, callbacks){
var app = this.app
, middleware = [];
, callbacks = utils.flatten(toArray(arguments, 2));
// slice middleware
if (arguments.length > 3) {
middleware = toArray(arguments, 2);
fn = middleware.pop();
middleware = utils.flatten(middleware);
}
// ensure path and callback are given
if (!path) throw new Error(method + 'route requires a path');
if (!fn) throw new Error(method + ' route ' + path + ' requires a callback');
// ensure path was given
if (!path) throw new Error('app.' + method + '() requires a path');
// create the route
var route = new Route(method, path, fn, {
var route = new Route(method, path, callbacks, {
sensitive: app.enabled('case sensitive routes')
, strict: app.enabled('strict routing')
, middleware: middleware
});
// add it

View File

@@ -54,6 +54,14 @@ var RFC5323 = ['SEARCH'];
var RFC5789 = ['PATCH'];
/**
* PURGE Method for caching reverse-proxy
* http://wiki.squid-cache.org/SquidFaq/OperatingSquid#How_can_I_purge_an_object_from_my_cache.3F
* https://www.varnish-cache.org/docs/trunk/tutorial/purging.html
*/
var CACHE_PURGE = ['PURGE'];
/**
* Expose the methods.
*/
@@ -65,6 +73,7 @@ module.exports = [].concat(
, RFC3648
, RFC3744
, RFC5323
, RFC5789).map(function(method){
, RFC5789
, CACHE_PURGE).map(function(method){
return method.toLowerCase();
});

View File

@@ -13,27 +13,25 @@ module.exports = Route;
/**
* Initialize `Route` with the given HTTP `method`, `path`,
* and callback `fn` and `options`.
* and an array of `callbacks` and `options`.
*
* Options:
*
* - `sensitive` enable case-sensitive routes
* - `strict` enable strict matching for trailing slashes
* - `middleware` array of middleware
*
* @param {String} method
* @param {String} path
* @param {Function} fn
* @param {Array} callbacks
* @param {Object} options.
* @api private
*/
function Route(method, path, fn, options) {
function Route(method, path, callbacks, options) {
options = options || {};
this.callback = fn;
this.path = path;
this.method = method;
this.middleware = options.middleware;
this.callbacks = callbacks;
this.regexp = normalize(path
, this.keys = []
, options.sensitive
@@ -81,10 +79,10 @@ function normalize(path, keys, sensitive, strict) {
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || '([^/]+?)') + ')'
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
+ (optional || '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.+)');
.replace(/\*/g, '(.*)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
}

View File

@@ -5,6 +5,19 @@
* MIT Licensed
*/
/**
* Check if `path` looks absolute.
*
* @param {String} path
* @return {Boolean}
* @api private
*/
exports.isAbsolute = function(path){
if ('/' == path[0]) return true;
if (':' == path[1] && '\\' == path[2]) return true;
};
/**
* Merge object `b` with `a` giving precedence to
* values in object `a`.

View File

@@ -47,7 +47,10 @@ exports.register = View.register;
*/
exports.compile = function(view, cache, cid, options){
if (cache && cid && cache[cid]) return cache[cid];
if (cache && cid && cache[cid]){
options.filename = cache[cid].path;
return cache[cid];
}
// lookup
view = exports.lookup(view, options);
@@ -58,13 +61,13 @@ exports.compile = function(view, cache, cid, options){
var err = new Error('failed to locate view "' + view.original.view + '"');
err.view = view.original;
throw err;
}
}
// compile
options.filename = view.path;
view.fn = view.templateEngine.compile(view.contents, options);
cache[cid] = view;
return view;
};
@@ -81,11 +84,11 @@ exports.compile = function(view, cache, cid, options){
*
* Lookup:
*
* - partial `_<name>`
* - any `<name>/index`
* - non-layout `../<name>/index`
* - any `<root>/<name>`
* - partial `<root>/_<name>`
* - partial `_<name>`
* - any `<name>/index`
* - non-layout `../<name>/index`
* - any `<root>/<name>`
* - partial `<root>/_<name>`
*
* @param {String} view
* @param {Object} options
@@ -161,7 +164,7 @@ function renderPartial(res, view, options, parentLocals, parent){
// Inherit locals from parent
union(options, parentLocals);
// Merge locals
if (locals) merge(options, locals);
@@ -179,8 +182,6 @@ function renderPartial(res, view, options, parentLocals, parent){
options[name] = object;
} else if (name === global) {
merge(options, object);
} else {
options.scope = object;
}
}
return res.render(view, options, null, parent, true);
@@ -204,7 +205,7 @@ function renderPartial(res, view, options, parentLocals, parent){
options.lastInCollection = i == len - 1;
object = val;
buf += render();
}
}
} else {
keys = Object.keys(collection);
len = keys.length;
@@ -229,20 +230,20 @@ function renderPartial(res, view, options, parentLocals, parent){
};
/**
* Render `view` partial with the given `options`. Optionally a
* Render `view` partial with the given `options`. Optionally a
* callback `fn(err, str)` may be passed instead of writing to
* the socket.
*
* Options:
*
* - `object` Single object with name derived from the view (unless `as` is present)
* - `object` Single object with name derived from the view (unless `as` is present)
*
* - `as` Variable name for each `collection` value, defaults to the view name.
* * as: 'something' will add the `something` local variable
* * as: this will use the collection value as the template context
* * as: global will merge the collection value's properties with `locals`
*
* - `collection` Array of objects, the name is derived from the view name itself.
* - `collection` Array of objects, the name is derived from the view name itself.
* For example _video.html_ will have a object _video_ available to it.
*
* @param {String} view
@@ -296,7 +297,7 @@ res.partial = function(view, options, fn){
* automatically, however otherwise a response of _200_ and _text/html_ is given.
*
* Options:
*
*
* - `scope` Template evaluation context (the value of `this`)
* - `debug` Output debugging information
* - `status` Response status code
@@ -432,7 +433,7 @@ res._render = function(view, opts, fn, parent, sub){
// partial return
} else if (partial) {
return str;
// render complete, and
// render complete, and
// callback given
} else if (fn) {
fn(null, str);
@@ -456,4 +457,4 @@ function hintAtViewPaths(view, options) {
console.error(' - %s', path);
});
console.error();
}
}

View File

@@ -10,6 +10,7 @@
*/
var path = require('path')
, utils = require('../utils')
, extname = path.extname
, dirname = path.dirname
, basename = path.basename
@@ -99,7 +100,7 @@ View.prototype.resolvePath = function(){
// Implicit engine
if (!~this.basename.indexOf('.')) path += this.extension;
// Absolute
if ('/' == path[0]) return path;
if (utils.isAbsolute(path)) return path;
// Relative to parent
if (this.relative && this.parent) return this.parent.dirname + '/' + path;
// Relative to root

View File

@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "2.4.3",
"version": "2.5.9",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
@@ -10,18 +10,19 @@
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
],
"dependencies": {
"connect": ">= 1.5.2 < 2.0.0",
"mime": ">= 0.0.1",
"qs": ">= 0.0.6"
"connect": "1.x",
"mime": "1.2.4",
"qs": "0.4.x",
"mkdirp": "0.3.0"
},
"devDependencies": {
"connect-form": "0.2.1",
"ejs": "0.4.2",
"expresso": "0.7.2",
"hamljs": "0.5.1",
"jade": "0.11.0",
"expresso": "0.9.2",
"hamljs": "0.6.x",
"jade": "0.16.2",
"stylus": "0.13.0",
"should": "0.2.1",
"should": "0.3.2",
"express-messages": "0.0.2",
"node-markdown": ">= 0.0.1",
"connect-redis": ">= 0.0.1"
@@ -34,5 +35,5 @@
"test": "make test",
"prepublish" : "npm prune"
},
"engines": { "node": ">= 0.4.1 < 0.5.0" }
"engines": { "node": ">= 0.4.1 < 0.7.0" }
}

View File

@@ -3,7 +3,7 @@
* Module dependencies.
*/
var express = require('express')
var express = require('../')
, connect = require('connect')
, assert = require('assert')
, should = require('should')
@@ -37,22 +37,22 @@ module.exports = {
'test basic server': function(){
var server = express.createServer();
server.get('/', function(req, res){
server.set('env').should.equal('test');
res.writeHead(200, {});
res.end('wahoo');
});
server.put('/user/:id', function(req, res){
res.writeHead(200, {});
res.end('updated user ' + req.params.id)
});
server.del('/something', function(req, res){
res.send('Destroyed');
});
server.delete('/something/else', function(req, res){
res.send('Destroyed');
});
@@ -61,7 +61,7 @@ module.exports = {
req.staff = { id: req.params.id };
next();
});
server.get('/staff/:id', function(req, res){
res.send('GET Staff ' + req.staff.id);
});
@@ -73,7 +73,7 @@ module.exports = {
server.all('*', function(req, res){
res.send('requested ' + req.url);
});
assert.response(server,
{ url: '/' },
{ body: 'wahoo' });
@@ -81,15 +81,15 @@ module.exports = {
assert.response(server,
{ url: '/user/12', method: 'PUT' },
{ body: 'updated user 12' });
assert.response(server,
{ url: '/something', method: 'DELETE' },
{ body: 'Destroyed' });
assert.response(server,
{ url: '/something/else', method: 'DELETE' },
{ body: 'Destroyed' });
assert.response(server,
{ url: '/staff/12' },
{ body: 'GET Staff 12' });
@@ -105,17 +105,17 @@ module.exports = {
'test constructor middleware': function(beforeExit){
var calls = [];
function one(req, res, next){
calls.push('one');
next();
}
function two(req, res, next){
calls.push('two');
next();
}
var app = express.createServer(one, two);
app.get('/', function(req, res){
res.writeHead(200, {});
@@ -144,14 +144,14 @@ module.exports = {
assert.response(app,
{ url: '/' },
{ body: 'Internal Server Error' });
// Custom handler
var app = express.createServer();
app.error(function(err, req, res){
res.send('Shit: ' + err.message, 500);
});
app.get('/', function(req, res, next){
next(new Error('broken'));
});
@@ -174,7 +174,7 @@ module.exports = {
app.error(function(err, req, res, next){
res.send(err.message, 500);
});
app.get('/', function(req, res, next){
throw new Error('broken');
});
@@ -220,7 +220,7 @@ module.exports = {
'test #use()': function(){
var app = express.createServer();
app.get('/users', function(req, res, next){
next(new Error('fail!!'));
});
@@ -244,13 +244,13 @@ module.exports = {
}).configure('production', function(){
calls.push('production');
});
should.equal(ret, server, 'Test #configure() returns server for chaining');
assert.response(server,
{ url: '/' },
{ body: 'Cannot GET /' });
beforeExit(function(){
calls.should.eql(['any', 'dev']);
});
@@ -258,7 +258,7 @@ module.exports = {
'test #configure() immediate call': function(){
var app = express.createServer();
app.configure(function(){
app.use(connect.bodyParser());
});
@@ -266,18 +266,17 @@ module.exports = {
app.post('/', function(req, res){
res.send(req.param('name') || 'nope');
});
assert.response(app,
{ url: '/', method: 'POST', data: 'name=tj', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }},
{ body: 'tj' });
},
'test #configure() precedence': function(){
var app = express.createServer();
app.configure(function(){
app.use(function(req, res, next){
res.writeHead(200, {});
res.write('first');
next();
});
@@ -291,28 +290,28 @@ module.exports = {
res.write(' route ');
next();
});
assert.response(app,
{ url: '/' },
{ body: 'first route last' });
},
'test #configure() multiple envs': function(){
var app = express.createServer();
app.set('env', 'prod');
var calls = [];
app.configure('stage', 'prod', function(){
calls.push('stage/prod');
});
app.configure('prod', function(){
calls.push('prod');
});
calls.should.eql(['stage/prod', 'prod']);
},
'test #set()': function(){
var app = express.createServer();
var ret = app.set('title', 'My App').set('something', 'else');
@@ -320,7 +319,7 @@ module.exports = {
app.set('title').should.equal('My App');
app.set('something').should.equal('else');
},
'test .settings': function(){
var app = express.createServer();
app.set('title', 'My App');
@@ -352,56 +351,67 @@ module.exports = {
var app = express.createServer();
app.use(connect.bodyParser());
assert.equal(2, app.stack.length);
app.post('/', function(req, res){
res.send(JSON.stringify(req.body || ''));
});
app.get('/', function(){
});
assert.equal(3, app.stack.length);
assert.response(app,
{ url: '/', method: 'POST', data: 'name=tj', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }},
{ body: '{"name":"tj"}' });
},
'test "basepath" setting': function(){
var app = express.createServer();
app.set('basepath', '/shop');
app.get('/redirect', function(req, res){
res.redirect('/cart');
});
assert.response(app,
{ url: '/redirect', headers: { Host: 'foo.com' }},
{ headers: { Location: 'http://foo.com/shop/cart' }});
},
'test mounting': function(){
var called
, app = express.createServer()
, blog = express.createServer()
, map = express.createServer()
, reg = connect.createServer();
map.set('home', '/map');
map.mounted(function(parent){
called = true;
assert.equal(this, map, 'mounted() is not in context of the child app');
assert.equal(app, parent, 'mounted() was not called with parent app');
});
reg.use(function(req, res){ res.end('hey'); });
app.use('/regular', reg);
app.use('/blog', blog);
app.use('/contact', map);
blog.route.should.equal('/blog');
map.route.should.equal('/contact');
should.equal(true, called);
app.set("test", "parent setting");
blog.set('test').should.equal('parent setting');
app.get('/', function(req, res){
app.set('home').should.equal('/');
blog.set('home').should.equal('/blog');
map.set('home').should.equal('/contact/map');
blog.set('basepath').should.equal('/blog');
map.set('basepath').should.equal('/contact');
res.send('main app');
});
blog.get('/', function(req, res){
res.send('blog index');
});
@@ -426,26 +436,26 @@ module.exports = {
{ url: '/regular' },
{ body: 'hey' });
},
'test .app property after returning control to parent': function() {
var app = express.createServer()
, blog = express.createServer();
// Mounted servers did not restore `req.app` and `res.app` when
// passing control back to parent via `out()` in `#handle()`.
blog.get('/', function(req, res, next){
req.app.should.equal(blog);
res.app.should.equal(blog);
next();
});
app.use(blog);
app.use(function(req, res, next) {
res.send((res.app === app) ? 'restored' : 'not-restored');
});
assert.response(app,
{ url: '/' },
{ body: 'restored' }
@@ -456,23 +466,23 @@ module.exports = {
function handle(req, res) {
res.send('got ' + req.string);
}
var app = express.createServer();
app.get('/', function(req, res, next){
req.string = '/';
next();
}, handle);
app.get('/another', function(req, res, next){
req.string = '/another';
next();
}, handle);
assert.response(app,
{ url: '/' },
{ body: 'got /' });
assert.response(app,
{ url: '/another' },
{ body: 'got /another' });
@@ -480,11 +490,11 @@ module.exports = {
'invalid chars': function(){
var app = express.createServer();
app.get('/:name', function(req, res, next){
res.send('invalid');
});
assert.response(app,
{ url: '/%a0' },
{ status: 500 });

View File

@@ -3,4 +3,4 @@
- else if (lastInCollection)
li.last= word
- else
li(class: 'word-' + indexInCollection)= word
li(class='word-' + indexInCollection)= word

View File

@@ -1 +0,0 @@
p #{label} #{this.name}

View File

@@ -3,12 +3,24 @@
* Module dependencies.
*/
var express = require('express')
var express = require('../')
, connect = require('connect')
, assert = require('assert')
, should = require('should');
module.exports = {
'test #path': function(){
var app = express.createServer();
app.get('/search', function(req, res){
res.send(req.path);
});
assert.response(app,
{ url: '/search?q=tobi' },
{ body: '/search' });
},
'test #isXMLHttpRequest': function(){
var app = express.createServer();
@@ -155,12 +167,15 @@ module.exports = {
req.flash('info').should.eql(['one']);
req.flash('info', 'Email _sent_.');
req.flash('info', '<script>');
req.flash('info').should.eql(['Email <em>sent</em>.', '&lt;script&gt;']);
req.flash('info', '<em>%s</em>', 'html');
req.flash('info').should.eql(['Email <em>sent</em>.', '<em>html</em>']);
req.flash('info', 'Welcome _%s_ to %s', 'TJ', 'something');
req.flash('info').should.eql(['Welcome <em>TJ</em> to something']);
req.flash('info', 'Welcome %s', '<script>');
req.flash('info').should.eql(['Welcome &lt;script&gt;']);
req.flash('error', 'Foo %u', 'bar');
req.flash('error').should.eql(['Foo BAR']);

View File

@@ -3,7 +3,7 @@
* Module dependencies.
*/
var express = require('express')
var express = require('../')
, Stream = require('stream').Stream
, assert = require('assert')
, should = require('should');
@@ -68,8 +68,8 @@ module.exports = {
});
app.get('/json', function(req, res){
res.header('X-Foo', 'bar');
res.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
res.header('X-Foo', 'bar')
.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
});
app.get('/text', function(req, res){
@@ -97,7 +97,11 @@ module.exports = {
app.get('/noargs', function(req, res, next){
res.send();
});
app.get('/no-content', function(req, res, next){
res.send(204);
});
app.get('/undefined', function(req, res, next){
res.send(undefined);
});
@@ -162,6 +166,13 @@ module.exports = {
'Content-Type': 'application/octet-stream'
, 'Content-Length': '6'
}});
assert.response(app,
{ url: '/no-content' },
{ status: 204 }, function(res){
assert.equal(undefined, res.headers['content-type']);
assert.equal(undefined, res.headers['content-length']);
});
assert.response(app,
{ url: '/noargs' },
@@ -321,13 +332,13 @@ module.exports = {
var app = express.createServer()
, app2 = express.createServer();
app2.set('home', '/blog');
app2.set('basepath', '/blog');
app2.redirect('google', 'http://google.com');
app2.redirect('blog', function(req, res){
return req.params.id
? '/user/' + req.params.id + '/blog'
? '/user/' + req.params.id + '/posts'
: null;
});
@@ -368,59 +379,63 @@ module.exports = {
res.redirect('blog');
});
assert.response(app,
{ url: '/home', method: 'HEAD' },
{ body: '' });
assert.response(app,
{ url: '/html', headers: { Accept: 'text/html,text/plain', Host: 'foo.com' }},
{ body: '<p>Moved Temporarily. Redirecting to <a href="http://google.com">http://google.com</a></p>' });
assert.response(app,
{ url: '/', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Permanently. Redirecting to http://google.com'
, status: 301, headers: { Location: 'http://google.com' }});
assert.response(app,
{ url: '/back', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/'
, status: 302, headers: { Location: 'http://foo.com/', 'Content-Type': 'text/plain' }});
assert.response(app,
{ url: '/back', headers: { Referer: '/foo', Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/foo'
, status: 302, headers: { Location: 'http://foo.com/foo' }});
assert.response(app,
{ url: '/back', headers: { Referrer: '/foo', Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/foo'
, status: 302, headers: { Location: 'http://foo.com/foo' }});
assert.response(app,
{ url: '/home', headers: { Accept: 'text/plain', Host: 'foo.com' } },
{ body: 'Moved Temporarily. Redirecting to http://foo.com/'
, status: 302, headers: { Location: 'http://foo.com/' }});
assert.response(app2,
{ url: '/', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Permanently. Redirecting to http://google.com'
, status: 301, headers: { Location: 'http://google.com' }});
assert.response(app2,
{ url: '/back', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/blog'
, status: 302, headers: { Location: 'http://foo.com/blog' }});
assert.response(app2,
{ url: '/home', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/blog'
, status: 302, headers: { Location: 'http://foo.com/blog' }});
assert.response(app2,
{ url: '/google', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://google.com'
, status: 302, headers: { Location: 'http://google.com' }});
assert.response(app2,
{ url: '/user/12', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/user/12/blog'
, status: 302, headers: { Location: 'http://foo.com/user/12/blog', 'X-Foo': 'bar' }});
{ body: 'Moved Temporarily. Redirecting to http://foo.com/blog/user/12/posts'
, status: 302, headers: { Location: 'http://foo.com/blog/user/12/posts', 'X-Foo': 'bar' }});
},
'test #redirect() when mounted': function(){
@@ -495,10 +510,6 @@ module.exports = {
});
});
assert.response(app,
{ url: '/forum' },
{ body: 'got an error' });
assert.response(app,
{ url: '/does-not-exist' },
{ body: 'got an error' });
@@ -656,7 +667,7 @@ module.exports = {
'test #cookie() path default': function(){
var app = express.createServer();
app.set('home', '/foo');
app.set('basepath', '/foo');
app.get('/', function(req, res){
res.cookie('rememberme', 'yes', { expires: new Date(1), httpOnly: true });
@@ -675,11 +686,11 @@ module.exports = {
'test #cookie() explicit path': function(){
var app = express.createServer();
app.set('/home', '/foo');
app.set('/basepath', '/foo');
app.get('/', function(req, res){
res.cookie('rememberme', 'yes', { path: '/', expires: new Date(1), httpOnly: true });
res.cookie('something', 'else');
res.cookie('something', 'else', { path: '/' });
res.redirect('/');
});
@@ -694,7 +705,7 @@ module.exports = {
'test #cookie() null path': function(){
var app = express.createServer();
app.set('/home', '/foo');
app.set('/basepath', '/foo');
app.get('/', function(req, res){
res.cookie('rememberme', 'yes', { path: null, expires: new Date(1), httpOnly: true });
@@ -713,7 +724,7 @@ module.exports = {
'test #clearCookie() default path': function(){
var app = express.createServer();
app.set('home', '/foo');
app.set('basepath', '/foo');
app.get('/', function(req, res){
res.clearCookie('rememberme');
@@ -731,7 +742,7 @@ module.exports = {
'test #clearCookie() explicit path': function(){
var app = express.createServer();
app.set('home', '/bar');
app.set('basepath', '/bar');
app.get('/', function(req, res){
res.clearCookie('rememberme', { path: '/foo' });

View File

@@ -3,7 +3,7 @@
* Module dependencies.
*/
var express = require('express')
var express = require('../')
, connect = require('connect')
, assert = require('assert')
, should = require('should')
@@ -74,6 +74,245 @@ module.exports = {
});
},
'test app.param() multiple mapping functions': function(){
var app = express.createServer();
app.param(function(name, fn){
if (fn.length < 3) {
return function(req, res, next, val){
val = req.params[name] = fn(val);
if (false === val) {
next('route');
} else {
next();
}
};
}
});
app.param(function(name, range){
if (!~String(range).indexOf('..')) return;
var parts = range.split('..')
, from = parseInt(parts.shift())
, to = parseInt(parts.shift());
return function(req, res, next, val){
if (val < from || val > to) return next('route');
next();
}
});
app.param('user', Number);
app.param('user', '0..5');
app.get('/user/:user', function(req, res){
res.json(req.params.user);
});
assert.response(app,
{ url: '/user/3' },
{ body: '3' });
assert.response(app,
{ url: '/user/6' },
{ status: 404 });
},
'test app.param() name passing': function(){
var app = express.createServer();
app.param(function(name, fn){
if (fn.length < 3) {
return function(req, res, next, val){
val = req.params[name] = fn(val);
if (false === val) {
next('route');
} else {
next();
}
};
}
});
function within(a, b) {
return function(req, res, next, val, name){
if (val < a || val > b) {
return next(new Error(name + ' should be within ' + a + '..' + b));
}
next();
}
}
app.param('user', Number);
app.param('user', within(0, 5));
app.get('/user/:user', function(req, res){
res.json(req.params.user);
});
app.use(function(err, req, res, next){
res.json({ error: err.message });
});
assert.response(app,
{ url: '/user/0' },
{ body: '0' });
assert.response(app,
{ url: '/user/6' },
{ body: '{"error":"user should be within 0..5"}' });
},
'test app.param() multiple callbacks and array of params': function(){
var app = express.createServer();
var users = [{ name: 'tj' }];
var pets = [['tobi', 'loki', 'jane', 'manny', 'luna']];
function loadUser(req, res, next, id) {
req.user = users[id];
next();
}
function loadUserPets(req, res, next, id) {
req.user.pets = pets[id];
next();
}
app.param(['user_id', 'user'], loadUser, loadUserPets);
app.get('/user/:user_id', function(req, res){
res.send(req.user);
});
app.get('/account/:user', function(req, res){
res.send(req.user);
});
assert.response(app,
{ url: '/account/0' },
{ body: '{"name":"tj","pets":["tobi","loki","jane","manny","luna"]}' });
assert.response(app,
{ url: '/user/0' },
{ body: '{"name":"tj","pets":["tobi","loki","jane","manny","luna"]}' });
},
'test app.param() multiple callbacks': function(){
var app = express.createServer();
var users = [{ name: 'tj' }];
var pets = [['tobi', 'loki', 'jane', 'manny', 'luna']];
function loadUser(req, res, next, id) {
req.user = users[id];
next();
}
function loadUserPets(req, res, next, id) {
req.user.pets = pets[id];
next();
}
app.param('user_id', loadUser, loadUserPets);
app.get('/user/:user_id', function(req, res){
res.send(req.user);
});
assert.response(app,
{ url: '/user/0' },
{ body: '{"name":"tj","pets":["tobi","loki","jane","manny","luna"]}' });
},
'test app.param() multiple calls with error': function(){
var app = express.createServer();
var commits = ['foo', 'bar', 'baz'];
app.param('commit', function(req, res, next, id){
req.commit = parseInt(id);
if (isNaN(req.commit)) return next('route');
next();
});
app.param('commit', function(req, res, next, id){
req.commit = commits[req.commit];
next(new Error('failed'));
});
app.get('/commit/:commit', function(req, res){
res.send(req.commit);
});
assert.response(app,
{ url: '/commit/0' },
{ status: 500 });
},
'test app.param() multiple calls': function(){
var app = express.createServer();
var commits = ['foo', 'bar', 'baz'];
app.param('commit', function(req, res, next, id){
req.commit = parseInt(id);
if (isNaN(req.commit)) return next('route');
next();
});
app.param('commit', function(req, res, next, id){
req.commit = commits[req.commit];
next();
});
app.get('/commit/:commit', function(req, res){
res.send(req.commit);
});
assert.response(app,
{ url: '/commit/0' },
{ body: 'foo' });
assert.response(app,
{ url: '/commit/0x01' },
{ body: 'bar' });
assert.response(app,
{ url: '/commit/asdf' },
{ status: 404 });
},
'test app.param(fn)': function(){
var app = express.createServer();
app.param(function(name, fn){
if (fn instanceof RegExp) {
return function(req, res, next, val){
var captures;
if (captures = fn.exec(String(val))) {
req.params[name] = captures[1];
next();
} else {
next('route');
}
}
}
});
app.param('commit', /^(\d+)$/);
app.get('/commit/:commit', function(req, res){
res.send(req.params.commit);
});
assert.response(app,
{ url: '/commit/12' },
{ body: '12' });
assert.response(app,
{ url: '/commit/asdf' },
{ status: 404 });
},
'test precedence': function(){
var app = express.createServer();
@@ -138,6 +377,50 @@ module.exports = {
{ body: 'Cannot GET /user/ab' });
},
'test named capture group after dot': function(){
var app = express.createServer();
app.get('/user/:name.:format?', function(req, res){
res.send(req.params.name + ' - ' + (req.params.format || ''));
});
assert.response(app,
{ url: '/user/foo' },
{ body: 'foo - ' });
assert.response(app,
{ url: '/user/foo.json' },
{ body: 'foo - json' });
assert.response(app,
{ url: '/user/foo.bar.json' },
{ body: 'foo.bar - json' });
},
'test optional * value': function(){
var app = express.createServer();
app.get('/admin*', function(req, res){
res.send(req.params[0]);
});
app.get('/file/*.*', function(req, res){
res.send(req.params[0] + ' - ' + req.params[1]);
});
assert.response(app,
{ url: '/file/some.foo.bar' },
{ body: 'some.foo - bar' });
assert.response(app,
{ url: '/admin', },
{ body: '', status: 200 });
assert.response(app,
{ url: '/adminify', },
{ body: 'ify', status: 200 });
},
'test app.param()': function(){
var app = express.createServer();
@@ -225,7 +508,7 @@ module.exports = {
var route = app.get('/user/:id')[0]
route.should.be.an.instanceof(Route);
route.callback.should.be.a('function');
route.callbacks.should.be.an.instanceof(Array);
route.path.should.equal('/user/:id');
route.regexp.should.be.an.instanceof(RegExp);
route.method.should.equal('get');
@@ -287,7 +570,6 @@ module.exports = {
var route = app.match.get('/user/12')[0];
route.should.be.an.instanceof(Route);
route.callback.should.be.a('function');
route.path.should.equal('/user/:id');
route.regexp.should.be.an.instanceof(RegExp);
route.method.should.equal('get');
@@ -304,6 +586,17 @@ module.exports = {
app.match('/user/123').should.have.length(3);
},
'test app.routes.all()': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/edit', function(){});
app.routes.all()[0].should.be.an.instanceof(Route);
app.routes.all().length.should.equal(5);
},
'test Collection': function(){
var app = express.createServer();
app.get('/user', function(){});
@@ -403,5 +696,150 @@ module.exports = {
function(){
routes.should.eql(['/:foo?', '/foo']);
});
},
'test route callback error handling': function(){
var app = express.createServer()
, calls = [];
app.get('/user/:id', function(req, res, next){
calls.push('one');
next();
});
app.get('/user/:id', function(req, res, next){
calls.push('two');
next(new Error('fail'));
});
app.get('/user/:id', function(req, res, next){
calls.push('three');
next();
});
app.get('/user/*', function(err, req, res, next){
res.statusCode = 500;
res.send('error: ' + err.message);
});
app.get('/user/*', function(req, res, next){
calls.push('four');
next();
});
assert.response(app,
{ url: '/user/12' },
{ body: 'error: fail' }, function(){
calls.should.eql(['one', 'two']);
});
},
'test route callback thrown error handling': function(){
var app = express.createServer()
, calls = [];
app.get('/user/:id', function(req, res, next){
calls.push('one');
next();
});
app.get('/user/:id', function(req, res, next){
calls.push('two');
throw new Error('fail');
});
app.get('/user/:id', function(req, res, next){
calls.push('three');
next();
});
app.get('/user/*', function(err, req, res, next){
res.statusCode = 500;
res.send('error: ' + err.message);
});
app.get('/user/*', function(req, res, next){
calls.push('four');
next();
});
assert.response(app,
{ url: '/user/12' },
{ body: 'error: fail' }, function(){
calls.should.eql(['one', 'two']);
});
},
'test route callback error recovery': function(){
var app = express.createServer();
app.get('/user/:id', function(req, res, next){
next(new Error('fail'));
});
app.get('/user/*', function(err, req, res, next){
req.error = err;
next();
});
app.get('/user/*', function(req, res, next){
res.send('recovered from error: ' + req.error.message);
});
assert.response(app,
{ url: '/user/12' },
{ body: 'recovered from error: fail' });
},
'test multiple param callbacks': function(){
var app = express.createServer();
app.param('user', function(req, res, next, id){
req.user = { id: id };
next();
});
app.param('forum_id', function(req, res, next, id){
req.forum = { id: id };
next();
});
app.param('thread_id', function(req, res, next, id){
req.thread = { id: id };
next();
});
function array(req, res, next) {
req.arr = [req.user.id, req.forum.id, req.thread.id];
next();
}
app.get('/:user/:forum_id/:thread_id', array, function(req, res){
res.send(req.arr);
});
assert.response(app,
{ url: '/1/2/3' },
{ body: '["1","2","3"]' });
},
'test app.all for multiple deletes': function(beforeExit){
var app = express.createServer();
var deletes = 0;
app.all('*', function(req, res, next){
if (req.method === 'DELETE')
deletes++;
next();
});
assert.response(app,
{ url: '/', method: 'DELETE' },
{ status: 404 });
beforeExit(function(){
deletes.should.eql(1);
});
}
};

View File

@@ -3,7 +3,7 @@
* Module dependencies.
*/
var express = require('express')
var express = require('../')
, connect = require('connect')
, assert = require('assert')
, should = require('should')
@@ -453,19 +453,6 @@ module.exports = {
{ url: '/user' },
{ body: '<p>tj</p>' });
// as: this collection option
app.get('/person', function(req, res){
res.partial('person.jade', {
as: this,
collection: [{ name: 'tj' }],
locals: { label: 'name:' }
});
});
assert.response(app,
{ url: '/person' },
{ body: '<p>name: tj</p>' });
// as: global collection option
app.get('/videos', function(req, res){
res.partial('video.jade', {
@@ -513,19 +500,6 @@ module.exports = {
{ url: '/video-global' },
{ body: '<p>Tim Burton</p>' });
app.get('/person-this', function(req, res){
res.partial('person.jade', {
object: { name: 'tj' },
locals: { label: 'User:' },
as: this
});
});
// Non-collection as: this
assert.response(app,
{ url: '/person-this' },
{ body: '<p>User: tj</p>' });
// No options
app.get('/nothing', function(req, res){
res.partial('hello.ejs');