Compare commits

...

126 Commits

Author SHA1 Message Date
Tj Holowaychuk
4e1aefa5b5 Release 2.2.1 2011-04-04 12:23:37 -07:00
Tj Holowaychuk
89383ecc57 misc refactoring 2011-04-03 14:27:40 -07:00
Tj Holowaychuk
08046f7692 Added better partial() object collection support
only respecting .length is fine in some cases, but
if the object has a length and has holes, it will likely
produce an unexpected result, or an undefined local in the
render call, which may some times be ideal, but most likely not.

this change allows arbitrary objects to be passed to "collection: ",
however unfortunately there is no way to arbitrarily assume the object
is a collection without passing it as the option
2011-04-03 14:27:05 -07:00
Tj Holowaychuk
885fb1fa92 docs 2011-04-01 17:55:26 -07:00
Tj Holowaychuk
6a58c71528 Merge branch 'feature/view-layout-control' 2011-04-01 17:47:11 -07:00
Tj Holowaychuk
371d66ba2a Added layout(path) helper to change the layout within a view. Closes #610 2011-04-01 17:47:03 -07:00
Tj Holowaychuk
25bddf3fb5 added layout control example 2011-04-01 17:39:18 -07:00
Tj Holowaychuk
f6e9fb13f8 Removed "request" and "response" locals
sorry, changed my mind. easy enough to expose these if you want to,
but they are to large by default
2011-04-01 17:25:05 -07:00
Tj Holowaychuk
f0df8434e7 markdown escaping 2011-03-31 08:24:43 -07:00
Tj Holowaychuk
e4d3f239e5 Updated connect submodule 2011-03-30 22:01:16 -07:00
Tj Holowaychuk
bcc22dfa6f Updated connect submodule 2011-03-30 21:59:17 -07:00
Tj Holowaychuk
f614709a01 errorHandler title 2011-03-30 21:59:15 -07:00
Tj Holowaychuk
11250d22c9 misc refactoring 2011-03-30 21:44:21 -07:00
Tj Holowaychuk
4b4de29725 Performance improved with better view caching
the entire View object is now cached in-memory, along with the lookup
logic as well. This increases rendering (with jade at least) by about 260 rps
2011-03-30 21:40:05 -07:00
Tj Holowaychuk
99b49d2718 updated docs 2011-03-30 12:11:22 -07:00
Tj Holowaychuk
6230ec55be more docs 2011-03-30 12:02:14 -07:00
Tj Holowaychuk
0733d3c585 connect 1.2.0 2011-03-30 11:58:26 -07:00
Tj Holowaychuk
6f8370ff0e Updated connect submodule 2011-03-30 11:58:09 -07:00
Tj Holowaychuk
bc244ed07e docs for app.match.VERB() 2011-03-30 11:56:14 -07:00
Tj Holowaychuk
41266aa8e4 docs for app.lookup.VERB() 2011-03-30 11:49:21 -07:00
Tj Holowaychuk
45faee3e5b Release 2.2.0 2011-03-30 11:40:47 -07:00
Tj Holowaychuk
74ff735e10 Updated haml submodule 2011-03-30 11:00:47 -07:00
Tj Holowaychuk
97879f2b16 Updated formidable submodule 2011-03-30 11:00:43 -07:00
Tj Holowaychuk
1a338251ad Updated connect-form submodule 2011-03-30 11:00:34 -07:00
Tj Holowaychuk
354dc768c1 Updated jade submodule 2011-03-30 11:00:25 -07:00
Tj Holowaychuk
8a0796cd94 Updated ejs submodule 2011-03-30 11:00:21 -07:00
Tj Holowaychuk
cbc3b26584 changed express(1) --help 2011-03-30 10:58:01 -07:00
Tj Holowaychuk
d628583db8 removed colors from express(1) 2011-03-30 10:56:11 -07:00
Tj Holowaychuk
058777be1e connect >= 1.1.6 2011-03-29 18:11:00 -07:00
Tj Holowaychuk
260d03a0c4 Merge branch 'feature/route-lookup' 2011-03-29 18:04:49 -07:00
Tj Holowaychuk
6dcf6f41cc Added app.VERB() -> [Function...], app.lookup.VERB(), and app.match.VERB(). Closes #606 2011-03-29 18:04:43 -07:00
Tj Holowaychuk
799f790886 Updated connect submodule 2011-03-29 17:40:14 -07:00
Tj Holowaychuk
cb3c4b0ea9 Updated connect submodule 2011-03-29 17:38:39 -07:00
Tj Holowaychuk
798d255ba6 Release 2.1.1 2011-03-29 10:40:26 -07:00
Tj Holowaychuk
28ba9e8ac5 Fixed res.partial(); next(err) when no callback is given [reported by aheckmann] 2011-03-29 09:56:58 -07:00
Tj Holowaychuk
7888cb0506 docs 2011-03-29 09:51:38 -07:00
Tj Holowaychuk
5e284a20cc Updated connect submodule 2011-03-29 09:49:45 -07:00
Tj Holowaychuk
770357e727 Updated expresso submodule 2011-03-29 09:49:18 -07:00
Aaron Heckmann
673ba22555 res.send(undefined) returns a 204
closes #600

Signed-off-by: Tj Holowaychuk <tj@vision-media.ca>
2011-03-29 08:52:36 -07:00
Aaron Heckmann
fb38d9cfb7 add test for res.send(undefined)
Signed-off-by: Tj Holowaychuk <tj@vision-media.ca>
2011-03-29 08:52:34 -07:00
Aaron Heckmann
dda89a57ec ignore .swo .swp
Signed-off-by: Tj Holowaychuk <tj@vision-media.ca>
2011-03-29 08:52:34 -07:00
Tj Holowaychuk
62df63d3a0 doc typo 2011-03-29 08:39:35 -07:00
Tj Holowaychuk
e71696cf34 expose err.view when failing to locate a view
allows for:

   err.view.path

etc
2011-03-28 14:44:12 -07:00
Tj Holowaychuk
b5d8d58670 repo 2011-03-27 14:23:43 -07:00
Tj Holowaychuk
14e6a667f5 Release 2.1.0 2011-03-24 13:47:38 -07:00
Tj Holowaychuk
0c324783ae Merge branch 'feature/root-partial-lookup' 2011-03-24 13:34:02 -07:00
Tj Holowaychuk
5d6ce251ca Added <root>/_<name> partial lookup support 2011-03-24 13:33:52 -07:00
Tj Holowaychuk
92c04cee1d Added; partial lookup relative to view root. Closes #447
for example when nested deep, if you have a
partial located at ./views/messages.jade

partial("messages");

will still work
2011-03-24 13:26:32 -07:00
Tj Holowaychuk
7fdf587a7b added test for root partial lookup 2011-03-24 13:21:23 -07:00
Tj Holowaychuk
1e46218b09 doc typo 2011-03-24 11:46:40 -07:00
Tj Holowaychuk
c56fcd8fb9 better --help output 2011-03-22 13:00:36 -07:00
Tj Holowaychuk
319fbf7f64 Added "request", "response", and "app" locals 2011-03-22 12:16:58 -07:00
Tj Holowaychuk
bf06d9077c docs for "filename" local 2011-03-22 12:14:08 -07:00
Tj Holowaychuk
9d2bd29ee1 Added 'settings' local variable, containing the app's settings 2011-03-22 12:06:19 -07:00
Tj Holowaychuk
d11fa1f74e added .settings test 2011-03-22 12:02:57 -07:00
Tj Holowaychuk
c824da0dab test indentation 2011-03-22 11:59:29 -07:00
Tj Holowaychuk
9362c83a33 removed dead test 2011-03-22 11:59:05 -07:00
Tj Holowaychuk
0c38098f02 tweak req.flash() failure message 2011-03-21 16:13:40 -07:00
Pau Ramon
be7068f569 Better error output when using flash without session middleware. 2011-03-21 16:12:55 -07:00
Tj Holowaychuk
b122bf22e3 link typo 2011-03-21 12:06:36 -07:00
Tj Holowaychuk
b7232f38f3 Added res.send(bool) support
application/json
2011-03-21 10:34:04 -07:00
Tj Holowaychuk
c1b72ac1b7 Fixed stylus example for latest version 2011-03-21 08:45:43 -07:00
Roman Shtylman
b9e0d15878 check that this.params is valid before calling hasOwnProperty 2011-03-21 08:13:05 -07:00
Tj Holowaychuk
cf26cf7afc wrap try/catch around render() 2011-03-18 11:44:48 -07:00
Tj Holowaychuk
a75e60ae47 fixed docs due to markdown-js not supporting html 2011-03-18 09:18:29 -07:00
Tj Holowaychuk
187dc5dd03 connect 1.1.1 2011-03-18 08:49:59 -07:00
Tj Holowaychuk
9c9e2afade refactored res.redirect() 2011-03-18 08:49:46 -07:00
Tj Holowaychuk
fae1ba98c1 doc typo 2011-03-17 19:05:59 -07:00
Tj Holowaychuk
c3e632620a doc typo 2011-03-17 19:05:28 -07:00
Tj Holowaychuk
dd7158ac46 regenerated docs 2011-03-17 18:56:44 -07:00
Tj Holowaychuk
eefe51c7a7 removed manpages from make 2011-03-17 18:55:13 -07:00
Tj Holowaychuk
bf596dc023 Release 2.0.0 2011-03-17 18:06:30 -07:00
Tj Holowaychuk
220d88d654 Fixed up index view path alternative
previously was doing ../index, which was not intended
now doing ../VIEW/index
2011-03-17 15:36:44 -07:00
Tj Holowaychuk
a254e64bdb Changed; res.locals() without object returns the locals 2011-03-17 14:50:35 -07:00
Tj Holowaychuk
1555b92fb8 Release 2.0.0rc3 2011-03-17 13:01:59 -07:00
Tj Holowaychuk
d5b7a40b39 Fixed partials example 2011-03-17 13:00:43 -07:00
Tj Holowaychuk
bd1ab7ab96 pass the function 2011-03-17 12:48:06 -07:00
Tj Holowaychuk
2ff991bfcf refactored res.render() 2011-03-17 12:45:11 -07:00
Tj Holowaychuk
0b1378a539 Added res.locals(obj) 2011-03-17 12:13:59 -07:00
Tj Holowaychuk
723c908bd7 Added res.partial() callback support 2011-03-17 12:10:32 -07:00
Tj Holowaychuk
4874404701 typo 2011-03-17 11:33:35 -07:00
Tj Holowaychuk
4c1374840a Release 2.0.0rc2 2011-03-17 11:01:20 -07:00
Tj Holowaychuk
5da01633fd Fixed SlowBuffer support. Closes #584 2011-03-17 10:37:34 -07:00
Tj Holowaychuk
cdbd8af527 migration docs for partials 2011-03-17 09:31:32 -07:00
Tj Holowaychuk
a6fdc1bfd2 Fixed .filename view engine option [reported by drudge] 2011-03-16 16:58:02 -07:00
Tj Holowaychuk
20b8facb05 docs for partial changes 2011-03-16 15:53:27 -07:00
Tj Holowaychuk
909914f7af Changed; partial() "locals" are now optional
this means that:

   partial("user", { name: "tj" })

with the intent of receiving "user" instead of "name" in this
case is invalid, "name" here is a local, however passing a non-plain
object such as a User object is fine:

    partial("user", new User("tj"));
2011-03-16 15:37:05 -07:00
Tj Holowaychuk
3f31ebc676 fixed express-contrib example reference 2011-03-15 15:23:28 -07:00
Tj Holowaychuk
f3c068a90c Merge branch 'integration' 2011-03-15 10:52:58 -07:00
Tj Holowaychuk
90d7e193d1 Refactored Server#use()
eventually we should just emit some events from connect
2011-03-15 10:52:53 -07:00
Tj Holowaychuk
c9f5bb6f17 added .app test for the mounted server as well 2011-03-15 10:39:59 -07:00
Ben Weaver
9865a4c4f2 Clean up patch to Server#use(), add test case for restoring res#app property. 2011-03-15 10:37:18 -07:00
Ben Weaver
f12baf32d4 Restore original res.app when out() is called. 2011-03-15 10:37:18 -07:00
Tj Holowaychuk
80f4d08e8b Release 2.0.0rc 2011-03-14 15:01:37 -07:00
Tj Holowaychuk
07c9cae923 Fixed; expose HTTPSServer constructor 2011-03-14 14:36:22 -07:00
Tj Holowaychuk
d867cc9271 Fixed express(1) default test charset. Cloeses #579 [reported by secoif] 2011-03-13 10:15:53 -07:00
Tj Holowaychuk
3a1fe1e295 Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP] 2011-03-11 16:34:41 -08:00
Tj Holowaychuk
3cacf050df Docs for staticProvider() changes 2011-03-11 14:11:45 -08:00
Tj Holowaychuk
1536d73d1b Release 2.0.0beta3 2011-03-09 15:45:55 -08:00
Tj Holowaychuk
55143a9d44 Updated connect submodule 2011-03-09 15:39:10 -08:00
Tj Holowaychuk
c92e193916 Fixed res.redirect(). RFC states absolute 2011-03-09 15:18:22 -08:00
Tj Holowaychuk
d12452fc49 docs for charset 2011-03-09 10:59:47 -08:00
Tj Holowaychuk
3d8400a40c docs for res.contentType() literal 2011-03-09 10:54:35 -08:00
Tj Holowaychuk
1fe0aea0b0 Added test for res.contentType() literal 2011-03-09 10:53:50 -08:00
Tj Holowaychuk
5df2544883 fixed a test 2011-03-09 10:46:59 -08:00
Tj Holowaychuk
acbf224277 default res.send() string charset to utf8 2011-03-09 10:42:14 -08:00
Tj Holowaychuk
d0d17a0d35 charset tests using ISO-8859-1 2011-03-09 10:33:28 -08:00
Tj Holowaychuk
f7b53d33bc more tests 2011-03-09 10:33:06 -08:00
Tj Holowaychuk
92be06874b more tests 2011-03-09 10:31:45 -08:00
Tj Holowaychuk
f327455d9d Added charset option for render() 2011-03-09 10:31:10 -08:00
Tj Holowaychuk
54415bf2af moved a test 2011-03-09 10:29:03 -08:00
Tj Holowaychuk
bac62dfcd9 added charset test with res.render() 2011-03-09 10:26:37 -08:00
Tj Holowaychuk
820b43c1f3 Added .charset + res.send() test 2011-03-09 10:23:05 -08:00
Tj Holowaychuk
a5b69290d5 fixed a test 2011-03-09 10:20:44 -08:00
Tj Holowaychuk
5c7a9c86f6 Updated connect submodule 2011-03-09 10:20:03 -08:00
Tj Holowaychuk
7f83f916f6 Updated jade submodule 2011-03-09 09:46:47 -08:00
Masahiro Hayashi
c15a949cc3 Updated express command
- Fixed a genereated test's bug
- Added a forgotten semicolon
2011-03-08 13:42:06 -08:00
Tj Holowaychuk
ce47f96570 dont change NODE_ENV in tests 2011-03-08 13:39:30 -08:00
Tj Holowaychuk
57b035cd94 hinting at fully resolved paths 2011-03-08 12:58:16 -08:00
Tj Holowaychuk
f1f126171c Added view resolution hints when in development 2011-03-08 12:51:40 -08:00
Tj Holowaychuk
71e1bcd855 fixed a test 2011-03-08 12:02:35 -08:00
Tj Holowaychuk
b5579b6307 Added layout lookup support relative to the page view
for example if you render ./views/forum/thread and
./views/forum/thread/layout.jade exists, it will be used,
falling back on ./views/layout.jade.
2011-03-08 12:01:45 -08:00
Tj Holowaychuk
110b0fe14a misc refactor 2011-03-08 11:47:03 -08:00
Tj Holowaychuk
d7488bbb62 Removed Partial constructor (not used) 2011-03-08 11:42:03 -08:00
Tj Holowaychuk
2007407e7b docs 2011-03-08 09:56:24 -08:00
Tj Holowaychuk
d152e7e780 Added res.render() status support back
useful for error templates that display the status _and_
set the status code
2011-03-08 09:56:05 -08:00
64 changed files with 1670 additions and 3406 deletions

2
.gitignore vendored
View File

@@ -6,5 +6,7 @@ lib-cov
*.dat
*.out
*.pid
*.swp
*.swo
benchmarks/graphs
testing.js

View File

@@ -1,4 +1,91 @@
2.2.1 / 2011-04-04
==================
* Added `layout(path)` helper to change the layout within a view. Closes #610
* Fixed `partial()` collection object support.
Previously only anything with `.length` would work.
When `.length` is present one must still be aware of holes,
however now `{ collection: {foo: 'bar'}}` is valid, exposes
`keyInCollection` and `keysInCollection`.
* Performance improved with better view caching
* Removed `request` and `response` locals
* Changed; errorHandler page title is now `Express` instead of `Connect`
2.2.0 / 2011-03-30
==================
* Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606
* Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606
* Added `app.VERB(path)` as alias of `app.lookup.VERB()`.
* Dependency `connect >= 1.2.0`
2.1.1 / 2011-03-29
==================
* Added; expose `err.view` object when failing to locate a view
* Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann]
* Fixed; `res.send(undefined)` responds with 204 [aheckmann]
2.1.0 / 2011-03-24
==================
* Added `<root>/_?<name>` partial lookup support. Closes #447
* Added `request`, `response`, and `app` local variables
* Added `settings` local variable, containing the app's settings
* Added `req.flash()` exception if `req.session` is not available
* Added `res.send(bool)` support (json response)
* Fixed stylus example for latest version
* Fixed; wrap try/catch around `res.render()`
2.0.0 / 2011-03-17
==================
* Fixed up index view path alternative.
* Changed; `res.locals()` without object returns the locals
2.0.0rc3 / 2011-03-17
==================
* Added `res.locals(obj)` to compliment `res.local(key, val)`
* Added `res.partial()` callback support
* Fixed recursive error reporting issue in `res.render()`
2.0.0rc2 / 2011-03-17
==================
* Changed; `partial()` "locals" are now optional
* Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01]
* Fixed .filename view engine option [reported by drudge]
* Fixed blog example
* Fixed `{req,res}.app` reference when mounting [Ben Weaver]
2.0.0rc / 2011-03-14
==================
* Fixed; expose `HTTPSServer` constructor
* Fixed express(1) default test charset. Closes #579 [reported by secoif]
* Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]
2.0.0beta3 / 2011-03-09
==================
* Added support for `res.contentType()` literal
The original `res.contentType('.json')`,
`res.contentType('application/json')`, and `res.contentType('json')`
will work now.
* Added `res.render()` status option support back
* Added charset option for `res.render()`
* Added `.charset` support (via connect 1.0.4)
* Added view resolution hints when in development and a lookup fails
* Added layout lookup support relative to the page view.
For example while rendering `./views/user/index.jade` if you create
`./views/user/layout.jade` it will be used in favour of the root layout.
* Fixed `res.redirect()`. RFC states absolute url [reported by unlink]
* Fixed; default `res.send()` string charset to utf8
* Removed `Partial` constructor (not currently used)
2.0.0beta2 / 2011-03-07
==================

View File

