Compare commits

..

52 Commits

Author SHA1 Message Date
Tj Holowaychuk
52353da08b Release 1.0.2 2011-01-10 18:09:16 -08:00
Tj Holowaychuk
dc56b9b603 Removed nested require, using connect.router 2011-01-10 18:08:33 -08:00
Tj Holowaychuk
1446135545 docs 2011-01-03 13:33:51 -08:00
Tj Holowaychuk
cdc46307d7 Release 1.0.1 2010-12-29 11:38:02 -08:00
Tj Holowaychuk
dbf02c231a Fixed for middleware stacked via createServer() 2010-12-29 11:34:27 -08:00
Tj Holowaychuk
4732185e6f Fixed express(1) generated tests for express 0.7.0. Closes #484 2010-12-14 08:22:38 -08:00
Tj Holowaychuk
84a95b3101 Updated connect submodule 2010-12-06 14:54:50 -08:00
Tj Holowaychuk
09e8fe280b older expresso 2010-11-24 08:16:59 -08:00
Tj Holowaychuk
0e4ea9c16b Updated jade submodule 2010-11-24 08:14:44 -08:00
Tj Holowaychuk
c7542aef95 Merge branch 'master' into 1.x 2010-11-17 11:26:55 -08:00
Tj Holowaychuk
6873bce6c6 Merge branch 'master' into 1.x 2010-11-16 18:04:57 -08:00
Tj Holowaychuk
6ddacb6302 Merge branch 'master' into 1.x 2010-11-16 17:58:33 -08:00
Tj Holowaychuk
e1d33992a7 Merge branch 'master' into 1.x 2010-11-13 10:55:19 -08:00
Tj Holowaychuk
569c5139a6 Merge branch 'master' into 1.x 2010-11-12 08:46:07 -08:00
Tj Holowaychuk
6f5f5787e2 Merge branch 'master' into 1.x 2010-11-08 07:18:52 -08:00
Tj Holowaychuk
94cf769dd7 Merge branch 'master' into 1.x 2010-10-26 12:09:17 -07:00
Tj Holowaychuk
6d00b45eed Merge branch 'master' into 1.x 2010-10-26 01:18:24 -07:00
Tj Holowaychuk
6a4c6933aa Merge branch 'master' into 1.x 2010-10-22 16:20:47 -07:00
Tj Holowaychuk
c35ee2427e Merge branch 'master' into 1.x 2010-10-21 19:53:39 -07:00
Tj Holowaychuk
3d7216935a Merge branch 'master' into 1.x 2010-10-20 10:07:54 -07:00
Tj Holowaychuk
fc15c3d02b Merge branch 'master' into 1.x 2010-10-19 17:19:39 -07:00
Tj Holowaychuk
9eaec5b34e Merge branch 'master' into 1.x 2010-10-19 17:03:18 -07:00
Tj Holowaychuk
30712fa9d9 Merge branch 'master' into 1.x 2010-10-19 15:03:02 -07:00
Tj Holowaychuk
b817579d8a Merge branch 'master' into 1.x 2010-10-19 09:20:23 -07:00
Tj Holowaychuk
cbcaba3cec Merge branch 'master' into 1.x 2010-10-15 10:02:29 -07:00
Tj Holowaychuk
4e11fef43b Merge branch 'master' into 1.x 2010-10-15 08:26:04 -07:00
Tj Holowaychuk
067fdd5c4c Merge branch 'master' into 1.x 2010-10-14 09:08:19 -07:00
Tj Holowaychuk
fff815f666 Merge branch 'master' into 1.x 2010-10-14 04:17:19 -07:00
Tj Holowaychuk
609c18aa5b Merge branch 'master' into 1.x 2010-10-14 03:50:56 -07:00
Tj Holowaychuk
73c108ce90 Merge branch 'master' into 1.x 2010-10-14 03:03:24 -07:00
Tj Holowaychuk
1e9da205a7 Merge branch 'master' into 1.x 2010-10-12 12:59:14 -07:00
Tj Holowaychuk
d5539c7beb Merge branch 'master' into 1.x 2010-10-12 08:46:48 -07:00
Tj Holowaychuk
f87bd8c38f Merge branch 'master' into 1.x 2010-10-11 08:37:49 -07:00
Tj Holowaychuk
05515fa09e Merge branch 'master' into 1.x 2010-10-11 08:18:01 -07:00
Tj Holowaychuk
35c91ed6f5 Merge branch 'master' into 1.x 2010-10-08 11:39:14 -07:00
Tj Holowaychuk
26238c429d Merge branch 'master' into 1.x 2010-10-07 06:35:27 -07:00
Tj Holowaychuk
1b28ad16d4 Merge branch 'master' into 1.x 2010-10-07 05:46:45 -07:00
Tj Holowaychuk
e29f3aa5dd Merge branch 'master' into 1.x 2010-10-07 03:50:02 -07:00
Tj Holowaychuk
5c94603787 Merge branch 'master' into 1.x 2010-10-07 02:36:41 -07:00
Tj Holowaychuk
9409107f77 Merge branch 'master' into 1.x 2010-10-06 08:26:29 -07:00
Tj Holowaychuk
7f11aa25ea Merge branch 'master' into 1.x 2010-10-05 06:32:50 -07:00
Tj Holowaychuk
1994f24d82 Merge branch 'master' into 1.x 2010-10-04 11:16:56 -07:00
Tj Holowaychuk
b5b30a3f20 Merge branch 'master' into 1.x 2010-10-04 11:06:33 -07:00
Tj Holowaychuk
4fcbb961eb Merge branch 'master' into 1.x 2010-10-04 08:04:12 -07:00
Tj Holowaychuk
11cfad755a Merge branch 'master' into 1.x 2010-10-04 08:01:30 -07:00
Tj Holowaychuk
0b24bd08c9 Merge branch 'master' into 1.x 2010-10-01 16:33:34 -07:00
Tj Holowaychuk
b377839538 Merge branch 'master' into 1.x 2010-10-01 16:31:40 -07:00
Tj Holowaychuk
698d82f799 Merge branch 'master' into 1.x 2010-10-01 07:34:14 -07:00
Tj Holowaychuk
462a291eb8 Merge branch 'master' into 1.x 2010-09-23 09:37:05 -07:00
Tj Holowaychuk
79dc2467f7 Merge branch 'master' into 1.x 2010-09-22 16:09:15 -07:00
Tj Holowaychuk
0a0c86813d Merge branch 'master' into 1.x 2010-09-22 12:03:42 -07:00
Tj Holowaychuk
dfdc939816 Multipart typo 2010-09-21 12:39:14 -07:00
341 changed files with 14013 additions and 15158 deletions

33
.gitignore vendored
View File

@@ -1,26 +1,9 @@
# OS X
.DS_Store*
Icon?
._*
# Windows
Thumbs.db
ehthumbs.db
Desktop.ini
# Linux
.directory
*~
# npm
node_modules
.DS_Store
lib-cov
*.seed
*.log
*.gz
# Coveralls
coverage
# Benchmarking
benchmarks/graphs
*.csv
*.dat
*.out
*.pid
benchmarks/graphs

18
.gitmodules vendored Normal file
View File

@@ -0,0 +1,18 @@
[submodule "support/expresso"]
path = support/expresso
url = git://github.com/visionmedia/expresso.git
[submodule "support/haml"]
path = support/haml
url = git://github.com/visionmedia/haml.js.git
[submodule "support/ejs"]
path = support/ejs
url = git://github.com/visionmedia/ejs.git
[submodule "support/connect-form"]
path = support/connect-form
url = git://github.com/visionmedia/connect-form.git
[submodule "support/connect"]
path = support/connect
url = git://github.com/senchalabs/connect.git
[submodule "support/jade"]
path = support/jade
url = git://github.com/visionmedia/jade.git

View File

@@ -1,11 +0,0 @@
language: node_js
node_js:
- "0.8"
- "0.10"
- "0.11"
matrix:
allow_failures:
- node_js: "0.11"
fast_finish: true
script: "npm run-script test-travis"
after_script: "npm install coveralls@2.10.0 && cat ./coverage/lcov.info | coveralls"

1478
History.md

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
(The MIT License)
Copyright (c) 2009-2013 TJ Holowaychuk <tj@vision-media.ca>
Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

76
Makefile Normal file
View File

