Compare commits

...

27 Commits
2.0.0 ... 2.1.0

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

partial("messages");

will still work
2011-03-24 13:26:32 -07:00
Tj Holowaychuk
7fdf587a7b added test for root partial lookup 2011-03-24 13:21:23 -07:00
Tj Holowaychuk
1e46218b09 doc typo 2011-03-24 11:46:40 -07:00
Tj Holowaychuk
c56fcd8fb9 better --help output 2011-03-22 13:00:36 -07:00
Tj Holowaychuk
319fbf7f64 Added "request", "response", and "app" locals 2011-03-22 12:16:58 -07:00
Tj Holowaychuk
bf06d9077c docs for "filename" local 2011-03-22 12:14:08 -07:00
Tj Holowaychuk
9d2bd29ee1 Added 'settings' local variable, containing the app's settings 2011-03-22 12:06:19 -07:00
Tj Holowaychuk
d11fa1f74e added .settings test 2011-03-22 12:02:57 -07:00
Tj Holowaychuk
c824da0dab test indentation 2011-03-22 11:59:29 -07:00
Tj Holowaychuk
9362c83a33 removed dead test 2011-03-22 11:59:05 -07:00
Tj Holowaychuk
0c38098f02 tweak req.flash() failure message 2011-03-21 16:13:40 -07:00
Pau Ramon
be7068f569 Better error output when using flash without session middleware. 2011-03-21 16:12:55 -07:00
Tj Holowaychuk
b122bf22e3 link typo 2011-03-21 12:06:36 -07:00
Tj Holowaychuk
b7232f38f3 Added res.send(bool) support
application/json
2011-03-21 10:34:04 -07:00
Tj Holowaychuk
c1b72ac1b7 Fixed stylus example for latest version 2011-03-21 08:45:43 -07:00
Roman Shtylman
b9e0d15878 check that this.params is valid before calling hasOwnProperty 2011-03-21 08:13:05 -07:00
Tj Holowaychuk
cf26cf7afc wrap try/catch around render() 2011-03-18 11:44:48 -07:00
Tj Holowaychuk
a75e60ae47 fixed docs due to markdown-js not supporting html 2011-03-18 09:18:29 -07:00
Tj Holowaychuk
187dc5dd03 connect 1.1.1 2011-03-18 08:49:59 -07:00
Tj Holowaychuk
9c9e2afade refactored res.redirect() 2011-03-18 08:49:46 -07:00
Tj Holowaychuk
fae1ba98c1 doc typo 2011-03-17 19:05:59 -07:00
Tj Holowaychuk
c3e632620a doc typo 2011-03-17 19:05:28 -07:00
Tj Holowaychuk
dd7158ac46 regenerated docs 2011-03-17 18:56:44 -07:00
Tj Holowaychuk
eefe51c7a7 removed manpages from make 2011-03-17 18:55:13 -07:00
34 changed files with 493 additions and 3085 deletions

View File

