mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
126 Commits
2.0.0beta2
...
2.2.1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4e1aefa5b5 | ||
|
|
89383ecc57 | ||
|
|
08046f7692 | ||
|
|
885fb1fa92 | ||
|
|
6a58c71528 | ||
|
|
371d66ba2a | ||
|
|
25bddf3fb5 | ||
|
|
f6e9fb13f8 | ||
|
|
f0df8434e7 | ||
|
|
e4d3f239e5 | ||
|
|
bcc22dfa6f | ||
|
|
f614709a01 | ||
|
|
11250d22c9 | ||
|
|
4b4de29725 | ||
|
|
99b49d2718 | ||
|
|
6230ec55be | ||
|
|
0733d3c585 | ||
|
|
6f8370ff0e | ||
|
|
bc244ed07e | ||
|
|
41266aa8e4 | ||
|
|
45faee3e5b | ||
|
|
74ff735e10 | ||
|
|
97879f2b16 | ||
|
|
1a338251ad | ||
|
|
354dc768c1 | ||
|
|
8a0796cd94 | ||
|
|
cbc3b26584 | ||
|
|
d628583db8 | ||
|
|
058777be1e | ||
|
|
260d03a0c4 | ||
|
|
6dcf6f41cc | ||
|
|
799f790886 | ||
|
|
cb3c4b0ea9 | ||
|
|
798d255ba6 | ||
|
|
28ba9e8ac5 | ||
|
|
7888cb0506 | ||
|
|
5e284a20cc | ||
|
|
770357e727 | ||
|
|
673ba22555 | ||
|
|
fb38d9cfb7 | ||
|
|
dda89a57ec | ||
|
|
62df63d3a0 | ||
|
|
e71696cf34 | ||
|
|
b5d8d58670 | ||
|
|
14e6a667f5 | ||
|
|
0c324783ae | ||
|
|
5d6ce251ca | ||
|
|
92c04cee1d | ||
|
|
7fdf587a7b | ||
|
|
1e46218b09 | ||
|
|
c56fcd8fb9 | ||
|
|
319fbf7f64 | ||
|
|
bf06d9077c | ||
|
|
9d2bd29ee1 | ||
|
|
d11fa1f74e | ||
|
|
c824da0dab | ||
|
|
9362c83a33 | ||
|
|
0c38098f02 | ||
|
|
be7068f569 | ||
|
|
b122bf22e3 | ||
|
|
b7232f38f3 | ||
|
|
c1b72ac1b7 | ||
|
|
b9e0d15878 | ||
|
|
cf26cf7afc | ||
|
|
a75e60ae47 | ||
|
|
187dc5dd03 | ||
|
|
9c9e2afade | ||
|
|
fae1ba98c1 | ||
|
|
c3e632620a | ||
|
|
dd7158ac46 | ||
|
|
eefe51c7a7 | ||
|
|
bf596dc023 | ||
|
|
220d88d654 | ||
|
|
a254e64bdb | ||
|
|
1555b92fb8 | ||
|
|
d5b7a40b39 | ||
|
|
bd1ab7ab96 | ||
|
|
2ff991bfcf | ||
|
|
0b1378a539 | ||
|
|
723c908bd7 | ||
|
|
4874404701 | ||
|
|
4c1374840a | ||
|
|
5da01633fd | ||
|
|
cdbd8af527 | ||
|
|
a6fdc1bfd2 | ||
|
|
20b8facb05 | ||
|
|
909914f7af | ||
|
|
3f31ebc676 | ||
|
|
f3c068a90c | ||
|
|
90d7e193d1 | ||
|
|
c9f5bb6f17 | ||
|
|
9865a4c4f2 | ||
|
|
f12baf32d4 | ||
|
|
80f4d08e8b | ||
|
|
07c9cae923 | ||
|
|
d867cc9271 | ||
|
|
3a1fe1e295 | ||
|
|
3cacf050df | ||
|
|
1536d73d1b | ||
|
|
55143a9d44 | ||
|
|
c92e193916 | ||
|
|
d12452fc49 | ||
|
|
3d8400a40c | ||
|
|
1fe0aea0b0 | ||
|
|
5df2544883 | ||
|
|
acbf224277 | ||
|
|
d0d17a0d35 | ||
|
|
f7b53d33bc | ||
|
|
92be06874b | ||
|
|
f327455d9d | ||
|
|
54415bf2af | ||
|
|
bac62dfcd9 | ||
|
|
820b43c1f3 | ||
|
|
a5b69290d5 | ||
|
|
5c7a9c86f6 | ||
|
|
7f83f916f6 | ||
|
|
c15a949cc3 | ||
|
|
ce47f96570 | ||
|
|
57b035cd94 | ||
|
|
f1f126171c | ||
|
|
71e1bcd855 | ||
|
|
b5579b6307 | ||
|
|
110b0fe14a | ||
|
|
d7488bbb62 | ||
|
|
2007407e7b | ||
|
|
d152e7e780 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -6,5 +6,7 @@ lib-cov
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.swp
|
||||
*.swo
|
||||
benchmarks/graphs
|
||||
testing.js
|
||||
|
||||
87
History.md
87
History.md
@@ -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
|
||||
==================
|
||||
|
||||
|
||||
19
Makefile
19
Makefile
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
19
bin/express
19
bin/express
@@ -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');
|
||||
|
||||
@@ -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\.
|
||||
@@ -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 – 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>
|
||||
@@ -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\.
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
2099
docs/guide.1
2099
docs/guide.1
File diff suppressed because it is too large
Load Diff
492
docs/guide.html
492
docs/guide.html
@@ -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’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’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>’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 “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>
|
||||
|
||||
<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(‘connect’)</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 “stacks” 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 “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>
|
||||
|
||||
<pre><code> <form method="post" action="/">
|
||||
<input type="text" name="user[name]" />
|
||||
@@ -609,7 +608,7 @@ app.get('/', all, function(){});
|
||||
</form>
|
||||
</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 “using” 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’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 “no such file or directory”, 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 “development” 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 "<name>.<engine>", where <engine> is the name
|
||||
<p>View filenames take the form “<name>.<engine>”, where <engine> 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(‘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>
|
||||
|
||||
<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 “mini” 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 “magic” 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(‘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>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(‘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>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(‘users’)</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(‘users’)</em>, and the view system will try <em>../users/index</em>, preventing us from needing to call <em>partial(‘index’)</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’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');
|
||||
// => "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 “html” which is then converted internally
|
||||
to “text/html” using the mime lookup table.</p>
|
||||
|
||||
<pre><code>// Accept: text/html
|
||||
req.accepts('html');
|
||||
@@ -966,7 +966,7 @@ req.accepts('png');
|
||||
// => 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>“an image”</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>“image/jpeg”</em>, <em>“image/png”</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>“an image”</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>“application/json”</em>, and <em>“text/json”</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');
|
||||
// => 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 “utf8”, 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’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 “attachment”, 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’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 “redirect mapping”, 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 “home” setting and defaults to “/”.</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 “expires” 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 “user” 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: ‘something’ 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’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 “as” 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 “expand” 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’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)
|
||||
// => In view: movie.director
|
||||
</code></pre>
|
||||
|
||||
<p>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:</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');
|
||||
// => ...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');
|
||||
// => 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');
|
||||
// => 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');
|
||||
// => 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');
|
||||
// => 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(‘google’);</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><%= name(firstName, lastName) %>
|
||||
<pre><code><%= name(firstName, lastName) %>
|
||||
</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><%= session.name %>
|
||||
</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');
|
||||
// => [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');
|
||||
// => []
|
||||
</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
|
||||
// => /^\/user\/(?:([^\/]+?))(?:\/([^\/]+?))?\/?$/i
|
||||
|
||||
fn.keys
|
||||
// => ['id', 'op']
|
||||
|
||||
fn.path
|
||||
// => '/user/:id/:op?'
|
||||
|
||||
fn.method
|
||||
// => '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');
|
||||
// => [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');
|
||||
// => [Function]
|
||||
</code></pre>
|
||||
|
||||
<p> We can also use <em>all()</em> to disregard the http method:</p>
|
||||
|
||||
<pre><code> app.match.all('/user/20');
|
||||
// => [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
|
||||
// => ['id', 'op']
|
||||
|
||||
fn.params
|
||||
// => { id: '23', op: 'edit' }
|
||||
|
||||
fn.method
|
||||
// => '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 “.html”
|
||||
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 “hamljs” so instead
|
||||
of layout.hamljs, we can register the engine as “.haml”:</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 “{name}”.</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>
|
||||
178
docs/guide.md
178
docs/guide.md
@@ -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()_.
|
||||
|
||||
126
docs/index.1
126
docs/index.1
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
272
docs/migrate.1
272
docs/migrate.1
@@ -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
|
||||
|
||||
@@ -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 @@
|
||||
// => 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’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 “basic” 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>
|
||||
@@ -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 }));
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
| .
|
||||
|
||||
@@ -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){
|
||||
|
||||
35
examples/layout-control/app.js
Normal file
35
examples/layout-control/app.js
Normal 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');
|
||||
6
examples/layout-control/views/layouts/alternate.ejs
Normal file
6
examples/layout-control/views/layouts/alternate.ejs
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Alternate Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
6
examples/layout-control/views/layouts/default.ejs
Normal file
6
examples/layout-control/views/layouts/default.ejs
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Default Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
2
examples/layout-control/views/pages/alternate.ejs
Normal file
2
examples/layout-control/views/pages/alternate.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
<% layout('layouts/alternate') %>
|
||||
<h1>Page</h1>
|
||||
1
examples/layout-control/views/pages/default.ejs
Normal file
1
examples/layout-control/views/pages/default.ejs
Normal file
@@ -0,0 +1 @@
|
||||
<h1>Page</h1>
|
||||
@@ -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');
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
78
lib/http.js
78
lib/http.js
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
287
lib/view.js
287
lib/view.js
@@ -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();
|
||||
}
|
||||
@@ -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.
|
||||
*
|
||||
|
||||
@@ -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;
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
@@ -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" }
|
||||
|
||||
Submodule support/connect updated: 2796a2fdeb...5a4de3e19c
Submodule support/connect-form updated: ccefcd28db...e861cc85d6
Submodule support/ejs updated: 673e6f23cb...499c4815a5
Submodule support/expresso updated: 2c8759f147...855e0dea07
Submodule support/formidable updated: 63347d825b...5d98e9c75c
Submodule support/haml updated: 4122210f38...55bb6fdc79
Submodule support/jade updated: 3c002865b7...9ec358e0f1
@@ -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');
|
||||
}
|
||||
@@ -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
1
test/fixtures/_messages.jade
vendored
Normal file
@@ -0,0 +1 @@
|
||||
p Testing
|
||||
1
test/fixtures/error.jade
vendored
Normal file
1
test/fixtures/error.jade
vendored
Normal file
@@ -0,0 +1 @@
|
||||
= user.name
|
||||
1
test/fixtures/item-title.jade
vendored
Normal file
1
test/fixtures/item-title.jade
vendored
Normal file
@@ -0,0 +1 @@
|
||||
li #{title} #{item}
|
||||
2
test/fixtures/layout-switch.jade
vendored
Normal file
2
test/fixtures/layout-switch.jade
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
- layout('layouts/alternate')
|
||||
h1 My Page
|
||||
1
test/fixtures/layouts/alternate.jade
vendored
Normal file
1
test/fixtures/layouts/alternate.jade
vendored
Normal file
@@ -0,0 +1 @@
|
||||
#alternate!= body
|
||||
1
test/fixtures/nested/partial.jade
vendored
Normal file
1
test/fixtures/nested/partial.jade
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!= partial('stats', { hits: 15, misses: 1 })
|
||||
1
test/fixtures/nested/partial2.jade
vendored
Normal file
1
test/fixtures/nested/partial2.jade
vendored
Normal file
@@ -0,0 +1 @@
|
||||
!= partial('messages')
|
||||
1
test/fixtures/object-item.jade
vendored
Normal file
1
test/fixtures/object-item.jade
vendored
Normal file
@@ -0,0 +1 @@
|
||||
li #{keyInCollection}: #{item}
|
||||
2
test/fixtures/stats.jade
vendored
Normal file
2
test/fixtures/stats.jade
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
p Hits #{hits}
|
||||
p Misses #{misses}
|
||||
@@ -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' }});
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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' }});
|
||||
}
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user