@@ -0,0 +1,76 @@
PREFIX ?= /usr/local
LIB_PREFIX = ~/.node_libraries
DOCS = docs/index.md \
docs/executable.md \
docs/contrib.md \
docs/guide.md \
docs/migrate.md \
docs/applications.md
MANPAGES =$(DOCS:.md=.1)
HTMLDOCS =$(DOCS:.md=.html)
install: install-docs
@mkdir -p $(PREFIX)/bin
@mkdir -p $(LIB_PREFIX)
cp -f bin/express $(PREFIX)/bin/express
cp -fr lib/express $(LIB_PREFIX)/express
uninstall: uninstall-docs
rm -f $(PREFIX)/bin/express
rm -fr $(LIB_PREFIX)/express
install-support:
cd support/connect && $(MAKE) install
uninstall-support:
cd support/connect && $(MAKE) uninstall
install-docs:
@mkdir -p $(PREFIX)/share/man/man1
cp -f docs/executable.1 $(PREFIX)/share/man/man1/express.1
uninstall-docs:
rm -f $(PREFIX)/share/man/man1/express.1
test:
@NODE_ENV=test ./support/expresso/bin/expresso \
-I lib \
-I support/connect/lib \
-I support/haml/lib \
-I support/jade/lib \
-I support/ejs/lib \
$(TESTFLAGS) \
test/*.test.js
test-cov:
@TESTFLAGS=--cov $(MAKE) test
docs: docs/api.html $(MANPAGES) $(HTMLDOCS)
@ echo "... generating TOC"
@./support/toc.js docs/guide.html
docs/api.html: lib/express/*.js
dox \
--private \
--title Express \
--desc "High performance web framework for [node](http://nodejs.org)." \
$(shell find lib/express/* -type f) > $@
%.1: %.md
@echo "... $< -> $@"
@ronn -r --pipe $< > $@
%.html: %.md
@echo "... $< -> $@"
@ronn -5 --pipe --fragment $< \
| cat docs/layout/head.html - docs/layout/foot.html \
| sed 's/NAME/Express/g' \
> $@
docclean:
rm -f docs/*.{1,html}
.PHONY: install uninstall install-docs install-support uninstall-support install-docs uninstall-docs test test-cov docs docclean

146
Readme.md
View File

@@ -1,115 +1,87 @@
[![express logo](http://f.cl.ly/items/0V2S1n0K1i3y1c122g04/Screen%20Shot%202012-04-11%20at%209.59.42%20AM.png)](http://expressjs.com/)
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
# Express
Insanely fast (and small) server-side JavaScript web development framework
built on [node](http://nodejs.org) and [Connect](http://github.com/senchalabs/connect).
var app = express.createServer();
app.get('/', function(req, res){
res.send('Hello World');
});
[![NPM version](https://img.shields.io/npm/v/express.svg?style=flat)](https://www.npmjs.org/package/express)
[![Build Status](https://img.shields.io/travis/strongloop/express.svg?style=flat)](https://travis-ci.org/strongloop/express)
[![Coverage Status](https://img.shields.io/coveralls/strongloop/express.svg?style=flat)](https://coveralls.io/r/strongloop/express)
[![Gittip](https://img.shields.io/gittip/dougwilson.svg?style=flat)](https://www.gittip.com/dougwilson/)
```js
var express = require('express');
var app = express();
app.get('/', function(req, res){
res.send('Hello World');
});
app.listen(3000);
```
app.listen(3000);
## Installation
$ npm install -g express
npm:
## Quick Start
$ npm install express
The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:
curl:
Create the app:
$ npm install -g express
$ express /tmp/foo && cd /tmp/foo
Install dependencies:
$ npm install
Start the server:
$ node app
$ curl -# http://expressjs.com/install.sh | sh
## Features
* Built on [Connect](http://github.com/senchalabs/connect)
* Robust routing
* HTTP helpers (redirection, caching, etc)
* View system supporting 14+ template engines
* Redirection helpers
* Dynamic view helpers
* Content negotiation
* Focus on high performance
* View rendering and partials support
* Environment based configuration
* Executable for generating applications quickly
* Session based flash notifications
* Built on [Connect](http://github.com/senchalabs/connect)
* High test coverage
* Executable for generating applications quickly
* Application level view options
## Philosophy
Via Connect:
The Express philosophy is to provide small, robust tooling for HTTP servers, making
it a great solution for single page applications, web sites, hybrids, or public
HTTP APIs.
Built on Connect, you can use _only_ what you need, and nothing more. Applications
can be as big or as small as you like, even a single file. Express does
not force you to use any specific ORM or template engine. With support for over
14 template engines via [Consolidate.js](http://github.com/visionmedia/consolidate.js),
you can quickly craft your perfect framework.
## More Information
* [Website and Documentation](http://expressjs.com/) stored at [strongloop/expressjs.com](https://github.com/strongloop/expressjs.com)
* Join #express on freenode
* [Google Group](http://groups.google.com/group/express-js) for discussion
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
* Visit the [Wiki](http://github.com/strongloop/express/wiki)
* [Русскоязычная документация](http://jsman.ru/express/)
* Run express examples [online](https://runnable.com/express)
## Viewing Examples
Clone the Express repo, then install the dev dependencies to install all the example / test suite dependencies:
$ git clone git://github.com/strongloop/express.git --depth 1
$ cd express
$ npm install
Then run whichever tests you want:
$ node examples/content-negotiation
You can also view live examples here:
<a href="https://runnable.com/express" target="_blank"><img src="https://runnable.com/external/styles/assets/runnablebtn.png" style="width:67px;height:25px;"></a>
## Running Tests
To run the test suite, first invoke the following command within the repo, installing the development dependencies:
$ npm install
Then run the tests:
```sh
$ npm test
```
* Session support
* Cache API
* Mime helpers
* ETag support
* Persistent flash notifications
* Cookie support
* JSON-RPC
* Logging
* and _much_ more!
## Contributors
https://github.com/strongloop/express/graphs/contributors
The following are the major contributors of Express (in no specific order).
## License
* TJ Holowaychuk ([visionmedia](http://github.com/visionmedia))
* Ciaran Jessup ([ciaranj](http://github.com/ciaranj))
* Aaron Heckmann ([aheckmann](http://github.com/aheckmann))
* Guillermo Rauch ([guille](http://github.com/guille))
## More Information
* Express [Contrib](http://github.com/visionmedia/express-contrib) repo for additional functionality
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
* [Google Group](http://groups.google.com/group/express-js) for discussion
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
## Node Compatibility
The latest release of Express is compatible with node --version:
v0.2.5
and connect --version:
0.3.0
Express 1.x is maintained in the _1.x_ branch.
## License
(The MIT License)
Copyright (c) 2009-2012 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
Copyright (c) 2009-2010 TJ Holowaychuk &lt;tj@vision-media.ca&gt;
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the

View File

@@ -1,13 +0,0 @@
all:
@./run 1 middleware
@./run 5 middleware
@./run 10 middleware
@./run 15 middleware
@./run 20 middleware
@./run 30 middleware
@./run 50 middleware
@./run 100 middleware
@echo
.PHONY: all

View File

@@ -1,23 +0,0 @@
var http = require('http');
var express = require('..');
var app = express();
// number of middleware
var n = parseInt(process.env.MW || '1', 10);
console.log(' %s middleware', n);
while (n--) {
app.use(function(req, res, next){
next();
});
}
var body = new Buffer('Hello World');
app.use(function(req, res, next){
res.send(body);
});
app.listen(3333);

View File

@@ -1,16 +0,0 @@
#!/usr/bin/env bash
echo
MW=$1 node $2 &
pid=$!
sleep 2
wrk 'http://localhost:3333/?foo[bar]=baz' \
-d 3 \
-c 50 \
-t 8 \
| grep 'Requests/sec' \
| awk '{ print " " $2 }'
kill $pid

View File

@@ -4,102 +4,82 @@
* Module dependencies.
*/
var program = require('commander')
, mkdirp = require('mkdirp')
, pkg = require('../package.json')
, version = pkg.version
, os = require('os')
, fs = require('fs');
// CLI
program
.version(version)
.usage('[options] [dir]')
.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);
// Path
var path = program.args.shift() || '.';
// end-of-line code
var eol = os.EOL
// Template engine
program.template = 'jade';
if (program.ejs) program.template = 'ejs';
if (program.jshtml) program.template = 'jshtml';
if (program.hogan) program.template = 'hjs';
var fs = require('fs')
, sys = require('sys')
, exec = require('child_process').exec;
/**
* Routes index template.
* Framework version.
*/
var index = [
''
, '/*'
, ' * GET home page.'
, ' */'
, ''
, 'exports.index = function(req, res){'
, ' res.render(\'index\', { title: \'Express\' });'
, '};'
].join(eol);
var version = '1.0.2';
/**
* Routes users template.
* stdin stream.
*/
var users = [
''
, '/*'
, ' * GET users listing.'
, ' */'
, ''
, 'exports.list = function(req, res){'
, ' res.send("respond with a resource");'
, '};'
].join(eol);
var stdin;
/**
* Add session support.
*/
var sessions = false;
/**
* CSS engine to utilize.
*/
var cssEngine;
/**
* Template engine to utilize.
*/
var templateEngine = 'jade';
/**
* Usage documentation.
*/
var usage = ''
+ '\x1b[1mUsage\x1b[0m: express [options] [PATH]\n'
+ '\n'
+ '\x1b[1mOptions\x1b[0m:\n'
+ ' -s, --sessions Add session support\n'
+ ' -t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade\n'
+ ' -c, --css ENGINE Add stylesheet ENGINE support (less|sass). Defaults to plain css\n'
+ ' -v, --version Output framework version\n'
+ ' -h, --help Output help information\n'
;
/**
* Jade layout template.
*/
var jadeLayout = [
'doctype html'
'!!!'
, 'html'
, ' head'
, ' title= title'
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
, ' body'
, ' block content'
].join(eol);
, ' body!= body'
].join('\n');
/**
* Jade index template.
*/
var jadeIndex = [
'extends layout'
, ''
, 'block content'
, ' h1= title'
, ' p Welcome to #{title}'
].join(eol);
'h1= title'
, 'p Welcome to #{title}'
].join('\n');
/**
* EJS index template.
* EJS layout template.
*/
var ejsIndex = [
var ejsLayout = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
@@ -107,54 +87,19 @@ var ejsIndex = [
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' <h1><%= title %></h1>'
, ' <p>Welcome to <%= title %></p>'
, ' <%- body %>'
, ' </body>'
, '</html>'
].join(eol);
].join('\n');
/**
* JSHTML layout template.
* EJS index template.
*/
var jshtmlLayout = [
'<!DOCTYPE html>'
, '<html>'
, ' <head>'
, ' <title> @write(title) </title>'
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
, ' </head>'
, ' <body>'
, ' @write(body)'
, ' </body>'
, '</html>'
].join(eol);
/**
* JSHTML index template.
*/
var jshtmlIndex = [
'<h1>@write(title)</h1>'
, '<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);
var ejsIndex = [
'<h1><%= title %></h1>'
, '<p>Welcome to <%= title %></p>'
].join('\n');
/**
* Default css template.
@@ -165,11 +110,7 @@ var css = [
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
].join('\n');
/**
* Default less template.
@@ -180,23 +121,44 @@ var less = [
, ' padding: 50px;'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
, '}'
, ''
, 'a {'
, ' color: #00B7FF;'
, '}'
].join(eol);
].join('\n');
/**
* Default stylus template.
* Default sass template.
*/
var stylus = [
var sass = [
'body'
, ' padding: 50px'
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
, 'a'
, ' color: #00B7FF'
].join(eol);
, ' :padding 50px'
, ' :font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
].join('\n');
/**
* App test template.
*/
var appTest = [
""
, "// Run $ expresso"
, ""
, "/**"
, " * Module dependencies."
, " */"
, ""
, "var app = require('../app')"
, " , assert = require('assert');"
, "",
, "module.exports = {"
, " 'GET /': function(){"
, " assert.response(app,"
, " { url: '/' },"
, " { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' }},"
, " function(res){"
, " assert.includes(res.body, '<title>Express</title>');"
, " });"
, " }"
, "};"
].join('\n');
/**
* App template.
@@ -209,49 +171,95 @@ var app = [
, ' */'
, ''
, 'var express = require(\'express\');'
, 'var routes = require(\'./routes\');'
, 'var user = require(\'./routes/user\');'
, 'var http = require(\'http\');'
, 'var path = require(\'path\');'
, ''
, 'var app = express();'
, 'var app = module.exports = express.createServer();'
, ''
, '// all environments'
, 'app.set(\'port\', process.env.PORT || 3000);'
, 'app.set(\'views\', path.join(__dirname, \'views\'));'
, 'app.set(\'view engine\', \':TEMPLATE\');'
, 'app.use(express.favicon());'
, 'app.use(express.logger(\'dev\'));'
, 'app.use(express.json());'
, 'app.use(express.urlencoded());'
, 'app.use(express.methodOverride());{sess}'
, 'app.use(app.router);{css}'
, 'app.use(express.static(path.join(__dirname, \'public\')));'
, '// Configuration'
, ''
, '// development only'
, 'if (\'development\' == app.get(\'env\')) {'
, ' app.use(express.errorHandler());'
, '}'
, ''
, 'app.get(\'/\', routes.index);'
, 'app.get(\'/users\', user.list);'
, ''
, 'http.createServer(app).listen(app.get(\'port\'), function(){'
, ' console.log(\'Express server listening on port \' + app.get(\'port\'));'
, 'app.configure(function(){'
, ' app.set(\'views\', __dirname + \'/views\');'
, ' app.set(\'view engine\', \':TEMPLATE\');'
, ' app.use(express.bodyDecoder());'
, ' app.use(express.methodOverride());:SESS:CSS'
, ' app.use(app.router);'
, ' app.use(express.staticProvider(__dirname + \'/public\'));'
, '});'
, ''
].join(eol);
, 'app.configure(\'development\', function(){'
, ' app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); '
, '});'
, ''
, 'app.configure(\'production\', function(){'
, ' app.use(express.errorHandler()); '
, '});'
, ''
, '// Routes'
, ''
, 'app.get(\'/\', function(req, res){'
, ' res.render(\'index\', {'
, ' locals: {'
, ' title: \'Express\''
, ' }'
, ' });'
, '});'
, ''
, '// Only listen on $ node app.js'
, ''
, 'if (!module.parent) {'
, ' app.listen(3000);'
, ' console.log("Express server listening on port %d", app.address().port)'
, '}'
, ''
].join('\n');
// Parse arguments
var args = process.argv.slice(2)
, path = '.';
while (args.length) {
var arg = args.shift();
switch (arg) {
case '-h':
case '--help':
abort(usage);
break;
case '-v':
case '--version':
abort(version);
break;
case '-s':
case '--session':
case '--sessions':
sessions = true;
break;
case '-c':
case '--css':
args.length
? (cssEngine = args.shift())
: abort('--css requires an argument');
break;
case '-t':
case '--template':
args.length
? (templateEngine = args.shift())
: abort('--template requires an argument');
break;
default:
path = arg;
}
}
// Generate application
(function createApplication(path) {
emptyDirectory(path, function(empty){
if (empty || program.force) {
if (empty) {
createApplicationAt(path);
} else {
program.confirm('destination is not empty, continue? ', function(ok){
confirm('destination is not empty, continue? ', function(ok){
if (ok) {
process.stdin.destroy();
stdin.destroy();
createApplicationAt(path);
} else {
abort('aborting');
@@ -268,105 +276,70 @@ var app = [
*/
function createApplicationAt(path) {
console.log();
process.on('exit', function(){
console.log();
console.log(' install dependencies:');
console.log(' $ cd %s && npm install', path);
console.log();
console.log(' run the app:');
console.log(' $ node app');
console.log();
});
mkdir(path, function(){
mkdir(path + '/public');
mkdir(path + '/pids');
mkdir(path + '/logs');
mkdir(path + '/public/javascripts');
mkdir(path + '/public/images');
mkdir(path + '/public/stylesheets', function(){
switch (program.css) {
switch (cssEngine) {
case 'less':
write(path + '/public/stylesheets/style.less', less);
break;
case 'stylus':
write(path + '/public/stylesheets/style.styl', stylus);
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);
write(path + '/routes/user.js', users);
});
mkdir(path + '/views', function(){
switch (program.template) {
mkdir(path + '/views/partials', function(){
switch (templateEngine) {
case 'ejs':
write(path + '/views/layout.ejs', ejsLayout);
write(path + '/views/index.ejs', ejsIndex);
break;
case 'jade':
write(path + '/views/layout.jade', jadeLayout);
write(path + '/views/index.jade', jadeIndex);
break;
case 'jshtml':
write(path + '/views/layout.jshtml', jshtmlLayout);
write(path + '/views/index.jshtml', jshtmlIndex);
break;
case 'hjs':
write(path + '/views/index.hjs', hoganIndex);
break;
}
});
mkdir(path + '/test', function(){
write(path + '/test/app.test.js', appTest);
});
// CSS Engine support
switch (program.css) {
switch (cssEngine) {
case 'sass':
case 'less':
app = app.replace('{css}', eol + 'app.use(require(\'less-middleware\')({ src: path.join(__dirname, \'public\') }));');
break;
case 'stylus':
app = app.replace('{css}', eol + 'app.use(require(\'stylus\').middleware(path.join(__dirname, \'public\')));');
app = app.replace(':CSS', '\n app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'' + cssEngine + '\'] }));');
break;
default:
app = app.replace('{css}', '');
app = app.replace(':CSS', '');
}
// Session support
app = app.replace('{sess}', program.sessions
? eol + 'app.use(express.session({ secret: \'your secret here\' }));'
app = app.replace(':SESS', sessions
? '\n app.use(express.cookieDecoder());\n app.use(express.session());'
: '');
// Template support
app = app.replace(':TEMPLATE', program.template);
app = app.replace(':TEMPLATE', templateEngine);
// package.json
var pkg = {
name: 'application-name'
, version: '0.0.1'
, private: true
, scripts: { start: 'node app.js' }
, dependencies: {
express: version
}
}
if (program.template) pkg.dependencies[program.template] = '*';
// CSS Engine support
switch (program.css) {
case 'less':
pkg.dependencies['less-middleware'] = '~0.1.15';
break;
default:
if (program.css) {
pkg.dependencies[program.css] = '*';
}
}
write(path + '/package.json', JSON.stringify(pkg, null, 2));
write(path + '/app.js', app);
// Suggestions
process.on('exit', function(){
if (cssEngine) {
console.log(' - make sure you have installed %s: \x1b[33m$ npm install %s\x1b[0m'
, cssEngine
, cssEngine);
}
console.log(' - make sure you have installed %s: \x1b[33m$ npm install %s\x1b[0m'
, templateEngine
, templateEngine);
});
});
}
@@ -379,7 +352,7 @@ function createApplicationAt(path) {
function emptyDirectory(path, fn) {
fs.readdir(path, function(err, files){
if (err && 'ENOENT' != err.code) throw err;
if (err && err.errno !== process.ENOENT) throw err;
fn(!files || !files.length);
});
}
@@ -393,7 +366,37 @@ function emptyDirectory(path, fn) {
function write(path, str) {
fs.writeFile(path, str);
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
console.log(' \x1b[33mcreate\x1b[0m : ' + path);
}
/**
* Prompt confirmation with the given `msg`.
*
* @param {String} msg
* @param {Function} fn
*/
function confirm(msg, fn) {
prompt(msg, function(val){
fn(/^ *y(es)?/i.test(val));
});
}
/**
* Prompt input with the given `msg` and callback `fn`.
*
* @param {String} msg
* @param {Function} fn
*/
function prompt(msg, fn) {
stdin = stdin || process.openStdin();
sys[msg[msg.length - 1] == ' ' ? 'print' : 'puts'](msg);
stdin.setEncoding('ascii');
stdin.addListener('data', function(data){
fn(data);
stdin.removeListener('data', arguments.callee);
});
}
/**
@@ -404,9 +407,9 @@ function write(path, str) {
*/
function mkdir(path, fn) {
mkdirp(path, 0755, function(err){
exec('mkdir -p ' + path, function(err){
if (err) throw err;
console.log(' \033[36mcreate\033[0m : ' + path);
console.log(' \x1b[33mcreate\x1b[0m : ' + path);
fn && fn();
});
}

1676
docs/api.html Normal file

File diff suppressed because it is too large Load Diff

70
docs/applications.1 Normal file
View File

@@ -0,0 +1,70 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "APPLICATIONS" "" "October 2010" "" ""
.
.SH "NAME"
\fBapplications\fR
.
.P
Learnboost \fIhttp://learnboost\.com\fR is a free online gradebook application, aimed to crush the competition with innovative, realtime, enjoyable features\.
.
.P
\fIhttp://learnboost\.com\fR
.
.P
Storify \fIhttp://storify\.com\fR lets you turn what people post on social media websites into compelling stories\.
.
.P
\fIhttp://storify\.com\fR
.
.P
Pakistan Survey \fIhttp://pakistansurvey\.org/\fR by Development Seed \fIhttp://developmentseed\.org\fR, provides in\-depth agency\-specific analysis from regional experts with data from 1,000 interviews across 120 villages in all seven tribal agencies and mapping of 142 reported drone strikes in FATA through July 2010\.
.
.P
\fIhttp://pakistansurvey\.org\fR
.
.P
Markup\.IO \fIhttp://markup\.io\fR allows you to draw directly on \fIany\fR website, then share with others to share your thoughts\.
.
.P
\fIhttp://markup\.io\fR
.
.P
Scrabb\.ly \fIhttp://scrabb\.ly\fR is a massively multiplayer scrabble game initially created for the Node Knockout \fIhttp://nodeknockout\.com/\fR competition\.
.
.P
\fIhttp://scrabb\.ly\fR
.
.P
ClickDummy \fIhttp://clickdummy\.net/\fR is a rapid mockup prototyping application for designers and dummies\.
.
.P
\fIhttp://clickdummy\.net\fR
.
.P
Node Knockout \fIhttp://nodeknockout\.com\fR organized the first ever node\-specific competition with hundreds of contestants\.
.
.P
\fIhttp://nodeknockout\.com\fR
.
.P
Widescript \fIhttp://widescript\.com\fR is an innovative app that helps you focus and interact with your texts \- on your desktop, your couch or on the go\.
.
.P
\fIhttp://widescript\.com\fR
.
.P
e\-resistable \fIhttp://www\.e\-resistible\.co\.uk/\fR is an online order takeaway system providing an intuitive way to fill your belly from your computer!
.
.P
\fIhttp://www\.e\-resistible\.co\.uk\fR
.
.P
Top Twitter Trends \fIhttp://toptwittertrends\.com\fR utilizes MongoDB, Socket\.IO, jQuery and many other exciting libraries to bring you trending tweets in realtime\.
.
.P
\fIhttp://toptwittertrends\.com\fR
.
.P
The applications shown above are not listed in any specific order\. To have an application added or removed please contact TJ Holowaychuk \fIhttp://github\.com/visionmedia\fR\.

252
docs/applications.html Normal file
View File

@@ -0,0 +1,252 @@
<html>
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style>
#tagline {
margin-left: 75px;
margin-bottom: 30px;
color: rgba(255,255,255,0.7); }
html {
background: #1c1c1c url(images/bg.tile.jpg); }
body {
margin: 0;
padding-bottom: 30px;
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
* {
outline: none; }
em {
color: white; }
a img {
border: none !important; }
a {
font-weight: bold;
text-decoration: none;
color: white;
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
a:hover {
opacity: 0.8; }
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
padding: 25px 20px;
background: rgba(0,0,0,0.5);
border: 1px solid #323232;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-border-radius: 5px;
-moz-border-radius: 5px; }
code {
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
ul {
margin: 15px 0;
padding: 0 0 0 35px; }
ul li {
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
img {
margin-left: 20px;
margin-bottom: 15px;
}
#logo {
display: block;
margin-left: 30%;
margin-bottom: 30px;
width: 194px;
height: 51px;
background: url(images/logo.png) 0 0 no-repeat;
text-indent: -99999px; }
#logo:hover {
opacity: 0.7; }
#logo:active {
opacity: 0.3; }
#ribbon {
position: fixed;
top: 0;
right: 0;
z-index: 2; }
#wrapper {
width: 100%;
min-height: 800px;
background: url(images/top.png) 0 0 repeat-x; }
#container {
margin: 0 auto;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
padding: 0;
list-style: none;
}
#toc li a {
font-size: 11px;
}
#menu {
margin-left: 65px;
padding: 0;
padding-bottom: 30px; }
#menu li {
display: inline;
list-style: none; }
#menu li a {
display: block;
float: left;
margin: 0 2px;
padding: 3px 15px;
background: rgba(0,0,0,0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
#menu li a:hover,
#menu li a.active {
background: rgba(0,0,0,0.5); }
#menu li a:active {
background: rgba(0,0,0,0.1);
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
<p id="tagline">
High performance, high class web development for
<a href="http://nodejs.org">Node.js</a>
</p>
<ul id="menu">
<li><a href="index.html">Home</a></li>
<li><a href="guide.html">Guide</a></li>
<li><a href="contrib.html">Contributing</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>applications</code>
</p>
<br />
<p><a href="http://learnboost.com">Learnboost</a> is a free online gradebook application, aimed to crush the competition with innovative, realtime, enjoyable features.</p>
<p><a href="http://learnboost.com"><img src="images/apps/learnboost.png" alt="LearnBoost" /></a></p>
<p><a href="http://storify.com">Storify</a> lets you turn what people post on social media websites into compelling stories.</p>
<p><a href="http://storify.com"><img src="images/apps/storify.png" alt="Storify" /></a></p>
<p><a href="http://pakistansurvey.org/">Pakistan Survey</a> by <a href="http://developmentseed.org">Development Seed</a>, provides in-depth agency-specific analysis from regional experts with data from 1,000 interviews across 120 villages in all seven tribal agencies and mapping of 142 reported drone strikes in FATA through July 2010.</p>
<p><a href="http://pakistansurvey.org"><img src="images/apps/developmentseed.png" alt="Pakistan Survey" /></a></p>
<p><a href="http://markup.io">Markup.IO</a> allows you to draw directly on <em>any</em> website, then share with others to share your thoughts.</p>
<p><a href="http://markup.io"><img src="images/apps/markupio.png" alt="Markup.IO" /></a></p>
<p><a href="http://scrabb.ly">Scrabb.ly</a> is a massively multiplayer scrabble game initially created for the <a href="http://nodeknockout.com/">Node Knockout</a> competition.</p>
<p><a href="http://scrabb.ly"><img src="images/apps/scrabbly.png" alt="Online Realtime Scrabble" /></a></p>
<p><a href="http://clickdummy.net/">ClickDummy</a> is a rapid mockup prototyping application for designers and dummies.</p>
<p><a href="http://clickdummy.net"><img src="images/apps/clickdummy.png" alt="Mockup Prototying" /></a></p>
<p><a href="http://nodeknockout.com">Node Knockout</a> organized the first ever node-specific competition with hundreds of contestants.</p>
<p><a href="http://nodeknockout.com"><img src="images/apps/nodeko.png" alt="Node Knockout Competition Express" /></a></p>
<p><a href="http://widescript.com">Widescript</a> is an innovative app that helps you focus and interact with your texts - on your desktop, your couch or on the go.</p>
<p><a href="http://widescript.com"><img src="images/apps/widescript.png" alt="Widescript" /></a></p>
<p><a href="http://www.e-resistible.co.uk/">e-resistable</a> is an online order takeaway system providing an intuitive way to fill your belly from your computer!</p>
<p><a href="http://www.e-resistible.co.uk"><img src="images/apps/e-resistable.png" alt="Online Takeaway" /></a></p>
<p><a href="http://toptwittertrends.com">Top Twitter Trends</a> utilizes MongoDB, Socket.IO, jQuery and many other exciting libraries to bring you trending tweets in realtime.</p>
<p><a href="http://toptwittertrends.com"><img src="images/apps/toptwittertrends.png" alt="Twitter Trends" /></a></p>
<br />
<p>The applications shown above are not listed in any specific order. To have an application added or removed please contact <a href="http://github.com/visionmedia">TJ Holowaychuk</a>.</p>
</div>
</div>
</div>
</body>
</html>

47
docs/applications.md Normal file
View File

@@ -0,0 +1,47 @@
<br />
[Learnboost](http://learnboost.com) is a free online gradebook application, aimed to crush the competition with innovative, realtime, enjoyable features.
[![LearnBoost](images/apps/learnboost.png)](http://learnboost.com)
[Storify](http://storify.com) lets you turn what people post on social media websites into compelling stories.
[![Storify](images/apps/storify.png)](http://storify.com)
[Pakistan Survey](http://pakistansurvey.org/) by [Development Seed](http://developmentseed.org), provides in-depth agency-specific analysis from regional experts with data from 1,000 interviews across 120 villages in all seven tribal agencies and mapping of 142 reported drone strikes in FATA through July 2010.
[![Pakistan Survey](images/apps/developmentseed.png)](http://pakistansurvey.org)
[Markup.IO](http://markup.io) allows you to draw directly on _any_ website, then share with others to share your thoughts.
[![Markup.IO](images/apps/markupio.png)](http://markup.io)
[Scrabb.ly](http://scrabb.ly) is a massively multiplayer scrabble game initially created for the [Node Knockout](http://nodeknockout.com/) competition.
[![Online Realtime Scrabble](images/apps/scrabbly.png)](http://scrabb.ly)
[ClickDummy](http://clickdummy.net/) is a rapid mockup prototyping application for designers and dummies.
[![Mockup Prototying](images/apps/clickdummy.png)](http://clickdummy.net)
[Node Knockout](http://nodeknockout.com) organized the first ever node-specific competition with hundreds of contestants.
[![Node Knockout Competition Express](images/apps/nodeko.png)](http://nodeknockout.com)
[Widescript](http://widescript.com) is an innovative app that helps you focus and interact with your texts - on your desktop, your couch or on the go.
[![Widescript](images/apps/widescript.png)](http://widescript.com)
[e-resistable](http://www.e-resistible.co.uk/) is an online order takeaway system providing an intuitive way to fill your belly from your computer!
[![Online Takeaway](images/apps/e-resistable.png)](http://www.e-resistible.co.uk)
[Top Twitter Trends](http://toptwittertrends.com) utilizes MongoDB, Socket.IO, jQuery and many other exciting libraries to bring you trending tweets in realtime.
[![Twitter Trends](images/apps/toptwittertrends.png)](http://toptwittertrends.com)
<br />
The applications shown above are not listed in any specific order. To have an application added or removed please contact [TJ Holowaychuk](http://github.com/visionmedia).

82
docs/contrib.1 Normal file
View File

@@ -0,0 +1,82 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "CONTRIB" "" "October 2010" "" ""
.
.SH "NAME"
\fBcontrib\fR
.
.SS "Development Dependencies"
Express development dependencies are stored within the \fI\./support\fR directory\. To update them execute:
.
.IP "" 4
.
.nf
$ git submodule update \-\-init
.
.fi
.
.IP "" 0
.
.SS "Running Tests"
Express uses the Expresso \fIhttp://github\.com/visionmedia/expresso\fR TDD framework to write and run elegant test suites extremely fast\. To run all test suites simply execute:
.
.IP "" 4
.
.nf
$ make test
.
.fi
.
.IP "" 0
.
.P
To target specific suites we may specify the files via:
.
.IP "" 4
.
.nf
$ make test TESTS=test/view\.test\.js
.
.fi
.
.IP "" 0
.
.P
To check test coverage run:
.
.IP "" 4
.
.nf
$ make test\-cov
.
.fi
.
.IP "" 0
.
.SS "Contributions"
To accept a contribution, you should follow these guidelines:
.
.IP "\(bu" 4
All tests \fImust\fR pass
.
.IP "\(bu" 4
Your alterations or additions \fImust\fR include tests
.
.IP "\(bu" 4
Your commit(s) should be \fIfocused\fR, do not commit once for several changes
.
.IP "\(bu" 4
Do \fInot\fR alter release information such as the \fIversion\fR, or \fIHistory\.md\fR
.
.IP "\(bu" 4
Indents are \fI2\fR spaces\.
.
.IP "" 0
.
.SS "Documentation"
To contribute documentation edit the markdown files in \fI\./docs\fR, however do \fInot\fR run \fImake docs\fR, as they will be re\-built and published with each release\.

247
docs/contrib.html Normal file
View File

@@ -0,0 +1,247 @@
<html>
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style>
#tagline {
margin-left: 75px;
margin-bottom: 30px;
color: rgba(255,255,255,0.7); }
html {
background: #1c1c1c url(images/bg.tile.jpg); }
body {
margin: 0;
padding-bottom: 30px;
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
* {
outline: none; }
em {
color: white; }
a img {
border: none !important; }
a {
font-weight: bold;
text-decoration: none;
color: white;
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
a:hover {
opacity: 0.8; }
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
padding: 25px 20px;
background: rgba(0,0,0,0.5);
border: 1px solid #323232;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-border-radius: 5px;
-moz-border-radius: 5px; }
code {
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
ul {
margin: 15px 0;
padding: 0 0 0 35px; }
ul li {
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
img {
margin-left: 20px;
margin-bottom: 15px;
}
#logo {
display: block;
margin-left: 30%;
margin-bottom: 30px;
width: 194px;
height: 51px;
background: url(images/logo.png) 0 0 no-repeat;
text-indent: -99999px; }
#logo:hover {
opacity: 0.7; }
#logo:active {
opacity: 0.3; }
#ribbon {
position: fixed;
top: 0;
right: 0;
z-index: 2; }
#wrapper {
width: 100%;
min-height: 800px;
background: url(images/top.png) 0 0 repeat-x; }
#container {
margin: 0 auto;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
padding: 0;
list-style: none;
}
#toc li a {
font-size: 11px;
}
#menu {
margin-left: 65px;
padding: 0;
padding-bottom: 30px; }
#menu li {
display: inline;
list-style: none; }
#menu li a {
display: block;
float: left;
margin: 0 2px;
padding: 3px 15px;
background: rgba(0,0,0,0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
#menu li a:hover,
#menu li a.active {
background: rgba(0,0,0,0.5); }
#menu li a:active {
background: rgba(0,0,0,0.1);
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
<p id="tagline">
High performance, high class web development for
<a href="http://nodejs.org">Node.js</a>
</p>
<ul id="menu">
<li><a href="index.html">Home</a></li>
<li><a href="guide.html">Guide</a></li>
<li><a href="contrib.html">Contributing</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>contrib</code>
</p>
<h3 id="Development-Dependencies">Development Dependencies</h3>
<p>Express development dependencies are stored within the <em>./support</em> directory. To
update them execute:</p>
<pre><code>$ git submodule update --init
</code></pre>
<h3 id="Running-Tests">Running Tests</h3>
<p>Express uses the <a href="http://github.com/visionmedia/expresso">Expresso</a> TDD
framework to write and run elegant test suites extremely fast. To run all test suites
simply execute:</p>
<pre><code>$ make test
</code></pre>
<p>To target specific suites we may specify the files via:</p>
<pre><code>$ make test TESTS=test/view.test.js
</code></pre>
<p>To check test coverage run:</p>
<pre><code>$ make test-cov
</code></pre>
<h3 id="Contributions">Contributions</h3>
<p>To accept a contribution, you should follow these guidelines:</p>
<ul>
<li>All tests <em>must</em> pass</li>
<li>Your alterations or additions <em>must</em> include tests</li>
<li>Your commit(s) should be <em>focused</em>, do not commit once for several changes</li>
<li>Do <em>not</em> alter release information such as the <em>version</em>, or <em>History.md</em></li>
<li>Indents are <em>2</em> spaces.</li>
</ul>
<h3 id="Documentation">Documentation</h3>
<p>To contribute documentation edit the markdown files in <em>./docs</em>, however
do <em>not</em> run <em>make docs</em>, as they will be re-built and published with each release.</p>
</div>
</div>
</div>
</body>
</html>

38
docs/contrib.md Normal file
View File

@@ -0,0 +1,38 @@
### Development Dependencies
Express development dependencies are stored within the _./support_ directory. To
update them execute:
$ git submodule update --init
### Running Tests
Express uses the [Expresso](http://github.com/visionmedia/expresso) TDD
framework to write and run elegant test suites extremely fast. To run all test suites
simply execute:
$ make test
To target specific suites we may specify the files via:
$ make test TESTS=test/view.test.js
To check test coverage run:
$ make test-cov
### Contributions
To accept a contribution, you should follow these guidelines:
* All tests _must_ pass
* Your alterations or additions _must_ include tests
* Your commit(s) should be _focused_, do not commit once for several changes
* Do _not_ alter release information such as the _version_, or _History.md_
* Indents are _2_ spaces.
### Documentation
To contribute documentation edit the markdown files in _./docs_, however
do _not_ run _make docs_, as they will be re-built and published with each release.

30
docs/executable.1 Normal file
View File

@@ -0,0 +1,30 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "EXECUTABLE" "" "October 2010" "" ""
.
.SH "NAME"
\fBexecutable\fR
.
.SH "Synopsis"
.
.nf
express [\-h|\-\-help] [\-v|\-\-version] [\-c|\-css ENGINE] [PATH]
.
.fi
.
.SH "Description"
The \fBexpress\fR executable generates apps at the given \fBPATH\fR or the current working directory\. Although Express is not bound to a specific application structure, this executable creates a maintainable base app\.
.
.SH "Options"
.
.nf
\-s, \-\-sessions Add session support
\-c, \-\-css ENGINE Add css ENGINE support (less|sass)\. Defaults to plain css
\-v, \-\-version Output framework version
\-h, \-\-help Display help information
.
.fi

221
docs/executable.html Normal file
View File

@@ -0,0 +1,221 @@
<html>
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style>
#tagline {
margin-left: 75px;
margin-bottom: 30px;
color: rgba(255,255,255,0.7); }
html {
background: #1c1c1c url(images/bg.tile.jpg); }
body {
margin: 0;
padding-bottom: 30px;
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
* {
outline: none; }
em {
color: white; }
a img {
border: none !important; }
a {
font-weight: bold;
text-decoration: none;
color: white;
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
a:hover {
opacity: 0.8; }
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
padding: 25px 20px;
background: rgba(0,0,0,0.5);
border: 1px solid #323232;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-border-radius: 5px;
-moz-border-radius: 5px; }
code {
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
ul {
margin: 15px 0;
padding: 0 0 0 35px; }
ul li {
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
img {
margin-left: 20px;
margin-bottom: 15px;
}
#logo {
display: block;
margin-left: 30%;
margin-bottom: 30px;
width: 194px;
height: 51px;
background: url(images/logo.png) 0 0 no-repeat;
text-indent: -99999px; }
#logo:hover {
opacity: 0.7; }
#logo:active {
opacity: 0.3; }
#ribbon {
position: fixed;
top: 0;
right: 0;
z-index: 2; }
#wrapper {
width: 100%;
min-height: 800px;
background: url(images/top.png) 0 0 repeat-x; }
#container {
margin: 0 auto;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
padding: 0;
list-style: none;
}
#toc li a {
font-size: 11px;
}
#menu {
margin-left: 65px;
padding: 0;
padding-bottom: 30px; }
#menu li {
display: inline;
list-style: none; }
#menu li a {
display: block;
float: left;
margin: 0 2px;
padding: 3px 15px;
background: rgba(0,0,0,0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
#menu li a:hover,
#menu li a.active {
background: rgba(0,0,0,0.5); }
#menu li a:active {
background: rgba(0,0,0,0.1);
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
<p id="tagline">
High performance, high class web development for
<a href="http://nodejs.org">Node.js</a>
</p>
<ul id="menu">
<li><a href="index.html">Home</a></li>
<li><a href="guide.html">Guide</a></li>
<li><a href="contrib.html">Contributing</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>executable</code>
</p>
<h2 id="Synopsis">Synopsis</h2>
<pre><code>express [-h|--help] [-v|--version] [-c|-css ENGINE] [PATH]
</code></pre>
<h2 id="Description">Description</h2>
<p>The <code>express</code> executable generates apps at the given <strong>PATH</strong> or the
current working directory. Although Express is not bound to a specific
application structure, this executable creates a maintainable base app.</p>
<h2 id="Options">Options</h2>
<pre><code>-s, --sessions Add session support
-c, --css ENGINE Add css ENGINE support (less|sass). Defaults to plain css
-v, --version Output framework version
-h, --help Display help information
</code></pre>
</div>
</div>
</div>
</body>
</html>

18
docs/executable.md Normal file
View File

@@ -0,0 +1,18 @@
## Synopsis
express [-h|--help] [-v|--version] [-c|-css ENGINE] [PATH]
## Description
The `express` executable generates apps at the given **PATH** or the
current working directory. Although Express is not bound to a specific
application structure, this executable creates a maintainable base app.
## Options
-s, --sessions Add session support
-c, --css ENGINE Add css ENGINE support (less|sass). Defaults to plain css
-v, --version Output framework version
-h, --help Display help information

1871
docs/guide.1 Normal file

File diff suppressed because it is too large Load Diff

1419
docs/guide.html Normal file

File diff suppressed because it is too large Load Diff

1043
docs/guide.md Normal file

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 159 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

BIN
docs/images/apps/nodeko.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 181 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

BIN
docs/images/bg.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
docs/images/bg.tile.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.0 KiB

BIN
docs/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.2 KiB

BIN
docs/images/top.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

106
docs/index.1 Normal file
View File

@@ -0,0 +1,106 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "INDEX" "" "October 2010" "" ""
.
.SH "NAME"
\fBindex\fR
.
.IP "" 4
.
.nf
var app = express\.createServer();
app\.get(\'/\', function(req, res){
res\.send(\'Hello World\');
});
app\.listen(3000);
.
.fi
.
.IP "" 0
.
.SH "Features"
.
.IP "\(bu" 4
Robust routing
.
.IP "\(bu" 4
Redirection helpers
.
.IP "\(bu" 4
Dynamic view helpers
.
.IP "\(bu" 4
Application level view options
.
.IP "\(bu" 4
Content negotiation
.
.IP "\(bu" 4
Focus on high performance
.
.IP "\(bu" 4
View rendering and partials support
.
.IP "\(bu" 4
Environment based configuration
.
.IP "\(bu" 4
Session based flash notifications
.
.IP "\(bu" 4
Built on Connect \fIhttp://github\.com/senchalabs/connect\fR
.
.IP "\(bu" 4
Executable \fIexecutable\.html\fR for generating applications quickly
.
.IP "\(bu" 4
High test coverage
.
.IP "" 0
.
.SH "Contributors"
The following are the major contributors of Express (in no specific order)\.
.
.IP "\(bu" 4
TJ Holowaychuk (visionmedia \fIhttp://github\.com/visionmedia\fR)
.
.IP "\(bu" 4
Ciaran Jessup (ciaranj \fIhttp://github\.com/ciaranj\fR)
.
.IP "\(bu" 4
Aaron Heckmann (aheckmann \fIhttp://github\.com/aheckmann\fR)
.
.IP "\(bu" 4
Guillermo Rauch (guille \fIhttp://github\.com/guille\fR)
.
.IP "" 0
.
.SH "More Information"
.
.IP "\(bu" 4
Google Group \fIhttp://groups\.google\.com/group/express\-js\fR for discussion
.
.IP "\(bu" 4
Follow tjholowaychuk \fIhttp://twitter\.com/tjholowaychuk\fR on twitter for updates
.
.IP "\(bu" 4
Annotated source documentation \fIapi\.html\fR
.
.IP "\(bu" 4
View the Connect \fIhttp://github\.com/senchalabs/connect\fR repo for middleware usage
.
.IP "\(bu" 4
View the Connect Wiki \fIhttp://wiki\.github\.com/senchalabs/connect/\fR for contrib middleware
.
.IP "\(bu" 4
View the examples \fIhttp://github\.com/visionmedia/express/tree/master/examples/\fR
.
.IP "\(bu" 4
View the source \fIhttp://github\.com/visionmedia/express\fR
.
.IP "" 0

254
docs/index.html Normal file
View File

@@ -0,0 +1,254 @@
<html>
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style>
#tagline {
margin-left: 75px;
margin-bottom: 30px;
color: rgba(255,255,255,0.7); }
html {
background: #1c1c1c url(images/bg.tile.jpg); }
body {
margin: 0;
padding-bottom: 30px;
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
* {
outline: none; }
em {
color: white; }
a img {
border: none !important; }
a {
font-weight: bold;
text-decoration: none;
color: white;
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
a:hover {
opacity: 0.8; }
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
padding: 25px 20px;
background: rgba(0,0,0,0.5);
border: 1px solid #323232;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-border-radius: 5px;
-moz-border-radius: 5px; }
code {
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
ul {
margin: 15px 0;
padding: 0 0 0 35px; }
ul li {
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
img {
margin-left: 20px;
margin-bottom: 15px;
}
#logo {
display: block;
margin-left: 30%;
margin-bottom: 30px;
width: 194px;
height: 51px;
background: url(images/logo.png) 0 0 no-repeat;
text-indent: -99999px; }
#logo:hover {
opacity: 0.7; }
#logo:active {
opacity: 0.3; }
#ribbon {
position: fixed;
top: 0;
right: 0;
z-index: 2; }
#wrapper {
width: 100%;
min-height: 800px;
background: url(images/top.png) 0 0 repeat-x; }
#container {
margin: 0 auto;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
padding: 0;
list-style: none;
}
#toc li a {
font-size: 11px;
}
#menu {
margin-left: 65px;
padding: 0;
padding-bottom: 30px; }
#menu li {
display: inline;
list-style: none; }
#menu li a {
display: block;
float: left;
margin: 0 2px;
padding: 3px 15px;
background: rgba(0,0,0,0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
#menu li a:hover,
#menu li a.active {
background: rgba(0,0,0,0.5); }
#menu li a:active {
background: rgba(0,0,0,0.1);
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
<p id="tagline">
High performance, high class web development for
<a href="http://nodejs.org">Node.js</a>
</p>
<ul id="menu">
<li><a href="index.html">Home</a></li>
<li><a href="guide.html">Guide</a></li>
<li><a href="contrib.html">Contributing</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>index</code>
</p>
<pre><code>var app = express.createServer();
app.get('/', function(req, res){
res.send('Hello World');
});
app.listen(3000);
</code></pre>
<h2 id="Features">Features</h2>
<ul>
<li>Robust routing</li>
<li>Redirection helpers</li>
<li>Dynamic view helpers</li>
<li>Application level view options</li>
<li>Content negotiation</li>
<li>Focus on high performance</li>
<li>View rendering and partials support</li>
<li>Environment based configuration</li>
<li>Session based flash notifications</li>
<li>Built on <a href="http://github.com/senchalabs/connect">Connect</a></li>
<li><a href="executable.html">Executable</a> for generating applications quickly</li>
<li>High test coverage</li>
</ul>
<h2 id="Contributors">Contributors</h2>
<p>The following are the major contributors of Express (in no specific order).</p>
<ul>
<li>TJ Holowaychuk (<a href="http://github.com/visionmedia">visionmedia</a>)</li>
<li>Ciaran Jessup (<a href="http://github.com/ciaranj">ciaranj</a>)</li>
<li>Aaron Heckmann (<a href="http://github.com/aheckmann">aheckmann</a>)</li>
<li>Guillermo Rauch (<a href="http://github.com/guille">guille</a>)</li>
</ul>
<h2 id="More-Information">More Information</h2>
<ul>
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
<li>Follow <a href="http://twitter.com/tjholowaychuk">tjholowaychuk</a> on twitter for updates</li>
<li>Annotated source <a href="api.html">documentation</a></li>
<li>View the <a href="http://github.com/senchalabs/connect">Connect</a> repo for middleware usage</li>
<li>View the <a href="http://wiki.github.com/senchalabs/connect/">Connect Wiki</a> for contrib middleware</li>
<li>View the <a href="http://github.com/visionmedia/express/tree/master/examples/">examples</a></li>
<li>View the <a href="http://github.com/visionmedia/express">source</a></li>
</ul>
</div>
</div>
</div>
</body>
</html>

42
docs/index.md Normal file
View File

@@ -0,0 +1,42 @@
var app = express.createServer();
app.get('/', function(req, res){
res.send('Hello World');
});
app.listen(3000);
## Features
* Robust routing
* Redirection helpers
* Dynamic view helpers
* Application level view options
* Content negotiation
* Focus on high performance
* View rendering and partials support
* Environment based configuration
* Session based flash notifications
* Built on [Connect](http://github.com/senchalabs/connect)
* [Executable](executable.html) for generating applications quickly
* High test coverage
## Contributors
The following are the major contributors of Express (in no specific order).
* TJ Holowaychuk ([visionmedia](http://github.com/visionmedia))
* Ciaran Jessup ([ciaranj](http://github.com/ciaranj))
* Aaron Heckmann ([aheckmann](http://github.com/aheckmann))
* Guillermo Rauch ([guille](http://github.com/guille))
## More Information
* [Google Group](http://groups.google.com/group/express-js) for discussion
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
* Annotated source [documentation](api.html)
* View the [Connect](http://github.com/senchalabs/connect) repo for middleware usage
* View the [Connect Wiki](http://wiki.github.com/senchalabs/connect/) for contrib middleware
* View the [examples](http://github.com/visionmedia/express/tree/master/examples/)
* View the [source](http://github.com/visionmedia/express)

4
docs/layout/foot.html Normal file
View File

@@ -0,0 +1,4 @@
</div>
</div>
</body>
</html>

192
docs/layout/head.html Normal file
View File

@@ -0,0 +1,192 @@
<html>
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style>
#tagline {
margin-left: 75px;
margin-bottom: 30px;
color: rgba(255,255,255,0.7); }
html {
background: #1c1c1c url(images/bg.tile.jpg); }
body {
margin: 0;
padding-bottom: 30px;
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
* {
outline: none; }
em {
color: white; }
a img {
border: none !important; }
a {
font-weight: bold;
text-decoration: none;
color: white;
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
a:hover {
opacity: 0.8; }
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
padding: 25px 20px;
background: rgba(0,0,0,0.5);
border: 1px solid #323232;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-border-radius: 5px;
-moz-border-radius: 5px; }
code {
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
ul {
margin: 15px 0;
padding: 0 0 0 35px; }
ul li {
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
img {
margin-left: 20px;
margin-bottom: 15px;
}
#logo {
display: block;
margin-left: 30%;
margin-bottom: 30px;
width: 194px;
height: 51px;
background: url(images/logo.png) 0 0 no-repeat;
text-indent: -99999px; }
#logo:hover {
opacity: 0.7; }
#logo:active {
opacity: 0.3; }
#ribbon {
position: fixed;
top: 0;
right: 0;
z-index: 2; }
#wrapper {
width: 100%;
min-height: 800px;
background: url(images/top.png) 0 0 repeat-x; }
#container {
margin: 0 auto;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
padding: 0;
list-style: none;
}
#toc li a {
font-size: 11px;
}
#menu {
margin-left: 65px;
padding: 0;
padding-bottom: 30px; }
#menu li {
display: inline;
list-style: none; }
#menu li a {
display: block;
float: left;
margin: 0 2px;
padding: 3px 15px;
background: rgba(0,0,0,0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
#menu li a:hover,
#menu li a.active {
background: rgba(0,0,0,0.5); }
#menu li a:active {
background: rgba(0,0,0,0.1);
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
<p id="tagline">
High performance, high class web development for
<a href="http://nodejs.org">Node.js</a>
</p>
<ul id="menu">
<li><a href="index.html">Home</a></li>
<li><a href="guide.html">Guide</a></li>
<li><a href="contrib.html">Contributing</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>

347
docs/migrate.1 Normal file
View File

@@ -0,0 +1,347 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "MIGRATE" "" "October 2010" "" ""
.
.SH "NAME"
\fBmigrate\fR
.
.SS "Built On Connect"
Express 1\.x is written to run on\-top of the Connect \fIhttp://extjs\.github\.com/Connect\fR middlware framework, thus the \fIPlugin\fR has been replaced by Connect\'s middleware\. By abstracting our middleware to Connect we allow additional community frameworks to develop robust, high\-level frameworks using the same technologies as Express\.
.
.SS "Creating Applications"
Previously due to legacy code implemented in the early days of node, Express unfortunately had some globals\. The DSL would previously be accessed as shown below:
.
.IP "" 4
.
.nf
require(\'express\');
configure(function(){
// app configuration
});
get(\'/\', function(){
return \'hello world\';
});
.
.fi
.
.IP "" 0
.
.P
Now we utilize the CommonJS module system appropriately, and introduce \fIexpress\.createServer()\fR which accepts the same arguments as \fIhttp\.createServer()\fR:
.
.IP "" 4
.
.nf
var express = require(\'express\'),
app = express\.createServer();
app\.configure(function(){
// app configuration
});
app\.get(\'/\', function(req, res){
res\.send(\'hello world\');
});
.
.fi
.
.IP "" 0
.
.P
Express 1\.x does \fInot\fR currently allow returning of a string\.
.
.SS "Plugins vs Middleware"
Previously Express was bundled with plugins, which were essentially what are now Connect middleware\. Previously plugins would be utilized in a manor similar to below:
.
.IP "" 4
.
.nf
use(Logger);
use(MethodOverride);
use(Cookie);
.
.fi
.
.IP "" 0
.
.P
Which we can now \fIuse()\fR within our app, or pass to the \fIexpress\.createServer()\fR method:
.
.IP "" 4
.
.nf
var app = express\.createServer(
express\.logger(),
express\.methodOverride(),
express\.cookieDecoder()
);
.
.fi
.
.IP "" 0
.
.P
or:
.
.IP "" 4
.
.nf
var app = express\.createServer();
app\.use(express\.logger());
app\.use(express\.methodOverride());
app\.use(express\.cookieDecoder());
.
.fi
.
.IP "" 0
.
.P
For documentation on creating Connect middleware visit Middleware Authoring \fIhttp://extjs\.github\.com/Connect/#Middleware\-Authoring\fR\.
.
.SS "Running Applications"
Previously a global function \fIrun()\fR, was available:
.
.IP "" 4
.
.nf
run();
.
.fi
.
.IP "" 0
.
.P
The new \fIexpress\.Server\fR has the same API as \fIhttp\.Server\fR, so we can do things like:
.
.IP "" 4
.
.nf
app\.listen();
app\.listen(3000);
.
.fi
.
.IP "" 0
.
.SS "Route Parameters"
Previously we could use \fIthis\.param()\fR to attempt fetching a route, query string, or request body parameter:
.
.IP "" 4
.
.nf
get(\'/user/:id\', function(){
this\.param(\'id\');
});
.
.fi
.
.IP "" 0
.
.P
Polymorphic parameter access can be done using \fBreq\.param()\fR:
.
.IP "" 4
.
.nf
app\.get(\'/user/:id\', function(req, res){
req\.param(\'id\');
});
.
.fi
.
.IP "" 0
.
.P
Route parameters are available via \fBreq\.params\fR:
.
.IP "" 4
.
.nf
app\.get(\'/user/:id\', function(req, res){
req\.params\.id;
});
.
.fi
.
.IP "" 0
.
.SS "Passing Route Control"
Old express had a weak notion of route passing, which did not support async, and was never properly implemented for practical use:
.
.IP "" 4
.
.nf
get(\'/\', function(){
this\.pass(\'/foobar\');
});
.
.fi
.
.IP "" 0
.
.P
Now Express has access to Connect\'s \fInext()\fR function, which is passed as the third and final argument\. Calling \fInext()\fR will pass control to the next \fImatching route\fR, or continue down the stack of Connect middleware\.
.
.IP "" 4
.
.nf
app\.get(\'/user/:id?\', function(req, res, next){
next();
});
app\.get(\'/user\', function(){
// \.\.\. respond
});
.
.fi
.
.IP "" 0
.
.SS "View Rendering"
View filenames no longer take the form \fINAME\fR\.\fITYPE\fR\.\fIENGINE\fR, the \fIContent\-Type\fR can be set via \fIres\.contentType()\fR or \fIres\.header()\fR\. For example what was previously \fIlayout\.html\.haml\fR, should now be \fIlayout\.haml\fR\.
.
.P
Previously a view render looked something like this:
.
.IP "" 4
.
.nf
get(\'/\', function(){
this\.render(\'index\.html\.haml\', {
locals: { title: \'My Site\' }
});
});
.
.fi
.
.IP "" 0
.
.P
We now have \fIres\.render()\fR, however the options passed to haml \fIhttp://github\.com/visionmedia/haml\.js\fR, jade \fIhttp://github\.com/visionmedia/jade\fR, and others remain the same\.
.
.IP "" 4
.
.nf
app\.get(\'/\', function(req, res){
res\.render(\'index\.haml\', {
locals: { title: \'My Site\' }
});
});
.
.fi
.
.IP "" 0
.
.P
Previously rendering of a collection via \fIpartial()\fR would look something like this:
.
.IP "" 4
.
.nf
this\.partial(\'comment\.html\.haml\', { collection: comments });
.
.fi
.
.IP "" 0
.
.P
Although this worked just fine, it was generally to verbose, the similar but new API looks like this, as \fIpartial()\fR is \fIalways\fR passed as a local variable:
.
.IP "" 4
.
.nf
partial(\'comment\.haml\', { collection: comments });
.
.fi
.
.IP "" 0
.
.P
To make things even less verbose we can assume the extension when omitted:
.
.IP "" 4
.
.nf
partial(\'comment\', { collection: comments });
.
.fi
.
.IP "" 0
.
.P
And once again even further, when rendering a collection we can simply pass an array, if no other options are desired:
.
.IP "" 4
.
.nf
partial(\'comments\', comments);
.
.fi
.
.IP "" 0
.
.SS "Redirecting"
Previously you would
.
.IP "" 4
.
.nf
this\.redirect(\'/somewhere\');
.
.fi
.
.IP "" 0
.
.P
However you would now:
.
.IP "" 4
.
.nf
res\.redirect(\'/somewhere\');
res\.redirect(\'/somewhere\', 301);
.
.fi
.
.IP "" 0
.
.SS "HTTP Client"
Previously Express provided a high level http client, this library is no more as it does not belong in Express, however it may be resurrected as a separate module\.
.
.SS "Core Extensions"
Express is no longer dependent on the JavaScript Extensions \fIhttp://github\.com/visionmedia/ext\.js\fR library, so those of you using the methods provided by it such as \fBObject\.merge(a, b)\fR will need to roll your own, or install the module via:
.
.IP "" 4
.
.nf
$ npm install ext
.
.fi
.
.IP "" 0

412
docs/migrate.html Normal file
View File

@@ -0,0 +1,412 @@
<html>
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<style>
#tagline {
margin-left: 75px;
margin-bottom: 30px;
color: rgba(255,255,255,0.7); }
html {
background: #1c1c1c url(images/bg.tile.jpg); }
body {
margin: 0;
padding-bottom: 30px;
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
* {
outline: none; }
em {
color: white; }
a img {
border: none !important; }
a {
font-weight: bold;
text-decoration: none;
color: white;
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
a:hover {
opacity: 0.8; }
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
padding: 25px 20px;
background: rgba(0,0,0,0.5);
border: 1px solid #323232;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-border-radius: 5px;
-moz-border-radius: 5px; }
code {
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
ul {
margin: 15px 0;
padding: 0 0 0 35px; }
ul li {
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
img {
margin-left: 20px;
margin-bottom: 15px;
}
#logo {
display: block;
margin-left: 30%;
margin-bottom: 30px;
width: 194px;
height: 51px;
background: url(images/logo.png) 0 0 no-repeat;
text-indent: -99999px; }
#logo:hover {
opacity: 0.7; }
#logo:active {
opacity: 0.3; }
#ribbon {
position: fixed;
top: 0;
right: 0;
z-index: 2; }
#wrapper {
width: 100%;
min-height: 800px;
background: url(images/top.png) 0 0 repeat-x; }
#container {
margin: 0 auto;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
padding: 0;
list-style: none;
}
#toc li a {
font-size: 11px;
}
#menu {
margin-left: 65px;
padding: 0;
padding-bottom: 30px; }
#menu li {
display: inline;
list-style: none; }
#menu li a {
display: block;
float: left;
margin: 0 2px;
padding: 3px 15px;
background: rgba(0,0,0,0.2);
-webkit-border-radius: 8px;
-moz-border-radius: 8px;
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
-webkit-transition-duration: 0.15s;
-webkit-transition-timing-function: ease-out; }
#menu li a:hover,
#menu li a.active {
background: rgba(0,0,0,0.5); }
#menu li a:active {
background: rgba(0,0,0,0.1);
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
<p id="tagline">
High performance, high class web development for
<a href="http://nodejs.org">Node.js</a>
</p>
<ul id="menu">
<li><a href="index.html">Home</a></li>
<li><a href="guide.html">Guide</a></li>
<li><a href="contrib.html">Contributing</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>migrate</code>
</p>
<h3 id="Built-On-Connect">Built On Connect</h3>
<p>Express 1.x is written to run on-top of the <a href="http://extjs.github.com/Connect">Connect</a> middlware
framework, thus the <em>Plugin</em> has been replaced by Connect's middleware. By abstracting our middleware
to Connect we allow additional community frameworks to develop robust, high-level frameworks using
the same technologies as Express.</p>
<h3 id="Creating-Applications">Creating Applications</h3>
<p>Previously due to legacy code implemented in the early days of node,
Express unfortunately had some globals. The DSL would previously be
accessed as shown below:</p>
<pre><code>require('express');
configure(function(){
// app configuration
});
get('/', function(){
return 'hello world';
});
</code></pre>
<p>Now we utilize the CommonJS module system appropriately, and
introduce <em>express.createServer()</em> which accepts the same arguments
as <em>http.createServer()</em>:</p>
<pre><code>var express = require('express'),
app = express.createServer();
app.configure(function(){
// app configuration
});
app.get('/', function(req, res){
res.send('hello world');
});
</code></pre>
<p>Express 1.x does <em>not</em> currently allow returning of a string.</p>
<h3 id="Plugins-vs-Middleware">Plugins vs Middleware</h3>
<p>Previously Express was bundled with plugins, which were essentially what
are now Connect middleware. Previously plugins would be utilized in a manor
similar to below:</p>
<pre><code>use(Logger);
use(MethodOverride);
use(Cookie);
</code></pre>
<p>Which we can now <em>use()</em> within our app, or pass to the <em>express.createServer()</em> method:</p>
<pre><code>var app = express.createServer(
express.logger(),
express.methodOverride(),
express.cookieDecoder()
);
</code></pre>
<p>or:</p>
<pre><code>var app = express.createServer();
app.use(express.logger());
app.use(express.methodOverride());
app.use(express.cookieDecoder());
</code></pre>
<p>For documentation on creating Connect middleware visit <a href="http://extjs.github.com/Connect/#Middleware-Authoring">Middleware Authoring</a>.</p>
<h3 id="Running-Applications">Running Applications</h3>
<p>Previously a global function <em>run()</em>, was available:</p>
<pre><code>run();
</code></pre>
<p>The new <em>express.Server</em> has the same API as <em>http.Server</em>,
so we can do things like:</p>
<pre><code>app.listen();
app.listen(3000);
</code></pre>
<h3 id="Route-Parameters">Route Parameters</h3>
<p>Previously we could use <em>this.param()</em> to attempt
fetching a route, query string, or request body parameter:</p>
<pre><code>get('/user/:id', function(){
this.param('id');
});
</code></pre>
<p>Polymorphic parameter access can be done using <code>req.param()</code>:</p>
<pre><code>app.get('/user/:id', function(req, res){
req.param('id');
});
</code></pre>
<p>Route parameters are available via <code>req.params</code>:</p>
<pre><code>app.get('/user/:id', function(req, res){
req.params.id;
});
</code></pre>
<h3 id="Passing-Route-Control">Passing Route Control</h3>
<p>Old express had a weak notion of route passing,
which did not support async, and was never properly
implemented for practical use:</p>
<pre><code>get('/', function(){
this.pass('/foobar');
});
</code></pre>
<p>Now Express has access to Connect's <em>next()</em> function,
which is passed as the third and final argument. Calling <em>next()</em> will
pass control to the next <em>matching route</em>, or continue down the stack
of Connect middleware.</p>
<pre><code>app.get('/user/:id?', function(req, res, next){
next();
});
app.get('/user', function(){
// ... respond
});
</code></pre>
<h3 id="View-Rendering">View Rendering</h3>
<p>View filenames no longer take the form <em>Express</em>.<em>TYPE</em>.<em>ENGINE</em>,
the <em>Content-Type</em> can be set via <em>res.contentType()</em> or
<em>res.header()</em>. For example what was previously <em>layout.html.haml</em>,
should now be <em>layout.haml</em>.</p>
<p>Previously a view render looked something like this:</p>
<pre><code>get('/', function(){
this.render('index.html.haml', {
locals: { title: 'My Site' }
});
});
</code></pre>
<p>We now have <em>res.render()</em>, however the options passed to <a href="http://github.com/visionmedia/haml.js">haml</a>, <a href="http://github.com/visionmedia/jade">jade</a>, and others
remain the same.</p>
<pre><code>app.get('/', function(req, res){
res.render('index.haml', {
locals: { title: 'My Site' }
});
});
</code></pre>
<p>Previously rendering of a collection via <em>partial()</em> would look something like this:</p>
<pre><code>this.partial('comment.html.haml', { collection: comments });
</code></pre>
<p>Although this worked just fine, it was generally to verbose, the similar but new API
looks like this, as <em>partial()</em> is <em>always</em> passed as a local variable:</p>
<pre><code>partial('comment.haml', { collection: comments });
</code></pre>
<p>To make things even less verbose we can assume the extension when omitted:</p>
<pre><code>partial('comment', { collection: comments });
</code></pre>
<p>And once again even further, when rendering a collection we can simply pass
an array, if no other options are desired:</p>
<pre><code>partial('comments', comments);
</code></pre>
<h3 id="Redirecting">Redirecting</h3>
<p>Previously you would</p>
<pre><code>this.redirect('/somewhere');
</code></pre>
<p>However you would now:</p>
<pre><code>res.redirect('/somewhere');
res.redirect('/somewhere', 301);
</code></pre>
<h3 id="HTTP-Client">HTTP Client</h3>
<p>Previously Express provided a high level http client, this library is no more
as it does not belong in Express, however it may be resurrected as a separate module.</p>
<h3 id="Core-Extensions">Core Extensions</h3>
<p>Express is no longer dependent on the <a href="http://github.com/visionmedia/ext.js">JavaScript Extensions</a> library, so those of you using the methods provided by it such as <code>Object.merge(a, b)</code> will need to
roll your own, or install the module via:</p>
<pre><code>$ npm install ext
</code></pre>
</div>
</div>
</div>
</body>
</html>

189
docs/migrate.md Normal file
View File

@@ -0,0 +1,189 @@
### Built On Connect
Express 1.x is written to run on-top of the [Connect](http://extjs.github.com/Connect) middlware
framework, thus the _Plugin_ has been replaced by Connect's middleware. By abstracting our middleware
to Connect we allow additional community frameworks to develop robust, high-level frameworks using
the same technologies as Express.
### Creating Applications
Previously due to legacy code implemented in the early days of node,
Express unfortunately had some globals. The DSL would previously be
accessed as shown below:
require('express');
configure(function(){
// app configuration
});
get('/', function(){
return 'hello world';
});
Now we utilize the CommonJS module system appropriately, and
introduce _express.createServer()_ which accepts the same arguments
as _http.createServer()_:
var express = require('express'),
app = express.createServer();
app.configure(function(){
// app configuration
});
app.get('/', function(req, res){
res.send('hello world');
});
Express 1.x does _not_ currently allow returning of a string.
### Plugins vs Middleware
Previously Express was bundled with plugins, which were essentially what
are now Connect middleware. Previously plugins would be utilized in a manor
similar to below:
use(Logger);
use(MethodOverride);
use(Cookie);
Which we can now _use()_ within our app, or pass to the _express.createServer()_ method:
var app = express.createServer(
express.logger(),
express.methodOverride(),
express.cookieDecoder()
);
or:
var app = express.createServer();
app.use(express.logger());
app.use(express.methodOverride());
app.use(express.cookieDecoder());
For documentation on creating Connect middleware visit [Middleware Authoring](http://extjs.github.com/Connect/#Middleware-Authoring).
### Running Applications
Previously a global function _run()_, was available:
run();
The new _express.Server_ has the same API as _http.Server_,
so we can do things like:
app.listen();
app.listen(3000);
### Route Parameters
Previously we could use _this.param()_ to attempt
fetching a route, query string, or request body parameter:
get('/user/:id', function(){
this.param('id');
});
Polymorphic parameter access can be done using `req.param()`:
app.get('/user/:id', function(req, res){
req.param('id');
});
Route parameters are available via `req.params`:
app.get('/user/:id', function(req, res){
req.params.id;
});
### Passing Route Control
Old express had a weak notion of route passing,
which did not support async, and was never properly
implemented for practical use:
get('/', function(){
this.pass('/foobar');
});
Now Express has access to Connect's _next()_ function,
which is passed as the third and final argument. Calling _next()_ will
pass control to the next _matching route_, or continue down the stack
of Connect middleware.
app.get('/user/:id?', function(req, res, next){
next();
});
app.get('/user', function(){
// ... respond
});
### View Rendering
View filenames no longer take the form _NAME_._TYPE_._ENGINE_,
the _Content-Type_ can be set via _res.contentType()_ or
_res.header()_. For example what was previously _layout.html.haml_,
should now be _layout.haml_.
Previously a view render looked something like this:
get('/', function(){
this.render('index.html.haml', {
locals: { title: 'My Site' }
});
});
We now have _res.render()_, however the options passed to [haml](http://github.com/visionmedia/haml.js), [jade](http://github.com/visionmedia/jade), and others
remain the same.
app.get('/', function(req, res){
res.render('index.haml', {
locals: { title: 'My Site' }
});
});
Previously rendering of a collection via _partial()_ would look something like this:
this.partial('comment.html.haml', { collection: comments });
Although this worked just fine, it was generally to verbose, the similar but new API
looks like this, as _partial()_ is _always_ passed as a local variable:
partial('comment.haml', { collection: comments });
To make things even less verbose we can assume the extension when omitted:
partial('comment', { collection: comments });
And once again even further, when rendering a collection we can simply pass
an array, if no other options are desired:
partial('comments', comments);
### Redirecting
Previously you would
this.redirect('/somewhere');
However you would now:
res.redirect('/somewhere');
res.redirect('/somewhere', 301);
### HTTP Client
Previously Express provided a high level http client, this library is no more
as it does not belong in Express, however it may be resurrected as a separate module.
### Core Extensions
Express is no longer dependent on the [JavaScript Extensions](http://github.com/visionmedia/ext.js) library, so those of you using the methods provided by it such as `Object.merge(a, b)` will need to
roll your own, or install the module via:
$ npm install ext

View File

@@ -1,67 +1,65 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('../..')
, hash = require('./pass').hash;
var express = require('../../lib/express'),
crypto = require('crypto');
var app = module.exports = express();
var app = express.createServer();
// config
app.set('view engine', 'ejs');
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
// middleware
app.use(express.bodyDecoder());
app.use(express.cookieDecoder());
app.use(express.session());
app.use(express.urlencoded({ extended: false }));
app.use(express.session({ secret: 'shhhh, very secret' }));
// Message helper, ideally we would use req.flash()
// however this is more light-weight for an example
// Session-persisted message middleware
app.use(function(req, res, next){
var err = req.session.error
, msg = req.session.success;
delete req.session.error;
delete req.session.success;
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();
app.dynamicHelpers({
message: function(req){
var err = req.session.error,
msg = req.session.success;
delete req.session.error;
delete req.session.success;
if (err) return '<p class="msg error">' + err + '</p>';
if (msg) return '<p class="msg success">' + msg + '</p>';
}
});
// dummy database
// Generate a salt for the user to prevent rainbow table attacks
var users = {
tj: { name: 'tj' }
tj: {
name: 'tj',
salt: 'randomly-generated-salt',
pass: md5('foobar' + 'randomly-generated-salt')
}
};
// 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;
});
// Used to generate a hash of the plain-text password + salt
function md5(str) {
return crypto.createHash('md5').update(str).digest('hex');
}
// Authenticate using our plain-object database of doom!
function authenticate(name, pass, fn) {
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
hash(pass, user.salt, function(err, hash){
if (err) return fn(err);
if (hash == user.hash) return fn(null, user);
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 md5 against the pass / salt, if there is a match we
// found the user
if (user.pass == md5(pass + user.salt)) return fn(null, user);
// Otherwise password is invalid
fn(new Error('invalid password'));
})
}
function restrict(req, res, next) {
@@ -73,52 +71,56 @@ function restrict(req, res, next) {
}
}
function accessLogger(req, res, next) {
console.log('/restricted accessed by %s', req.session.user.name);
next();
}
app.get('/', function(req, res){
res.redirect('login');
res.redirect('/login');
});
app.get('/restricted', restrict, function(req, res){
res.send('Wahoo! restricted area, click to <a href="/logout">logout</a>');
app.get('/restricted', restrict, accessLogger, function(req, res){
res.send('Wahoo! restricted area');
});
app.get('/logout', function(req, res){
// destroy the user's session to log them out
// will be re-created next request
req.session.destroy(function(){
res.redirect('/');
});
// destroy the user's session to log them out
// will be re-created next request
req.session.destroy(function(){
res.redirect('home');
});
});
app.get('/login', function(req, res){
res.render('login');
if (req.session.user) {
req.session.success = 'Authenticated as ' + req.session.user.name
+ ' click to <a href="/logout">logout</a>. '
+ ' You may now access <a href="/restricted">/restricted</a>.';
}
res.render('login');
});
app.post('/login', function(req, res){
authenticate(req.body.username, req.body.password, function(err, user){
if (user) {
// Regenerate session when signing in
// to prevent fixation
req.session.regenerate(function(){
// Store the user's primary key
// in the session store to be retrieved,
// or in this case the entire user object
req.session.user = user;
req.session.success = 'Authenticated as ' + user.name
+ ' click to <a href="/logout">logout</a>. '
+ ' You may now access <a href="/restricted">/restricted</a>.';
res.redirect('back');
});
} else {
req.session.error = 'Authentication failed, please check your '
+ ' username and password.'
+ ' (use "tj" and "foobar")';
res.redirect('login');
}
});
authenticate(req.body.username, req.body.password, function(err, user){
if (user) {
// Regenerate session when signing in
// to prevent fixation
req.session.regenerate(function(){
// Store the user's primary key
// in the session store to be retrieved,
// or in this case the entire user object
req.session.user = user;
res.redirect('back');
});
} else {
req.session.error = 'Authentication failed, please check your '
+ ' username and password.'
+ ' (use "tj" and "foobar")';
res.redirect('back');
}
});
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1,48 +0,0 @@
// 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, function(err, hash){
fn(err, hash.toString('base64'));
});
} 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.toString('base64'));
});
});
}
};

View File

@@ -1,2 +0,0 @@
</body>
</html>

View File

@@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
<title>Authentication Example</title>
<style>
body {
padding: 50px;
@@ -16,3 +16,6 @@
</style>
</head>
<body>
<%- body %>
</body>
</html>

View File

@@ -1,10 +1,6 @@
<% var title = 'Authentication Example' %>
<% include head %>
<h1>Login</h1>
<%- message %>
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
Try accessing <a href="/restricted">/restricted</a>.
<form method="post" action="/login">
<p>
<label>Username:</label>
@@ -17,6 +13,4 @@ Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj"
<p>
<input type="submit" value="Login">
</p>
</form>
<% include foot %>
</form>

View File

@@ -1,27 +0,0 @@
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 });
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,12 +0,0 @@
style.
body {
padding: 50px;
font: 16px "Helvetica Neue", Helvetica;
}
table
for pet in pets
tr
td= pet.name
td= pet.age
td= pet.species

25
examples/blog/app.js Normal file
View File

@@ -0,0 +1,25 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('./../../lib/express');
// Our app IS the exports, this prevents require('./app').app,
// instead it is require('./app');
var app = module.exports = express.createServer();
// Illustrates that an app can be broken into
// several files, but yet extend the same app
require('./main');
require('./contact');
// Illustrates that one app (Server instance) can
// be "mounted" to another at the given route.
app.use('/blog', require('./blog'));
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -0,0 +1,45 @@
/**
* Module dependencies.
*/
var express = require('./../../../lib/express'),
fs = require('fs');
// Export our app as the module
var app = module.exports = express.createServer();
// Set views directory
app.set('views', __dirname + '/views');
// Load our posts
var posts = JSON.parse(fs.readFileSync(__dirname + '/posts.json', 'utf8'));
// Set our default view engine to "ejs"
app.set('view engine', 'ejs');
app.dynamicHelpers({
basepath: function(){
// "this" is the app, we can
// dynamically provide the "home"
// setting to all views
return this.set('home');
}
});
app.get('/', function(req, res){
res.render('index', {
locals: {
posts: posts
}
});
});
app.get('/post/:id', function(req, res){
var id = req.params.id;
res.render('post', {
locals: {
post: posts[id]
}
});
});

View File

@@ -0,0 +1,4 @@
[
{ "id": 0, "title": "Post one", "body": "Just some lame post" },
{ "id": 1, "title": "Post two", "body": "Just another lame post" }
]

View File

@@ -0,0 +1,2 @@
<h2>Posts</h2>
<%- partial('post', { collection: posts, as: global }) %>

View File

@@ -0,0 +1,9 @@
<html>
<head>
<title>Blog Example</title>
</head>
<body>
<h1>Best Blog Ever</h1>
<%- body %>
</body>
</html>

View File

@@ -0,0 +1,2 @@
<h3><a href="<%= basepath %>/post/<%= id %>"><%= title %></a></h3>
<p><%= body %></p>

View File

@@ -0,0 +1 @@
<%- partial('post', { object: post, as: global }) %>

9
examples/blog/contact.js Normal file
View File

@@ -0,0 +1,9 @@
// in ./app.js we did "module.exports", allowing
// us to grab the app from the parent module (the one
// which required it)
var app = module.parent.exports;
app.get('/contact', function(req, res){
res.send('<p>Wahoo contact page</p>');
});

10
examples/blog/main.js Normal file
View File

@@ -0,0 +1,10 @@
// in ./app.js we did "module.exports", allowing
// us to grab the app from the parent module (the one
// which required it)
var app = module.parent.exports;
app.get('/', function(req, res){
res.send('<p>Visit <a href="/blog">/blog</a>'
+ ' or <a href="/contact">/contact</a></p>');
});

View File

@@ -1,8 +0,0 @@
var users = [];
users.push({ name: 'Tobi' });
users.push({ name: 'Loki' });
users.push({ name: 'Jane' });
module.exports = users;

View File

@@ -1,44 +0,0 @@
var express = require('../../')
, app = module.exports = express()
, users = require('./db');
// so either you can deal with different types of formatting
// for expected response in index.js
app.get('/', function(req, res){
res.format({
html: function(){
res.send('<ul>' + users.map(function(user){
return '<li>' + user.name + '</li>';
}).join('') + '</ul>');
},
text: function(){
res.send(users.map(function(user){
return ' - ' + user.name + '\n';
}).join(''));
},
json: function(){
res.json(users);
}
})
});
// or you could write a tiny middleware like
// this to add a layer of abstraction
// and make things a bit more declarative:
function format(path) {
var obj = require(path);
return function(req, res){
res.format(obj);
}
}
app.get('/users', format('./users'));
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,18 +0,0 @@
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);
};

View File

@@ -1,33 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../');
var app = module.exports = express();
// ignore GET /favicon.ico
app.use(express.favicon());
// pass a secret to cookieParser() for signed cookies
app.use(express.cookieParser('manny is cool'));
// add req.session cookie support
app.use(express.cookieSession());
// do something with the session
app.use(count);
// custom middleware
function count(req, res) {
req.session.count = req.session.count || 0;
var n = req.session.count++;
res.send('viewed ' + n + ' times\n');
}
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -3,52 +3,44 @@
* Module dependencies.
*/
var express = require('../../')
, app = module.exports = express();
var express = require('./../../lib/express');
var app = express.createServer(
// Place default Connect favicon above logger so it is not in
// the logging output
express.favicon(),
// add favicon() before logger() so
// GET /favicon.ico requests are not
// logged, because this middleware
// reponds to /favicon.ico and does not
// call next()
app.use(express.favicon());
// Custom logger format
express.logger({ format: '\x1b[1m:method\x1b[0m \x1b[33m:url\x1b[0m :response-time' }),
// custom log format
if ('test' != process.env.NODE_ENV) app.use(express.logger(':method :url'));
// Provides req.cookies
express.cookieDecoder(),
// parses request cookies, populating
// req.cookies and req.signedCookies
// when the secret is passed, used
// for signing the cookies.
app.use(express.cookieParser('my secret here'));
// parses json, x-www-form-urlencoded, and multipart/form-data
app.use(express.bodyParser());
// Parses x-www-form-urlencoded request bodies (and json)
express.bodyDecoder()
);
app.get('/', function(req, res){
if (req.cookies.remember) {
res.send('Remembered :). Click to <a href="/forget">forget</a>!.');
} else {
res.send('<form method="post"><p>Check to <label>'
+ '<input type="checkbox" name="remember"/> remember me</label> '
+ '<input type="submit" value="Submit"/>.</p></form>');
}
if (req.cookies.remember) {
res.send('Remembered :). Click to <a href="/forget">forget</a>!.');
} else {
res.send('<form method="post"><p>Check to <label>'
+ '<input type="checkbox" name="remember"/> remember me</label> '
+ '<input type="submit" value="Submit"/>.</p></form>');
}
});
app.get('/forget', function(req, res){
res.clearCookie('remember');
res.redirect('back');
res.clearCookie('remember');
res.redirect('back');
});
app.post('/', function(req, res){
var minute = 60000;
if (req.body.remember) res.cookie('remember', 1, { maxAge: minute });
res.redirect('back');
if (req.body.remember) {
res.cookie('remember', '1', { path: '/', expires: new Date(Date.now() + 900000), httpOnly: true });
}
res.redirect('back');
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1,46 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../..')
, app = express()
, api = express();
// app middleware
app.use(express.static(__dirname + '/public'));
// api middleware
api.use(express.logger('dev'));
api.use(express.bodyParser());
/**
* CORS support.
*/
api.all('*', function(req, res, next){
if (!req.get('Origin')) return next();
// use "*" here to accept any origin
res.set('Access-Control-Allow-Origin', 'http://localhost:3000');
res.set('Access-Control-Allow-Methods', 'GET, POST');
res.set('Access-Control-Allow-Headers', 'X-Requested-With, Content-Type');
// res.set('Access-Control-Allow-Max-Age', 3600);
if ('OPTIONS' == req.method) return res.send(200);
next();
});
/**
* POST a user.
*/
api.post('/user', function(req, res){
console.log(req.body);
res.send(201);
});
app.listen(3000);
api.listen(3001);
console.log('app listening on 3000');
console.log('api listening on 3001');

View File

@@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<body>
<script>
var req = new XMLHttpRequest;
req.open('POST', 'http://localhost:3001/user', false);
req.setRequestHeader('Content-Type', 'application/json');
req.send('{"name":"tobi","species":"ferret"}');
console.log(req.responseText);
</script>
</body>
</html>

View File

@@ -3,43 +3,30 @@
* Module dependencies.
*/
var express = require('../../')
, app = module.exports = express();
var express = require('./../../lib/express');
var app = express.createServer();
app.get('/', function(req, res){
res.send('<ul>'
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
+ '</ul>');
res.send('<ul>'
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
+ '</ul>');
});
// /files/* is accessed via req.params[0]
// but here we name it :file
app.get('/files/:file(*)', function(req, res, next){
var file = req.params.file
, path = __dirname + '/files/' + file;
res.download(path);
app.get('/files/*', function(req, res){
var file = req.params[0];
res.download(__dirname + '/files/' + file);
});
// error handling middleware. Because it's
// below our routes, you will be able to
// "intercept" errors, otherwise Connect
// will respond with 500 "Internal Server Error".
app.use(function(err, req, res, next){
// special-case 404s,
// remember you could
// render a 404 template here
if (404 == err.status) {
res.statusCode = 404;
res.send('Cant find that file, sorry!');
} else {
next(err);
}
app.error(function(err, req, res, next){
if (process.ENOENT == err.errno) {
res.send('Cant find that file, sorry!');
} else {
// Not a 404
next(err);
}
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}
app.listen(3000);
console.log('Express started on port 3000');

View File

@@ -1 +1 @@
what an amazing download
wow supes cool are you glad you downloaded this?

32
examples/ejs/app.js Normal file
View File

@@ -0,0 +1,32 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('./../../lib/express');
var app = express.createServer();
// Optional since express defaults to CWD/views
app.set('views', __dirname + '/views');
// Dummy users
var users = [
{ name: 'tj', email: 'tj@sencha.com' },
{ name: 'ciaran', email: 'ciaranj@gmail.com' },
{ name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
];
app.get('/', function(req, res){
res.render('users.ejs', {
locals: {
users: users
}
});
});
app.listen(3000);

View File

@@ -1,51 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../');
var app = module.exports = express();
// Register ejs as .html. If we did
// not call this, we would need to
// name our views foo.ejs instead
// of foo.html. The __express method
// is simply a function that engines
// use to hook into the Express view
// system by default, so if we want
// to change "foo.ejs" to "foo.html"
// we simply pass _any_ function, in this
// case `ejs.__express`.
app.engine('.html', require('ejs').__express);
// Optional since express defaults to CWD/views
app.set('views', __dirname + '/views');
// Without this you would need to
// supply the extension to res.render()
// ex: res.render('users.html').
app.set('view engine', 'html');
// Dummy users
var users = [
{ name: 'tobi', email: 'tobi@learnboost.com' },
{ name: 'loki', email: 'loki@learnboost.com' },
{ name: 'jane', email: 'jane@learnboost.com' }
];
app.get('/', function(req, res){
res.render('users', {
users: users,
title: "EJS example",
header: "Some users"
});
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,2 +0,0 @@
</body>
</html>

View File

@@ -1,13 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title> <%= title %> </title>
<style type="text/css">
body {
padding: 50px;
font: 13px Helvetica, Arial, sans-serif;
}
</style>
</head>
<body>

View File

@@ -0,0 +1,6 @@
<html>
<body>
<h1>Users</h1>
<%- body %>
</body>
</html>

View File

@@ -0,0 +1 @@
<li><%= user.name %> &lt;<%= user.email %>&gt;</li>

View File

@@ -0,0 +1,3 @@
<ul id="users">
<%- partial('user', users) %>
</ul>

View File

@@ -1,10 +0,0 @@
<% include header.html %>
<h1>Users</h1>
<ul id="users">
<% users.forEach(function(user){ %>
<li><%= user.name %> &lt;<%= user.email %>&gt;</li>
<% }) %>
</ul>
<% include footer.html %>

103
examples/error-pages/app.js Normal file
View File

@@ -0,0 +1,103 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('./../../lib/express');
var app = express.createServer(),
sys = require('sys');
// Serve default connect favicon
app.use(express.favicon());
// Logger is placed below favicon, so favicon.ico
// requests will not be logged
app.use(express.logger({ format: '":method :url" :status' }));
// "app.router" positions our routes
// specifically above the middleware
// assigned below
app.use(app.router);
// When no more middleware require execution, aka
// our router is finished and did not respond, we
// can assume that it is "not found". Instead of
// letting Connect deal with this, we define our
// custom middleware here to simply pass a NotFound
// exception
app.use(function(req, res, next){
next(new NotFound(req.url));
});
app.set('views', __dirname + '/views');
// Provide our app with the notion of NotFound exceptions
function NotFound(path){
this.name = 'NotFound';
if (path) {
Error.call(this, 'Cannot find ' + path);
this.path = path;
} else {
Error.call(this, 'Not Found');
}
Error.captureStackTrace(this, arguments.callee);
}
sys.inherits(NotFound, Error);
// We can call app.error() several times as shown below.
// Here we check for an instanceof NotFound and show the
// 404 page, or we pass on to the next error handler.
// These handlers could potentially be defined within
// configure() blocks to provide introspection when
// in the development environment.
app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.jade', {
status: 404,
locals: {
error: err
}
});
} else {
next(err);
}
});
// Here we assume all errors as 500 for the simplicity of
// this demo, however you can choose whatever you like
app.error(function(err, req, res){
res.render('500.jade', {
status: 500,
locals: {
error: err
}
});
});
// Routes
app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/404', function(req, res){
throw new NotFound;
});
app.get('/500', function(req, res, next){
next(new Error('keyboard cat!'));
});
app.listen(3000);
console.log('Express app started on port 3000');

View File

@@ -1,111 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../')
, app = module.exports = express()
, silent = 'test' == process.env.NODE_ENV;
// general config
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
// our custom "verbose errors" setting
// which we can use in the templates
// via settings['verbose errors']
app.enable('verbose errors');
// disable them in production
// use $ NODE_ENV=production node examples/error-pages
if ('production' == app.settings.env) app.disable('verbose errors');
app.use(express.favicon());
silent || app.use(express.logger('dev'));
// "app.router" positions our routes
// above the middleware defined below,
// this means that Express will attempt
// to match & call routes _before_ continuing
// on, at which point we assume it's a 404 because
// no route has handled the request.
app.use(app.router);
// Since this is the last non-error-handling
// middleware use()d, we assume 404, as nothing else
// responded.
// $ curl http://localhost:3000/notfound
// $ curl http://localhost:3000/notfound -H "Accept: application/json"
// $ curl http://localhost:3000/notfound -H "Accept: text/plain"
app.use(function(req, res, next){
res.status(404);
// respond with html page
if (req.accepts('html')) {
res.render('404', { url: req.url });
return;
}
// respond with json
if (req.accepts('json')) {
res.send({ error: 'Not found' });
return;
}
// default to plain-text. send()
res.type('txt').send('Not found');
});
// error-handling middleware, take the same form
// as regular middleware, however they require an
// arity of 4, aka the signature (err, req, res, next).
// when connect has an error, it will invoke ONLY error-handling
// middleware.
// If we were to next() here any remaining non-error-handling
// middleware would then be executed, or if we next(err) to
// continue passing the error, only error-handling middleware
// would remain being executed, however here
// we simply respond with an error page.
app.use(function(err, req, res, next){
// we may use properties of the error object
// here and next(err) appropriately, or if
// we possibly recovered from the error, simply next().
res.status(err.status || 500);
res.render('500', { error: err });
});
// Routes
app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/404', function(req, res, next){
// trigger a 404 since no other middleware
// will match /404 after this one, and we're not
// responding here
next();
});
app.get('/403', function(req, res, next){
// trigger a 403 error
var err = new Error('not allowed!');
err.status = 403;
next(err);
});
app.get('/500', function(req, res, next){
// trigger a generic (500) error
next(new Error('keyboard cat!'));
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,5 +1,4 @@
extends error
block content
h2 Cannot find #{url}
- if (error.path)
h2 Cannot find #{error.path}
- else
h2 Page Not Found

View File

@@ -1,13 +1,2 @@
// note that we extend a different
// layout with jade for 4xx & 5xx
// responses
extends error
block content
h1 Error: #{error.message}
if settings['verbose errors']
pre= error.stack
else
p An error ocurred!
h1 Error: #{error.message}
pre= error.stack

View File

@@ -1,6 +0,0 @@
html
head
title Error
body
h1 An error occurred!
block content

View File

@@ -1,15 +1,8 @@
extends layout
block content
h2 Pages Example
ul
li
| visit
a(href="/500") 500
li
| visit
a(href="/404") 404
li
| visit
a(href='/403') 403
h2 Pages Example
ul
li
| visit
a(href="/500") 500
li
| visit
a(href="/404") 404

View File

@@ -3,4 +3,4 @@ html
title Custom Pages Example
body
h1 My Site
block content
!= body

25
examples/error/app.js Normal file
View File

@@ -0,0 +1,25 @@
/**
* Module dependencies.
*/
var express = require('./../../lib/express');
var app = express.createServer();
app.get('/', function(req, res){
// Caught and passed down to the errorHandler middleware
throw new Error('something broke!');
});
app.get('/next', function(req, res, next){
// We can also pass exceptions to next()
next(new Error('oh no!'))
});
// The errorHandler middleware in this case will dump exceptions to stderr
// as well as show the stack trace in responses, currently handles text/plain,
// text/html, and application/json responses to aid in development
app.use('/', express.errorHandler({ dumpExceptions: true, showStack: true }));
app.listen(3000);

View File

@@ -1,49 +0,0 @@
/**
* Module dependencies.
*/
var express = require('../../')
, app = module.exports = express()
, test = app.get('env') == 'test';
if (!test) app.use(express.logger('dev'));
app.use(app.router);
// the error handler is strategically
// placed *below* the app.router; if it
// were above it would not receive errors
// from app.get() etc
app.use(error);
// error handling middleware have an arity of 4
// instead of the typical (req, res, next),
// otherwise they behave exactly like regular
// middleware, you may have several of them,
// in different orders etc.
function error(err, req, res, next) {
// log it
if (!test) console.error(err.stack);
// respond with 500 "Internal Server Error".
res.send(500);
}
app.get('/', function(req, res){
// Caught and passed down to the errorHandler middleware
throw new Error('something broke!');
});
app.get('/next', function(req, res, next){
// We can also pass exceptions to next()
process.nextTick(function(){
next(new Error('oh no!'));
});
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,63 +0,0 @@
var express = require('../..')
, app = express();
app.set('view engine', 'jade');
app.set('views', __dirname + '/views');
function User(name) {
this.private = 'heyyyy';
this.secret = 'something';
this.name = name;
this.id = 123;
}
// You'll probably want to do
// something like this so you
// dont expose "secret" data.
User.prototype.toJSON = function(){
return {
id: this.id,
name: this.name
}
};
app.use(express.logger('dev'));
// earlier on expose an object
// that we can tack properties on.
// all res.locals props are exposed
// to the templates, so "expose" will
// be present.
app.use(function(req, res, next){
res.locals.expose = {};
// you could alias this as req or res.expose
// to make it shorter and less annoying
next();
});
// pretend we loaded a user
app.use(function(req, res, next){
req.user = new User('Tobi');
next();
});
app.get('/', function(req, res){
res.redirect('/user');
});
app.get('/user', function(req, res){
// we only want to expose the user
// to the client for this route:
res.locals.expose.user = req.user;
res.render('page');
});
/* istanbul ignore next */
if (!module.parent) {
app.listen(3000);
console.log('Express started on port 3000');
}

View File

@@ -1,14 +0,0 @@
html
head
title Express
script.
// call this whatever you like,
// or dump them into individual
// props like "var user ="
var data = !{JSON.stringify(expose)}
body
h1 Expose client data
p The following was exposed to the client:
pre
script.
document.write(JSON.stringify(data, null, 2))

78
examples/flash/app.js Normal file
View File

@@ -0,0 +1,78 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('../../lib/express');
// App with session support
var app = express.createServer(
express.cookieDecoder(),
express.session()
);
// View settings
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
// Dynamic helpers are functions which are executed
// on each view render, unless dynamicHelpers is false.
// So for example we do not need to call messages() in our
// template, "messages" will be populated with the return
// value of this function.
app.dynamicHelpers({
messages: function(req, res){
// In the case of flash messages
// we return a function, allowing
// flash messages to only be flushed
// when called, otherwise every request
// will flush flash messages regardless.
return function(){
// Grab the flash messages
var messages = req.flash();
// We will render the "messages.ejs" partial
return res.partial('messages', {
// Our target object is our messages
object: messages,
// We want it to be named "types" in the partial
// since they are keyed like this:
// { info: ['foo'], error: ['bar']}
as: 'types',
// Pass a local named "hasMessages" so we can easily
// check if we have any messages at all
locals: { hasMessages: Object.keys(messages).length },
// We dont want dynamicHelpers in this partial, as
// it would cause infinite recursion
dynamicHelpers: false
});
}
}
});
app.dynamicHelpers({
// Another dynamic helper example. Since dynamic
// helpers resolve at view rendering time, we can
// "inject" the "page" local variable per request
// providing us with the request url.
page: function(req, res){
return req.url;
}
});
app.get('/', function(req, res){
// Not very realistic notifications but illustrates usage
req.flash('info', 'email queued');
req.flash('info', 'email sent');
req.flash('error', 'delivery failed');
res.render('index');
});
app.listen(3000);
console.log('Express app started on port 3000');

View File

@@ -0,0 +1,3 @@
<h1>Flash Message Example</h1>
<p>on page <%- page %></p>
<%- messages() %>

View File

@@ -0,0 +1,5 @@
<html>
<body>
<%- body %>
</body>
</html>

View File

@@ -0,0 +1,12 @@
<% if (hasMessages) { %>
<div id="messages">
<% for (var type in types) { %>
<h2><%= type %> messages</h2>
<ul id="<%= type %>">
<% types[type].forEach(function(msg){ %>
<li><%= msg %></li>
<% }) %>
</ul>
<% } %>
</div>
<% } %>

79
examples/form/app.js Normal file
View File

@@ -0,0 +1,79 @@
/**
* Module dependencies.
*/
var express = require('./../../lib/express'),
sys = require('sys');
var app = express.createServer();
// Here we use the bodyDecoder middleware
// to parse urlencoded request bodies
// which populates req.body
app.use(express.bodyDecoder());
// 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.cookieDecoder());
// Required by req.flash() for persistent
// notifications
app.use(express.session());
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);
});
app.listen(3000);
console.log('Express app started on port 3000');

63
examples/format/app.js Normal file
View File

@@ -0,0 +1,63 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var app = express.createServer();
// Fake items
var items = [
{ name: 'foo' },
{ name: 'bar' },
{ name: 'baz' }
];
// Routes
app.get('/', function(req, res, next){
res.send('Visit /item/2');
});
app.get('/item/:id.:format?', function(req, res, next){
var id = req.params.id,
format = req.params.format,
item = items[id];
// Ensure item exists
if (item) {
// Serve the format
switch (format) {
case 'json':
// Detects json
res.send(item);
break;
case 'xml':
// Set contentType as xml then sends
// the string
var xml = ''
+ '<items>'
+ '<item>' + item.name + '</item>'
+ '</items>';
res.contentType('.xml');
res.send(xml);
break;
case 'html':
default:
// By default send some hmtl
res.send('<h1>' + item.name + '</h1>');
}
} else {
// We could simply pass route control and potentially 404
// by calling next(), or pass an exception like below.
next(new Error('Item ' + id + ' does not exist'));
}
});
// Middleware
app.use(express.errorHandler({ showStack: true }));
app.listen(3000);
console.log('Express app started on port 3000');

119
examples/github/app.js Normal file
View File

@@ -0,0 +1,119 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('./../../lib/express'),
http = require('http');
var app = express.createServer();
// Expose our views
app.set('views', __dirname + '/views');
/**
* 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.addListener('response', function(res){
res.body = '';
res.addListener('data', function(chunk){ res.body += chunk; });
res.addListener('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.jade', {
locals: {
users: users
}
});
}
})(names.shift());
});
// Serve statics from ./public
app.use(express.staticProvider(__dirname + '/public'));
// Listen on port 3000
app.listen(3000);
console.log('Express app started on port 3000');

View File

@@ -0,0 +1,19 @@
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: 25%;
}
table td:nth-child(2) {
padding: 0 5px;
}

View File

@@ -0,0 +1,8 @@
- 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!= partial('repo', user.repos)

Some files were not shown because too many files have changed in this diff Show More