@@ -1,4 +1,15 @@
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
==================

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ var fs = require('fs')
* Framework version.
*/
var version = '2.0.0';
var version = '2.1.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'
+ ' \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'
;
/**

View File

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

View File

@@ -190,11 +190,6 @@
<li><a href="screencasts.html">Screencasts</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>applications</code>
</p>
<br />
@@ -228,7 +223,7 @@
<p><a href="http://nodeknockout.com"><img src="images/apps/nodeko.png" alt="Node Knockout Competition Express" /></a></p>
<p><a href="http://widescript.com">Widescript</a> is an innovative app that helps you focus and interact with your texts - on your desktop, your couch or on the go.</p>
<p><a href="http://widescript.com">Widescript</a> is an innovative app that helps you focus and interact with your texts &ndash; on your desktop, your couch or on the go.</p>
<p><a href="http://widescript.com"><img src="images/apps/widescript.png" alt="Widescript" /></a></p>
@@ -244,9 +239,7 @@
<p>The applications shown above are not listed in any specific order. To have an application added or removed please contact <a href="http://github.com/visionmedia">TJ Holowaychuk</a>.</p>
</div>
</div>
</div>
</body>
</html>
</html>

View File

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

View File

@@ -190,12 +190,7 @@
<li><a href="screencasts.html">Screencasts</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>contrib</code>
</p>
<h3 id="Development-Dependencies">Development Dependencies</h3>
<h3>Development Dependencies</h3>
<p>Express development dependencies are stored within the <em>./support</em> directory. To
update them execute:</p>
@@ -203,7 +198,7 @@ update them execute:</p>
<pre><code>$ git submodule update --init
</code></pre>
<h3 id="Running-Tests">Running Tests</h3>
<h3>Running Tests</h3>
<p>Express uses the <a href="http://github.com/visionmedia/expresso">Expresso</a> TDD
framework to write and run elegant test suites extremely fast. To run all test suites
@@ -222,7 +217,7 @@ simply execute:</p>
<pre><code>$ make test-cov
</code></pre>
<h3 id="Contributions">Contributions</h3>
<h3>Contributions</h3>
<p>To accept a contribution, you should follow these guidelines:</p>
@@ -235,13 +230,11 @@ simply execute:</p>
</ul>
<h3 id="Documentation">Documentation</h3>
<h3>Documentation</h3>
<p>To contribute documentation edit the markdown files in <em>./docs</em>, however
do <em>not</em> run <em>make docs</em>, as they will be re-built and published with each release.</p>
</div>
</div>
</div>
</body>
</html>
</html>

View File

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

View File

@@ -190,23 +190,18 @@
<li><a href="screencasts.html">Screencasts</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>executable</code>
</p>
<h2 id="Synopsis">Synopsis</h2>
<h2>Synopsis</h2>
<pre><code>express [options] [PATH]
</code></pre>
<h2 id="Description">Description</h2>
<h2>Description</h2>
<p>The <em>express</em> executable generates apps at the given <strong>PATH</strong> or the
current working directory. Although Express is not bound to a specific
application structure, this executable creates a maintainable base app.</p>
<h2 id="Options">Options</h2>
<h2>Options</h2>
<pre><code> -s, --sessions Add session support
-t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade
@@ -214,9 +209,7 @@ application structure, this executable creates a maintainable base app.</p>
-v, --version Output framework version
-h, --help Output help information
</code></pre>
</div>
</div>
</div>
</body>
</html>
</html>

File diff suppressed because it is too large Load Diff

View File

@@ -179,60 +179,62 @@
</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.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 +248,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 +266,15 @@ app.get('/', function(req, res){
app.listen(3000);
</code></pre>
<h3 id="Creating-An-HTTPS-Server">Creating An HTTPS Server</h3>
<h3 id="creating-an https server">Creating An HTTPS Server</h3>
<p> To initialize a <em>express.HTTPSServer</em> we do the same as above, however we
pass an options object, accepting <em>key</em>, <em>cert</em> and the others mentioned in node's <a href="http://nodejs.org/docs/v0.3.7/api/https.html#https.createServer">https documentation</a>.</p>
pass an options object, accepting <em>key</em>, <em>cert</em> and the others mentioned in node&rsquo;s <a href="http://nodejs.org/docs/v0.3.7/api/https.html#https.createServer">https documentation</a>.</p>
<pre><code> var app = require('express').createServer({ key: ... });
</code></pre>
<h3 id="Configuration">Configuration</h3>
<h3 id="configuration">Configuration</h3>
<p>Express supports arbitrary environments, such as <em>production</em> and <em>development</em>. Developers
can use the <em>configure()</em> method to setup needs required by the current environment. When
@@ -302,7 +299,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 +329,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 +341,10 @@ app.configure('production', function(){
</ul>
<h3 id="Routing">Routing</h3>
<h3 id="routing">Routing</h3>
<p>Express utilizes the HTTP verbs to provide a meaningful, expressive routing API.
For example we may want to render a user's account for the path <em>/user/12</em>, this
For example we may want to render a user&rsquo;s account for the path <em>/user/12</em>, this
can be done by defining the route below. The values associated to the named placeholders
are available as <code>req.params</code>.</p>
@@ -363,7 +360,7 @@ when <em>/user/:id</em> is compiled, a simplified version of the regexp may look
</code></pre>
<p>Regular expression literals may also be passed for complex uses. Since capture
groups with literal <em>RegExp</em>'s are anonymous we can access them directly <code>req.params</code>. So our first capture group would be <em>req.params[0]</em> and the second would follow as <em>req.params[1]</em>.</p>
groups with literal <em>RegExp</em>&rsquo;s are anonymous we can access them directly <code>req.params</code>. So our first capture group would be <em>req.params[0]</em> and the second would follow as <em>req.params[1]</em>.</p>
<pre><code>app.get(/^\/users?(?:\/(\d+)(?:\.\.(\d+))?)?/, function(req, res){
res.send(req.params);
@@ -432,9 +429,9 @@ app.post('/', function(req, res){
app.listen(3000);
</code></pre>
<p>Typically we may use a "dumb" placeholder such as "/user/:id" which has no restrictions, however say for example we are limiting a user id to digits, we may use <em>'/user/:id(\d+)'</em> which will <em>not</em> match unless the placeholder value contains only digits.</p>
<p>Typically we may use a &ldquo;dumb&rdquo; placeholder such as &ldquo;/user/:id&rdquo; which has no restrictions, however say for example we are limiting a user id to digits, we may use <em>&lsquo;/user/:id(\d+)&rsquo;</em> which will <em>not</em> match unless the placeholder value contains only digits.</p>
<h3 id="Passing-Route-Control">Passing Route Control</h3>
<h3 id="passing-route control">Passing Route Control</h3>
<p>We may pass control to the next <em>matching</em> route, by calling the <em>third</em> argument,
the <em>next()</em> function. When a match cannot be made, control is passed back to Connect,
@@ -489,7 +486,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 +504,7 @@ var app = express.createServer(
<pre><code>app.use(express.logger({ format: ':method :uri' }));
</code></pre>
<p>Typically with connect middleware you would <em>require('connect')</em> like so:</p>
<p>Typically with connect middleware you would <em>require(&lsquo;connect&rsquo;)</em> like so:</p>
<pre><code>var connect = require('connect');
app.use(connect.logger());
@@ -520,7 +517,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 +577,7 @@ app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
});
</code></pre>
<p>Commonly used "stacks" of middleware can be passed as an array (<em>applied recursively</em>), which can be mixed and matched to any degree.</p>
<p>Commonly used &ldquo;stacks&rdquo; of middleware can be passed as an array (<em>applied recursively</em>), which can be mixed and matched to any degree.</p>
<pre><code>var a = [middleware1, middleware2]
, b = [middleware3, middleware4]
@@ -596,11 +593,11 @@ app.get('/', all, function(){});
<p>For this example in full, view the <a href="http://github.com/visionmedia/express/blob/master/examples/route-middleware/app.js">route middleware example</a> in the repository.</p>
<h3 id="HTTP-Methods">HTTP Methods</h3>
<h3 id="http-methods">HTTP Methods</h3>
<p>We have seen <em>app.get()</em> a few times, however Express also exposes other familiar HTTP verbs in the same manor, such as <em>app.post()</em>, <em>app.del()</em>, etc.</p>
<p> A common example for <em>POST</em> usage, is when "submitting" a form. Below we simply set our form method to "post" in our html, and control will be given to the route we have defined below it.</p>
<p> A common example for <em>POST</em> usage, is when &ldquo;submitting&rdquo; a form. Below we simply set our form method to &ldquo;post&rdquo; in our html, and control will be given to the route we have defined below it.</p>
<pre><code> &lt;form method="post" action="/"&gt;
&lt;input type="text" name="user[name]" /&gt;
@@ -609,7 +606,7 @@ app.get('/', all, function(){});
&lt;/form&gt;
</code></pre>
<p>By default Express does not know what to do with this request body, so we should add the <em>bodyParser</em> middleware, which will parse <em>application/x-www-form-urlencoded</em> and <em>application/json</em> request bodies and place the variables in <em>req.body</em>. We can do this by "using" the middleware as shown below:</p>
<p>By default Express does not know what to do with this request body, so we should add the <em>bodyParser</em> middleware, which will parse <em>application/x-www-form-urlencoded</em> and <em>application/json</em> request bodies and place the variables in <em>req.body</em>. We can do this by &ldquo;using&rdquo; the middleware as shown below:</p>
<pre><code>app.use(express.bodyParser());
</code></pre>
@@ -622,7 +619,7 @@ app.get('/', all, function(){});
});
</code></pre>
<p>When using methods such as <em>PUT</em> with a form, we can utilize a hidden input named <em>_method</em>, which can be used to alter the HTTP method. To do so we first need the <em>methodOverride</em> middleware, which should be placed below <em>bodyParser</em> so that it can utilize it's <em>req.body</em> containing the form values.</p>
<p>When using methods such as <em>PUT</em> with a form, we can utilize a hidden input named <em>_method</em>, which can be used to alter the HTTP method. To do so we first need the <em>methodOverride</em> middleware, which should be placed below <em>bodyParser</em> so that it can utilize it&rsquo;s <em>req.body</em> containing the form values.</p>
<pre><code>app.use(express.bodyParser());
app.use(express.methodOverride());
@@ -643,7 +640,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 +652,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 +682,7 @@ handle exceptions in different ways based on the environment.</p>
</code></pre>
<p>Here we assume all errors as 500 for the simplicity of
this demo, however you can choose whatever you like. For example when node performs filesystem syscalls, you may receive an error object with the <em>error.code</em> of <em>ENOENT</em>, meaning "no such file or directory", we can utilize this in our error handling and display a page specific to this if desired.</p>
this demo, however you can choose whatever you like. For example when node performs filesystem syscalls, you may receive an error object with the <em>error.code</em> of <em>ENOENT</em>, meaning &ldquo;no such file or directory&rdquo;, we can utilize this in our error handling and display a page specific to this if desired.</p>
<pre><code>app.error(function(err, req, res){
res.render('500.jade', {
@@ -696,7 +693,7 @@ this demo, however you can choose whatever you like. For example when node perfo
<p>Our apps could also utilize the Connect <em>errorHandler</em> middleware
to report on exceptions. For example if we wish to output exceptions
in "development" mode to <em>stderr</em> we can use:</p>
in &ldquo;development&rdquo; mode to <em>stderr</em> we can use:</p>
<pre><code>app.use(express.errorHandler({ dumpExceptions: true }));
</code></pre>
@@ -710,7 +707,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 +748,12 @@ is present, which is useful for developing apps that rely heavily on client-side
<pre><code>app.param(['from', 'to'], function(n){ return parseInt(n, 10); });
</code></pre>
<h3 id="View-Rendering">View Rendering</h3>
<h3 id="view-rendering">View Rendering</h3>
<p>View filenames take the form "&lt;name&gt;.&lt;engine&gt;", where &lt;engine&gt; is the name
<p>View filenames take the form &ldquo;&lt;name&gt;.&lt;engine&gt;&rdquo;, where &lt;engine&gt; is the name
of the module that will be required. For example the view <em>layout.ejs</em> will
tell the view system to <em>require('ejs')</em>, the module being loaded must export the method <em>exports.compile(str, options)</em>, and return a <em>Function</em> to comply with Express. To alter this behaviour
<em>app.register()</em> can be used to map engines to file extensions, so that for example "foo.html" can be rendered by ejs.</p>
tell the view system to <em>require(&lsquo;ejs&rsquo;)</em>, the module being loaded must export the method <em>exports.compile(str, options)</em>, and return a <em>Function</em> to comply with Express. To alter this behaviour
<em>app.register()</em> can be used to map engines to file extensions, so that for example &ldquo;foo.html&rdquo; can be rendered by ejs.</p>
<p>Below is an example using <a href="http://github.com/visionmedia/jade">Jade</a> to render <em>index.html</em>,
and since we do not use <em>layout: false</em> the rendered contents of <em>index.jade</em> will be passed as
@@ -824,20 +821,20 @@ mix and match template engines:</p>
});
</code></pre>
<h3 id="View-Partials">View Partials</h3>
<h3 id="view-partials">View Partials</h3>
<p>The Express view system has built-in support for partials and collections, which are "mini" views representing a document fragment. For example rather than iterating
in a view to display comments, we would use a partial with collection support:</p>
<p>The Express view system has built-in support for partials and collections, which are &ldquo;mini&rdquo; views representing a document fragment. For example rather than iterating
in a view to display comments, we could use partial collection:</p>
<pre><code>partial('comment', { collection: comments });
</code></pre>
<p>If no other options are desired, we can omit the object and simply pass our array, which is equivalent to above:</p>
<p>If no other options or local variables are desired, we can omit the object and simply pass our array, which is equivalent to above:</p>
<pre><code>partial('comment', comments);
</code></pre>
<p>When using the partial collection support a few "magic" variables are provided
<p>When using the partial collection support a few &ldquo;magic&rdquo; locals are provided
for free:</p>
<ul>
@@ -848,25 +845,26 @@ for free:</p>
</ul>
<p>Local variables passed (or generated) take precedence, however locals passed to the parent view are available in the child view as well. So for example if we were to render a blog post with <em>partial('blog/post', post)</em> it would generate the <em>post</em> local, but the view calling this function had the local <em>user</em>, it would be available to the <em>blog/post</em> view as well.</p>
<p>Local variables passed (or generated) take precedence, however locals passed to the parent view are available in the child view as well. So for example if we were to render a blog post with <em>partial(&lsquo;blog/post&rsquo;, post)</em> it would generate the <em>post</em> local, but the view calling this function had the local <em>user</em>, it would be available to the <em>blog/post</em> view as well.</p>
<p>For documentation on altering the object name view <a href="http://expressjs.com/guide.html#res-partial-view-options-">res.partial()</a>.</p>
<p><strong>NOTE:</strong> be careful about when you use partial collections, as rendering an array with a length of 100 means we have to render 100 views. For simple collections you may inline the iteration instead of using partial collection support to decrease overhead.</p>
<h3 id="View-Lookup">View Lookup</h3>
<h3 id="view-lookup">View Lookup</h3>
<p>View lookup is performed relative to the parent view, for example if we had a page view named <em>views/user/list.jade</em>, and within that view we did <em>partial('edit')</em> it would attempt to load <em>views/user/edit.jade</em>, whereas <em>partial('../messages')</em> would load <em>views/messages.jade</em>.</p>
<p>View lookup is performed relative to the parent view, for example if we had a page view named <em>views/user/list.jade</em>, and within that view we did <em>partial(&lsquo;edit&rsquo;)</em> it would attempt to load <em>views/user/edit.jade</em>, whereas <em>partial(&lsquo;../messages&rsquo;)</em> would load <em>views/messages.jade</em>.</p>
<p>The view system also allows for index templates, allowing you to have a directory of the same name. For example within a route we may have <em>res.render('users')</em> either <em>views/users.jade</em>, or <em>views/users/index.jade</em>.</p>
<p>The view system also allows for index templates, allowing you to have a directory of the same name. For example within a route we may have <em>res.render(&lsquo;users&rsquo;)</em> either <em>views/users.jade</em>, or <em>views/users/index.jade</em>.</p>
<p>When utilizing index views as shown above, we may reference <em>views/users/index.jade</em> from a view in the same directory by <em>partial('users')</em>, and the view system will try <em>../users/index</em>, preventing us from needing to call <em>partial('index')</em>.</p>
<p>When utilizing index views as shown above, we may reference <em>views/users/index.jade</em> from a view in the same directory by <em>partial(&lsquo;users&rsquo;)</em>, and the view system will try <em>../users/index</em>, preventing us from needing to call <em>partial(&lsquo;index&rsquo;)</em>.</p>
<h3 id="Template-Engines">Template Engines</h3>
<h3 id="template-engines">Template Engines</h3>
<p>Below are a few template engines commonly used with Express:</p>
<ul>
<li><a href="http://github.com/visionmedia/haml.js">Haml</a> haml implementation</li>
<li><a href="http://jade-lang.com">Jade</a> haml.js successor</li>
<li><a href="http://github.com/visionmedia/ejs">EJS</a> Embedded JavaScript</li>
<li><a href="http://github.com/mauricemach/coffeekup">CoffeeKup</a> CoffeeScript based templating</li>
@@ -874,9 +872,9 @@ for free:</p>
</ul>
<h3 id="Session-Support">Session Support</h3>
<h3 id="session-support">Session Support</h3>
<p>Sessions support can be added by using Connect's <em>session</em> middleware. To do so we also need the <em>cookieParser</em> middleware place above it, which will parse and populate cookie data to <em>req.cookies</em>.</p>
<p>Sessions support can be added by using Connect&rsquo;s <em>session</em> middleware. To do so we also need the <em>cookieParser</em> middleware place above it, which will parse and populate cookie data to <em>req.cookies</em>.</p>
<pre><code>app.use(express.cookieParser());
app.use(express.session({ secret: "keyboard cat" }));
@@ -917,11 +915,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 +939,14 @@ req.header('Referrer');
// =&gt; "http://google.com"
</code></pre>
<h3 id="req-accepts-type-">req.accepts(type)</h3>
<h3 id="req.accepts()">req.accepts(type)</h3>
<p>Check if the <em>Accept</em> header is present, and includes the given <em>type</em>.</p>
<p>When the <em>Accept</em> header is not present <em>true</em> is returned. Otherwise
the given <em>type</em> is matched by an exact match, and then subtypes. You
may pass the subtype such as "html" which is then converted internally
to "text/html" using the mime lookup table.</p>
may pass the subtype such as &ldquo;html&rdquo; which is then converted internally
to &ldquo;text/html&rdquo; using the mime lookup table.</p>
<pre><code>// Accept: text/html
req.accepts('html');
@@ -966,7 +964,7 @@ req.accepts('png');
// =&gt; false
</code></pre>
<h3 id="req-is-type-">req.is(type)</h3>
<h3 id="req.is()">req.is(type)</h3>
<p>Check if the incoming request contains the <em>Content-Type</em>
header field, and it contains the give mime <em>type</em>.</p>
@@ -987,7 +985,7 @@ header field, and it contains the give mime <em>type</em>.</p>
<p>Ad-hoc callbacks can also be registered with Express, to perform
assertions again the request, for example if we need an expressive
way to check if our incoming request is an image, we can register <em>"an image"</em>
way to check if our incoming request is an image, we can register <em>&ldquo;an image&rdquo;</em>
callback:</p>
<pre><code> app.is('an image', function(req){
@@ -996,7 +994,7 @@ callback:</p>
</code></pre>
<p>Now within our route callbacks, we can use to to assert content types
such as <em>"image/jpeg"</em>, <em>"image/png"</em>, etc.</p>
such as <em>&ldquo;image/jpeg&rdquo;</em>, <em>&ldquo;image/png&rdquo;</em>, etc.</p>
<pre><code> app.post('/image/upload', function(req, res, next){
if (req.is('an image')) {
@@ -1010,17 +1008,17 @@ such as <em>"image/jpeg"</em>, <em>"image/png"</em>, etc.</p>
<p>Keep in mind this method is <em>not</em> limited to checking <em>Content-Type</em>, you
can perform any request assertion you wish.</p>
<p>Wildcard matches can also be made, simplifying our example above for <em>"an image"</em>, by asserting the <em>subtype</em> only:</p>
<p>Wildcard matches can also be made, simplifying our example above for <em>&ldquo;an image&rdquo;</em>, by asserting the <em>subtype</em> only:</p>
<pre><code>req.is('image/*');
</code></pre>
<p>We may also assert the <em>type</em> as shown below, which would return true for <em>"application/json"</em>, and <em>"text/json"</em>.</p>
<p>We may also assert the <em>type</em> as shown below, which would return true for <em>&ldquo;application/json&rdquo;</em>, and <em>&ldquo;text/json&rdquo;</em>.</p>
<pre><code>req.is('*/json');
</code></pre>
<h3 id="req-param-name-default-">req.param(name[, default])</h3>
<h3 id="req.param()">req.param(name[, default])</h3>
<p>Return the value of param <em>name</em> when present or <em>default</em>.</p>
@@ -1035,7 +1033,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 +1057,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 +1066,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 +1080,28 @@ res.header('Content-Length');
// =&gt; 123
</code></pre>
<h3 id="res-contentType-type-">res.contentType(type)</h3>
<h3 id="res.charset">res.charset</h3>
<p>Sets the charset for subsequent <code>Content-Type</code> header fields. For example <code>res.send()</code> and <code>res.render()</code> default to &ldquo;utf8&rdquo;, so we may explicitly set the charset before rendering a template:</p>
<pre><code>res.charset = 'ISO-8859-1';
res.render('users');
</code></pre>
<p>or before responding with <code>res.send()</code>:</p>
<pre><code>res.charset = 'ISO-8859-1';
res.send(str);
</code></pre>
<p>or with node&rsquo;s <code>res.end()</code>:</p>
<pre><code>res.charset = 'ISO-8859-1';
res.header('Content-Type', 'text/plain');
res.end(str);
</code></pre>
<h3 id="res.contenttype()">res.contentType(type)</h3>
<p>Sets the <em>Content-Type</em> response header to the given <em>type</em>.</p>
@@ -1091,14 +1110,24 @@ res.header('Content-Length');
// Content-Type is now "image/png"
</code></pre>
<h3 id="res-attachment-filename-">res.attachment([filename])</h3>
<p>A literal <em>Content-Type</em> works as well:</p>
<p>Sets the <em>Content-Disposition</em> response header to "attachment", with optional <em>filename</em>.</p>
<pre><code> res.contentType('application/json');
</code></pre>
<p>Or simply the extension without leading <code>.</code>:</p>
<pre><code> res.contentType('json');
</code></pre>
<h3 id="res.attachment()">res.attachment([filename])</h3>
<p>Sets the <em>Content-Disposition</em> response header to &ldquo;attachment&rdquo;, with optional <em>filename</em>.</p>
<pre><code> res.attachment('path/to/my/image.png');
</code></pre>
<h3 id="res-sendfile-path-options-callback-">res.sendfile(path[, options[, callback]])</h3>
<h3 id="res.sendfile()">res.sendfile(path[, options[, callback]])</h3>
<p>Used by <code>res.download()</code> to transfer an arbitrary file.</p>
@@ -1124,7 +1153,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 +1174,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 +1192,9 @@ objects to respond with json, strings for html, Buffer instances, or numbers rep
assigned through <code>res.send()</code> or previously with <code>res.header()</code> or <code>res.contentType()</code>
it will not be set again.</p>
<p>Note that this method <em>end()</em>s the response, so you will want to use node's <em>res.write()</em> for multiple writes or streaming.</p>
<p>Note that this method <em>end()</em>s the response, so you will want to use node&rsquo;s <em>res.write()</em> for multiple writes or streaming.</p>
<h3 id="res-redirect-url-status-">res.redirect(url[, status])</h3>
<h3 id="res.redirect()">res.redirect(url[, status])</h3>
<p>Redirect to the given <em>url</em> with a default response <em>status</em> of 302.</p>
@@ -1176,11 +1205,11 @@ res.redirect('home');
res.redirect('back');
</code></pre>
<p>Express supports "redirect mapping", which by default provides <em>home</em>, and <em>back</em>.
<p>Express supports &ldquo;redirect mapping&rdquo;, which by default provides <em>home</em>, and <em>back</em>.
The <em>back</em> map checks the <em>Referrer</em> and <em>Referer</em> headers, while <em>home</em> utilizes
the "home" setting and defaults to "/".</p>
the &ldquo;home&rdquo; setting and defaults to &ldquo;/&rdquo;.</p>
<h3 id="res-cookie-name-val-options-">res.cookie(name, val[, options])</h3>
<h3 id="res.cookie()">res.cookie(name, val[, options])</h3>
<p>Sets the given cookie <em>name</em> to <em>val</em>, with options <em>httpOnly</em>, <em>secure</em>, <em>expires</em> etc.</p>
@@ -1202,26 +1231,26 @@ app.get('/', function(req, res){
});
</code></pre>
<h3 id="res-clearCookie-name-">res.clearCookie(name)</h3>
<h3 id="res.clearcookie()">res.clearCookie(name)</h3>
<p>Clear cookie <em>name</em> by setting "expires" far in the past.</p>
<p>Clear cookie <em>name</em> by setting &ldquo;expires&rdquo; far in the past.</p>
<pre><code>res.clearCookie('rememberme');
</code></pre>
<h3 id="res-render-view-options-fn-">res.render(view[, options[, fn]])</h3>
<h3 id="res.render()">res.render(view[, options[, fn]])</h3>
<p>Render <em>view</em> with the given <em>options</em> and optional callback <em>fn</em>.
When a callback function is given a response will <em>not</em> be made
automatically, however otherwise a response of <em>200</em> and <em>text/html</em> is given.</p>
<p>The <em>options</em> passed are the local variables as well, for example if we want to expose "user" to the view, and prevent a local we do so within the same object:</p>
<p>The <em>options</em> passed are the local variables as well, for example if we want to expose &ldquo;user&rdquo; to the view, and prevent a local we do so within the same object:</p>
<pre><code>var user = { name: 'tj' };
res.render('index', { layout: false, user: user });
</code></pre>
<h3 id="res-partial-view-options-">res.partial(view[, options])</h3>
<h3 id="res.partial()">res.partial(view[, options])</h3>
<p>Render <em>view</em> partial with the given <em>options</em>. This method is always available
to the view as a local variable.</p>
@@ -1231,9 +1260,9 @@ to the view as a local variable.</p>
<li><p><em>as</em> Variable name for each <em>collection</em> or <em>object</em> value, defaults to the view name.</p>
<ul>
<li>as: 'something' will add the <em>something</em> local variable</li>
<li>as: &lsquo;something&rsquo; will add the <em>something</em> local variable</li>
<li>as: this will use the collection value as the template context</li>
<li>as: global will merge the collection value's properties with <em>locals</em></li>
<li>as: global will merge the collection value&rsquo;s properties with <em>locals</em></li>
</ul>
</li>
<li><p><em>collection</em> Array of objects, the name is derived from the view name itself.
@@ -1252,7 +1281,7 @@ partial('movie', movies);
// In view: movie.director
</code></pre>
<p>To change the local from <em>movie</em> to <em>video</em> we can use the "as" option:</p>
<p>To change the local from <em>movie</em> to <em>video</em> we can use the &ldquo;as&rdquo; option:</p>
<pre><code>partial('movie', { collection: movies, as: 'video' });
// In view: video.director
@@ -1265,7 +1294,7 @@ of <em>movie.director</em> we could use <em>this.director</em>.</p>
// In view: this.director
</code></pre>
<p>Another alternative is to "explode" the properties of the collection item into
<p>Another alternative is to &ldquo;expand&rdquo; the properties of the collection item into
pseudo globals (local variables) by using <em>as: global</em>, which again is syntactic sugar:</p>
<pre><code>partial('movie', { collection: movies, as: global });
@@ -1287,12 +1316,26 @@ partial('movie', { object: movie });
// In view: movie.director
</code></pre>
<p>When a non-collection (does <em>not</em> have <em>.length</em>) is passed as the second argument, it is assumed to be the <em>object</em>, after which the object's local variable name is derived from the view name:</p>
<p>When a non-collection (does <em>not</em> have <em>.length</em>) is passed as the second argument, it is assumed to be the <em>object</em>, after which the object&rsquo;s local variable name is derived from the view name:</p>
<pre><code>partial('movie', movie);
<pre><code>var movie = new Movie('Nightmare Before Christmas', 'Tim Burton')
partial('movie', movie)
// =&gt; In view: movie.director
</code></pre>
<p>The exception of this, is when a &ldquo;plain&rdquo; object, aka &ldquo;{}&rdquo; or &ldquo;new Object&rdquo; is passed, which is considered an object with local variable. For example some may expect a &ldquo;movie&rdquo; local with the following, however since it is a plain object &ldquo;director&rdquo; and &ldquo;title&rdquo; are simply locals:</p>
<pre><code>var movie = { title: 'Nightmare Before Christmas', director: 'Tim Burton' };
partial('movie', movie)
</code></pre>
<p>For cases like this where passing a plain object is desired, simply assign it to a key, or use the <code>object</code> key which will use the filename-derived variable name. The examples below are equivalent:</p>
<pre><code> partial('movie', { locals: { movie: movie }})
partial('movie', { movie: movie })
partial('movie', { object: movie })
</code></pre>
<p>This exact API can be utilized from within a route, to respond with a fragment via Ajax or WebSockets, for example we can render a collection of users directly from a route:</p>
<pre><code>app.get('/users', function(req, res){
@@ -1309,7 +1352,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 +1370,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 +1396,7 @@ app.set('views');
// =&gt; ...path...
</code></pre>
<h3 id="app-enable-name-">app.enable(name)</h3>
<h3 id="app.enable()">app.enable(name)</h3>
<p>Enable the given setting <em>name</em>:</p>
@@ -1355,7 +1408,7 @@ app.enabled('some arbitrary setting');
// =&gt; true
</code></pre>
<h3 id="app-enabled-name-">app.enabled(name)</h3>
<h3 id="app.enabled()">app.enabled(name)</h3>
<p>Check if setting <em>name</em> is enabled:</p>
@@ -1367,7 +1420,7 @@ app.enabled('view cache');
// =&gt; true
</code></pre>
<h3 id="app-disable-name-">app.disable(name)</h3>
<h3 id="app.disable()">app.disable(name)</h3>
<p>Disable the given setting <em>name</em>:</p>
@@ -1379,7 +1432,7 @@ app.disabled('some setting');
// =&gt; false
</code></pre>
<h3 id="app-disabled-name-">app.disabled(name)</h3>
<h3 id="app.disabled()">app.disabled(name)</h3>
<p>Check if setting <em>name</em> is disabled:</p>
@@ -1393,7 +1446,7 @@ app.disabled('view cache');
// =&gt; true
</code></pre>
<h3 id="app-configure-env-function-function-">app.configure(env|function[, function])</h3>
<h3 id="app.configure()">app.configure(env|function[, function])</h3>
<p>Define a callback function for the given <em>env</em> (or all environments) with callback <em>function</em>:</p>
@@ -1406,7 +1459,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 +1468,7 @@ app.configure('development', function(){
<p>Now in a route we may call:</p>
<p> res.redirect('google');</p>
<p> res.redirect(&lsquo;google&rsquo;);</p>
<p>We may also map dynamic redirects:</p>
@@ -1438,7 +1491,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,7 +1502,7 @@ 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>
@@ -1465,10 +1518,10 @@ should call <em>next(err)</em> if it does not wish to deal with the exception:</
<p>Our view could now utilize the <em>firstName</em> and <em>lastName</em> variables,
as well as the <em>name()</em> function exposed.</p>
<pre><code>&lt;%= name(firstName, lastName) %>
<pre><code>&lt;%= name(firstName, lastName) %&gt;
</code></pre>
<h3 id="app-dynamicHelpers-obj-">app.dynamicHelpers(obj)</h3>
<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 +1540,7 @@ becomes the local variable it is associated with.</p>
<pre><code>&lt;%= session.name %&gt;
</code></pre>
<h3 id="app-mounted-fn-">app.mounted(fn)</h3>
<h3 id="app.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 +1555,10 @@ blog.mounted(function(parent){
app.use(blog);
</code></pre>
<h3 id="app-register-ext-exports-">app.register(ext, exports)</h3>
<h3 id="app.register()">app.register(ext, exports)</h3>
<p>Register the given template engine <em>exports</em>
as <em>ext</em>. For example we may wish to map ".html"
as <em>ext</em>. For example we may wish to map &ldquo;.html&rdquo;
files to jade:</p>
<pre><code> app.register('.html', require('jade'));
@@ -1513,8 +1566,8 @@ files to jade:</p>
<p>This is also useful for libraries that may not
match extensions correctly. For example my haml.js
library is installed from npm as "hamljs" so instead
of layout.hamljs, we can register the engine as ".haml":</p>
library is installed from npm as &ldquo;hamljs&rdquo; so instead
of layout.hamljs, we can register the engine as &ldquo;.haml&rdquo;:</p>
<pre><code> app.register('.haml', require('haml-js'));
</code></pre>
@@ -1523,7 +1576,7 @@ of layout.hamljs, we can register the engine as ".haml":</p>
specification, we can also wrap their api this way. Below
we map <em>.md</em> to render markdown files, rendering the html once
since it will not change on subsequent calls, and support local substitution
in the form of "{name}".</p>
in the form of &ldquo;{name}&rdquo;.</p>
<pre><code> app.register('.md', {
compile: function(str, options){
@@ -1537,7 +1590,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 +1616,7 @@ Content-Length: 11
Hello World
</code></pre>
</div>
</div>
</div>
</body>
</html>
</html>

View File

@@ -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;
@@ -1007,7 +1007,7 @@ Get or set the given local variable _name_. The locals built up for a response a
res.render('movie', { displayReviews: true });
});
### res.local(obj)
### res.locals(obj)
Assign several locals with the given _obj_. The following are equivalent:
@@ -1130,17 +1130,27 @@ 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
- `request` the request object
- `response` the response object
- `app` the application itself
This method is aliased as _app.locals()_.
### app.dynamicHelpers(obj)
Registers dynamic view helpers. Dynamic view helpers

View File

@@ -1,126 +0,0 @@
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "INDEX" "" "March 2011" "" ""
.
.SH "NAME"
\fBindex\fR
.
.IP "" 4
.
.nf
var app = express\.createServer();
app\.get(\'/\', function(req, res){
res\.send(\'Hello World\');
});
app\.listen(3000);
.
.fi
.
.IP "" 0
.
.SH "Features"
.
.IP "\(bu" 4
Robust routing
.
.IP "\(bu" 4
Redirection helpers
.
.IP "\(bu" 4
Dynamic view helpers
.
.IP "\(bu" 4
Application level view options
.
.IP "\(bu" 4
Content negotiation
.
.IP "\(bu" 4
Application mounting
.
.IP "\(bu" 4
Focus on high performance
.
.IP "\(bu" 4
View rendering and partials support
.
.IP "\(bu" 4
Environment based configuration
.
.IP "\(bu" 4
Session based flash notifications
.
.IP "\(bu" 4
Built on Connect \fIhttp://github\.com/senchalabs/connect\fR
.
.IP "\(bu" 4
Executable \fIexecutable\.html\fR for generating applications quickly
.
.IP "\(bu" 4
High test coverage
.
.IP "" 0
.
.SH "Contributors"
The following are the major contributors of Express (in no specific order)\.
.
.IP "\(bu" 4
TJ Holowaychuk (visionmedia \fIhttp://github\.com/visionmedia\fR)
.
.IP "\(bu" 4
Ciaran Jessup (ciaranj \fIhttp://github\.com/ciaranj\fR)
.
.IP "\(bu" 4
Aaron Heckmann (aheckmann \fIhttp://github\.com/aheckmann\fR)
.
.IP "\(bu" 4
Guillermo Rauch (guille \fIhttp://github\.com/guille\fR)
.
.IP "" 0
.
.SH "Third\-Party Modules"
The following modules compliment or extend Express directly:
.
.IP "\(bu" 4
express\-resource \fIhttp://github\.com/visionmedia/express\-resource\fR provides resourceful routing
.
.IP "\(bu" 4
express\-messages \fIhttp://github\.com/visionmedia/express\-messages\fR flash message notification rendering
.
.IP "\(bu" 4
express\-configure \fIhttp://github\.com/visionmedia/express\-configure\fR async configuration support (load settings from redis etc)
.
.IP "\(bu" 4
express\-namespace \fIhttp://github\.com/visionmedia/express\-namespace\fR namespaced routing support
.
.IP "" 0
.
.SH "More Information"
.
.IP "\(bu" 4
Google Group \fIhttp://groups\.google\.com/group/express\-js\fR for discussion
.
.IP "\(bu" 4
Follow tjholowaychuk \fIhttp://twitter\.com/tjholowaychuk\fR on twitter for updates
.
.IP "\(bu" 4
View the Connect \fIhttp://senchalabs\.github\.com/connect\fR documentation
.
.IP "\(bu" 4
View the Connect Wiki \fIhttp://wiki\.github\.com/senchalabs/connect/\fR for contrib middleware
.
.IP "\(bu" 4
View the examples \fIhttp://github\.com/visionmedia/express/tree/master/examples/\fR
.
.IP "\(bu" 4
View the source \fIhttp://github\.com/visionmedia/express\fR
.
.IP "\(bu" 4
View the contrib guide \fIcontrib\.html\fR
.
.IP "" 0

View File

@@ -190,11 +190,6 @@
<li><a href="screencasts.html">Screencasts</a></li>
<li><a href="applications.html">Applications</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>index</code>
</p>
<pre><code>var app = express.createServer();
app.get('/', function(req, res){
@@ -204,7 +199,7 @@ app.get('/', function(req, res){
app.listen(3000);
</code></pre>
<h2 id="Features">Features</h2>
<h2>Features</h2>
<ul>
<li>Robust routing</li>
@@ -223,7 +218,7 @@ app.listen(3000);
</ul>
<h2 id="Contributors">Contributors</h2>
<h2>Contributors</h2>
<p>The following are the major contributors of Express (in no specific order).</p>
@@ -235,7 +230,7 @@ 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>
@@ -247,7 +242,7 @@ app.listen(3000);
</ul>
<h2 id="More-Information">More Information</h2>
<h2>More Information</h2>
<ul>
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
@@ -259,9 +254,7 @@ app.listen(3000);
<li>View the <a href="contrib.html">contrib guide</a></li>
</ul>
</div>
</div>
</div>
</body>
</html>
</html>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -83,6 +83,18 @@ Server.prototype.init = function(middleware){
return fn;
});
// default locals
this.locals({
settings: this.settings
, app: this
});
// default dynamic locals
this.dynamicHelpers({
request: function(req, res){ return req; }
, response: function(req, res){ return res; }
});
// default development configuration
this.configure('development', function(){
this.enable('hints');

View File

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

View File

@@ -67,6 +67,7 @@ res.send = function(body, headers, status){
this.contentType('.html');
}
break;
case 'boolean':
case 'object':
if (Buffer.isBuffer(body)) {
if (!this.header('Content-Type')) {
@@ -347,7 +348,7 @@ res.redirect = function(url, status){
// Absolute
var host = req.headers.host
, tls = req.connection.constructor.name == 'CleartextStream';
, tls = req.connection.encrypted;
url = 'http' + (tls ? 's' : '') + '://' + host + url;
}

View File

@@ -214,6 +214,25 @@ res.render = function(view, opts, fn, parent, sub){
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
} 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
@@ -266,7 +285,7 @@ res.render = function(view, opts, fn, parent, sub){
// Populate view
var orig = view = new View(view, options);
// Try _ prefix ex: ./views/_user.jade
// Try _ prefix ex: ./views/_<name>.jade
if (!view.exists) view = new View(orig.prefixPath, options);
// Try index ex: ./views/user/index.jade
@@ -276,8 +295,11 @@ res.render = function(view, opts, fn, parent, sub){
// when calling partial('user') within the same dir
if (!view.exists && !options.isLayout) view = new View(orig.upIndexPath, options);
// Try layout relative to the "views" dir
if (!view.exists && options.isLayout) view = new View(orig.rootPath, 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) {
@@ -314,44 +336,30 @@ res.render = function(view, opts, fn, parent, sub){
options.filename = view.path;
// 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);
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);
// layout expected
if (layout) {
options.isLayout = true;
options.layout = false;
options.body = str;
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);
}
} catch (err) {
// callback given
if (fn) {
fn(err);
// unwind to root call
} else if (sub) {
throw err;
// root template, next(err)
} else {
this.req.next(err);
}
// layout expected
if (layout) {
options.isLayout = true;
options.layout = false;
options.body = str;
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

View File

@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "2.0.0",
"version": "2.1.0",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
@@ -10,7 +10,7 @@
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
],
"dependencies": {
"connect": ">= 1.1.0 < 2.0.0",
"connect": ">= 1.1.1 < 2.0.0",
"mime": ">= 0.0.1",
"qs": ">= 0.0.6"
},

View File

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

View File

@@ -188,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();
@@ -303,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(){

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

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

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

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

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

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

View File

@@ -49,7 +49,16 @@ module.exports = {
app.get('/noargs', function(req, res, next){
res.send();
});
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>'

View File

@@ -599,6 +599,26 @@ module.exports = {
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>' });
},
'test #partial() with several calls': function(){