@@ -1,13 +1,5 @@
DOCS = docs/index.md \
docs/screencasts.md \
docs/executable.md \
docs/contrib.md \
docs/guide.md \
docs/migrate.md \
docs/applications.md
MANPAGES =$(DOCS:.md=.1)
DOCS = $(shell find docs/*.md)
HTMLDOCS =$(DOCS:.md=.html)
test:
@@ -24,19 +16,14 @@ test:
test-cov:
@TESTFLAGS=--cov $(MAKE) test
docs: $(MANPAGES) $(HTMLDOCS)
docs: $(HTMLDOCS)
@ echo "... generating TOC"
@./support/toc.js docs/guide.html
%.1: %.md
@echo "... $< -> $@"
@ronn -r --pipe $< > $@
%.html: %.md
@echo "... $< -> $@"
@ronn -5 --pipe --fragment $< \
@markdown $< \
| cat docs/layout/head.html - docs/layout/foot.html \
| sed 's/NAME/Express/g' \
> $@
site:

View File

@@ -54,7 +54,7 @@ The following are the major contributors of Express (in no specific order).
## More Information
* [express-configure](http://github.com/visionmedia/express-configure) async configuration support
* [express-configure](http://github.com/visionmedia/express-configuration) async configuration support
* [express-messages](http://github.com/visionmedia/express-messages) flash notification rendering helper
* [express-namespace](http://github.com/visionmedia/express-namespace) namespaced route support
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates

View File

@@ -11,7 +11,7 @@ var fs = require('fs')
* Framework version.
*/
var version = '2.0.0beta2';
var version = '2.2.0';
/**
* Add session support.
@@ -36,14 +36,15 @@ var templateEngine = 'jade';
*/
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|stylus). Defaults to plain css\n'
+ ' -v, --version Output framework version\n'
+ ' -h, --help Output help information\n'
+ ' Usage: express [options] [path]\n'
+ '\n'
+ ' Options:\n'
+ ' -s, --sessions add session support\n'
+ ' -t, --template <engine> add template <engine> support (jade|ejs). default=jade\n'
+ ' -c, --css <engine> add stylesheet <engine> support (less|sass|stylus). default=plain css\n'
+ ' -v, --version output framework version\n'
+ ' -h, --help output help information\n'
;
/**
@@ -220,7 +221,7 @@ var app = [
, ''
, 'if (!module.parent) {'
, ' app.listen(3000);'
, ' console.log("Express server listening on port %d", app.address().port)'
, ' console.log("Express server listening on port %d", app.address().port);'
, '}'
, ''
].join('\n');

View File

@@ -1,70 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "APPLICATIONS" "" "March 2011" "" ""
.
.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\.

View File

@@ -190,11 +190,6 @@
<li><a href="screencasts.html">Screencasts</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 />
@@ -228,7 +223,7 @@
<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">Widescript</a> is an innovative app that helps you focus and interact with your texts &ndash; 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>
@@ -244,9 +239,7 @@
<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>
</html>

View File

@@ -1,82 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "CONTRIB" "" "March 2011" "" ""
.
.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\.

View File

@@ -190,12 +190,7 @@
<li><a href="screencasts.html">Screencasts</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>
<h3>Development Dependencies</h3>
<p>Express development dependencies are stored within the <em>./support</em> directory. To
update them execute:</p>
@@ -203,7 +198,7 @@ update them execute:</p>
<pre><code>$ git submodule update --init
</code></pre>
<h3 id="Running-Tests">Running Tests</h3>
<h3>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
@@ -222,7 +217,7 @@ simply execute:</p>
<pre><code>$ make test-cov
</code></pre>
<h3 id="Contributions">Contributions</h3>
<h3>Contributions</h3>
<p>To accept a contribution, you should follow these guidelines:</p>
@@ -235,13 +230,11 @@ simply execute:</p>
</ul>
<h3 id="Documentation">Documentation</h3>
<h3>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>
</html>

View File

@@ -1,31 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "EXECUTABLE" "" "March 2011" "" ""
.
.SH "NAME"
\fBexecutable\fR
.
.SH "Synopsis"
.
.nf
express [options] [PATH]
.
.fi
.
.SH "Description"
The \fIexpress\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
\-t, \-\-template ENGINE Add template ENGINE support (jade|ejs)\. Defaults to jade
\-c, \-\-css ENGINE Add stylesheet ENGINE support (less|sass|stylus)\. Defaults to plain css
\-v, \-\-version Output framework version
\-h, \-\-help Output help information
.
.fi

View File

@@ -190,23 +190,18 @@
<li><a href="screencasts.html">Screencasts</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>
<h2>Synopsis</h2>
<pre><code>express [options] [PATH]
</code></pre>
<h2 id="Description">Description</h2>
<h2>Description</h2>
<p>The <em>express</em> 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>
<h2>Options</h2>
<pre><code> -s, --sessions Add session support
-t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade
@@ -214,9 +209,7 @@ application structure, this executable creates a maintainable base app.</p>
-v, --version Output framework version
-h, --help Output help information
</code></pre>
</div>
</div>
</div>
</body>
</html>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -179,60 +179,64 @@
</a>
<div id="wrapper">
<div id="container"><ul id="toc">
<li><a href="#Installation">Installation</a></li>
<li><a href="#Creating-A-Server">Creating A Server</a></li>
<li><a href="#Creating-An-HTTPS-Server">Creating An HTTPS Server</a></li>
<li><a href="#Configuration">Configuration</a></li>
<li><a href="#Settings">Settings</a></li>
<li><a href="#Routing">Routing</a></li>
<li><a href="#Passing-Route-Control">Passing Route Control</a></li>
<li><a href="#Middleware">Middleware</a></li>
<li><a href="#Route-Middleware">Route Middleware</a></li>
<li><a href="#HTTP-Methods">HTTP Methods</a></li>
<li><a href="#Error-Handling">Error Handling</a></li>
<li><a href="#Route-Param-Pre-conditions">Route Param Pre-conditions</a></li>
<li><a href="#View-Rendering">View Rendering</a></li>
<li><a href="#View-Partials">View Partials</a></li>
<li><a href="#View-Lookup">View Lookup</a></li>
<li><a href="#Template-Engines">Template Engines</a></li>
<li><a href="#Session-Support">Session Support</a></li>
<li><a href="#Migration-Guide">Migration Guide</a></li>
<li><a href="#installation">Installation</a></li>
<li><a href="#creating-a server">Creating A Server</a></li>
<li><a href="#creating-an https server">Creating An HTTPS Server</a></li>
<li><a href="#configuration">Configuration</a></li>
<li><a href="#settings">Settings</a></li>
<li><a href="#routing">Routing</a></li>
<li><a href="#passing-route control">Passing Route Control</a></li>
<li><a href="#middleware">Middleware</a></li>
<li><a href="#route-middleware">Route Middleware</a></li>
<li><a href="#http-methods">HTTP Methods</a></li>
<li><a href="#error-handling">Error Handling</a></li>
<li><a href="#route-param pre-conditions">Route Param Pre-conditions</a></li>
<li><a href="#view-rendering">View Rendering</a></li>
<li><a href="#view-partials">View Partials</a></li>
<li><a href="#view-lookup">View Lookup</a></li>
<li><a href="#template-engines">Template Engines</a></li>
<li><a href="#session-support">Session Support</a></li>
<li><a href="#migration-guide">Migration Guide</a></li>
<li><a href="#" class="toggle">+</a> <a class="section-title" href="#">Request</a><ul class="section" id="section-Request">
<li><a href="#req-header-key-defaultValue-">header()</a></li>
<li><a href="#req-accepts-type-">accepts()</a></li>
<li><a href="#req-is-type-">is()</a></li>
<li><a href="#req-param-name-default-">param()</a></li>
<li><a href="#req-flash-type-msg-">flash()</a></li>
<li><a href="#req-isXMLHttpRequest">isXMLHttpRequest</a></li>
<li><a href="#req.header()">header()</a></li>
<li><a href="#req.accepts()">accepts()</a></li>
<li><a href="#req.is()">is()</a></li>
<li><a href="#req.param()">param()</a></li>
<li><a href="#req.flash()">flash()</a></li>
<li><a href="#req.isxmlhttprequest">isXMLHttpRequest</a></li>
</ul></li>
<li><a href="#" class="toggle">+</a> <a class="section-title" href="#">Response</a><ul class="section" id="section-Response">
<li><a href="#res-header-key-val-">header()</a></li>
<li><a href="#res-contentType-type-">contentType()</a></li>
<li><a href="#res-attachment-filename-">attachment()</a></li>
<li><a href="#res-sendfile-path-options-callback-">sendfile()</a></li>
<li><a href="#res-download-file-filename-callback-">download()</a></li>
<li><a href="#res-send-body-status-headers-status-status-">send()</a></li>
<li><a href="#res-redirect-url-status-">redirect()</a></li>
<li><a href="#res-cookie-name-val-options-">cookie()</a></li>
<li><a href="#res-clearCookie-name-">clearCookie()</a></li>
<li><a href="#res-render-view-options-fn-">render()</a></li>
<li><a href="#res-partial-view-options-">partial()</a></li>
<li><a href="#res-local-name-val-">local()</a></li>
<li><a href="#res.header()">header()</a></li>
<li><a href="#res.charset">charset</a></li>
<li><a href="#res.contenttype()">contentType()</a></li>
<li><a href="#res.attachment()">attachment()</a></li>
<li><a href="#res.sendfile()">sendfile()</a></li>
<li><a href="#res.download()">download()</a></li>
<li><a href="#res.send()">send()</a></li>
<li><a href="#res.redirect()">redirect()</a></li>
<li><a href="#res.cookie()">cookie()</a></li>
<li><a href="#res.clearcookie()">clearCookie()</a></li>
<li><a href="#res.render()">render()</a></li>
<li><a href="#res.partial()">partial()</a></li>
<li><a href="#res.local()">local()</a></li>
<li><a href="#res.locals()">locals()</a></li>
</ul></li>
<li><a href="#" class="toggle">+</a> <a class="section-title" href="#">Server</a><ul class="section" id="section-Server">
<li><a href="#app-set-name-val-">set()</a></li>
<li><a href="#app-enable-name-">enable()</a></li>
<li><a href="#app-enabled-name-">enabled()</a></li>
<li><a href="#app-disable-name-">disable()</a></li>
<li><a href="#app-disabled-name-">disabled()</a></li>
<li><a href="#app-configure-env-function-function-">configure()</a></li>
<li><a href="#app-redirect-name-val-">redirect()</a></li>
<li><a href="#app-error-function-">error()</a></li>
<li><a href="#app-helpers-obj-">helpers()</a></li>
<li><a href="#app-dynamicHelpers-obj-">dynamicHelpers()</a></li>
<li><a href="#app-mounted-fn-">mounted()</a></li>
<li><a href="#app-register-ext-exports-">register()</a></li>
<li><a href="#app-listen-port-host-">listen()</a></li>
<li><a href="#app.set()">set()</a></li>
<li><a href="#app.enable()">enable()</a></li>
<li><a href="#app.enabled()">enabled()</a></li>
<li><a href="#app.disable()">disable()</a></li>
<li><a href="#app.disabled()">disabled()</a></li>
<li><a href="#app.configure()">configure()</a></li>
<li><a href="#app.redirect()">redirect()</a></li>
<li><a href="#app.error()">error()</a></li>
<li><a href="#app.helpers()">helpers()</a></li>
<li><a href="#app.dynamichelpers()">dynamicHelpers()</a></li>
<li><a href="#app.lookup">lookup</a></li>
<li><a href="#app.match">match</a></li>
<li><a href="#app.mounted()">mounted()</a></li>
<li><a href="#app.register()">register()</a></li>
<li><a href="#app.listen()">listen()</a></li>
</ul></li>
</ul>
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
@@ -246,17 +250,12 @@
<li><a href="screencasts.html">Screencasts</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>guide</code>
</p>
<h3 id="Installation">Installation</h3>
<h3 id="installation">Installation</h3>
<pre><code>$ npm install express
</code></pre>
<h3 id="Creating-A-Server">Creating A Server</h3>
<h3 id="creating-a server">Creating A Server</h3>
<p> To create an instance of the <em>express.HTTPServer</em>, simply invoke the <em>createServer()</em> method. With our instance <em>app</em> we can then define routes based on the HTTP verbs, in this example <em>app.get()</em>.</p>
@@ -269,15 +268,15 @@ app.get('/', function(req, res){
app.listen(3000);
</code></pre>
<h3 id="Creating-An-HTTPS-Server">Creating An HTTPS Server</h3>
<h3 id="creating-an https server">Creating An HTTPS Server</h3>
<p> To initialize a <em>express.HTTPSServer</em> we do the same as above, however we
pass an options object, accepting <em>key</em>, <em>cert</em> and the others mentioned in node's <a href="http://nodejs.org/docs/v0.3.7/api/https.html#https.createServer">https documentation</a>.</p>
pass an options object, accepting <em>key</em>, <em>cert</em> and the others mentioned in node&rsquo;s <a href="http://nodejs.org/docs/v0.3.7/api/https.html#https.createServer">https documentation</a>.</p>
<pre><code> var app = require('express').createServer({ key: ... });
</code></pre>
<h3 id="Configuration">Configuration</h3>
<h3 id="configuration">Configuration</h3>
<p>Express supports arbitrary environments, such as <em>production</em> and <em>development</em>. Developers
can use the <em>configure()</em> method to setup needs required by the current environment. When
@@ -302,7 +301,7 @@ app.configure('development', function(){
app.configure('production', function(){
var oneYear = 31557600000;
app.use(express.static({ root: __dirname + '/public', maxAge: oneYear }));
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
app.use(express.errorHandler());
});
</code></pre>
@@ -332,7 +331,7 @@ app.configure('production', function(){
<p>This is <em>very</em> important, as many caching mechanisms are <em>only enabled</em> when in production.</p>
<h3 id="Settings">Settings</h3>
<h3 id="settings">Settings</h3>
<p>Express supports the following settings out of the box:</p>
@@ -344,10 +343,10 @@ app.configure('production', function(){
</ul>
<h3 id="Routing">Routing</h3>
<h3 id="routing">Routing</h3>
<p>Express utilizes the HTTP verbs to provide a meaningful, expressive routing API.
For example we may want to render a user's account for the path <em>/user/12</em>, this
For example we may want to render a user&rsquo;s account for the path <em>/user/12</em>, this
can be done by defining the route below. The values associated to the named placeholders
are available as <code>req.params</code>.</p>
@@ -363,7 +362,7 @@ when <em>/user/:id</em> is compiled, a simplified version of the regexp may look
</code></pre>
<p>Regular expression literals may also be passed for complex uses. Since capture
groups with literal <em>RegExp</em>'s are anonymous we can access them directly <code>req.params</code>. So our first capture group would be <em>req.params[0]</em> and the second would follow as <em>req.params[1]</em>.</p>
groups with literal <em>RegExp</em>&rsquo;s are anonymous we can access them directly <code>req.params</code>. So our first capture group would be <em>req.params[0]</em> and the second would follow as <em>req.params[1]</em>.</p>
<pre><code>app.get(/^\/users?(?:\/(\d+)(?:\.\.(\d+))?)?/, function(req, res){
res.send(req.params);
@@ -432,9 +431,9 @@ app.post('/', function(req, res){
app.listen(3000);
</code></pre>
<p>Typically we may use a "dumb" placeholder such as "/user/:id" which has no restrictions, however say for example we are limiting a user id to digits, we may use <em>'/user/:id(\d+)'</em> which will <em>not</em> match unless the placeholder value contains only digits.</p>
<p>Typically we may use a &ldquo;dumb&rdquo; placeholder such as &ldquo;/user/:id&rdquo; which has no restrictions, however say for example we are limiting a user id to digits, we may use <em>&lsquo;/user/:id(\d+)&rsquo;</em> which will <em>not</em> match unless the placeholder value contains only digits.</p>
<h3 id="Passing-Route-Control">Passing Route Control</h3>
<h3 id="passing-route control">Passing Route Control</h3>
<p>We may pass control to the next <em>matching</em> route, by calling the <em>third</em> argument,
the <em>next()</em> function. When a match cannot be made, control is passed back to Connect,
@@ -489,7 +488,7 @@ app.get('*', function(req, res){
app.listen(3000);
</code></pre>
<h3 id="Middleware">Middleware</h3>
<h3 id="middleware">Middleware</h3>
<p>Middleware via <a href="http://github.com/senchalabs/connect">Connect</a> can be
passed to <em>express.createServer()</em> as you would with a regular Connect server. For example:</p>
@@ -507,7 +506,7 @@ var app = express.createServer(
<pre><code>app.use(express.logger({ format: ':method :uri' }));
</code></pre>
<p>Typically with connect middleware you would <em>require('connect')</em> like so:</p>
<p>Typically with connect middleware you would <em>require(&lsquo;connect&rsquo;)</em> like so:</p>
<pre><code>var connect = require('connect');
app.use(connect.logger());
@@ -520,7 +519,7 @@ app.use(connect.bodyParser());
app.use(express.bodyParser());
</code></pre>
<h3 id="Route-Middleware">Route Middleware</h3>
<h3 id="route-middleware">Route Middleware</h3>
<p>Routes may utilize route-specific middleware by passing one or more additional callbacks (or arrays) to the method. This feature is extremely useful for restricting access, loading data used by the route etc.</p>
@@ -580,7 +579,7 @@ app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
});
</code></pre>
<p>Commonly used "stacks" of middleware can be passed as an array (<em>applied recursively</em>), which can be mixed and matched to any degree.</p>
<p>Commonly used &ldquo;stacks&rdquo; of middleware can be passed as an array (<em>applied recursively</em>), which can be mixed and matched to any degree.</p>
<pre><code>var a = [middleware1, middleware2]
, b = [middleware3, middleware4]
@@ -596,11 +595,11 @@ app.get('/', all, function(){});
<p>For this example in full, view the <a href="http://github.com/visionmedia/express/blob/master/examples/route-middleware/app.js">route middleware example</a> in the repository.</p>
<h3 id="HTTP-Methods">HTTP Methods</h3>
<h3 id="http-methods">HTTP Methods</h3>
<p>We have seen <em>app.get()</em> a few times, however Express also exposes other familiar HTTP verbs in the same manor, such as <em>app.post()</em>, <em>app.del()</em>, etc.</p>
<p> A common example for <em>POST</em> usage, is when "submitting" a form. Below we simply set our form method to "post" in our html, and control will be given to the route we have defined below it.</p>
<p> A common example for <em>POST</em> usage, is when &ldquo;submitting&rdquo; a form. Below we simply set our form method to &ldquo;post&rdquo; in our html, and control will be given to the route we have defined below it.</p>
<pre><code> &lt;form method="post" action="/"&gt;
&lt;input type="text" name="user[name]" /&gt;
@@ -609,7 +608,7 @@ app.get('/', all, function(){});
&lt;/form&gt;
</code></pre>
<p>By default Express does not know what to do with this request body, so we should add the <em>bodyParser</em> middleware, which will parse <em>application/x-www-form-urlencoded</em> and <em>application/json</em> request bodies and place the variables in <em>req.body</em>. We can do this by "using" the middleware as shown below:</p>
<p>By default Express does not know what to do with this request body, so we should add the <em>bodyParser</em> middleware, which will parse <em>application/x-www-form-urlencoded</em> and <em>application/json</em> request bodies and place the variables in <em>req.body</em>. We can do this by &ldquo;using&rdquo; the middleware as shown below:</p>
<pre><code>app.use(express.bodyParser());
</code></pre>
@@ -622,7 +621,7 @@ app.get('/', all, function(){});
});
</code></pre>
<p>When using methods such as <em>PUT</em> with a form, we can utilize a hidden input named <em>_method</em>, which can be used to alter the HTTP method. To do so we first need the <em>methodOverride</em> middleware, which should be placed below <em>bodyParser</em> so that it can utilize it's <em>req.body</em> containing the form values.</p>
<p>When using methods such as <em>PUT</em> with a form, we can utilize a hidden input named <em>_method</em>, which can be used to alter the HTTP method. To do so we first need the <em>methodOverride</em> middleware, which should be placed below <em>bodyParser</em> so that it can utilize it&rsquo;s <em>req.body</em> containing the form values.</p>
<pre><code>app.use(express.bodyParser());
app.use(express.methodOverride());
@@ -643,7 +642,7 @@ app.put('/', function(){
});
</code></pre>
<h3 id="Error-Handling">Error Handling</h3>
<h3 id="error-handling">Error Handling</h3>
<p>Express provides the <em>app.error()</em> method which receives exceptions thrown within a route,
or passed to <em>next(err)</em>. Below is an example which serves different pages based on our
@@ -655,7 +654,7 @@ ad-hoc <em>NotFound</em> exception:</p>
Error.captureStackTrace(this, arguments.callee);
}
NotFound.protoype.__proto__ = Error.prototype;
NotFound.prototype.__proto__ = Error.prototype;
app.get('/404', function(req, res){
throw new NotFound;
@@ -685,7 +684,7 @@ handle exceptions in different ways based on the environment.</p>
</code></pre>
<p>Here we assume all errors as 500 for the simplicity of
this demo, however you can choose whatever you like. For example when node performs filesystem syscalls, you may receive an error object with the <em>error.code</em> of <em>ENOENT</em>, meaning "no such file or directory", we can utilize this in our error handling and display a page specific to this if desired.</p>
this demo, however you can choose whatever you like. For example when node performs filesystem syscalls, you may receive an error object with the <em>error.code</em> of <em>ENOENT</em>, meaning &ldquo;no such file or directory&rdquo;, we can utilize this in our error handling and display a page specific to this if desired.</p>
<pre><code>app.error(function(err, req, res){
res.render('500.jade', {
@@ -696,7 +695,7 @@ this demo, however you can choose whatever you like. For example when node perfo
<p>Our apps could also utilize the Connect <em>errorHandler</em> middleware
to report on exceptions. For example if we wish to output exceptions
in "development" mode to <em>stderr</em> we can use:</p>
in &ldquo;development&rdquo; mode to <em>stderr</em> we can use:</p>
<pre><code>app.use(express.errorHandler({ dumpExceptions: true }));
</code></pre>
@@ -710,7 +709,7 @@ that are passed or thrown, so we can set <em>showStack</em> to true:</p>
<p>The <em>errorHandler</em> middleware also responds with <em>json</em> if <em>Accept: application/json</em>
is present, which is useful for developing apps that rely heavily on client-side JavaScript.</p>
<h3 id="Route-Param-Pre-conditions">Route Param Pre-conditions</h3>
<h3 id="route-param pre-conditions">Route Param Pre-conditions</h3>
<p>Route param pre-conditions can drastically improve the readability of your application, through implicit loading of data, and validation of request urls. For example if you are constantly fetching common data for several routes, such as loading a user for <em>/user/:id</em>, we might typically do something like below:</p>
@@ -751,12 +750,12 @@ is present, which is useful for developing apps that rely heavily on client-side
<pre><code>app.param(['from', 'to'], function(n){ return parseInt(n, 10); });
</code></pre>
<h3 id="View-Rendering">View Rendering</h3>
<h3 id="view-rendering">View Rendering</h3>
<p>View filenames take the form "&lt;name&gt;.&lt;engine&gt;", where &lt;engine&gt; is the name
<p>View filenames take the form &ldquo;&lt;name&gt;.&lt;engine&gt;&rdquo;, where &lt;engine&gt; is the name
of the module that will be required. For example the view <em>layout.ejs</em> will
tell the view system to <em>require('ejs')</em>, the module being loaded must export the method <em>exports.compile(str, options)</em>, and return a <em>Function</em> to comply with Express. To alter this behaviour
<em>app.register()</em> can be used to map engines to file extensions, so that for example "foo.html" can be rendered by ejs.</p>
tell the view system to <em>require(&lsquo;ejs&rsquo;)</em>, the module being loaded must export the method <em>exports.compile(str, options)</em>, and return a <em>Function</em> to comply with Express. To alter this behaviour
<em>app.register()</em> can be used to map engines to file extensions, so that for example &ldquo;foo.html&rdquo; can be rendered by ejs.</p>
<p>Below is an example using <a href="http://github.com/visionmedia/jade">Jade</a> to render <em>index.html</em>,
and since we do not use <em>layout: false</em> the rendered contents of <em>index.jade</em> will be passed as
@@ -824,20 +823,20 @@ mix and match template engines:</p>
});
</code></pre>
<h3 id="View-Partials">View Partials</h3>
<h3 id="view-partials">View Partials</h3>
<p>The Express view system has built-in support for partials and collections, which are "mini" views representing a document fragment. For example rather than iterating
in a view to display comments, we would use a partial with collection support:</p>
<p>The Express view system has built-in support for partials and collections, which are &ldquo;mini&rdquo; views representing a document fragment. For example rather than iterating
in a view to display comments, we could use partial collection:</p>
<pre><code>partial('comment', { collection: comments });
</code></pre>
<p>If no other options are desired, we can omit the object and simply pass our array, which is equivalent to above:</p>
<p>If no other options or local variables are desired, we can omit the object and simply pass our array, which is equivalent to above:</p>
<pre><code>partial('comment', comments);
</code></pre>
<p>When using the partial collection support a few "magic" variables are provided
<p>When using the partial collection support a few &ldquo;magic&rdquo; locals are provided
for free:</p>
<ul>
@@ -848,25 +847,26 @@ for free:</p>
</ul>
<p>Local variables passed (or generated) take precedence, however locals passed to the parent view are available in the child view as well. So for example if we were to render a blog post with <em>partial('blog/post', post)</em> it would generate the <em>post</em> local, but the view calling this function had the local <em>user</em>, it would be available to the <em>blog/post</em> view as well.</p>
<p>Local variables passed (or generated) take precedence, however locals passed to the parent view are available in the child view as well. So for example if we were to render a blog post with <em>partial(&lsquo;blog/post&rsquo;, post)</em> it would generate the <em>post</em> local, but the view calling this function had the local <em>user</em>, it would be available to the <em>blog/post</em> view as well.</p>
<p>For documentation on altering the object name view <a href="http://expressjs.com/guide.html#res-partial-view-options-">res.partial()</a>.</p>
<p><strong>NOTE:</strong> be careful about when you use partial collections, as rendering an array with a length of 100 means we have to render 100 views. For simple collections you may inline the iteration instead of using partial collection support to decrease overhead.</p>
<h3 id="View-Lookup">View Lookup</h3>
<h3 id="view-lookup">View Lookup</h3>
<p>View lookup is performed relative to the parent view, for example if we had a page view named <em>views/user/list.jade</em>, and within that view we did <em>partial('edit')</em> it would attempt to load <em>views/user/edit.jade</em>, whereas <em>partial('../messages')</em> would load <em>views/messages.jade</em>.</p>
<p>View lookup is performed relative to the parent view, for example if we had a page view named <em>views/user/list.jade</em>, and within that view we did <em>partial(&lsquo;edit&rsquo;)</em> it would attempt to load <em>views/user/edit.jade</em>, whereas <em>partial(&lsquo;../messages&rsquo;)</em> would load <em>views/messages.jade</em>.</p>
<p>The view system also allows for index templates, allowing you to have a directory of the same name. For example within a route we may have <em>res.render('users')</em> either <em>views/users.jade</em>, or <em>views/users/index.jade</em>.</p>
<p>The view system also allows for index templates, allowing you to have a directory of the same name. For example within a route we may have <em>res.render(&lsquo;users&rsquo;)</em> either <em>views/users.jade</em>, or <em>views/users/index.jade</em>.</p>
<p>When utilizing index views as shown above, we may reference <em>views/users/index.jade</em> from a view in the same directory by <em>partial('users')</em>, and the view system will try <em>../users/index</em>, preventing us from needing to call <em>partial('index')</em>.</p>
<p>When utilizing index views as shown above, we may reference <em>views/users/index.jade</em> from a view in the same directory by <em>partial(&lsquo;users&rsquo;)</em>, and the view system will try <em>../users/index</em>, preventing us from needing to call <em>partial(&lsquo;index&rsquo;)</em>.</p>
<h3 id="Template-Engines">Template Engines</h3>
<h3 id="template-engines">Template Engines</h3>
<p>Below are a few template engines commonly used with Express:</p>
<ul>
<li><a href="http://github.com/visionmedia/haml.js">Haml</a> haml implementation</li>
<li><a href="http://jade-lang.com">Jade</a> haml.js successor</li>
<li><a href="http://github.com/visionmedia/ejs">EJS</a> Embedded JavaScript</li>
<li><a href="http://github.com/mauricemach/coffeekup">CoffeeKup</a> CoffeeScript based templating</li>
@@ -874,9 +874,9 @@ for free:</p>
</ul>
<h3 id="Session-Support">Session Support</h3>
<h3 id="session-support">Session Support</h3>
<p>Sessions support can be added by using Connect's <em>session</em> middleware. To do so we also need the <em>cookieParser</em> middleware place above it, which will parse and populate cookie data to <em>req.cookies</em>.</p>
<p>Sessions support can be added by using Connect&rsquo;s <em>session</em> middleware. To do so we also need the <em>cookieParser</em> middleware place above it, which will parse and populate cookie data to <em>req.cookies</em>.</p>
<pre><code>app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat" }));
@@ -917,11 +917,11 @@ app.get('/add-to-cart', function(req, res){
<p>The <em>req.session</em> object also has methods such as <em>Session#touch()</em>, <em>Session#destroy()</em>, <em>Session#regenerate()</em> among others to maintain and manipulate sessions. For more information view the <a href="http://senchalabs.github.com/connect/middleware-session.html">Connect Session</a> documentation.</p>
<h3 id="Migration-Guide">Migration Guide</h3>
<h3 id="migration-guide">Migration Guide</h3>
<p> Express 1.x developers may reference the <a href="migrate.html">Migration Guide</a> to get up to speed on how to upgrade your application to work with Express 2.x, Connect 1.x, and Node 0.4.x.</p>
<h3 id="req-header-key-defaultValue-">req.header(key[, defaultValue])</h3>
<h3 id="req.header()">req.header(key[, defaultValue])</h3>
<p>Get the case-insensitive request header <em>key</em>, with optional <em>defaultValue</em>:</p>
@@ -941,14 +941,14 @@ req.header('Referrer');
// =&gt; "http://google.com"
</code></pre>
<h3 id="req-accepts-type-">req.accepts(type)</h3>
<h3 id="req.accepts()">req.accepts(type)</h3>
<p>Check if the <em>Accept</em> header is present, and includes the given <em>type</em>.</p>
<p>When the <em>Accept</em> header is not present <em>true</em> is returned. Otherwise
the given <em>type</em> is matched by an exact match, and then subtypes. You
may pass the subtype such as "html" which is then converted internally
to "text/html" using the mime lookup table.</p>
may pass the subtype such as &ldquo;html&rdquo; which is then converted internally
to &ldquo;text/html&rdquo; using the mime lookup table.</p>
<pre><code>// Accept: text/html
req.accepts('html');
@@ -966,7 +966,7 @@ req.accepts('png');
// =&gt; false
</code></pre>
<h3 id="req-is-type-">req.is(type)</h3>
<h3 id="req.is()">req.is(type)</h3>
<p>Check if the incoming request contains the <em>Content-Type</em>
header field, and it contains the give mime <em>type</em>.</p>
@@ -987,7 +987,7 @@ header field, and it contains the give mime <em>type</em>.</p>
<p>Ad-hoc callbacks can also be registered with Express, to perform
assertions again the request, for example if we need an expressive
way to check if our incoming request is an image, we can register <em>"an image"</em>
way to check if our incoming request is an image, we can register <em>&ldquo;an image&rdquo;</em>
callback:</p>
<pre><code> app.is('an image', function(req){
@@ -996,7 +996,7 @@ callback:</p>
</code></pre>
<p>Now within our route callbacks, we can use to to assert content types
such as <em>"image/jpeg"</em>, <em>"image/png"</em>, etc.</p>
such as <em>&ldquo;image/jpeg&rdquo;</em>, <em>&ldquo;image/png&rdquo;</em>, etc.</p>
<pre><code> app.post('/image/upload', function(req, res, next){
if (req.is('an image')) {
@@ -1010,17 +1010,17 @@ such as <em>"image/jpeg"</em>, <em>"image/png"</em>, etc.</p>
<p>Keep in mind this method is <em>not</em> limited to checking <em>Content-Type</em>, you
can perform any request assertion you wish.</p>
<p>Wildcard matches can also be made, simplifying our example above for <em>"an image"</em>, by asserting the <em>subtype</em> only:</p>
<p>Wildcard matches can also be made, simplifying our example above for <em>&ldquo;an image&rdquo;</em>, by asserting the <em>subtype</em> only:</p>
<pre><code>req.is('image/*');
</code></pre>
<p>We may also assert the <em>type</em> as shown below, which would return true for <em>"application/json"</em>, and <em>"text/json"</em>.</p>
<p>We may also assert the <em>type</em> as shown below, which would return true for <em>&ldquo;application/json&rdquo;</em>, and <em>&ldquo;text/json&rdquo;</em>.</p>
<pre><code>req.is('*/json');
</code></pre>
<h3 id="req-param-name-default-">req.param(name[, default])</h3>
<h3 id="req.param()">req.param(name[, default])</h3>
<p>Return the value of param <em>name</em> when present or <em>default</em>.</p>
@@ -1035,7 +1035,7 @@ can perform any request assertion you wish.</p>
should be an object. This can be done by using
the _express.bodyParser middleware.</p>
<h3 id="req-flash-type-msg-">req.flash(type[, msg])</h3>
<h3 id="req.flash()">req.flash(type[, msg])</h3>
<p>Queue flash <em>msg</em> of the given <em>type</em>.</p>
@@ -1059,7 +1059,7 @@ req.flash();
<pre><code>req.flash('info', 'email delivery to _%s_ from _%s_ failed.', toUser, fromUser);
</code></pre>
<h3 id="req-isXMLHttpRequest">req.isXMLHttpRequest</h3>
<h3 id="req.isxmlhttprequest">req.isXMLHttpRequest</h3>
<p>Also aliased as <em>req.xhr</em>, this getter checks the <em>X-Requested-With</em> header
to see if it was issued by an <em>XMLHttpRequest</em>:</p>
@@ -1068,7 +1068,7 @@ to see if it was issued by an <em>XMLHttpRequest</em>:</p>
req.isXMLHttpRequest
</code></pre>
<h3 id="res-header-key-val-">res.header(key[, val])</h3>
<h3 id="res.header()">res.header(key[, val])</h3>
<p>Get or set the response header <em>key</em>.</p>
@@ -1082,7 +1082,28 @@ res.header('Content-Length');
// =&gt; 123
</code></pre>
<h3 id="res-contentType-type-">res.contentType(type)</h3>
<h3 id="res.charset">res.charset</h3>
<p>Sets the charset for subsequent <code>Content-Type</code> header fields. For example <code>res.send()</code> and <code>res.render()</code> default to &ldquo;utf8&rdquo;, so we may explicitly set the charset before rendering a template:</p>
<pre><code>res.charset = 'ISO-8859-1';
res.render('users');
</code></pre>
<p>or before responding with <code>res.send()</code>:</p>
<pre><code>res.charset = 'ISO-8859-1';
res.send(str);
</code></pre>
<p>or with node&rsquo;s <code>res.end()</code>:</p>
<pre><code>res.charset = 'ISO-8859-1';
res.header('Content-Type', 'text/plain');
res.end(str);
</code></pre>
<h3 id="res.contenttype()">res.contentType(type)</h3>
<p>Sets the <em>Content-Type</em> response header to the given <em>type</em>.</p>
@@ -1091,14 +1112,24 @@ res.header('Content-Length');
// Content-Type is now "image/png"
</code></pre>
<h3 id="res-attachment-filename-">res.attachment([filename])</h3>
<p>A literal <em>Content-Type</em> works as well:</p>
<p>Sets the <em>Content-Disposition</em> response header to "attachment", with optional <em>filename</em>.</p>
<pre><code> res.contentType('application/json');
</code></pre>
<p>Or simply the extension without leading <code>.</code>:</p>
<pre><code> res.contentType('json');
</code></pre>
<h3 id="res.attachment()">res.attachment([filename])</h3>
<p>Sets the <em>Content-Disposition</em> response header to &ldquo;attachment&rdquo;, with optional <em>filename</em>.</p>
<pre><code> res.attachment('path/to/my/image.png');
</code></pre>
<h3 id="res-sendfile-path-options-callback-">res.sendfile(path[, options[, callback]])</h3>
<h3 id="res.sendfile()">res.sendfile(path[, options[, callback]])</h3>
<p>Used by <code>res.download()</code> to transfer an arbitrary file.</p>
@@ -1124,7 +1155,7 @@ an error occurs, or when the transfer is complete. By default failures call <cod
});
</code></pre>
<h3 id="res-download-file-filename-callback-">res.download(file[, filename[, callback]])</h3>
<h3 id="res.download()">res.download(file[, filename[, callback]])</h3>
<p>Transfer the given <em>file</em> as an attachment with optional alternative <em>filename</em>.</p>
@@ -1145,7 +1176,7 @@ res.sendfile(file);
});
</code></pre>
<h3 id="res-send-body-status-headers-status-status-">res.send(body|status[, headers|status[, status]])</h3>
<h3 id="res.send()">res.send(body|status[, headers|status[, status]])</h3>
<p>The <em>res.send()</em> method is a high level response utility allowing you to pass
objects to respond with json, strings for html, Buffer instances, or numbers representing the status code. The following are all valid uses:</p>
@@ -1163,9 +1194,9 @@ objects to respond with json, strings for html, Buffer instances, or numbers rep
assigned through <code>res.send()</code> or previously with <code>res.header()</code> or <code>res.contentType()</code>
it will not be set again.</p>
<p>Note that this method <em>end()</em>s the response, so you will want to use node's <em>res.write()</em> for multiple writes or streaming.</p>
<p>Note that this method <em>end()</em>s the response, so you will want to use node&rsquo;s <em>res.write()</em> for multiple writes or streaming.</p>
<h3 id="res-redirect-url-status-">res.redirect(url[, status])</h3>
<h3 id="res.redirect()">res.redirect(url[, status])</h3>
<p>Redirect to the given <em>url</em> with a default response <em>status</em> of 302.</p>
@@ -1176,11 +1207,11 @@ res.redirect('home');
res.redirect('back');
</code></pre>
<p>Express supports "redirect mapping", which by default provides <em>home</em>, and <em>back</em>.
<p>Express supports &ldquo;redirect mapping&rdquo;, which by default provides <em>home</em>, and <em>back</em>.
The <em>back</em> map checks the <em>Referrer</em> and <em>Referer</em> headers, while <em>home</em> utilizes
the "home" setting and defaults to "/".</p>
the &ldquo;home&rdquo; setting and defaults to &ldquo;/&rdquo;.</p>
<h3 id="res-cookie-name-val-options-">res.cookie(name, val[, options])</h3>
<h3 id="res.cookie()">res.cookie(name, val[, options])</h3>
<p>Sets the given cookie <em>name</em> to <em>val</em>, with options <em>httpOnly</em>, <em>secure</em>, <em>expires</em> etc.</p>
@@ -1193,7 +1224,7 @@ res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000), httpOn
<pre><code>res.cookie('rememberme', 'yes', { maxAge: 900000 });
</code></pre>
<p>To parse incoming <em>Cookie</em> headers, use the <em>cookieDecoder</em> middleware, which provides the <em>req.cookies</em> object:</p>
<p>To parse incoming <em>Cookie</em> headers, use the <em>cookieParser</em> middleware, which provides the <em>req.cookies</em> object:</p>
<pre><code>app.use(express.cookieParser());
@@ -1202,26 +1233,26 @@ app.get('/', function(req, res){
});
</code></pre>
<h3 id="res-clearCookie-name-">res.clearCookie(name)</h3>
<h3 id="res.clearcookie()">res.clearCookie(name)</h3>
<p>Clear cookie <em>name</em> by setting "expires" far in the past.</p>
<p>Clear cookie <em>name</em> by setting &ldquo;expires&rdquo; far in the past.</p>
<pre><code>res.clearCookie('rememberme');
</code></pre>
<h3 id="res-render-view-options-fn-">res.render(view[, options[, fn]])</h3>
<h3 id="res.render()">res.render(view[, options[, fn]])</h3>
<p>Render <em>view</em> with the given <em>options</em> and optional callback <em>fn</em>.
When a callback function is given a response will <em>not</em> be made
automatically, however otherwise a response of <em>200</em> and <em>text/html</em> is given.</p>
<p>The <em>options</em> passed are the local variables as well, for example if we want to expose "user" to the view, and prevent a local we do so within the same object:</p>
<p>The <em>options</em> passed are the local variables as well, for example if we want to expose &ldquo;user&rdquo; to the view, and prevent a local we do so within the same object:</p>
<pre><code>var user = { name: 'tj' };
res.render('index', { layout: false, user: user });
</code></pre>
<h3 id="res-partial-view-options-">res.partial(view[, options])</h3>
<h3 id="res.partial()">res.partial(view[, options])</h3>
<p>Render <em>view</em> partial with the given <em>options</em>. This method is always available
to the view as a local variable.</p>
@@ -1231,9 +1262,9 @@ to the view as a local variable.</p>
<li><p><em>as</em> Variable name for each <em>collection</em> or <em>object</em> value, defaults to the view name.</p>
<ul>
<li>as: 'something' will add the <em>something</em> local variable</li>
<li>as: &lsquo;something&rsquo; will add the <em>something</em> local variable</li>
<li>as: this will use the collection value as the template context</li>
<li>as: global will merge the collection value's properties with <em>locals</em></li>
<li>as: global will merge the collection value&rsquo;s properties with <em>locals</em></li>
</ul>
</li>
<li><p><em>collection</em> Array of objects, the name is derived from the view name itself.
@@ -1252,7 +1283,7 @@ partial('movie', movies);
// In view: movie.director
</code></pre>
<p>To change the local from <em>movie</em> to <em>video</em> we can use the "as" option:</p>
<p>To change the local from <em>movie</em> to <em>video</em> we can use the &ldquo;as&rdquo; option:</p>
<pre><code>partial('movie', { collection: movies, as: 'video' });
// In view: video.director
@@ -1265,7 +1296,7 @@ of <em>movie.director</em> we could use <em>this.director</em>.</p>
// In view: this.director
</code></pre>
<p>Another alternative is to "explode" the properties of the collection item into
<p>Another alternative is to &ldquo;expand&rdquo; the properties of the collection item into
pseudo globals (local variables) by using <em>as: global</em>, which again is syntactic sugar:</p>
<pre><code>partial('movie', { collection: movies, as: global });
@@ -1287,12 +1318,26 @@ partial('movie', { object: movie });
// In view: movie.director
</code></pre>
<p>When a non-collection (does <em>not</em> have <em>.length</em>) is passed as the second argument, it is assumed to be the <em>object</em>, after which the object's local variable name is derived from the view name:</p>
<p>When a non-collection (does <em>not</em> have <em>.length</em>) is passed as the second argument, it is assumed to be the <em>object</em>, after which the object&rsquo;s local variable name is derived from the view name:</p>
<pre><code>partial('movie', movie);
<pre><code>var movie = new Movie('Nightmare Before Christmas', 'Tim Burton')
partial('movie', movie)
// =&gt; In view: movie.director
</code></pre>
<p>The exception of this, is when a &ldquo;plain&rdquo; object, aka &ldquo;{}&rdquo; or &ldquo;new Object&rdquo; is passed, which is considered an object with local variable. For example some may expect a &ldquo;movie&rdquo; local with the following, however since it is a plain object &ldquo;director&rdquo; and &ldquo;title&rdquo; are simply locals:</p>
<pre><code>var movie = { title: 'Nightmare Before Christmas', director: 'Tim Burton' };
partial('movie', movie)
</code></pre>
<p>For cases like this where passing a plain object is desired, simply assign it to a key, or use the <code>object</code> key which will use the filename-derived variable name. The examples below are equivalent:</p>
<pre><code> partial('movie', { locals: { movie: movie }})
partial('movie', { movie: movie })
partial('movie', { object: movie })
</code></pre>
<p>This exact API can be utilized from within a route, to respond with a fragment via Ajax or WebSockets, for example we can render a collection of users directly from a route:</p>
<pre><code>app.get('/users', function(req, res){
@@ -1309,7 +1354,7 @@ partial('movie', { object: movie });
});
</code></pre>
<h3 id="res-local-name-val-">res.local(name[, val])</h3>
<h3 id="res.local()">res.local(name[, val])</h3>
<p>Get or set the given local variable <em>name</em>. The locals built up for a response are applied to those given to the view rendering methods such as <code>res.render()</code>.</p>
@@ -1327,7 +1372,17 @@ partial('movie', { object: movie });
});
</code></pre>
<h3 id="app-set-name-val-">app.set(name[, val])</h3>
<h3 id="res.locals()">res.locals(obj)</h3>
<p> Assign several locals with the given <em>obj</em>. The following are equivalent:</p>
<pre><code> res.local('foo', bar);
res.local('bar', baz);
res.locals({ foo: bar, bar, baz });
</code></pre>
<h3 id="app.set()">app.set(name[, val])</h3>
<p>Apply an application level setting <em>name</em> to <em>val</em>, or
get the value of <em>name</em> when <em>val</em> is not present:</p>
@@ -1343,7 +1398,7 @@ app.set('views');
// =&gt; ...path...
</code></pre>
<h3 id="app-enable-name-">app.enable(name)</h3>
<h3 id="app.enable()">app.enable(name)</h3>
<p>Enable the given setting <em>name</em>:</p>
@@ -1355,7 +1410,7 @@ app.enabled('some arbitrary setting');
// =&gt; true
</code></pre>
<h3 id="app-enabled-name-">app.enabled(name)</h3>
<h3 id="app.enabled()">app.enabled(name)</h3>
<p>Check if setting <em>name</em> is enabled:</p>
@@ -1367,7 +1422,7 @@ app.enabled('view cache');
// =&gt; true
</code></pre>
<h3 id="app-disable-name-">app.disable(name)</h3>
<h3 id="app.disable()">app.disable(name)</h3>
<p>Disable the given setting <em>name</em>:</p>
@@ -1379,7 +1434,7 @@ app.disabled('some setting');
// =&gt; false
</code></pre>
<h3 id="app-disabled-name-">app.disabled(name)</h3>
<h3 id="app.disabled()">app.disabled(name)</h3>
<p>Check if setting <em>name</em> is disabled:</p>
@@ -1393,7 +1448,7 @@ app.disabled('view cache');
// =&gt; true
</code></pre>
<h3 id="app-configure-env-function-function-">app.configure(env|function[, function])</h3>
<h3 id="app.configure()">app.configure(env|function[, function])</h3>
<p>Define a callback function for the given <em>env</em> (or all environments) with callback <em>function</em>:</p>
@@ -1406,7 +1461,7 @@ app.configure('development', function(){
});
</code></pre>
<h3 id="app-redirect-name-val-">app.redirect(name, val)</h3>
<h3 id="app.redirect()">app.redirect(name, val)</h3>
<p>For use with <em>res.redirect()</em> we can map redirects at the application level as shown below:</p>
@@ -1415,7 +1470,7 @@ app.configure('development', function(){
<p>Now in a route we may call:</p>
<p> res.redirect('google');</p>
<p> res.redirect(&lsquo;google&rsquo;);</p>
<p>We may also map dynamic redirects:</p>
@@ -1438,7 +1493,7 @@ redirect <em>Location</em> would be <em>/post/12/comments</em>.</p>
<pre><code>res.redirect('/posts');
</code></pre>
<h3 id="app-error-function-">app.error(function)</h3>
<h3 id="app.error()">app.error(function)</h3>
<p>Adds an error handler <em>function</em> which will receive the exception as the first parameter as shown below.
Note that we may set several error handlers by making several calls to this method, however the handler
@@ -1449,26 +1504,35 @@ should call <em>next(err)</em> if it does not wish to deal with the exception:</
});
</code></pre>
<h3 id="app-helpers-obj-">app.helpers(obj)</h3>
<h3 id="app.helpers()">app.helpers(obj)</h3>
<p>Registers static view helpers.</p>
<p> app.helpers({</p>
<pre><code> name: function(first, last){ return first + ', ' + last }
, firstName: 'tj'
, lastName: 'holowaychuk'
<pre><code>app.helpers({
name: function(first, last){ return first + ', ' + last }
, firstName: 'tj'
, lastName: 'holowaychuk'
});
</code></pre>
<p> });</p>
<p>Our view could now utilize the <em>firstName</em> and <em>lastName</em> variables,
as well as the <em>name()</em> function exposed.</p>
<pre><code>&lt;%= name(firstName, lastName) %>
<pre><code>&lt;%= name(firstName, lastName) %&gt;
</code></pre>
<h3 id="app-dynamicHelpers-obj-">app.dynamicHelpers(obj)</h3>
<p>Express also provides a few locals by default:</p>
<pre><code>- `settings` the app's settings object
- `filename` the view's filename
- `request` the request object
- `response` the response object
- `app` the application itself
</code></pre>
<p>This method is aliased as <em>app.locals()</em>.</p>
<h3 id="app.dynamichelpers()">app.dynamicHelpers(obj)</h3>
<p>Registers dynamic view helpers. Dynamic view helpers
are simply functions which accept <em>req</em>, <em>res</em>, and are
@@ -1487,7 +1551,111 @@ becomes the local variable it is associated with.</p>
<pre><code>&lt;%= session.name %&gt;
</code></pre>
<h3 id="app-mounted-fn-">app.mounted(fn)</h3>
<h3 id="app.lookup">app.lookup</h3>
<p> The <em>app.lookup</em> http methods returns an array of callback functions
associated with the given <em>path</em>.</p>
<p> Suppose we define the following routes:</p>
<pre><code> app.get('/user/:id', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
</code></pre>
<p> We can utilize this lookup functionality to check which routes
have been defined, which can be extremely useful for higher level
frameworks built on Express.</p>
<pre><code> app.lookup.get('/user/:id');
// =&gt; [Function]
app.lookup.get('/user/:id/:op?');
// =&gt; [Function]
app.lookup.put('/user/:id');
// =&gt; [Function]
app.lookup.all('/user/:id');
// =&gt; [Function, Function]
app.lookup.all('/hey');
// =&gt; []
</code></pre>
<p> To alias <em>app.lookup.VERB()</em>, we can simply invoke <em>app.VERB()</em>
without a callback, as a shortcut, for example the following are
equivalent:</p>
<pre><code> app.lookup.get('/user');
app.get('/user');
</code></pre>
<p> Each function returned has the following properties:</p>
<pre><code> var fn = app.get('/user/:id/:op?')[0];
fn.regexp
// =&gt; /^\/user\/(?:([^\/]+?))(?:\/([^\/]+?))?\/?$/i
fn.keys
// =&gt; ['id', 'op']
fn.path
// =&gt; '/user/:id/:op?'
fn.method
// =&gt; 'GET'
</code></pre>
<h3 id="app.match">app.match</h3>
<p> The <em>app.match</em> http methods return an array of callback functions
which match the given <em>url</em>, which may include a query string etc. This
is useful when you want reflect on which routes have the opportunity to
respond.</p>
<p> Suppose we define the following routes:</p>
<pre><code> app.get('/user/:id', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
</code></pre>
<p> Our match against <strong>GET</strong> will return two functions, since the <em>:op</em>
in our second route is optional.</p>
<pre><code> app.match.get('/user/1');
// =&gt; [Function, Function]
</code></pre>
<p> This second call returns only the callback for <em>/user/:id/:op?</em>.</p>
<pre><code> app.match.get('/user/23/edit');
// =&gt; [Function]
</code></pre>
<p> We can also use <em>all()</em> to disregard the http method:</p>
<pre><code> app.match.all('/user/20');
// =&gt; [Function, Function, Function]
</code></pre>
<p> Each function matched has the following properties:</p>
<pre><code> var fn = app.match.get('/user/23/edit')[0];
fn.keys
// =&gt; ['id', 'op']
fn.params
// =&gt; { id: '23', op: 'edit' }
fn.method
// =&gt; 'GET'
</code></pre>
<h3 id="app.mounted()">app.mounted(fn)</h3>
<p>Assign a callback <em>fn</em> which is called when this <em>Server</em> is passed to <em>Server#use()</em>.</p>
@@ -1502,10 +1670,10 @@ blog.mounted(function(parent){
app.use(blog);
</code></pre>
<h3 id="app-register-ext-exports-">app.register(ext, exports)</h3>
<h3 id="app.register()">app.register(ext, exports)</h3>
<p>Register the given template engine <em>exports</em>
as <em>ext</em>. For example we may wish to map ".html"
as <em>ext</em>. For example we may wish to map &ldquo;.html&rdquo;
files to jade:</p>
<pre><code> app.register('.html', require('jade'));
@@ -1513,8 +1681,8 @@ files to jade:</p>
<p>This is also useful for libraries that may not
match extensions correctly. For example my haml.js
library is installed from npm as "hamljs" so instead
of layout.hamljs, we can register the engine as ".haml":</p>
library is installed from npm as &ldquo;hamljs&rdquo; so instead
of layout.hamljs, we can register the engine as &ldquo;.haml&rdquo;:</p>
<pre><code> app.register('.haml', require('haml-js'));
</code></pre>
@@ -1523,7 +1691,7 @@ of layout.hamljs, we can register the engine as ".haml":</p>
specification, we can also wrap their api this way. Below
we map <em>.md</em> to render markdown files, rendering the html once
since it will not change on subsequent calls, and support local substitution
in the form of "{name}".</p>
in the form of &ldquo;{name}&rdquo;.</p>
<pre><code> app.register('.md', {
compile: function(str, options){
@@ -1537,7 +1705,7 @@ in the form of "{name}".</p>
});
</code></pre>
<h3 id="app-listen-port-host-">app.listen([port[, host]])</h3>
<h3 id="app.listen()">app.listen([port[, host]])</h3>
<p>Bind the app server to the given <em>port</em>, which defaults to 3000. When <em>host</em> is omitted all
connections will be accepted via <em>INADDR_ANY</em>.</p>
@@ -1563,9 +1731,7 @@ Content-Length: 11
Hello World
</code></pre>
</div>
</div>
</div>
</body>
</html>
</html>

View File

@@ -47,7 +47,7 @@ otherwise the first call to _app.get()_, _app.post()_, etc will mount the routes
app.configure('production', function(){
var oneYear = 31557600000;
app.use(express.static({ root: __dirname + '/public', maxAge: oneYear }));
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
app.use(express.errorHandler());
});
@@ -165,7 +165,7 @@ For example we can __POST__ some json, and echo the json back using the _bodyPar
app.listen(3000);
Typically we may use a "dumb" placeholder such as "/user/:id" which has no restrictions, however say for example we are limiting a user id to digits, we may use _'/user/:id(\\d+)'_ which will _not_ match unless the placeholder value contains only digits.
Typically we may use a "dumb" placeholder such as "/user/:id" which has no restrictions, however say for example we are limiting a user id to digits, we may use _'/user/:id(\\\\d+)'_ which will _not_ match unless the placeholder value contains only digits.
### Passing Route Control
@@ -372,7 +372,7 @@ ad-hoc _NotFound_ exception:
Error.captureStackTrace(this, arguments.callee);
}
NotFound.protoype.__proto__ = Error.prototype;
NotFound.prototype.__proto__ = Error.prototype;
app.get('/404', function(req, res){
throw new NotFound;
@@ -523,15 +523,15 @@ A good example of this is specifying custom _ejs_ opening and closing tags:
### View Partials
The Express view system has built-in support for partials and collections, which are "mini" views representing a document fragment. For example rather than iterating
in a view to display comments, we would use a partial with collection support:
in a view to display comments, we could use partial collection:
partial('comment', { collection: comments });
If no other options are desired, we can omit the object and simply pass our array, which is equivalent to above:
If no other options or local variables are desired, we can omit the object and simply pass our array, which is equivalent to above:
partial('comment', comments);
When using the partial collection support a few "magic" variables are provided
When using the partial collection support a few "magic" locals are provided
for free:
* _firstInCollection_ true if this is the first object
@@ -753,6 +753,24 @@ Get or set the response header _key_.
res.header('Content-Length');
// => 123
### res.charset
Sets the charset for subsequent `Content-Type` header fields. For example `res.send()` and `res.render()` default to "utf8", so we may explicitly set the charset before rendering a template:
res.charset = 'ISO-8859-1';
res.render('users');
or before responding with `res.send()`:
res.charset = 'ISO-8859-1';
res.send(str);
or with node's `res.end()`:
res.charset = 'ISO-8859-1';
res.header('Content-Type', 'text/plain');
res.end(str);
### res.contentType(type)
Sets the _Content-Type_ response header to the given _type_.
@@ -761,6 +779,14 @@ Sets the _Content-Type_ response header to the given _type_.
res.contentType(filename);
// Content-Type is now "image/png"
A literal _Content-Type_ works as well:
res.contentType('application/json');
Or simply the extension without leading `.`:
res.contentType('json');
### res.attachment([filename])
Sets the _Content-Disposition_ response header to "attachment", with optional _filename_.
@@ -852,7 +878,7 @@ The _maxAge_ property may be used to set _expires_ relative to _Date.now()_ in m
res.cookie('rememberme', 'yes', { maxAge: 900000 });
To parse incoming _Cookie_ headers, use the _cookieDecoder_ middleware, which provides the _req.cookies_ object:
To parse incoming _Cookie_ headers, use the _cookieParser_ middleware, which provides the _req.cookies_ object:
app.use(express.cookieParser());
@@ -912,7 +938,7 @@ of _movie.director_ we could use _this.director_.
partial('movie', { collection: movies, as: this });
// In view: this.director
Another alternative is to "explode" the properties of the collection item into
Another alternative is to "expand" the properties of the collection item into
pseudo globals (local variables) by using _as: global_, which again is syntactic sugar:
partial('movie', { collection: movies, as: global });
@@ -934,9 +960,21 @@ This same logic applies to a single partial object usage:
When a non-collection (does _not_ have _.length_) is passed as the second argument, it is assumed to be the _object_, after which the object's local variable name is derived from the view name:
partial('movie', movie);
var movie = new Movie('Nightmare Before Christmas', 'Tim Burton')
partial('movie', movie)
// => In view: movie.director
The exception of this, is when a "plain" object, aka "{}" or "new Object" is passed, which is considered an object with local variable. For example some may expect a "movie" local with the following, however since it is a plain object "director" and "title" are simply locals:
var movie = { title: 'Nightmare Before Christmas', director: 'Tim Burton' };
partial('movie', movie)
For cases like this where passing a plain object is desired, simply assign it to a key, or use the `object` key which will use the filename-derived variable name. The examples below are equivalent:
partial('movie', { locals: { movie: movie }})
partial('movie', { movie: movie })
partial('movie', { object: movie })
This exact API can be utilized from within a route, to respond with a fragment via Ajax or WebSockets, for example we can render a collection of users directly from a route:
app.get('/users', function(req, res){
@@ -969,6 +1007,15 @@ Get or set the given local variable _name_. The locals built up for a response a
res.render('movie', { displayReviews: true });
});
### res.locals(obj)
Assign several locals with the given _obj_. The following are equivalent:
res.local('foo', bar);
res.local('bar', baz);
res.locals({ foo: bar, bar, baz });
### app.set(name[, val])
Apply an application level setting _name_ to _val_, or
@@ -1083,17 +1130,25 @@ should call _next(err)_ if it does not wish to deal with the exception:
Registers static view helpers.
app.helpers({
name: function(first, last){ return first + ', ' + last }
, firstName: 'tj'
, lastName: 'holowaychuk'
});
app.helpers({
name: function(first, last){ return first + ', ' + last }
, firstName: 'tj'
, lastName: 'holowaychuk'
});
Our view could now utilize the _firstName_ and _lastName_ variables,
as well as the _name()_ function exposed.
<%= name(firstName, lastName) %>
Express also provides a few locals by default:
- `settings` the app's settings object
- `filename` the view's filename
- `layout(path)` specify the layout from within a view
This method is aliased as _app.locals()_.
### app.dynamicHelpers(obj)
Registers dynamic view helpers. Dynamic view helpers
@@ -1111,6 +1166,101 @@ All views would now have _session_ available so that session data can be accesse
<%= session.name %>
### app.lookup
The _app.lookup_ http methods returns an array of callback functions
associated with the given _path_.
Suppose we define the following routes:
app.get('/user/:id', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
We can utilize this lookup functionality to check which routes
have been defined, which can be extremely useful for higher level
frameworks built on Express.
app.lookup.get('/user/:id');
// => [Function]
app.lookup.get('/user/:id/:op?');
// => [Function]
app.lookup.put('/user/:id');
// => [Function]
app.lookup.all('/user/:id');
// => [Function, Function]
app.lookup.all('/hey');
// => []
To alias _app.lookup.VERB()_, we can simply invoke _app.VERB()_
without a callback, as a shortcut, for example the following are
equivalent:
app.lookup.get('/user');
app.get('/user');
Each function returned has the following properties:
var fn = app.get('/user/:id/:op?')[0];
fn.regexp
// => /^\/user\/(?:([^\/]+?))(?:\/([^\/]+?))?\/?$/i
fn.keys
// => ['id', 'op']
fn.path
// => '/user/:id/:op?'
fn.method
// => 'GET'
### app.match
The _app.match_ http methods return an array of callback functions
which match the given _url_, which may include a query string etc. This
is useful when you want reflect on which routes have the opportunity to
respond.
Suppose we define the following routes:
app.get('/user/:id', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
Our match against __GET__ will return two functions, since the _:op_
in our second route is optional.
app.match.get('/user/1');
// => [Function, Function]
This second call returns only the callback for _/user/:id/:op?_.
app.match.get('/user/23/edit');
// => [Function]
We can also use _all()_ to disregard the http method:
app.match.all('/user/20');
// => [Function, Function, Function]
Each function matched has the following properties:
var fn = app.match.get('/user/23/edit')[0];
fn.keys
// => ['id', 'op']
fn.params
// => { id: '23', op: 'edit' }
fn.method
// => 'GET'
### app.mounted(fn)
Assign a callback _fn_ which is called when this _Server_ is passed to _Server#use()_.

View File

@@ -1,126 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "INDEX" "" "March 2011" "" ""
.
.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
Application mounting
.
.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 "Third\-Party Modules"
The following modules compliment or extend Express directly:
.
.IP "\(bu" 4
express\-resource \fIhttp://github\.com/visionmedia/express\-resource\fR provides resourceful routing
.
.IP "\(bu" 4
express\-messages \fIhttp://github\.com/visionmedia/express\-messages\fR flash message notification rendering
.
.IP "\(bu" 4
express\-configure \fIhttp://github\.com/visionmedia/express\-configure\fR async configuration support (load settings from redis etc)
.
.IP "\(bu" 4
express\-namespace \fIhttp://github\.com/visionmedia/express\-namespace\fR namespaced routing support
.
.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
View the Connect \fIhttp://senchalabs\.github\.com/connect\fR documentation
.
.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 "\(bu" 4
View the contrib guide \fIcontrib\.html\fR
.
.IP "" 0

View File

@@ -190,11 +190,6 @@
<li><a href="screencasts.html">Screencasts</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){
@@ -204,7 +199,7 @@ app.get('/', function(req, res){
app.listen(3000);
</code></pre>
<h2 id="Features">Features</h2>
<h2>Features</h2>
<ul>
<li>Robust routing</li>
@@ -223,7 +218,7 @@ app.listen(3000);
</ul>
<h2 id="Contributors">Contributors</h2>
<h2>Contributors</h2>
<p>The following are the major contributors of Express (in no specific order).</p>
@@ -235,19 +230,19 @@ app.listen(3000);
</ul>
<h2 id="Third-Party-Modules">Third-Party Modules</h2>
<h2>Third-Party Modules</h2>
<p>The following modules compliment or extend Express directly:</p>
<ul>
<li><a href="http://github.com/visionmedia/express-resource">express-resource</a> provides resourceful routing</li>
<li><a href="http://github.com/visionmedia/express-messages">express-messages</a> flash message notification rendering</li>
<li><a href="http://github.com/visionmedia/express-configure">express-configure</a> async configuration support (load settings from redis etc)</li>
<li><a href="http://github.com/visionmedia/express-configuration">express-configure</a> async configuration support (load settings from redis etc)</li>
<li><a href="http://github.com/visionmedia/express-namespace">express-namespace</a> namespaced routing support</li>
</ul>
<h2 id="More-Information">More Information</h2>
<h2>More Information</h2>
<ul>
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
@@ -259,9 +254,7 @@ app.listen(3000);
<li>View the <a href="contrib.html">contrib guide</a></li>
</ul>
</div>
</div>
</div>
</body>
</html>
</html>

View File

@@ -38,7 +38,7 @@ The following modules compliment or extend Express directly:
* [express-resource](http://github.com/visionmedia/express-resource) provides resourceful routing
* [express-messages](http://github.com/visionmedia/express-messages) flash message notification rendering
* [express-configure](http://github.com/visionmedia/express-configure) async configuration support (load settings from redis etc)
* [express-configure](http://github.com/visionmedia/express-configuration) async configuration support (load settings from redis etc)
* [express-namespace](http://github.com/visionmedia/express-namespace) namespaced routing support
## More Information

View File

@@ -1,272 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "MIGRATE" "" "March 2011" "" ""
.
.SH "NAME"
\fBmigrate\fR
.
.SS "Express 1\.x to 2\.x Migration"
.
.SS "HTTPS"
Creating an HTTPS server is simply, simply pass the TLS options to \fIexpress\.createServer()\fR:
.
.IP "" 4
.
.nf
var app = express\.createServer({
key: \.\.\.
, cert: \.\.\.
});
app\.listen(443);
.
.fi
.
.IP "" 0
.
.SS "req\.header() Referrer"
Previously if anyone was doing something similar to:
.
.IP "" 4
.
.nf
req\.headers\.referrer || req\.headers\.referer
req\.header(\'Referrer\') || req\.header(\'Referer\')
.
.fi
.
.IP "" 0
.
.P
With the new special\-case we may now simply use \fIReferrer\fR which will return either if defined:
.
.IP "" 4
.
.nf
req\.header(\'Referrer\')
.
.fi
.
.IP "" 0
.
.SS "res\.local(name, val)"
Previously all local variables had to be passed to \fIres\.render()\fR, or either \fIapp\.helpers()\fR or \fIapp\.dynamicHelpers()\fR, now we may do this at the request\-level progressively\. The \fIres\.local()\fR method accepts a \fIname\fR and \fIval\fR, however the locals passed to \fIres\.render()\fR will take precedence\.
.
.P
For example we may utilize this feature to create locals in middleware:
.
.IP "" 4
.
.nf
function loadUser(req, res, next) {
User\.get(req\.params\.id, function(err, user){
res\.local(\'user\', user);
next();
});
}
app\.get(\'/user/:id\', loadUser, function(req, res){
res\.render(\'user\');
});
.
.fi
.
.IP "" 0
.
.SS "req\.param(name[, defaultValue])"
Previously only \fIname\fR was accepted, so some of you may have been doing the following:
.
.IP "" 4
.
.nf
var id = req\.param(\'id\') || req\.user\.id;
.
.fi
.
.IP "" 0
.
.P
The new \fIdefaultValue\fR argument can handle this nicely:
.
.IP "" 4
.
.nf
var id = req\.param(\'id\', req\.user\.id);
.
.fi
.
.IP "" 0
.
.SS "app\.helpers() / app\.locals()"
\fIapp\.locals()\fR is now an alias of \fIapp\.helpers()\fR, as helpers makes more sense for functions\.
.
.SS "req\.accepts(type)"
\fIreq\.accepts()\fR now accepts extensions:
.
.IP "" 4
.
.nf
// Accept: text/html
req\.accepts(\'html\');
req\.accepts(\'\.html\');
// => true
// Accept: text/*; application/json
req\.accepts(\'html\');
req\.accepts(\'text/*\');
req\.accepts(\'text/plain\');
req\.accepts(\'application/json\');
// => true
req\.accepts(\'image/png\');
req\.accepts(\'png\');
// => false
.
.fi
.
.IP "" 0
.
.SS "res\.cookie()"
Previously only directly values could be passed, so for example:
.
.IP "" 4
.
.nf
res\.cookie(\'rememberme\', \'yes\', { expires: new Date(Date\.now() + 900000) });
.
.fi
.
.IP "" 0
.
.P
However now we have the alternative \fImaxAge\fR property which may be used to set \fIexpires\fR relative to \fIDate\.now()\fR in milliseconds, so our example above can now become:
.
.IP "" 4
.
.nf
res\.cookie(\'rememberme\', \'yes\', { maxAge: 900000 });
.
.fi
.
.IP "" 0
.
.SS "res\.download() / res\.sendfile()"
Both of these methods now utilize Connect\'s static file server behind the scenes (actually the previous Express code was ported to Connect 1\.0)\. With this change comes a change to the callback as well\. Previously the \fIpath\fR and \fIstream\fR were passed, however now only an \fIerror\fR is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete\. The callback remains optional:
.
.IP "" 4
.
.nf
res\.download(\'/path/to/file\');
res\.download(\'/path/to/file\', function(err){
if (err) {
console\.error(err);
} else {
console\.log(\'transferred\');
}
});
.
.fi
.
.IP "" 0
.
.P
The \fIstream threshold\fR setting was removed\.
.
.SS "res\.render()"
Previously locals were passed as a separate key:
.
.IP "" 4
.
.nf
res\.render(\'user\', { layout: false, locals: { user: user }});
.
.fi
.
.IP "" 0
.
.P
In Express 2\.0 both the locals and the options are one in the same, meaning you cannot have a local variable named \fIlayout\fR as it is reserved for express, however this cleans up the API:
.
.IP "" 4
.
.nf
res\.render(\'user\', { layout: false, user: user });
.
.fi
.
.IP "" 0
.
.SS "res\.partial()"
Express 2\.0 adds the \fIres\.partial()\fR method, helpful for rendering partial fragments over WebSockets or Ajax requests etc\. The API is identical to the \fIpartial()\fR calls within views\.
.
.IP "" 4
.
.nf
// render a collection of comments
res\.partial(\'comment\', [comment1, comment2]);
// render a single comment
res\.partial(\'comment\', comment);
.
.fi
.
.IP "" 0
.
.SS "Template Engine Compliance"
To comply with Express previously engines needed the following signature:
.
.IP "" 4
.
.nf
engine\.render(str, options, function(err){});
.
.fi
.
.IP "" 0
.
.P
Now they must export a \fIcompile()\fR function, returning a function which when called with local variables will render the template\. This allows Express to cache the compiled function in memory during production\.
.
.IP "" 4
.
.nf
var fn = engine\.compile(str, options);
fn(locals);
.
.fi
.
.IP "" 0
.
.SS "View Partial Lookup"
Previously partials were loaded relative to the now removed \fIview partials\fR directory setting, or by default \fIviews/partials\fR, now they are relative to the view calling them, read more on view lookup \fIguide\.html#View\-Lookup\fR\.
.
.SS "Mime Types"
Express and Connect now utilize the \fImime\fR module in npm, so to add more use:
.
.IP "" 4
.
.nf
require(\'mime\')\.define({ \'foo/bar\': [\'foo\', \'bar\'] });
.
.fi
.
.IP "" 0

View File

@@ -190,14 +190,9 @@
<li><a href="screencasts.html">Screencasts</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="Express-1-x-to-2-x-Migration">Express 1.x to 2.x Migration</h3>
<h3>Express 1.x to 2.x Migration</h3>
<h3 id="HTTPS">HTTPS</h3>
<h3>HTTPS</h3>
<p> Creating an HTTPS server is simply, simply pass the TLS options to <em>express.createServer()</em>:</p>
@@ -209,7 +204,7 @@
app.listen(443);
</code></pre>
<h3 id="req-header-Referrer">req.header() Referrer</h3>
<h3>req.header() Referrer</h3>
<p> Previously if anyone was doing something similar to:</p>
@@ -222,7 +217,7 @@
<pre><code> req.header('Referrer')
</code></pre>
<h3 id="res-local-name-val-">res.local(name, val)</h3>
<h3>res.local(name, val)</h3>
<p> Previously all local variables had to be passed to <em>res.render()</em>, or either <em>app.helpers()</em> or <em>app.dynamicHelpers()</em>, now we may do this at the request-level progressively. The <em>res.local()</em> method accepts a <em>name</em> and <em>val</em>, however the locals passed to <em>res.render()</em> will take precedence.</p>
@@ -240,7 +235,7 @@
});
</code></pre>
<h3 id="req-param-name-defaultValue-">req.param(name[, defaultValue])</h3>
<h3>req.param(name[, defaultValue])</h3>
<p> Previously only <em>name</em> was accepted, so some of you may have been doing the following:</p>
@@ -252,11 +247,11 @@
<pre><code> var id = req.param('id', req.user.id);
</code></pre>
<h3 id="app-helpers-app-locals-">app.helpers() / app.locals()</h3>
<h3>app.helpers() / app.locals()</h3>
<p> <em>app.locals()</em> is now an alias of <em>app.helpers()</em>, as helpers makes more sense for functions.</p>
<h3 id="req-accepts-type-">req.accepts(type)</h3>
<h3>req.accepts(type)</h3>
<p> <em>req.accepts()</em> now accepts extensions:</p>
@@ -277,7 +272,7 @@
// =&gt; false
</code></pre>
<h3 id="res-cookie-">res.cookie()</h3>
<h3>res.cookie()</h3>
<p> Previously only directly values could be passed, so for example:</p>
@@ -289,9 +284,9 @@
<pre><code>res.cookie('rememberme', 'yes', { maxAge: 900000 });
</code></pre>
<h3 id="res-download-res-sendfile-">res.download() / res.sendfile()</h3>
<h3>res.download() / res.sendfile()</h3>
<p> Both of these methods now utilize Connect's static file server behind the scenes (actually the previous Express code was ported to Connect 1.0). With this change comes a change to the callback as well. Previously the <em>path</em> and <em>stream</em> were passed, however now only an <em>error</em> is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete. The callback remains optional:</p>
<p> Both of these methods now utilize Connect&rsquo;s static file server behind the scenes (actually the previous Express code was ported to Connect 1.0). With this change comes a change to the callback as well. Previously the <em>path</em> and <em>stream</em> were passed, however now only an <em>error</em> is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete. The callback remains optional:</p>
<pre><code> res.download('/path/to/file');
@@ -306,7 +301,7 @@
<p> The <em>stream threshold</em> setting was removed.</p>
<h3 id="res-render-">res.render()</h3>
<h3>res.render()</h3>
<p> Previously locals were passed as a separate key:</p>
@@ -318,7 +313,7 @@
<pre><code> res.render('user', { layout: false, user: user });
</code></pre>
<h3 id="res-partial-">res.partial()</h3>
<h3>res.partial()</h3>
<p> Express 2.0 adds the <em>res.partial()</em> method, helpful for rendering partial fragments over WebSockets or Ajax requests etc. The API is identical to the <em>partial()</em> calls within views.</p>
@@ -329,7 +324,35 @@
res.partial('comment', comment);
</code></pre>
<h3 id="Template-Engine-Compliance">Template Engine Compliance</h3>
<h3>partial() locals</h3>
<p> Both <em>res.partial()</em> and the <em>partial()</em> functions accept an single object consisting of both the options and the locals. Previously with Express 1.x you may pass <em>user</em> to a partial, along with <em>date</em> like so:</p>
<pre><code> partial('user', { object: user, locals: { date: new Date }})
</code></pre>
<p>or perhaps if you preferred not to use the inferred name <em>user</em> you may used a local for this as well:</p>
<pre><code> partial('user', { locals: { user: user, date: new Date }})
</code></pre>
<p> With recent changes to Express 2.x the object passed is now both, so the following is valid for the <em>object</em> option and locals:</p>
<pre><code> partial('user', { object: user, date: new Date })
</code></pre>
<p> Or the following which is equivalent, however the local var name is explicitly set to <em>user</em> instead of deduced from the filename.</p>
<pre><code> partial('user', { user: user, date: new Date })
</code></pre>
<p> When a &ldquo;basic&rdquo; object aka <em>{}</em> or <em>new Object</em> is passed, it is considered options, otherwise it is considered the <em>object</em>. The following are equivalent:</p>
<pre><code> partial('user', user);
partial('user', { object: user });
</code></pre>
<h3>Template Engine Compliance</h3>
<p> To comply with Express previously engines needed the following signature:</p>
@@ -342,19 +365,29 @@
fn(locals);
</code></pre>
<h3 id="View-Partial-Lookup">View Partial Lookup</h3>
<h3>View Partial Lookup</h3>
<p> Previously partials were loaded relative to the now removed <em>view partials</em> directory setting, or by default <em>views/partials</em>, now they are relative to the view calling them, read more on <a href="guide.html#View-Lookup">view lookup</a>.</p>
<h3 id="Mime-Types">Mime Types</h3>
<h3>Mime Types</h3>
<p> Express and Connect now utilize the <em>mime</em> module in npm, so to add more use:</p>
<pre><code> require('mime').define({ 'foo/bar': ['foo', 'bar'] });
</code></pre>
</div>
<h3>static() middleware</h3>
<p> Previously named <code>staticProvider()</code>, the now <code>static()</code> middleware takes a single directory path, followed by options.</p>
<pre><code> app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
</code></pre>
<p>Previously when using options the <code>root</code> option would be used for this:</p>
<pre><code> app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));
</code></pre>
</div>
</div>
</body>
</html>
</html>

View File

@@ -121,6 +121,29 @@ However now we have the alternative _maxAge_ property which may be used to set _
// render a single comment
res.partial('comment', comment);
### partial() locals
Both _res.partial()_ and the _partial()_ functions accept an single object consisting of both the options and the locals. Previously with Express 1.x you may pass _user_ to a partial, along with _date_ like so:
partial('user', { object: user, locals: { date: new Date }})
or perhaps if you preferred not to use the inferred name _user_ you may used a local for this as well:
partial('user', { locals: { user: user, date: new Date }})
With recent changes to Express 2.x the object passed is now both, so the following is valid for the _object_ option and locals:
partial('user', { object: user, date: new Date })
Or the following which is equivalent, however the local var name is explicitly set to _user_ instead of deduced from the filename.
partial('user', { user: user, date: new Date })
When a "basic" object aka _{}_ or _new Object_ is passed, it is considered options, otherwise it is considered the _object_. The following are equivalent:
partial('user', user);
partial('user', { object: user });
### Template Engine Compliance
To comply with Express previously engines needed the following signature:
@@ -140,4 +163,15 @@ However now we have the alternative _maxAge_ property which may be used to set _
Express and Connect now utilize the _mime_ module in npm, so to add more use:
require('mime').define({ 'foo/bar': ['foo', 'bar'] });
require('mime').define({ 'foo/bar': ['foo', 'bar'] });
### static() middleware
Previously named `staticProvider()`, the now `static()` middleware takes a single directory path, followed by options.
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
Previously when using options the `root` option would be used for this:
app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));

View File

@@ -1,28 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "SCREENCASTS" "" "March 2011" "" ""
.
.SH "NAME"
\fBscreencasts\fR
.
.SS "Introduction"
This introduction screencast covers the basics of Express, and how to get started with your first application\.
.
.P
.
.SS "View Partials"
In this screencast we work with partials to display a collection of users using the Jade \fIhttp://jade\-lang\.com\fR template engine, and learn about view path resolution\.
.
.P
.
.SS "Route Specific Middleware"
In the screencast below we learn about the benefits of route\-specific middleware\.
.
.P
.
.SS "Route Param Preconditions"
Learn about route parameter (\fI/user/:id\fR) pre\-conditions, providing automated validation, and loading of data via the named route param segments\.
.
.P

View File

@@ -190,37 +190,34 @@
<li><a href="screencasts.html">Screencasts</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>screencasts</code>
</p>
<h3 id="Introduction">Introduction</h3>
<h3>Introduction</h3>
<p>This introduction screencast covers the basics of Express, and how to get started with your first application.</p>
<p><object height="345" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="560" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0"><param name="movie" value="http://screenr.com/Content/assets/screenr_1116090935.swf" /><param name="flashvars" value="i=139583" /><param name="allowFullScreen" value="true" /><embed pluginspage="http://www.macromedia.com/go/getflashplayer" allowfullscreen="true" src="http://screenr.com/Content/assets/screenr_1116090935.swf" height="345" flashvars="i=139583" width="560"></embed></object></p>
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139583' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139583' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
<h3 id="View-Partials">View Partials</h3>
<h3>View Partials</h3>
<p>In this screencast we work with partials to display a collection of users using the <a href="http://jade-lang.com">Jade</a> template engine, and learn about view path resolution.</p>
<p><object height="345" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="560" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0"><param name="movie" value="http://screenr.com/Content/assets/screenr_1116090935.swf" /><param name="flashvars" value="i=139591" /><param name="allowFullScreen" value="true" /><embed pluginspage="http://www.macromedia.com/go/getflashplayer" allowfullscreen="true" src="http://screenr.com/Content/assets/screenr_1116090935.swf" height="345" flashvars="i=139591" width="560"></embed></object></p>
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139591' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139591' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
<h3 id="Route-Specific-Middleware">Route Specific Middleware</h3>
<h3>Route Specific Middleware</h3>
<p>In the screencast below we learn about the benefits of route-specific middleware.</p>
<p><object height="345" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="560" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0"><param name="movie" value="http://screenr.com/Content/assets/screenr_1116090935.swf" /><param name="flashvars" value="i=140296" /><param name="allowFullScreen" value="true" /><embed pluginspage="http://www.macromedia.com/go/getflashplayer" allowfullscreen="true" src="http://screenr.com/Content/assets/screenr_1116090935.swf" height="345" flashvars="i=140296" width="560"></embed></object></p>
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140296' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140296' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
<h3 id="Route-Param-Preconditions">Route Param Preconditions</h3>
<h3>Route Param Preconditions</h3>
<p>Learn about route parameter (<em>/user/:id</em>) pre-conditions, providing automated validation, and loading of data via the named route param segments.</p>
<p><object height="345" classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000" width="560" codebase="http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0"><param name="movie" value="http://screenr.com/Content/assets/screenr_1116090935.swf" /><param name="flashvars" value="i=140300" /><param name="allowFullScreen" value="true" /><embed pluginspage="http://www.macromedia.com/go/getflashplayer" allowfullscreen="true" src="http://screenr.com/Content/assets/screenr_1116090935.swf" height="345" flashvars="i=140300" width="560"></embed></object></p>
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140300' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140300' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
</div>
</div>
</div>
</body>
</html>
</html>

View File

@@ -7,7 +7,7 @@ require.paths.unshift(__dirname + '/../../support');
*/
var express = require('../../lib/express')
, messages = require('express-contrib/messages');
, messages = require('express-messages');
var app = module.exports = express.createServer();
@@ -22,8 +22,8 @@ app.mounted(function(other){
console.log('ive been mounted!');
});
// Flash message helper provided by express-contrib
// $ npm install express-contrib
// Flash message helper provided by express-messages
// $ npm install express-messages
app.dynamicHelpers({
messages: messages

View File

@@ -10,10 +10,10 @@ h1 Blog
| It looks like you have no posts!
p
| Click
a(href=base + '/post/add') here
a(href=base + '/post/add') here
| to create a post. Login
| as
em "admin"
em "admin"
| and
em "express"
| .

View File

@@ -15,10 +15,11 @@ var pub = __dirname + '/public';
// Auto-compile sass to css with "compiler"
// and then serve with connect's staticProvider
var app = express.createServer(
express.compiler({ src: pub, enable: ['sass'] })
, express.static(pub)
);
var app = express.createServer();
app.use(express.compiler({ src: pub, enable: ['sass'] }));
app.use(app.router);
app.use(express.static(pub));
app.use(express.errorHandler({ dump: true, stack: true }));
// Optional since express defaults to CWD/views
@@ -29,17 +30,30 @@ app.set('views', __dirname + '/views');
// (although you can still mix and match)
app.set('view engine', 'jade');
function User(name, email) {
this.name = name;
this.email = email;
}
// Dummy users
var users = [
{ name: 'tj', email: 'tj@vision-media.ca' }
, { name: 'ciaran', email: 'ciaranj@gmail.com' }
, { name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
new User('tj', 'tj@vision-media.ca')
, new User('ciaran', 'ciaranj@gmail.com')
, new User('aaron', 'aaron.heckmann+github@gmail.com')
];
app.get('/', function(req, res){
res.render('users', { users: users });
});
app.get('/users/callback', function(req, res){
// a callback is also accepted
res.partial('users/user', users, function(err, html){
if (err) throw err;
res.send(html);
});
});
app.get('/users', function(req, res){
// we can use res.partial() as if
// we were in a view, utilizing the same api
@@ -48,7 +62,11 @@ app.get('/users', function(req, res){
});
app.get('/users/list', function(req, res){
res.partial('users/list', { object: users });
// use "object" to utilize the name deduced from
// the view filename. The examples below are equivalent
//res.partial('users/list', { object: users });
res.partial('users/list', { list: users });
});
app.get('/user/:id', function(req, res){

View File

@@ -0,0 +1,35 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var app = express.createServer();
app.set('views', __dirname + '/views');
// set default layout, usually "layout"
app.set('view options', { layout: 'layouts/default' });
// Set our default template engine to "jade"
// which prevents the need for extensions
// (although you can still mix and match)
app.set('view engine', 'ejs');
app.get('/', function(req, res){
res.render('pages/default');
});
app.get('/alternate', function(req, res){
// note that we do not explicitly
// state the layout here, the view does,
// although we could do it here as well.
res.render('pages/alternate');
});
app.listen(3000);
console.log('Express app started on port 3000');

View File

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

View File

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

View File

@@ -0,0 +1,2 @@
<% layout('layouts/alternate') %>
<h1>Page</h1>

View File

@@ -0,0 +1 @@
<h1>Page</h1>

View File

@@ -28,13 +28,8 @@ var ninja = {
};
app.get('/', function(req, res){
res.render('ninjas', { ninja: ninja });
res.render('ninja', { ninja: ninja });
});
app.get('/li', function(req, res){
res.partial('li', { object: 'Testing', as: 'value' });
});
app.listen(3000);
console.log('Express app started on port 3000');

View File

@@ -1,8 +1,12 @@
h1= ninja.name
// file, partial name, and partial object all match ('summary')
// the partial filename prefix '_' is completely optional
#summary!= partial('summary', ninja.summary)
// the partial filename prefix '_' is completely optional.
// In this case we need to specify ninja.summary as the object
// option, since it is a "plain" object Express cannot otherwise
// tell if it is intended to be locals, or THE summary object
#summary!= partial('summary', { object: ninja.summary })
// file, partial name = '_weapon', resolves to 'weapon' object within partial
#weapons

View File

@@ -18,11 +18,10 @@ var app = express.createServer();
// define additional functions exposed to Stylus,
// alter settings, etc
function compile(str, path, fn) {
stylus(str)
function compile(str, path) {
return stylus(str)
.set('filename', path)
.set('compress', true)
.render(fn);
.set('compress', true);
};
// add the stylus middleware, which re-compiles when

View File

@@ -27,7 +27,7 @@ var exports = module.exports = connect.middleware;
* Framework version.
*/
exports.version = '2.0.0beta2';
exports.version = '2.2.1';
/**
* Shortcut for `new Server(...)`.
@@ -46,10 +46,11 @@ exports.createServer = function(options){
};
/**
* Expose `HTTPServer`.
* Expose constructors.
*/
exports.HTTPServer = HTTPServer;
exports.HTTPSServer = HTTPSServer;
/**
* View extensions.
@@ -68,3 +69,8 @@ require('./response');
*/
require('./request');
// Error handler title
exports.errorHandler.title = 'Express';

View File

@@ -44,6 +44,8 @@ Server.prototype.__proto__ = connect.HTTPServer.prototype;
Server.prototype.init = function(middleware){
var self = this;
this.match = {};
this.lookup = {};
this.settings = {};
this.redirects = {};
this.isCallbacks = {};
@@ -51,7 +53,7 @@ Server.prototype.init = function(middleware){
this.dynamicViewHelpers = {};
this.errorHandlers = [];
// default "home" to /
// default "home" to /
this.set('home', '/');
// set "env" to NODE_ENV, defaulting to "development"
@@ -83,6 +85,17 @@ Server.prototype.init = function(middleware){
return fn;
});
// default locals
this.locals({
settings: this.settings
, app: this
});
// default development configuration
this.configure('development', function(){
this.enable('hints');
});
// default production configuration
this.configure('production', function(){
this.enable('cache views');
@@ -91,6 +104,21 @@ Server.prototype.init = function(middleware){
// register error handlers on "listening"
// so that they disregard definition position.
this.on('listening', this.registerErrorHandlers.bind(this));
// route lookup methods
methods.forEach(function(method){
self.match[method] = function(url){
return self.router.match(url, 'all' == method
? null
: method);
};
self.lookup[method] = function(path){
return self.router.lookup(path, 'all' == method
? null
: method);
};
});
};
/**
@@ -128,25 +156,36 @@ Server.prototype.registerErrorHandlers = function(){
*/
Server.prototype.use = function(route, middleware){
var app, home;
var app, home, handle;
if ('string' != typeof route) {
middleware = route, route = '/';
}
// express app
if (middleware.handle && middleware.set) app = middleware;
// restore .app property on req and res
if (app) {
app.route = route;
middleware = function(req, res, next) {
var orig = req.app;
app.handle(req, res, function(err){
req.app = res.app = orig;
next(err);
});
};
}
connect.HTTPServer.prototype.use.call(this, route, middleware);
// mounted an app
if (middleware.handle) {
app = middleware;
// express app
if (app.set) {
home = app.set('home');
if ('/' == home) home = '';
app.set('home', app.route + home);
app.parent = this;
}
// mounted hook
// mounted an app, invoke the hook
// and adjust some settings
if (app) {
home = app.set('home');
if ('/' == home) home = '';
app.set('home', app.route + home);
app.parent = this;
if (app.__mounted) app.__mounted.call(app, this);
}
@@ -200,7 +239,7 @@ Server.prototype.register = function(){
* @api public
*/
Server.prototype.helpers =
Server.prototype.helpers =
Server.prototype.locals = function(obj){
utils.merge(this.viewHelpers, obj);
return this;
@@ -225,7 +264,7 @@ Server.prototype.dynamicHelpers = function(obj){
*
* Param mapping is used to provide pre-conditions to routes
* which us normalized placeholders. For example ":user_id" may
* attempt to load the user from the database, where as ":num" may
* attempt to load the user from the database, where as ":num" may
* pass the value through `parseInt(num, 10)`.
*
* When the callback function accepts only a single argument, the
@@ -234,7 +273,7 @@ Server.prototype.dynamicHelpers = function(obj){
* app.param('page', function(n){ return parseInt(n, 10); });
*
* After which "/users/:page" would automatically provide us with
* an integer for `req.params.page`. If desired we could use the callback
* an integer for `req.params.page`. If desired we could use the callback
* signature shown below, and immediately `next(new Error('invalid page'))`
* when `parseInt` fails.
*
@@ -414,6 +453,13 @@ function generateMethod(method){
Server.prototype[method] = function(path, fn){
var self = this;
// Lookup
if (1 == arguments.length) {
return this.router.lookup(path, 'all' == method
? null
: method);
}
// Ensure router is mounted
if (!this.__usedRouter) {
this.use(this.router);

View File

@@ -140,7 +140,7 @@ req.accepts = function(type){
req.param = function(name, defaultValue){
// route params like /user/:id
if (this.params.hasOwnProperty(name) && undefined !== this.params[name]) {
if (this.params && this.params.hasOwnProperty(name) && undefined !== this.params[name]) {
return this.params[name];
}
// query string params
@@ -190,6 +190,7 @@ req.param = function(name, defaultValue){
*/
req.flash = function(type, msg){
if (this.session === undefined) throw Error('req.flash() requires sessions');
var msgs = this.session.flash = this.session.flash || {};
if (type && msg) {
var i = 2

View File

@@ -51,7 +51,7 @@ res.send = function(body, headers, status){
status = status || this.statusCode;
// allow 0 args as 204
if (!arguments.length) body = status = 204;
if (!arguments.length || undefined === body) body = status = 204;
// determine content type
switch (typeof body) {
@@ -63,11 +63,13 @@ res.send = function(body, headers, status){
break;
case 'string':
if (!this.header('Content-Type')) {
this.charset = this.charset || 'utf-8';
this.contentType('.html');
}
break;
case 'boolean':
case 'object':
if (body instanceof Buffer) {
if (Buffer.isBuffer(body)) {
if (!this.header('Content-Type')) {
this.contentType('.bin');
}
@@ -86,7 +88,7 @@ res.send = function(body, headers, status){
// populate Content-Length
if (!this.header('Content-Length')) {
this.header('Content-Length', body instanceof Buffer
this.header('Content-Length', Buffer.isBuffer(body)
? body.length
: Buffer.byteLength(body));
}
@@ -315,34 +317,44 @@ res.cookie = function(name, val, options){
res.redirect = function(url, status){
var app = this.app
, req = this.req
, base = app.set('home') || '/'
, status = status || 302
, body;
// Setup redirect map
var map = {
back: this.req.header('Referrer', base)
back: req.header('Referrer', base)
, home: base
};
// Support custom redirect map
map.__proto__ = this.app.redirects;
map.__proto__ = app.redirects;
// Attempt mapped redirect
var mapped = 'function' == typeof map[url]
? map[url](this.req, this)
? map[url](req, this)
: map[url];
// Perform redirect
url = mapped || url;
// Respect mount-point
if (app.route && !~url.indexOf('://')) {
url = join(app.route, url);
}
// Relative
if (!~url.indexOf('://')) {
// Respect mount-point
if (app.route) {
url = join(app.route, url);
}
// Absolute
var host = req.headers.host
, tls = req.connection.encrypted;
url = 'http' + (tls ? 's' : '') + '://' + host + url;
}
// Support text/{plain,html} by default
if (this.req.accepts('html')) {
if (req.accepts('html')) {
body = '<p>' + http.STATUS_CODES[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
this.header('Content-Type', 'text/html');
} else {
@@ -367,8 +379,27 @@ res.redirect = function(url, status){
*/
res.local = function(name, val){
this.locals = this.locals || {};
this._locals = this._locals || {};
return undefined === val
? this.locals[name]
: this.locals[name] = val;
? this._locals[name]
: this._locals[name] = val;
};
/**
* Assign several locals with the given `obj`,
* or return the locals.
*
* @param {Object} obj
* @return {Object|Undefined}
* @api public
*/
res.locals = function(obj){
if (obj) {
for (var key in obj) {
this.local(key, obj[key]);
}
} else {
return this._locals;
}
};

View File

@@ -15,7 +15,7 @@ var path = require('path')
, basename = path.basename
, utils = require('connect').utils
, View = require('./view/view')
, Partial = require('./view/partial')
, partial = require('./view/partial')
, union = require('./utils').union
, merge = utils.merge
, http = require('http')
@@ -34,7 +34,6 @@ var cache = {};
*/
exports = module.exports = View;
exports.Partial = Partial;
/**
* Export template engine registrar.
@@ -48,67 +47,105 @@ exports.register = View.register;
* @api private
*/
function renderPartial(res, view, options, locals, parent){
function renderPartial(res, view, options, parentLocals, parent){
var collection, object, locals;
// Inherit parent view extension when not present
if (parent && !~view.indexOf('.')) {
view += parent.extension;
}
// Allow collection to be passed as second param
if (options) {
if ('length' in options) {
options = { collection: options };
} else if (!options.collection && !options.locals && !options.object) {
options = { object: options };
// collection
if (options.collection) {
collection = options.collection;
delete options.collection;
} else if ('length' in options) {
collection = options;
options = {};
}
// locals
if (options.locals) {
locals = options.locals;
delete options.locals;
}
// object
if ('Object' != options.constructor.name) {
object = options;
options = {};
} else if (undefined != options.object) {
object = options.object;
delete options.object;
}
} else {
options = {};
}
// Inherit locals from parent
union(options, locals);
union(options, parentLocals);
// Merge locals
if (options.locals) {
merge(options, options.locals);
}
if (locals) merge(options, locals);
// Partials dont need layouts
options.renderPartial = true;
options.layout = false;
// Deduce name from view path
var name = options.as || Partial.resolveObjectName(view);
var name = options.as || partial.resolveObjectName(view);
// Render partial
function render(){
if (options.object) {
if (object) {
if ('string' == typeof name) {
options[name] = options.object;
options[name] = object;
} else if (name === global) {
merge(options, options.object);
merge(options, object);
} else {
options.scope = options.object;
options.scope = object;
}
}
return res.render(view, options, null, parent);
return res.render(view, options, null, parent, true);
}
// Collection support
var collection = options.collection;
if (collection) {
var len = collection.length
, buf = '';
delete options.collection;
, buf = ''
, keys
, key
, val;
options.collectionLength = len;
for (var i = 0; i < len; ++i) {
var val = collection[i];
options.firstInCollection = i === 0;
options.indexInCollection = i;
options.lastInCollection = i === len - 1;
options.object = val;
buf += render();
if ('number' == typeof len || Array.isArray(collection)) {
for (var i = 0; i < len; ++i) {
val = collection[i];
options.firstInCollection = i == 0;
options.indexInCollection = i;
options.lastInCollection = i == len - 1;
object = val;
buf += render();
}
} else {
keys = Object.keys(collection);
len = keys.length;
options.collectionLength = len;
options.collectionKeys = keys;
for (var i = 0; i < len; ++i) {
key = keys[i];
val = collection[key];
options.keyInCollection = key;
options.firstInCollection = i == 0;
options.indexInCollection = i;
options.lastInCollection = i == len - 1;
object = val;
buf += render();
}
}
return buf;
} else {
return render();
@@ -116,7 +153,9 @@ function renderPartial(res, view, options, locals, parent){
};
/**
* Render `view` partial with the given `options`.
* Render `view` partial with the given `options`. Optionally a
* callback `fn(err, str)` may be passed instead of writing to
* the socket.
*
* Options:
*
@@ -131,16 +170,23 @@ function renderPartial(res, view, options, locals, parent){
* For example _video.html_ will have a object _video_ available to it.
*
* @param {String} view
* @param {Object|Array} options, collection, or object
* @param {Object|Array|Function} options, collection, callback, or object
* @param {Function} fn
* @return {String}
* @api public
*/
res.partial = function(view, options){
res.partial = function(view, options, fn){
var app = this.app
, options = options || {}
, parent = {};
// accept callback as second argument
if ('function' == typeof options) {
fn = options;
options = {};
}
// root "views" option
parent.dirname = app.set('views') || process.cwd() + '/views';
@@ -149,8 +195,24 @@ res.partial = function(view, options){
parent.extension = '.' + app.set('view engine');
}
var str = renderPartial(this, view, options, null, parent);
this.send(str);
// render the partial
try {
var str = renderPartial(this, view, options, null, parent);
} catch (err) {
if (fn) {
fn(err);
} else {
this.req.next(err);
}
return;
}
// callback or transfer
if (fn) {
fn(null, str);
} else {
this.send(str);
}
};
/**
@@ -162,6 +224,7 @@ res.partial = function(view, options){
*
* - `scope` Template evaluation context (the value of `this`)
* - `debug` Output debugging information
* - `status` Response status code
*
* @param {String} view
* @param {Object|Function} options or callback function
@@ -169,25 +232,46 @@ res.partial = function(view, options){
* @api public
*/
res.render = function(view, opts, fn, parent){
res.render = function(view, opts, fn, parent, sub){
// support callback function as second arg
if (typeof opts === 'function') {
if ('function' == typeof opts) {
fn = opts, opts = null;
}
try {
return this._render(view, opts, fn, parent, sub);
} catch (err) {
// callback given
if (fn) {
fn(err);
// unwind to root call to prevent
// several next(err) calls
} else if (sub) {
throw err;
// root template, next(err)
} else {
this.req.next(err);
}
}
};
// private render()
res._render = function(view, opts, fn, parent, sub){
var options = {}
, self = this
, app = this.app
, helpers = app.viewHelpers
, dynamicHelpers = app.dynamicViewHelpers
, viewOptions = app.set('view options')
, cacheTemplates = app.set('cache views');
, cacheViews = app.set('cache views')
, root = app.set('views') || process.cwd() + '/views';
// merge "view options"
if (viewOptions) merge(options, viewOptions);
// merge res.locals
if (this.locals) merge(options, this.locals);
// merge res._locals
if (this._locals) merge(options, this._locals);
// merge render() options
if (opts) merge(options, opts);
@@ -195,10 +279,10 @@ res.render = function(view, opts, fn, parent){
// merge render() .locals
if (opts && opts.locals) merge(options, opts.locals);
// Defaults
var self = this
, root = app.set('views') || process.cwd() + '/views'
, partial = options.renderPartial
// status support
if (options.status) this.statusCode = options.status;
var partial = options.renderPartial
, layout = options.layout;
// Layout support
@@ -218,32 +302,8 @@ res.render = function(view, opts, fn, parent){
// "view engine" setting
options.defaultEngine = app.set('view engine');
// Populate view
// TODO: move this logic into the protos
var orig = view = partial
? new Partial(view, options)
: new View(view, options);
// Ensure view exists, otherwise try _ prefix
if (!view.exists) {
view = partial
? new Partial(view.prefixPath, options)
: new View(view.prefixPath, options);
}
// Ensure view _ prefix exists, otherwise try index
if (!view.exists) {
view = partial
? new Partial(orig.indexPath, options)
: new View(orig.indexPath, options);
}
// Ensure view exists, or try ../index
if (!view.exists && !options.isLayout) {
view = partial
? new Partial(orig.upIndexPath, options)
: new View(orig.upIndexPath, options);
}
// charset option
if (options.charset) this.charset = options.charset;
// Dynamic helper support
if (false !== options.dynamicHelpers) {
@@ -270,37 +330,86 @@ res.render = function(view, opts, fn, parent){
return renderPartial(self, path, opts, options, view);
};
function error(err) {
if (fn) {
fn(err);
} else {
self.req.next(err);
}
// Provide filename to engine
options.filename = view.path;
// cached view
if (cache[view]) {
view = cache[view];
// resolve view
} else {
var orig = view = new View(view, options);
// Try _ prefix ex: ./views/_<name>.jade
if (!view.exists) view = new View(orig.prefixPath, options);
// Try index ex: ./views/user/index.jade
if (!view.exists) view = new View(orig.indexPath, options);
// Try ../<name>/index ex: ../user/index.jade
// when calling partial('user') within the same dir
if (!view.exists && !options.isLayout) view = new View(orig.upIndexPath, options);
// Try root ex: <root>/user.jade
if (!view.exists) view = new View(orig.rootPath, options);
// Try root _ prefix ex: <root>/_user.jade
if (!view.exists && partial) view = new View(view.prefixPath, options);
// Does not exist
if (!view.exists) {
if (app.enabled('hints')) hintAtViewPaths(orig, options);
var err = new Error('failed to locate view "' + orig.view + '"');
err.view = orig;
throw err;
}
var engine = view.templateEngine;
view.fn = engine.compile(view.contents, options)
if (cacheViews) cache[orig.view] = view;
}
// Attempt render
try {
var engine = view.templateEngine
, template = cacheTemplates
? cache[view.path] || (cache[view.path] = engine.compile(view.contents, options))
: engine.compile(view.contents, options)
, str = template.call(options.scope, options);
} catch (err) {
return error(err);
}
// layout helper
options.layout = function(path){
layout = path;
};
// Layout support
// render
var str = view.fn.call(options.scope, options);
// layout expected
if (layout) {
options.isLayout = true;
options.layout = false;
options.body = str;
options.relative = false;
self.render(layout, options, fn, view);
this.render(layout, options, fn, view, true);
// partial return
} else if (partial) {
return str;
// render complete, and
// callback given
} else if (fn) {
fn(null, str);
// respond
} else {
this.send(str);
}
};
}
/**
* Hint at view path resolution, outputting the
* paths that Express has tried.
*
* @api private
*/
function hintAtViewPaths(view, options) {
console.error();
console.error('failed to locate view "' + view.view + '", tried:');
console.error(' - ' + new View(view.path, options).path);
console.error(' - ' + new View(view.prefixPath, options).path);
console.error(' - ' + new View(view.indexPath, options).path);
if (!options.isLayout) console.error(' - ' + new View(view.upIndexPath, options).path);
if (options.isLayout) console.error(' - ' + new View(view.rootPath, options).path);
console.error();
}

View File

@@ -5,38 +5,12 @@
* MIT Licensed
*/
/**
* Module dependencies.
*/
var View = require('./view')
, basename = require('path').basename;
/**
* Memory cache.
*/
var cache = {};
/**
* Initialize a new `Partial` for the given `view` and `options`.
*
* @param {String} view
* @param {Object} options
* @api private
*/
var Partial = exports = module.exports = function Partial(view, options) {
options = options || {};
View.call(this, view, options);
};
/**
* Inherit from `View.prototype`.
*/
Partial.prototype.__proto__ = View.prototype;
/**
* Resolve partial object name from the view path.
*

View File

@@ -17,17 +17,11 @@ var utils = require('../utils')
, stat = fs.statSync;
/**
* Memory cache.
* Require cache.
*/
var cache = {};
/**
* Existance cache.
*/
var exists = {};
/**
* Initialize a new `View` with the given `view` path and `options`.
*
@@ -38,7 +32,6 @@ var exists = {};
var View = exports = module.exports = function View(view, options) {
options = options || {};
// TODO: more caching
this.view = view;
this.root = options.root;
this.relative = false !== options.relative;
@@ -47,6 +40,7 @@ var View = exports = module.exports = function View(view, options) {
this.basename = basename(view);
this.engine = this.resolveEngine();
this.extension = '.' + this.engine;
this.name = this.basename.replace(this.extension, '');
this.path = this.resolvePath();
this.dirname = dirname(this.path);
};
@@ -59,16 +53,11 @@ var View = exports = module.exports = function View(view, options) {
*/
View.prototype.__defineGetter__('exists', function(){
var path = this.path;
if (null != exists[path]) {
return exists[path];
} else {
try {
stat(path);
return exists[path] = true;
} catch (err) {
return exists[path] = false;
}
try {
stat(this.path);
return true;
} catch (err) {
return false;
}
});
@@ -134,6 +123,18 @@ View.prototype.__defineGetter__('templateEngine', function(){
return cache[ext] || (cache[ext] = require(this.engine));
});
/**
* Return root path alternative.
*
* @return {String}
* @api public
*/
View.prototype.__defineGetter__('rootPath', function(){
this.relative = false;
return this.resolvePath();
});
/**
* Return index path alternative.
*
@@ -148,14 +149,14 @@ View.prototype.__defineGetter__('indexPath', function(){
});
/**
* Return ../index path alternative.
* Return ../<name>/index path alternative.
*
* @return {String}
* @api public
*/
View.prototype.__defineGetter__('upIndexPath', function(){
return this.dirname + '/index' + this.extension;
return this.dirname + '/../' + this.name + '/index' + this.extension;
});
/**

View File

@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "2.0.0beta2",
"version": "2.2.1",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
@@ -10,11 +10,12 @@
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
],
"dependencies": {
"connect": ">= 1.0.1 < 2.0.0",
"connect": ">= 1.2.0 < 2.0.0",
"mime": ">= 0.0.1",
"qs": ">= 0.0.6"
},
"keywords": ["framework", "sinatra", "web", "rest", "restful"],
"repository": "git://github.com/visionmedia/express",
"main": "index",
"bin": { "express": "./bin/express" },
"engines": { "node": ">= 0.4.1 < 0.5.0" }

View File

@@ -4,43 +4,41 @@ var fs = require('fs'),
file = process.argv[2];
if (file) {
var js = fs.readFileSync(file, 'utf8'),
headers = js.match(/<h3 id="(.*?)">(.*?)<\/h3>/g),
toc = ['<ul id="toc">'],
sections = {};
if (js.indexOf('id="toc"') < 0) {
headers.forEach(function(header){
var captures = header.match(/id="(.*?)">(.*?)</),
id = captures[1],
title = captures[2].replace(/\(.*?\)/, '()')
if (~title.indexOf('.')) {
var parts = title.split('.'),
recv = parts.shift(),
method = parts.shift();
if (recv == 'app') recv = 'Server';
if (recv == 'req') recv = 'Request';
if (recv == 'res') recv = 'Response';
sections[recv] = sections[recv] || [];
sections[recv].push('<li><a href="#' + id + '">' + method + '</a></li>');
} else {
toc.push('<li><a href="#' + id + '">' + title + '</a></li>');
}
});
toc.push(renderSections());
toc.push('</ul>');
js = js.replace('<div id="container">', '<div id="container">' + toc.join('\n'));
fs.writeFileSync(file, js);
}
var html = fs.readFileSync(file, 'utf8')
, toc = ['<ul id="toc">']
, sections = {};
html = html.replace(/<h3>(.*?)<\/h3>/g, function(_, title){
var id = title.toLowerCase().replace(' ', '-').replace(/\(.*?\)/, '()');
if (~title.indexOf('.')) {
var parts = title.split('.')
, recv = parts.shift()
, method = parts.shift().replace(/\(.*?\)/, '()');
if (recv == 'app') recv = 'Server';
if (recv == 'req') recv = 'Request';
if (recv == 'res') recv = 'Response';
sections[recv] = sections[recv] || [];
sections[recv].push('<li><a href="#' + id + '">' + method + '</a></li>');
} else {
toc.push('<li><a href="#' + id + '">' + title + '</a></li>');
}
return '<h3 id="' + id + '">' + title + '</h3>';
});
toc.push(renderSections());
toc.push('</ul>');
html = html.replace('<div id="container">', '<div id="container">' + toc.join('\n'));
fs.writeFileSync(file, html);
}
function renderSections() {
var buf = [];
Object.keys(sections).forEach(function(section){
var methods = sections[section],
a = '<a href="#" class="toggle">+</a> <a class="section-title" href="#">' + section + '</a>';
buf.push('<li>' + a + '<ul class="section" id="section-' + section + '">');
buf.push(methods.join('\n'));
buf.push('</ul></li>');
});
return buf.join('\n');
var buf = [];
Object.keys(sections).forEach(function(section){
var methods = sections[section],
a = '<a href="#" class="toggle">+</a> <a class="section-title" href="#">' + section + '</a>';
buf.push('<li>' + a + '<ul class="section" id="section-' + section + '">');
buf.push(methods.join('\n'));
buf.push('</ul></li>');
});
return buf.join('\n');
}

View File

@@ -13,6 +13,11 @@ module.exports = {
var server = express.createServer();
server.should.be.an.instanceof(connect.HTTPServer);
},
'test constructor exports': function(){
express.should.have.property('HTTPServer');
express.should.have.property('HTTPSServer');
},
'test connect middleware autoloaders': function(){
express.errorHandler.should.equal(connect.errorHandler);
@@ -183,12 +188,6 @@ module.exports = {
{ body: 'Internal Server Error' });
},
'test error() with route-specific middleware': function(){
var app = express.createServer();
},
'test next()': function(){
var app = express.createServer();
@@ -232,8 +231,8 @@ module.exports = {
'test #configure()': function(beforeExit){
var calls = [];
process.env.NODE_ENV = 'development';
var server = express.createServer();
server.set('env', 'development');
// Config blocks
var ret = server.configure(function(){
@@ -298,29 +297,38 @@ module.exports = {
},
'test #set()': function(){
var app = express.createServer();
var ret = app.set('title', 'My App').set('something', 'else');
ret.should.equal(app);
app.set('title').should.equal('My App');
app.set('something').should.equal('else');
var app = express.createServer();
var ret = app.set('title', 'My App').set('something', 'else');
ret.should.equal(app);
app.set('title').should.equal('My App');
app.set('something').should.equal('else');
},
'test .settings': function(){
var app = express.createServer();
app.set('title', 'My App');
app.settings.title.should.equal('My App');
app.settings.title = 'Something Else';
app.settings.title.should.equal('Something Else');
app.set('title').should.equal('Something Else');
},
'test #enable()': function(){
var app = express.createServer();
var ret = app.enable('some feature');
ret.should.equal(app);
app.set('some feature').should.be.true;
app.enabled('some feature').should.be.true;
app.enabled('something else').should.be.false;
var app = express.createServer();
var ret = app.enable('some feature');
ret.should.equal(app);
app.set('some feature').should.be.true;
app.enabled('some feature').should.be.true;
app.enabled('something else').should.be.false;
},
'test #disable()': function(){
var app = express.createServer();
var ret = app.disable('some feature');
ret.should.equal(app);
app.set('some feature').should.be.false;
app.disabled('some feature').should.be.true;
app.disabled('something else').should.be.true;
var app = express.createServer();
var ret = app.disable('some feature');
ret.should.equal(app);
app.set('some feature').should.be.false;
app.disabled('some feature').should.be.true;
app.disabled('something else').should.be.true;
},
'test middleware precedence': function(){
@@ -401,6 +409,31 @@ module.exports = {
{ url: '/regular' },
{ body: 'hey' });
},
'test .app property after returning control to parent': function() {
var app = express.createServer()
, blog = express.createServer();
// Mounted servers did not restore `req.app` and `res.app` when
// passing control back to parent via `out()` in `#handle()`.
blog.get('/', function(req, res, next){
req.app.should.equal(blog);
res.app.should.equal(blog);
next();
});
app.use(blog);
app.use(function(req, res, next) {
res.send((res.app === app) ? 'restored' : 'not-restored');
});
assert.response(app,
{ url: '/' },
{ body: 'restored' }
);
},
'test route middleware': function(){
var app = express.createServer();
@@ -528,5 +561,47 @@ module.exports = {
assert.response(app,
{ url: '/user/12', method: 'OPTIONS' },
{ headers: { Allow: 'GET,PUT' }});
},
'test app.lookup': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/edit', function(){});
app.get('/user').should.have.length(1);
app.get('/user/:id').should.have.length(1);
app.get('/user/:id/:op?').should.have.length(1);
app.put('/user/:id').should.have.length(1);
app.get('/user/:id/edit').should.have.length(1);
app.get('/').should.have.be.empty;
app.all('/user/:id').should.have.length(2);
app.lookup.get('/user').should.have.length(1);
app.lookup.get('/user/:id').should.have.length(1);
app.lookup.get('/user/:id/:op?').should.have.length(1);
app.lookup.put('/user/:id').should.have.length(1);
app.lookup.get('/user/:id/edit').should.have.length(1);
app.lookup.get('/').should.have.be.empty;
app.lookup.all('/user/:id').should.have.length(2);
},
'test app.match': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/edit', function(){});
app.match.get('/user').should.have.length(1);
app.match.get('/user/12').should.have.length(2);
app.match.get('/user/12/:op?').should.have.length(1);
app.match.put('/user/100').should.have.length(1);
app.match.get('/user/5/edit').should.have.length(2);
app.match.get('/').should.have.be.empty;
app.match.all('/user/123').should.have.length(3);
}
};
};

1
test/fixtures/_messages.jade vendored Normal file
View File

@@ -0,0 +1 @@
p Testing

1
test/fixtures/error.jade vendored Normal file
View File

@@ -0,0 +1 @@
= user.name

1
test/fixtures/item-title.jade vendored Normal file
View File

@@ -0,0 +1 @@
li #{title} #{item}

2
test/fixtures/layout-switch.jade vendored Normal file
View File

@@ -0,0 +1,2 @@
- layout('layouts/alternate')
h1 My Page

1
test/fixtures/layouts/alternate.jade vendored Normal file
View File

@@ -0,0 +1 @@
#alternate!= body

1
test/fixtures/nested/partial.jade vendored Normal file
View File

@@ -0,0 +1 @@
!= partial('stats', { hits: 15, misses: 1 })

1
test/fixtures/nested/partial2.jade vendored Normal file
View File

@@ -0,0 +1 @@
!= partial('messages')

1
test/fixtures/object-item.jade vendored Normal file
View File

@@ -0,0 +1 @@
li #{keyInCollection}: #{item}

2
test/fixtures/stats.jade vendored Normal file
View File

@@ -0,0 +1,2 @@
p Hits #{hits}
p Misses #{misses}

View File

@@ -11,7 +11,7 @@ var express = require('express')
module.exports = {
'test #send()': function(){
var app = express.createServer();
app.get('/html', function(req, res){
res.send('<p>test</p>', { 'Content-Language': 'en' });
});
@@ -30,7 +30,7 @@ module.exports = {
app.get('/text', function(req, res){
res.header('X-Foo', 'bar');
res.contentType('.txt');
res.contentType('txt');
res.send('wahoo');
});
@@ -49,15 +49,28 @@ module.exports = {
app.get('/noargs', function(req, res, next){
res.send();
});
app.get('/undefined', function(req, res, next){
res.send(undefined);
});
app.get('/bool', function(req, res, next){
res.send(true);
});
assert.response(app,
{ url: '/bool' },
{ body: 'true'
, headers: { 'Content-Type': 'application/json' }});
assert.response(app,
{ url: '/html' },
{ body: '<p>test</p>'
, headers: {
'Content-Language': 'en'
, 'Content-Type': 'text/html'
, 'Content-Type': 'text/html; charset=utf-8'
}});
assert.response(app,
{ url: '/json' },
{ body: '{"foo":"bar"}'
@@ -66,7 +79,7 @@ module.exports = {
'Content-Type': 'application/json'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=test' },
{ body: 'test({"foo":"bar"});'
@@ -75,7 +88,7 @@ module.exports = {
'Content-Type': 'text/javascript'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=baz' },
{ body: 'baz({"foo":"bar"});'
@@ -83,7 +96,7 @@ module.exports = {
'Content-Type': 'text/javascript'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/jsonp?callback=invalid()[]' },
{ body: 'invalid({"foo":"bar"});'
@@ -92,7 +105,7 @@ module.exports = {
'Content-Type': 'text/javascript'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/json?callback=test' },
{ body: '{"foo":"bar"}'
@@ -101,7 +114,7 @@ module.exports = {
'Content-Type': 'application/json'
, 'X-Foo': 'baz'
}});
assert.response(app,
{ url: '/text' },
{ body: 'wahoo'
@@ -109,22 +122,22 @@ module.exports = {
'Content-Type': 'text/plain'
, 'X-Foo': 'bar'
}});
assert.response(app,
{ url: '/status' },
{ body: 'Not Found'
, status: 404
, headers: { 'Content-Type': 'text/plain' }});
assert.response(app,
{ url: '/error' },
{ body: 'Oh shit!'
, status: 500
, headers: {
'Content-Type': 'text/plain'
'Content-Type': 'text/plain; charset=utf-8'
, 'Content-Length': '8'
}});
assert.response(app,
{ url: '/buffer' },
{ body: 'wahoo!'
@@ -132,13 +145,20 @@ module.exports = {
'Content-Type': 'application/octet-stream'
, 'Content-Length': '6'
}});
assert.response(app,
{ url: '/noargs' },
{ status: 204 }, function(res){
assert.equal(undefined, res.headers['content-type']);
assert.equal(undefined, res.headers['content-length']);
});
assert.response(app,
{ url: '/undefined' },
{ status: 204 }, function(res){
assert.equal(undefined, res.headers['content-type']);
assert.equal(undefined, res.headers['content-length']);
});
},
'test #contentType()': function(){
@@ -154,7 +174,15 @@ module.exports = {
res.contentType('json');
res.send('{"foo":"bar"}');
});
app.get('/literal', function(req, res){
res.contentType('application/json');
res.send('{"foo":"bar"}')
});
assert.response(app,
{ url: '/literal' },
{ headers: { 'Content-Type': 'application/json' }});
assert.response(app,
{ url: '/html' },
{ body: '<p>yay</p>', headers: { 'Content-Type': 'text/html' }});
@@ -239,58 +267,58 @@ module.exports = {
});
assert.response(app,
{ url: '/html', headers: { Accept: 'text/html,text/plain' }},
{ url: '/html', headers: { Accept: 'text/html,text/plain', Host: 'foo.com' }},
{ body: '<p>Moved Temporarily. Redirecting to <a href="http://google.com">http://google.com</a></p>' });
assert.response(app,
{ url: '/', headers: { Accept: 'text/plain' }},
{ url: '/', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Permanently. Redirecting to http://google.com'
, status: 301, headers: { Location: 'http://google.com' }});
assert.response(app,
{ url: '/back', headers: { Accept: 'text/plain' }},
{ body: 'Moved Temporarily. Redirecting to /'
, status: 302, headers: { Location: '/', 'Content-Type': 'text/plain' }});
{ url: '/back', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/'
, status: 302, headers: { Location: 'http://foo.com/', 'Content-Type': 'text/plain' }});
assert.response(app,
{ url: '/back', headers: { Referer: '/foo', Accept: 'text/plain' }},
{ body: 'Moved Temporarily. Redirecting to /foo'
, status: 302, headers: { Location: '/foo' }});
{ url: '/back', headers: { Referer: '/foo', Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/foo'
, status: 302, headers: { Location: 'http://foo.com/foo' }});
assert.response(app,
{ url: '/back', headers: { Referrer: '/foo', Accept: 'text/plain' }},
{ body: 'Moved Temporarily. Redirecting to /foo'
, status: 302, headers: { Location: '/foo' }});
{ url: '/back', headers: { Referrer: '/foo', Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/foo'
, status: 302, headers: { Location: 'http://foo.com/foo' }});
assert.response(app,
{ url: '/home', headers: { Accept: 'text/plain' } },
{ body: 'Moved Temporarily. Redirecting to /'
, status: 302, headers: { Location: '/' }});
{ url: '/home', headers: { Accept: 'text/plain', Host: 'foo.com' } },
{ body: 'Moved Temporarily. Redirecting to http://foo.com/'
, status: 302, headers: { Location: 'http://foo.com/' }});
assert.response(app2,
{ url: '/', headers: { Accept: 'text/plain' }},
{ url: '/', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Permanently. Redirecting to http://google.com'
, status: 301, headers: { Location: 'http://google.com' }});
assert.response(app2,
{ url: '/back', headers: { Accept: 'text/plain' }},
{ body: 'Moved Temporarily. Redirecting to /blog'
, status: 302, headers: { Location: '/blog' }});
{ url: '/back', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/blog'
, status: 302, headers: { Location: 'http://foo.com/blog' }});
assert.response(app2,
{ url: '/home', headers: { Accept: 'text/plain' }},
{ body: 'Moved Temporarily. Redirecting to /blog'
, status: 302, headers: { Location: '/blog' }});
{ url: '/home', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/blog'
, status: 302, headers: { Location: 'http://foo.com/blog' }});
assert.response(app2,
{ url: '/google', headers: { Accept: 'text/plain' }},
{ url: '/google', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://google.com'
, status: 302, headers: { Location: 'http://google.com' }});
assert.response(app2,
{ url: '/user/12', headers: { Accept: 'text/plain' }},
{ body: 'Moved Temporarily. Redirecting to /user/12/blog'
, status: 302, headers: { Location: '/user/12/blog', 'X-Foo': 'bar' }});
{ url: '/user/12', headers: { Accept: 'text/plain', Host: 'foo.com' }},
{ body: 'Moved Temporarily. Redirecting to http://foo.com/user/12/blog'
, status: 302, headers: { Location: 'http://foo.com/user/12/blog', 'X-Foo': 'bar' }});
},
'test #redirect() when mounted': function(){
@@ -309,9 +337,9 @@ module.exports = {
app.use('/blog', blog);
assert.response(app,
{ url: '/blog/posts' },
{ url: '/blog/posts', headers: { Host: 'foo.com' }},
{ status: 302
, headers: { Location: '/blog/posts/all' }});
, headers: { Location: 'http://foo.com/blog/posts/all' }});
},
'test #sendfile()': function(){
@@ -403,7 +431,7 @@ module.exports = {
{ url: '/large.json' },
{ headers: {
'Accept-Ranges': 'bytes'
, 'Cache-Control': 'public max-age=3600'
, 'Cache-Control': 'public, max-age=3600'
}});
beforeExit(function(){
@@ -529,7 +557,7 @@ module.exports = {
});
assert.response(app,
{ url: '/' },
{ url: '/', headers: { Host: 'foo.com' }},
function(res){
res.headers['set-cookie']
.should.eql(['rememberme=yes; expires=Thu, 01 Jan 1970 00:00:00 GMT; httpOnly', 'something=else']);
@@ -562,5 +590,18 @@ module.exports = {
assert.response(app,
{ url: '/', method: 'HEAD' },
{ body: '', headers: { 'Content-Length': 11 }});
},
'test .charset with res.send()': function(){
var app = express.createServer();
app.get('/', function(req, res){
res.charset = 'ISO-8859-1';
res.send('<p>hey</p>');
});
assert.response(app,
{ url: '/' },
{ headers: { 'Content-Type': 'text/html; charset=ISO-8859-1' }});
}
};

View File

@@ -8,7 +8,7 @@ var express = require('express')
, assert = require('assert')
, should = require('should')
, View = require('../lib/view')
, Partial = View.Partial
, partial = require('../lib/view/partial')
/**
* Create a test server with views set to ./fixtures.
@@ -93,8 +93,8 @@ module.exports = {
view.templateEngine.should.equal(require('jade'));
},
'test Partial.resolveObjectName()': function(){
var resolve = Partial.resolveObjectName;
'test partial.resolveObjectName()': function(){
var resolve = partial.resolveObjectName;
resolve('/path/to/user.ejs').should.equal('user');
resolve('/path/to/user-post.ejs').should.equal('userPost');
resolve('/path/to/user post.ejs').should.equal('userPost');
@@ -102,92 +102,80 @@ module.exports = {
resolve('forum thread post.ejs').should.equal('forumThreadPost');
},
'test Partial#path for partials': function(){
var fixtures = __dirname + '/fixtures';
var view = new Partial('user.jade', { root: fixtures });
view.path.should.equal(fixtures + '/user.jade');
var view = new Partial('user', { parentView: view, root: fixtures });
view.path.should.equal(fixtures + '/user.jade');
var view = new Partial('forum/thread', { parentView: view });
view.path.should.equal(fixtures + '/forum/thread.jade');
var view = new Partial('forum/thread.jade', { root: fixtures });
view.path.should.equal(fixtures + '/forum/thread.jade');
var view = new Partial('thread', { parentView: view });
view.path.should.equal(fixtures + '/forum/thread.jade');
},
'test #render()': function(){
var app = create();
app.set('view engine', 'jade');
app.get('/', function(req, res){
res.render('index.jade', { layout: false });
});
app.get('/jade', function(req, res){
res.render('index', { layout: false });
});
app.get('/haml', function(req, res){
res.render('hello.haml', { layout: false });
});
app.get('/callback/layout/no-options', function(req, res){
res.render('hello.jade', function(err, str){
assert.ok(!err);
res.send(str.replace(':(', ':)'));
});
});
app.get('/callback/layout', function(req, res){
res.render('hello.jade', {}, function(err, str){
assert.ok(!err);
res.send(str.replace(':(', ':)'));
});
});
app.get('/callback', function(req, res){
res.render('hello.haml', { layout: false }, function(err, str){
assert.ok(!err);
res.send(str.replace('Hello World', ':)'));
});
});
app.get('/invalid', function(req, res){
res.render('invalid.jade', { layout: false });
});
app.get('/invalid-async', function(req, res){
process.nextTick(function(){
res.render('invalid.jade', { layout: false });
});
});
app.get('/error', function(req, res){
res.render('invalid.jade', { layout: false }, function(err){
res.send(err.arguments[0]);
});
});
app.get('/absolute', function(req, res){
res.render(__dirname + '/fixtures/index.jade', { layout: false });
});
app.get('/ferret', function(req, res){
res.render('ferret', { layout: false, ferret: { name: 'Tobi' }});
});
app.get('/status', function(req, res){
res.render('hello.jade', { status: 500 });
});
assert.response(app,
{ url: '/status' },
{ status: 500 });
assert.response(app,
{ url: '/ferret' },
{ body: '<li class="ferret">Tobi</li>' });
assert.response(app,
{ url: '/' },
{ body: '<p>Welcome</p>', headers: { 'Content-Type': 'text/html' }});
{ body: '<p>Welcome</p>', headers: { 'Content-Type': 'text/html; charset=utf-8' }});
assert.response(app,
{ url: '/jade' },
{ body: '<p>Welcome</p>' });
@@ -226,14 +214,14 @@ module.exports = {
'test #render() layout': function(){
var app = create();
app.set('view engine', 'jade');
app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/jade', function(req, res){
res.render('index');
});
assert.response(app,
{ url: '/' },
{ body: '<html><body><p>Welcome</p></body></html>' });
@@ -242,7 +230,7 @@ module.exports = {
'test #render() specific layout': function(beforeExit){
var app = create()
, called;
app.get('/', function(req, res){
res.render('index.jade', { layout: 'cool-layout.jade' }, function(err, html){
called = true;
@@ -261,7 +249,7 @@ module.exports = {
app.get('/nope', function(req, res){
res.render('index.jade', { layout: 'nope.jade' });
});
assert.response(app,
{ url: '/' },
{ body: '<cool><p>Welcome</p></cool>' });
@@ -277,10 +265,10 @@ module.exports = {
assert.response(app,
{ url: '/nope' },
function(res){
assert.ok(~res.body.indexOf('Error: E'));
assert.ok(~res.body.indexOf('nope/index.jade'));
assert.ok(~res.body.indexOf('Error: failed to locate view'));
assert.ok(~res.body.indexOf('nope'));
});
beforeExit(function(){
assert.ok(called, 'Layout callback never called');
});
@@ -299,6 +287,19 @@ module.exports = {
{ body: '<cool><p>Welcome</p></cool>' });
},
'test #render() view layout control': function(){
var app = create();
app.set('view engine', 'jade');
app.get('/', function(req, res){
res.render('layout-switch');
});
assert.response(app,
{ url: '/' },
{ body: '<div id="alternate"><h1>My Page</h1></div>' });
},
'test #render() "view engine" with periods in dirname': function(){
var app = create();
app.set('view engine', 'jade');
@@ -315,17 +316,17 @@ module.exports = {
'test #render() view helpers': function(beforeExit){
var app = create()
, calls = 0;
app.locals({
lastName: 'holowaychuk'
});
app.helpers({
greetings: function(sess, lastName){
return 'Hello ' + sess.name + ' ' + lastName;
}
});
var ret = app.dynamicHelpers({
session: function(req, res){
++calls;
@@ -335,19 +336,19 @@ module.exports = {
return req.session;
}
});
assert.equal(app, ret, 'Server#helpers() is not chainable');
app.get('/', function(req, res){
req.session = { name: 'tj' };
res.render('dynamic-helpers.jade', { layout: false });
});
app.get('/ejs', function(req, res){
req.session = { name: 'tj' };
res.render('dynamic-helpers.ejs', { layout: false });
});
app.get('/precedence', function(req, res){
req.session = { name: 'tj' };
res.render('dynamic-helpers.jade', {
@@ -364,17 +365,30 @@ module.exports = {
assert.response(app,
{ url: '/precedence' },
{ body: '<html><body><p>Hello tj foobar</p></body></html>' });
beforeExit(function(){
assert.equal(3, calls);
});
},
'test #partial()': function(){
'test #partial() collection object': function(){
var app = create();
app.set('view engine', 'jade');
app.get('/', function(req, res){
var items = { 2: 'foo', bar: 'bar' };
res.partial('object-item.jade', { as: 'item', collection: items });
});
assert.response(app,
{ url: '/' },
{ body: '<li>2: foo</li><li>bar: bar</li>' });
},
'test #partial()': function(){
var app = create();
app.set('view engine', 'jade');
// Auto-assigned local w/ collection option
app.get('/', function(req, res){
res.render('items.jade', { items: ['one', 'two'] });
@@ -383,7 +397,7 @@ module.exports = {
assert.response(app,
{ url: '/' },
{ body: '<html><body><ul><li>one</li><li>two</li></ul></body></html>' });
// Auto-assigned local w/ collection array
var movies = [
{ title: 'Nightmare Before Christmas', director: 'Tim Burton' },
@@ -392,7 +406,7 @@ module.exports = {
app.get('/movies', function(req, res){
res.render('movies.jade', { movies: movies });
});
var html = [
'<html>',
'<body>',
@@ -409,11 +423,11 @@ module.exports = {
'</body>',
'</html>'
].join('');
assert.response(app,
{ url: '/movies' },
{ body: html });
// as: str collection option
app.get('/user', function(req, res){
res.partial('user', {
@@ -425,7 +439,7 @@ module.exports = {
assert.response(app,
{ url: '/user' },
{ body: '<p>tj</p>' });
// as: with object collection
app.get('/user/object', function(req, res){
res.partial('user.jade', {
@@ -437,7 +451,7 @@ module.exports = {
assert.response(app,
{ url: '/user' },
{ body: '<p>tj</p>' });
// as: this collection option
app.get('/person', function(req, res){
res.partial('person.jade', {
@@ -450,7 +464,7 @@ module.exports = {
assert.response(app,
{ url: '/person' },
{ body: '<p>name: tj</p>' });
// as: global collection option
app.get('/videos', function(req, res){
res.partial('video.jade', {
@@ -458,7 +472,7 @@ module.exports = {
collection: movies
});
});
assert.response(app,
{ url: '/videos' },
{ body: '<p>Tim Burton</p><p>James Cameron</p>' });
@@ -485,7 +499,7 @@ module.exports = {
assert.response(app,
{ url: '/movie' },
{ body: '<li><div class="title">Nightmare Before Christmas</div><div class="director">Tim Burton</div></li>' });
app.get('/video-global', function(req, res){
res.partial('video.jade', {
object: movies[0],
@@ -497,7 +511,7 @@ module.exports = {
assert.response(app,
{ url: '/video-global' },
{ body: '<p>Tim Burton</p>' });
app.get('/person-this', function(req, res){
res.partial('person.jade', {
object: { name: 'tj' },
@@ -510,7 +524,7 @@ module.exports = {
assert.response(app,
{ url: '/person-this' },
{ body: '<p>User: tj</p>' });
// No options
app.get('/nothing', function(req, res){
res.partial('hello.ejs');
@@ -519,29 +533,135 @@ module.exports = {
assert.response(app,
{ url: '/nothing' },
{ body: 'Hello' });
// Path segments + "as"
app.get('/role/as', function(req, res){
res.partial('user/role.ejs', { as: 'role', collection: ['admin', 'member'] });
});
assert.response(app,
{ url: '/role/as' },
{ body: '<li>Role: admin</li><li>Role: member</li>' });
// Deduce name from last segment
app.get('/role', function(req, res){
res.partial('user/role.ejs', ['admin', 'member']);
});
assert.response(app,
{ url: '/role' },
{ body: '<li>Role: admin</li><li>Role: member</li>' });
// Non-basic object support
function Movie(title, director){
this.title = title;
this.director = director;
}
app.get('/movie/object', function(req, res){
res.partial('movie', new Movie('The TJ', 'tj'));
});
assert.response(app,
{ url: '/movie/object' },
{ body: '<li><div class="title">The TJ</div><div class="director">tj</div></li>' });
// Locals
app.get('/stats', function(req, res){
res.partial('stats', {
hits: 12
, misses: 1
});
});
assert.response(app,
{ url: '/stats' },
{ body: '<p>Hits 12</p><p>Misses 1</p>' });
// Locals
app.get('/stats/locals', function(req, res){
res.partial('stats', {
locals: {
hits: 12
, misses: 1
}
});
});
assert.response(app,
{ url: '/stats/locals' },
{ body: '<p>Hits 12</p><p>Misses 1</p>' });
// Collection + locals
app.get('/items', function(req, res){
res.partial('item-title', {
collection: ['foo', 'bar']
, title: 'test'
, as: 'item'
});
});
assert.response(app,
{ url: '/items' },
{ body: '<li>test foo</li><li>test bar</li>' });
app.get('/stats/callback', function(req, res){
res.partial('stats', { hits: 12, misses: 1 }, function(err, html){
res.send('got: ' + html);
});
});
assert.response(app,
{ url: '/stats/callback' },
{ body: 'got: <p>Hits 12</p><p>Misses 1</p>' });
app.get('/stats/callback/2', function(req, res){
res.locals({ hits: 12, misses: 1 });
res.partial('stats', function(err, html){
res.send('got: ' + html);
});
});
assert.response(app,
{ url: '/stats/callback/2' },
{ body: 'got: <p>Hits 12</p><p>Misses 1</p>' });
// root lookup
app.get('/root', function(req, res){
res.partial('nested/partial');
});
assert.response(app,
{ url: '/root' },
{ body: '<p>Hits 15</p><p>Misses 1</p>' });
// root _* lookup
app.get('/root/underscore', function(req, res){
res.partial('nested/partial2');
});
assert.response(app,
{ url: '/root/underscore' },
{ body: '<p>Testing</p>' });
// error in template
app.get('/error', function(req, res){
process.nextTick(function(){
res.partial('error');
});
});
assert.response(app,
{ url: '/error' },
{ status: 500 });
},
'test #partial() with several calls': function(){
var app = create();
app.get('/', function(req, res, next){
res.render('list.jade', { layout: false });
});
@@ -553,7 +673,7 @@ module.exports = {
'test #partial() with several calls using locals': function(){
var app = create();
app.get('/', function(req, res, next){
res.render('list2.jade', { layout: false });
});
@@ -565,9 +685,9 @@ module.exports = {
'test #partial() locals': function(){
var app = create();
app.set('view engine', 'jade');
app.get('/', function(req, res, next){
res.partial('pet-count', {
locals: {
@@ -577,7 +697,7 @@ module.exports = {
}
});
});
assert.response(app,
{ url: '/' },
{ body: 'We have 5 cool pets\n' });
@@ -585,59 +705,71 @@ module.exports = {
'test #partial() locals precedence': function(){
var app = create();
app.get('/', function(req, res, next){
res.render('greetings.jade', {
name: 'TJ'
, locals: { otherName: 'Overridden' }
});
});
assert.response(app,
{ url: '/' },
{ body: '<html><body><h1>TJ</h1><p>Welcome Overridden</p></body></html>' });
},
'test #partial() index': function(){
var app = create();
app.set('view engine', 'jade');
function Ferret(name){ this.name = name; };
app.get('/ferret', function(req, res){
res.partial('ferret', { name: 'Tobi' });
res.partial('ferret', new Ferret('Tobi'));
});
app.get('/ferret/basic', function(req, res){
res.partial('ferret', { ferret: { name: 'Tobi' }});
});
assert.response(app,
{ url: '/ferret/basic' },
{ body: '<li class="ferret">Tobi</li>' });
assert.response(app,
{ url: '/ferret' },
{ body: '<li class="ferret">Tobi</li>' });
},
'test #partial() relative index': function(){
var app = create();
app.set('view engine', 'jade');
function Ferret(name) { this.name = name; }
app.get('/ferret', function(req, res){
var tobi = { name: 'Tobi' }
, loki = { name: 'Loki' };
var tobi = new Ferret('Tobi')
, loki = new Ferret('Loki');
res.partial('ferret/list', { object: [tobi, loki] });
});
assert.response(app,
{ url: '/ferret' },
{ body: '<ul id="ferrets"><li class="ferret">Tobi</li><li class="ferret">Loki</li></ul>' });
},
'test #partial() object': function(){
var app = create();
app.get('/', function(req, res, next){
res.partial('movie.jade', {
title: 'Foobar'
, director: 'Tim Burton'
});
function Movie(title, director) {
this.title = title;
this.director = director;
}
res.partial('movie.jade', new Movie('Foobar', 'Tim Burton'));
});
assert.response(app,
{ url: '/' },
{ body: '<li><div class="title">Foobar</div><div class="director">Tim Burton</div></li>' });
@@ -645,13 +777,13 @@ module.exports = {
'test #partial() locals with collection': function(){
var app = create();
app.get('/', function(req, res, next){
res.render('pet-land.jade', {
pets: ['Ewald']
});
});
assert.response(app,
{ url: '/' },
{ body: '<html><body><div><li>Ewald is the coolest of Animal land</li></div></body></html>' });
@@ -659,14 +791,14 @@ module.exports = {
'test #partial() inheriting initial locals': function(){
var app = create();
app.get('/pets', function(req, res, next){
res.render('pets.jade', {
site: 'My Cool Pets'
, pets: ['Tobi', 'Jane', 'Bandit']
});
});
var html = [
'<html>',
'<body>',
@@ -680,7 +812,7 @@ module.exports = {
'</body>',
'</html>'
].join('');
assert.response(app,
{ url: '/pets' },
{ body: html });
@@ -688,7 +820,7 @@ module.exports = {
'test #partial() with array-like collection': function(){
var app = create();
var movies = {
0: { title: 'Nightmare Before Christmas', director: 'Tim Burton' },
1: { title: 'Avatar', director: 'James Cameron' },
@@ -714,7 +846,7 @@ module.exports = {
'</body>',
'</html>'
].join('');
assert.response(app,
{ url: '/movies' },
{ body: html });
@@ -723,7 +855,7 @@ module.exports = {
'test "partials" setting': function(){
var app = create();
app.set('partials', __dirname + '/fixtures/sub-templates');
app.get('/', function(req, res){
res.render('items.jade', {
layout: false,
@@ -820,12 +952,12 @@ module.exports = {
, open: '<?'
, title: 'Original'
});
function setTitle(req, res, next) {
res.local('title', 'Wahoo');
next();
}
app.get('/video', setTitle, function(req, res, next){
res.local('close', '?>');
res.render('video.ejs', { layout: false, title: 'keyboard cat' });
@@ -835,20 +967,20 @@ module.exports = {
res.local('close', '?>');
res.render('video.ejs', { layout: false });
});
app.get('/video/3', function(req, res, next){
res.local('close', '?>');
res.render('video.ejs', { layout: false });
});
assert.response(app,
{ url: '/video' },
{ body: '<h1>keyboard cat</h1>' });
assert.response(app,
{ url: '/video/2' },
{ body: '<h1>Wahoo</h1>' });
assert.response(app,
{ url: '/video/3' },
{ body: '<h1>Original</h1>' });
@@ -873,11 +1005,11 @@ module.exports = {
}
next();
});
app.get('/pets', function(req, res, next){
res.render('pets.jade');
});
var html = [
'<html>',
'<body>',
@@ -891,9 +1023,60 @@ module.exports = {
'</body>',
'</html>'
].join('');
assert.response(app,
{ url: '/pets' },
{ body: html });
},
'test .charset with res.render()': function(){
var app = create();
app.get('/', function(req, res){
res.charset = 'ISO-8859-1';
res.render('hello.jade');
});
assert.response(app,
{ url: '/' },
{ headers: { 'Content-Type': 'text/html; charset=ISO-8859-1' }});
},
'test charset res.render() option': function(){
var app = create();
app.get('/', function(req, res){
res.render('hello.jade', { charset: 'ISO-8859-1' });
});
assert.response(app,
{ url: '/' },
{ headers: { 'Content-Type': 'text/html; charset=ISO-8859-1' }});
},
'test charset option': function(){
var app = create();
app.set('view options', { charset: 'ISO-8859-1' });
app.get('/', function(req, res){
res.render('hello.jade');
});
assert.response(app,
{ url: '/' },
{ headers: { 'Content-Type': 'text/html; charset=ISO-8859-1' }});
},
'test charset override': function(){
var app = create();
app.set('view options', { charset: 'ISO-8859-1' });
app.get('/', function(req, res){
res.render('hello.jade', { charset: 'utf8' });
});
assert.response(app,
{ url: '/' },
{ headers: { 'Content-Type': 'text/html; charset=utf8' }});
}
};