Comparar commits

...

121 Commits

Autor SHA1 Mensagem Data
Tj Holowaychuk 45f168e873 Release 2.3.6 2011-05-20 09:42:01 -07:00
Tj Holowaychuk 799938683d Merge branch 'refactor/dev-deps' 2011-05-20 09:39:34 -07:00
Tj Holowaychuk 7128f2d11f fixed last examples 2011-05-20 09:39:29 -07:00
Tj Holowaychuk 0634bf0b0d fixed redis example 2011-05-20 09:38:52 -07:00
Tj Holowaychuk f9e48c2972 fixed markdown example 2011-05-20 09:29:22 -07:00
Tj Holowaychuk 909960c0b3 refactoring examples more 2011-05-20 09:26:47 -07:00
Tj Holowaychuk 8a7876f4d1 cleaning up examples 2011-05-20 09:16:35 -07:00
Tj Holowaychuk 286c92b13b docs 2011-05-20 09:15:39 -07:00
Tj Holowaychuk b9872a278f docs 2011-05-20 09:15:35 -07:00
Tj Holowaychuk 1b34fd7efa misc 2011-05-20 09:11:15 -07:00
Tj Holowaychuk f05c351762 Fixed view caching, should not be enabled in development 2011-05-20 09:01:05 -07:00
Tj Holowaychuk 8323f19e96 fixing examples 2011-05-20 08:56:25 -07:00
Tj Holowaychuk 565eda9ee5 docs 2011-05-20 08:40:26 -07:00
Tj Holowaychuk 7aea7194d1 example docs 2011-05-20 08:39:43 -07:00
Tj Holowaychuk 2f68957c8c fixed tests 2011-05-20 08:37:23 -07:00
Tj Holowaychuk 4ca848e526 ignore node_modules 2011-05-20 08:35:20 -07:00
Tj Holowaychuk 31a8c7c19c test docs 2011-05-20 08:35:11 -07:00
Tj Holowaychuk f1c435e050 removed support submods 2011-05-20 08:31:13 -07:00
Tj Holowaychuk fac75a9bff connect 1.4.1 2011-05-20 08:29:02 -07:00
Tj Holowaychuk 4fe03ab223 dev deps 2011-05-20 08:28:02 -07:00
TJ Holowaychuk c6122da59b Merge pull request #668 from joemccann/master
Simple Update to Markdown example
2011-05-20 08:07:21 -07:00
Tj Holowaychuk 131f658779 Release 2.3.5 2011-05-20 07:32:16 -07:00
Joe McCann 127f77964e Updated markdown example to latest version of node-markdown and modified the compile method. 2011-05-20 09:16:53 -05:00
Tj Holowaychuk 9f2bd30dc7 router.routes 2011-05-19 18:36:41 -07:00
Tj Holowaychuk 6e633b31b4 return bool from req.is() 2011-05-19 09:48:19 -07:00
Tj Holowaychuk 1c65643488 more tests 2011-05-18 17:41:15 -07:00
Tj Holowaychuk 388ad9067a tweak tests 2011-05-17 13:59:24 -07:00
Tj Holowaychuk f470f0bdc5 export .view as alias for .View
reads better:

   express.view.lookup(...)
2011-05-16 16:27:17 -07:00
Tj Holowaychuk 72384b0523 misc refactor of res.partial() 2011-05-16 15:29:20 -07:00
Tj Holowaychuk 1b199b7d98 lookup docs 2011-05-16 15:26:12 -07:00
Tj Holowaychuk 09b384ea44 Merge branch 'refactor/views' 2011-05-16 15:20:26 -07:00
Tj Holowaychuk 56ae55f987 keep duplicates out of view resolution hint 2011-05-16 15:20:08 -07:00
Tj Holowaychuk 1c360a89ba added views.compile(view, cache, cid, options)
private for now
2011-05-16 15:17:06 -07:00
Tj Holowaychuk 8636dee13e docs 2011-05-16 14:28:11 -07:00
Tj Holowaychuk 70e6baf6fc misc refactoring 2011-05-16 09:32:53 -07:00
Tj Holowaychuk 3588c1eedc docs 2011-05-16 09:27:16 -07:00
Tj Holowaychuk 8d6f167a81 added better middleware docs and use-cases 2011-05-16 09:24:53 -07:00
Tj Holowaychuk 6106188347 docs 2011-05-15 18:01:47 -07:00
Tj Holowaychuk eeb77541cd Updated jade submodule 2011-05-14 11:53:26 -07:00
Tj Holowaychuk 99b244b47c export View 2011-05-13 19:38:23 -07:00
Tj Holowaychuk 3043672448 added exports.lookup(view, options) to view.js
private for now
2011-05-13 19:37:11 -07:00
Tj Holowaychuk 0477a53c9f misc refactor 2011-05-13 15:47:48 -07:00
Tj Holowaychuk d9aa7c3bc9 Release 2.3.4 2011-05-08 10:53:57 -07:00
Tj Holowaychuk 986fac583b Merge branch 'master' of github.com:visionmedia/express 2011-05-08 10:52:59 -07:00
Tj Holowaychuk c6d76086e2 Fixed res.sendfile() bug preventing the transfer of files with spaces
params are decoded so we need to encode before passing to send() which then
in turn decodes it again, however nodes url module chokes on the spaces.
2011-05-08 10:52:16 -07:00
Tj Holowaychuk e2771364eb Updated connect submodule 2011-05-08 10:45:42 -07:00
Tj Holowaychuk 0d5a63798b added failing test with spaces in filename 2011-05-08 10:40:37 -07:00
TJ Holowaychuk 7d15e2bf52 Merge pull request #653 from darrentorpey/patch-1.
Fixed a typo: "A route is simple a string" => "A route is simply a string
2011-05-04 09:35:49 -07:00
Darren Torpey 31fef407b6 Fixed a typo: "A route is simple a string" => "A route is simply a string" 2011-05-04 04:14:40 -07:00
Tj Holowaychuk 6bef3ef891 misc 2011-05-03 16:48:17 -07:00
Tj Holowaychuk b806846049 misc 2011-05-03 16:44:17 -07:00
Tj Holowaychuk bc16020976 added stupid say example 2011-05-03 16:40:20 -07:00
Tj Holowaychuk 8afb905a43 Release 2.3.3 2011-05-03 11:31:16 -07:00
Tj Holowaychuk 53667728a8 Fixed route-specific middleware when using the same callback function several times 2011-05-03 11:28:33 -07:00
Tj Holowaychuk 5f0a854e29 added test for route specific middleware regression 2011-05-03 11:25:54 -07:00
Tj Holowaychuk e9ef3dd9cd ws 2011-05-03 09:41:59 -07:00
Tj Holowaychuk f702884704 split methods supported by rfc [slaskis] 2011-05-03 09:24:51 -07:00
Tj Holowaychuk 0cb866845d npm 1.x docs 2011-05-02 12:48:45 -07:00
Tj Holowaychuk 26483029db docs for next("route"). Closes #650 2011-05-01 11:06:37 -07:00
Tj Holowaychuk d2adcbdf67 Added "case sensitive routes" option. 2011-04-29 16:41:20 -07:00
Tj Holowaychuk d2f963db2a fixed tests 2011-04-29 16:34:22 -07:00
TJ Holowaychuk fc2bc1362f Merged pull request #645 from 8bitDesigner/patch-1.
Incorrect reference to template in a comment
2011-04-27 18:56:00 -07:00
8bitDesigner 6ae45d0fd3 The comment here refers to using "jade" for layouts, but you're using "ejs" instead. 2011-04-27 16:11:39 -07:00
Tj Holowaychuk cc185a8c0e Release 2.3.2 2011-04-27 09:12:54 -07:00
Tj Holowaychuk ae1078944c Fixed view hints
populate attempts on new View
2011-04-27 09:07:56 -07:00
Tj Holowaychuk 385a05dd10 bump 2011-04-26 15:26:24 -07:00
Tj Holowaychuk 2572a78648 Release 2.3.1 2011-04-26 15:26:04 -07:00
Tj Holowaychuk 470774cfba Fixed template caching collision issue. Closes #644
the parent view is resolved first so we always have the absolute path
available, so this should prevent collisions.
2011-04-26 15:24:31 -07:00
Tj Holowaychuk 351f6abe4c Merge branch 'refactor/router' 2011-04-26 14:36:51 -07:00
Tj Holowaychuk 53a16e1795 Added app.match() as app.match.all() 2011-04-26 13:59:59 -07:00
Tj Holowaychuk ff77c8b205 Added app.lookup() as app.lookup.all() 2011-04-26 13:59:37 -07:00
Tj Holowaychuk b33f38b109 Added app.remove() for app.remove.all() 2011-04-26 13:58:43 -07:00
Tj Holowaychuk b9596d7ce8 Added app.remove.VERB() 2011-04-26 13:57:05 -07:00
Tj Holowaychuk 251175c025 test for previous commit 2011-04-26 13:30:47 -07:00
Tj Holowaychuk fda1bc4630 moved fn.params to route.params 2011-04-26 13:30:21 -07:00
Tj Holowaychuk 83c2c176a9 misc 2011-04-26 13:28:20 -07:00
Tj Holowaychuk 9be5992f22 misc refactoring 2011-04-26 13:26:49 -07:00
Tj Holowaychuk d8d23c0bf8 misc 2011-04-26 13:21:19 -07:00
Tj Holowaychuk b2689fc40e docs 2011-04-26 13:16:19 -07:00
Tj Holowaychuk a4cfde350f moved more tests 2011-04-26 13:14:21 -07:00
Tj Holowaychuk 7374027457 added router.test.js 2011-04-26 13:13:07 -07:00
Tj Holowaychuk 63328c2177 added .index to match() retvals 2011-04-26 13:01:53 -07:00
Tj Holowaychuk c4e2ce23e5 more Route tests 2011-04-26 12:59:25 -07:00
Tj Holowaychuk dacad53b2e Added instancoef Route test 2011-04-26 12:57:14 -07:00
Tj Holowaychuk 4ffd5280a7 expose Route 2011-04-26 12:55:31 -07:00
Tj Holowaychuk 74310fb464 misc refactoring 2011-04-26 12:53:57 -07:00
Tj Holowaychuk 8b2268cf38 Started Route implementation 2011-04-26 12:41:05 -07:00
Tj Holowaychuk fb655f4981 added lib/router/methods.js 2011-04-26 11:49:51 -07:00
Tj Holowaychuk 7208c33d72 refactored view.js 2011-04-26 11:40:44 -07:00
Tj Holowaychuk 4efb25d048 refactored https.js 2011-04-26 11:39:09 -07:00
Tj Holowaychuk a3678cd7f6 refactored http.js 2011-04-26 11:34:41 -07:00
Tj Holowaychuk 393d38f1ab stubbed Route 2011-04-26 11:31:31 -07:00
Tj Holowaychuk 805b9ac3a9 docs for "view cache" setting 2011-04-25 12:06:20 -07:00
Tj Holowaychuk 379b9812be refactored options() helper 2011-04-25 10:26:08 -07:00
Tj Holowaychuk a9992b5647 docs 2011-04-25 10:19:13 -07:00
Tj Holowaychuk b6c0a9b1b5 moved router to router/index 2011-04-25 10:17:13 -07:00
Tj Holowaychuk 1d2dd2a375 Merge branch 'feature/router' 2011-04-25 10:13:46 -07:00
Tj Holowaychuk 63db694aa2 utilizing local router 2011-04-25 10:13:30 -07:00
Tj Holowaychuk b6aca36ad9 htmlEscape -> escape 2011-04-25 10:11:38 -07:00
Tj Holowaychuk 8420ae93fd removed old path utils 2011-04-25 10:10:43 -07:00
Tj Holowaychuk 6722716fa7 Moved router over from connect 2011-04-25 10:08:54 -07:00
Tj Holowaychuk 658e064220 Release 2.3.0 2011-04-25 09:49:54 -07:00
Tj Holowaychuk 66b472e567 connect >= 1.4.0 2011-04-25 09:32:57 -07:00
Tj Holowaychuk 3d242a607e Fixed caching of views when using several apps. Closes #637
simple fix :)
2011-04-25 09:25:23 -07:00
Tj Holowaychuk bc68e5e7a3 misc 2011-04-25 09:20:31 -07:00
Tj Holowaychuk aa6858189c misc refactor 2011-04-23 18:54:49 -07:00
Tj Holowaychuk 06fda62c9e Merge branch 'refactor/route-middleware' 2011-04-22 16:49:04 -07:00
Tj Holowaychuk 5688ea650d Fixed gotcha invoking app.param() callbacks once per route middleware. Closes #638 2011-04-22 16:48:54 -07:00
Tj Holowaychuk f5c4e9d612 Updated connect submodule 2011-04-22 16:34:42 -07:00
Tj Holowaychuk b9eda2a59d Updated connect submodule 2011-04-22 16:11:13 -07:00
Tj Holowaychuk 8c168b0971 connect >= 1.3.1 < 2.0.0 2011-04-22 16:11:07 -07:00
Tj Holowaychuk 9c20a50ee2 Added failing test for gotcha 2011-04-22 14:44:08 -07:00
Tj Holowaychuk eac666574e viewHelpers -> _locals 2011-04-21 08:26:30 -07:00
Tj Holowaychuk e4c840f6b8 Added res.helpers() as alias of res.locals()
to match app.locals() / app.helpers()
2011-04-20 15:26:22 -07:00
Daniel Shaw 3afbcd0acf JSON and JSONP default to UTF-8 in the same way as HTML. Closes #632.
Signed-off-by: Tj Holowaychuk <tj@vision-media.ca>
2011-04-20 09:00:02 -07:00
Daniel Shaw 8f054dbcaf JSON and JSONP default to UTF-8 in the same way as HTML. Introduces app.set('charset') to set charset default at the application level. Closes #632.
Signed-off-by: Tj Holowaychuk <tj@vision-media.ca>
2011-04-20 09:00:02 -07:00
Tj Holowaychuk ccc39e5aa2 Fixed partial lookup precedence. Closes #631 2011-04-19 10:23:16 -07:00
Tj Holowaychuk 53d4da2a9c Added failing partial precedence test 2011-04-19 10:19:45 -07:00
Tj Holowaychuk d9e7153fc9 Renamed "cache views" to "view cache". Closes #628 2011-04-17 16:42:03 -07:00
Tj Holowaychuk dc02b0d5ae Added options support to res.clearCookie() 2011-04-17 16:37:16 -07:00
Aaron Heckmann e0bc5711b8 auto set Content-Type in res.attachement
Signed-off-by: Tj Holowaychuk <tj@vision-media.ca>
2011-04-14 13:48:22 -07:00
63 arquivos alterados com 1279 adições e 511 exclusões
+1
Ver Arquivo
@@ -10,3 +10,4 @@ lib-cov
*.swo
benchmarks/graphs
testing.js
node_modules/
-30
Ver Arquivo
@@ -1,30 +0,0 @@
[submodule "support/expresso"]
path = support/expresso
url = git://github.com/visionmedia/expresso.git
[submodule "support/haml"]
path = support/haml
url = git://github.com/visionmedia/haml.js.git
[submodule "support/ejs"]
path = support/ejs
url = git://github.com/visionmedia/ejs.git
[submodule "support/connect-form"]
path = support/connect-form
url = git://github.com/visionmedia/connect-form.git
[submodule "support/connect"]
path = support/connect
url = git://github.com/senchalabs/connect.git
[submodule "support/should"]
path = support/should
url = git://github.com/visionmedia/should.js.git
[submodule "support/formidable"]
path = support/formidable
url = git://github.com/felixge/node-formidable.git
[submodule "support/jade"]
path = support/jade
url = git://github.com/visionmedia/jade.git
[submodule "support/qs"]
path = support/qs
url = git://github.com/visionmedia/node-querystring.git
[submodule "support/mime"]
path = support/mime
url = https://github.com/bentomas/node-mime.git
+55
Ver Arquivo
@@ -1,4 +1,59 @@
2.3.6 / 2011-05-20
==================
* Changed; using devDependencies instead of git submodules
* Fixed redis session example
* Fixed markdown example
* Fixed view caching, should not be enabled in development
2.3.5 / 2011-05-20
==================
* Added export `.view` as alias for `.View`
2.3.4 / 2011-05-08
==================
* Added `./examples/say`
* Fixed `res.sendfile()` bug preventing the transfer of files with spaces
2.3.3 / 2011-05-03
==================
* Added "case sensitive routes" option.
* Changed; split methods supported per rfc [slaskis]
* Fixed route-specific middleware when using the same callback function several times
2.3.2 / 2011-04-27
==================
* Fixed view hints
2.3.1 / 2011-04-26
==================
* Added `app.match()` as `app.match.all()`
* Added `app.lookup()` as `app.lookup.all()`
* Added `app.remove()` for `app.remove.all()`
* Added `app.remove.VERB()`
* Fixed template caching collision issue. Closes #644
* Moved router over from connect and started refactor
2.3.0 / 2011-04-25
==================
* Added options support to `res.clearCookie()`
* Added `res.helpers()` as alias of `res.locals()`
* Added; json defaults to UTF-8 with `res.send()`. Closes #632. [Daniel * Dependency `connect >= 1.4.0`
* Changed; auto set Content-Type in res.attachement [Aaron Heckmann]
* Renamed "cache views" to "view cache". Closes #628
* Fixed caching of views when using several apps. Closes #637
* Fixed gotcha invoking `app.param()` callbacks once per route middleware.
Closes #638
* Fixed partial lookup precedence. Closes #631
Shaw]
2.2.2 / 2011-04-12
==================
+1 -6
Ver Arquivo
@@ -3,13 +3,8 @@ DOCS = $(shell find docs/*.md)
HTMLDOCS =$(DOCS:.md=.html)
test:
@NODE_ENV=test ./support/expresso/bin/expresso \
@NODE_ENV=test ./node_modules/.bin/expresso \
-I lib \
-I support \
-I support/connect/lib \
-I support/haml/lib \
-I support/jade/lib \
-I support/ejs/lib \
$(TESTFLAGS) \
test/*.test.js
+24
Ver Arquivo
@@ -16,6 +16,10 @@
$ npm install express
or to access the `express(1)` executable install globally:
$ npm install -g express
## Features
* Robust routing
@@ -72,6 +76,26 @@ Express 1.x is compatible with node 0.2.x and connect < 1.0.
Express 2.x is compatible with node 0.4.x and connect 1.x
## Viewing Examples
First install the dev dependencies to install all the example / test suite deps:
$ npm install
then run whichever tests you want:
$ node examples/jade/app.js
## Running Tests
To run the test suite first invoke the following command within the repo, installing the development dependencies:
$ npm install
then run the tests:
$ make test
## License
(The MIT License)
+1 -1
Ver Arquivo
@@ -11,7 +11,7 @@ var fs = require('fs')
* Framework version.
*/
var version = '2.2.2';
var version = '2.3.6';
/**
* Add session support.
+2 -3
Ver Arquivo
@@ -1,10 +1,9 @@
### Development Dependencies
Express development dependencies are stored within the _./support_ directory. To
update them execute:
First install the dev dependencies by executing the following command in the repo's directory:
$ git submodule update --init
$ npm install
### Running Tests
+46 -7
Ver Arquivo
@@ -340,6 +340,8 @@ app.configure('production', function(){
<li><em>views</em> Root views directory defaulting to <strong>CWD/views</strong></li>
<li><em>view engine</em> Default view engine name for views rendered without extensions</li>
<li><em>view options</em> An object specifying global view options</li>
<li><em>view cache</em> Enable view caching (enabled in production)</li>
<li><em>case sensitive routes</em> Enable case-sensitive routing</li>
</ul>
@@ -431,7 +433,7 @@ app.post('/', function(req, res){
app.listen(3000);
</code></pre>
<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>
<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([0-9]+)&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>
@@ -519,6 +521,34 @@ app.use(connect.bodyParser());
app.use(express.bodyParser());
</code></pre>
<p>Middleware ordering is important, when Connect receives a request the <em>first</em> middleware we pass to <em>createServer()</em> or <em>use()</em> is executed with three parameters, <em>request</em>, <em>response</em>, and a callback function usually named <em>next</em>. When <em>next()</em> is invoked the second middleware will then have it&rsquo;s turn and so on. This is important to note because many middleware depend on each other, for example <em>methodOverride()</em> checks <em>req.body.</em>method<em> for the HTTP method override, however </em>bodyParser()<em> parses the request body and populates </em>req.body<em>. Another example of this is cookie parsing and session support, we must first </em>use()<em> </em>cookieParser()<em> followed by </em>session()_.</p>
<p>Many Express applications may contain the line <em>app.use(app.router)</em>, while this may appear strange, it&rsquo;s simply the middleware function that contains all defined routes, and performs route lookup based on the current request url and HTTP method. Express allows you to position this middleware, though by default it will be added to the bottom. By positioning the router, we can alter middleware precedence, for example we may want to add error reporting as the <em>last</em> middleware so that any exception passed to <em>next()</em> will be handled by it, or perhaps we want static file serving to have low precedence, allowing our routes to intercept requests to a static file to count downloads etc. This may look a little like below</p>
<pre><code>app.use(express.logger(...));
app.use(express.bodyParser(...));
app.use(express.cookieParser(...));
app.use(express.session(...));
app.use(app.router);
app.use(express.static(...));
app.use(express.errorHandler(...));
</code></pre>
<p>First we add <em>logger()</em> so that it may wrap node&rsquo;s <em>req.end()</em> method, providing us with response-time data. Next the request&rsquo;s body will be parsed (if any), followed by cookie parsing and session support, meaning <em>req.session</em> will be defined by the time we hit our routes in <em>app.router</em>. If a request such as <em>GET /javascripts/jquery.js</em> is handled by our routes, and we do not call <em>next()</em> then the <em>static()</em> middleware will never see this request, however if were to define a route as shown below, we can record stats, refuse downloads, consume download credits etc.</p>
<pre><code>var downloads = {};
app.use(app.router);
app.use(express.static(__dirname + '/public'));
app.get('/*', function(req, res, next){
var file = req.params[0];
downloads[file] = downloads[file] || 0;
downloads[file]++;
next();
});
</code></pre>
<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>
@@ -595,6 +625,8 @@ 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>
<p>There are times when we may want to &ldquo;skip&rdquo; passed remaining route middleware, but continue matching subsequent routes. To do this we invoke <code>next()</code> with the string &ldquo;route&rdquo; <code>next('route')</code>. If no remaining routes match the request url then Express will respond with 404 Not Found.</p>
<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>
@@ -1155,7 +1187,7 @@ an error occurs, or when the transfer is complete. By default failures call <cod
});
</code></pre>
<h3 id="res.download()">res.download(file[, filename[, callback]])</h3>
<h3 id="res.download()">res.download(file[, filename[, callback[, callback2]]])</h3>
<p>Transfer the given <em>file</em> as an attachment with optional alternative <em>filename</em>.</p>
@@ -1169,13 +1201,22 @@ res.download('path/to/image.png', 'foo.png');
res.sendfile(file);
</code></pre>
<p>An optional callback may be supplied as either the second or third argument, which is passed to <em>res.sendfile()</em>:</p>
<p>An optional callback may be supplied as either the second or third argument, which is passed to <em>res.sendfile()</em>. Within this callback you may still respond, as the header has not been sent.</p>
<pre><code>res.download(path, 'expenses.doc', function(err){
// handle
});
</code></pre>
<p>An optional second callback, <em>callback2</em> may be given to allow you to act on connection related errors, however you should not attempt to respond.</p>
<pre><code>res.download(path, function(err){
// error or finished
}, function(err){
// connection related error
});
</code></pre>
<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
@@ -1233,7 +1274,7 @@ app.get('/', function(req, res){
});
</code></pre>
<h3 id="res.clearcookie()">res.clearCookie(name)</h3>
<h3 id="res.clearcookie()">res.clearCookie(name[, options])</h3>
<p>Clear cookie <em>name</em> by setting &ldquo;expires&rdquo; far in the past.</p>
@@ -1525,9 +1566,7 @@ as well as the <em>name()</em> function exposed.</p>
<pre><code>- `settings` the app's settings object
- `filename` the view's filename
- `request` the request object
- `response` the response object
- `app` the application itself
- `layout(path)` specify the layout from within a view
</code></pre>
<p>This method is aliased as <em>app.locals()</em>.</p>
+33 -2
Ver Arquivo
@@ -82,6 +82,8 @@ Express supports the following settings out of the box:
* _views_ Root views directory defaulting to **CWD/views**
* _view engine_ Default view engine name for views rendered without extensions
* _view options_ An object specifying global view options
* _view cache_ Enable view caching (enabled in production)
* _case sensitive routes_ Enable case-sensitive routing
### Routing
@@ -165,7 +167,7 @@ For example we can __POST__ some json, and echo the json back using the _bodyPar
app.listen(3000);
Typically we may use a "dumb" placeholder such as "/user/:id" which has no restrictions, however say for example we are limiting a user id to digits, we may use _'/user/:id(\\\\d+)'_ which will _not_ match unless the placeholder value contains only digits.
Typically we may use a "dumb" placeholder such as "/user/:id" which has no restrictions, however say for example we are limiting a user id to digits, we may use _'/user/:id([0-9]+)'_ which will _not_ match unless the placeholder value contains only digits.
### Passing Route Control
@@ -247,6 +249,33 @@ This is somewhat annoying, so express re-exports these middleware properties, ho
app.use(express.logger());
app.use(express.bodyParser());
Middleware ordering is important, when Connect receives a request the _first_ middleware we pass to _createServer()_ or _use()_ is executed with three parameters, _request_, _response_, and a callback function usually named _next_. When _next()_ is invoked the second middleware will then have it's turn and so on. This is important to note because many middleware depend on each other, for example _methodOverride()_ checks _req.body._method_ for the HTTP method override, however _bodyParser()_ parses the request body and populates _req.body_. Another example of this is cookie parsing and session support, we must first _use()_ _cookieParser()_ followed by _session()_.
Many Express applications may contain the line _app.use(app.router)_, while this may appear strange, it's simply the middleware function that contains all defined routes, and performs route lookup based on the current request url and HTTP method. Express allows you to position this middleware, though by default it will be added to the bottom. By positioning the router, we can alter middleware precedence, for example we may want to add error reporting as the _last_ middleware so that any exception passed to _next()_ will be handled by it, or perhaps we want static file serving to have low precedence, allowing our routes to intercept requests to a static file to count downloads etc. This may look a little like below
app.use(express.logger(...));
app.use(express.bodyParser(...));
app.use(express.cookieParser(...));
app.use(express.session(...));
app.use(app.router);
app.use(express.static(...));
app.use(express.errorHandler(...));
First we add _logger()_ so that it may wrap node's _req.end()_ method, providing us with response-time data. Next the request's body will be parsed (if any), followed by cookie parsing and session support, meaning _req.session_ will be defined by the time we hit our routes in _app.router_. If a request such as _GET /javascripts/jquery.js_ is handled by our routes, and we do not call _next()_ then the _static()_ middleware will never see this request, however if were to define a route as shown below, we can record stats, refuse downloads, consume download credits etc.
var downloads = {};
app.use(app.router);
app.use(express.static(__dirname + '/public'));
app.get('/*', function(req, res, next){
var file = req.params[0];
downloads[file] = downloads[file] || 0;
downloads[file]++;
next();
});
### Route Middleware
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.
@@ -318,6 +347,8 @@ Commonly used "stacks" of middleware can be passed as an array (_applied recursi
For this example in full, view the [route middleware example](http://github.com/visionmedia/express/blob/master/examples/route-middleware/app.js) in the repository.
There are times when we may want to "skip" passed remaining route middleware, but continue matching subsequent routes. To do this we invoke `next()` with the string "route" `next('route')`. If no remaining routes match the request url then Express will respond with 404 Not Found.
### HTTP Methods
We have seen _app.get()_ a few times, however Express also exposes other familiar HTTP verbs in the same manor, such as _app.post()_, _app.del()_, etc.
@@ -894,7 +925,7 @@ To parse incoming _Cookie_ headers, use the _cookieParser_ middleware, which pro
// use req.cookies.rememberme
});
### res.clearCookie(name)
### res.clearCookie(name[, options])
Clear cookie _name_ by setting "expires" far in the past.
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
+11 -7
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
@@ -24,13 +21,20 @@ app.get('/files/:file(*)', function(req, res, next){
, path = __dirname + '/files/' + file;
// either res.download(path) and let
// express handle failures, or provide
// a callback
// a callback as shown below
res.download(path, function(err){
// if an error occurs in this callback
// the file most likely does not exist,
// and it's safe to respond or next(err)
if (err) return next(err);
// the response has invoked .end()
// so you cannnot respond here (of course)
// but the callback is handy for statistics etc.
// the file has been transferred, do not respond
// from here, though you may use this callback
// for stats etc.
console.log('transferred %s', path);
}, function(err){
// this second optional callback is used when
// an error occurs during transmission
});
});
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
+2 -4
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
@@ -25,4 +22,5 @@ app.get('/next', function(req, res, next){
// text/html, and application/json responses to aid in development
app.use('/', express.errorHandler({ dump: true, stack: true }));
app.listen(3000);
app.listen(3000);
console.log('app listening on port 3000');
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
+1 -4
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
@@ -15,7 +12,7 @@ app.set('views', __dirname + '/views');
// set default layout, usually "layout"
app.set('view options', { layout: 'layouts/default' });
// Set our default template engine to "jade"
// Set our default template engine to "ejs"
// which prevents the need for extensions
// (although you can still mix and match)
app.set('view engine', 'ejs');
+2 -7
Ver Arquivo
@@ -1,15 +1,10 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
// $ npm install markdown
/**
* Module dependencies.
*/
var express = require('../../lib/express')
, md = require('markdown').markdown;
, md = require('node-markdown').Markdown;
var app = express.createServer();
@@ -19,7 +14,7 @@ var app = express.createServer();
app.register('.md', {
compile: function(str, options){
var html = md.toHTML(str);
var html = md(str);
return function(locals){
return html.replace(/\{([^}]+)\}/g, function(_, name){
return locals[name];
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
+44
Ver Arquivo
@@ -0,0 +1,44 @@
/**
* Module dependencies.
*/
var express = require('../../')
, path = require('path')
, exec = require('child_process').exec
, fs = require('fs');
/**
* Error handler.
*/
function errorHandler(voice) {
return function(err, req, res, next) {
var parts = err.stack.split('\n')[1].split(/[()]/)[1].split(':')
, filename = parts.shift()
, basename = path.basename(filename)
, lineno = parts.shift()
, col = parts.shift()
, lines = fs.readFileSync(filename, 'utf8').split('\n')
, line = lines[lineno - 1].replace(/\./, ' ');
exec('say -v "' + voice + '" '
+ err.message
+ ' on line ' + lineno
+ ' of ' + basename + '.'
+ ' The contents of this line is '
+ ' "' + line + '".');
res.send(500);
}
}
var app = express.createServer();
app.get('/', function(request, response){
if (request.is(foo)) response.end('bar');
});
app.use(errorHandler('Vicki'));
app.listen(3000);
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
+1 -4
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
@@ -15,7 +12,7 @@ var app = express.createServer(
express.logger(),
// Required by session() middleware
express.cookieDecoder(),
express.cookieParser(),
// Populates:
// - req.session
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
-3
Ver Arquivo
@@ -1,7 +1,4 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
+6 -3
Ver Arquivo
@@ -11,7 +11,8 @@
var connect = require('connect')
, HTTPSServer = require('./https')
, HTTPServer = require('./http');
, HTTPServer = require('./http')
, Route = require('./router/route')
/**
* Re-export connect auto-loaders.
@@ -27,7 +28,7 @@ var exports = module.exports = connect.middleware;
* Framework version.
*/
exports.version = '2.2.2';
exports.version = '2.3.6';
/**
* Shortcut for `new Server(...)`.
@@ -51,12 +52,14 @@ exports.createServer = function(options){
exports.HTTPServer = HTTPServer;
exports.HTTPSServer = HTTPSServer;
exports.Route = Route;
/**
* View extensions.
*/
require('./view');
exports.View =
exports.view = require('./view');
/**
* Response extensions.
+64 -58
Ver Arquivo
@@ -11,12 +11,24 @@
var qs = require('qs')
, connect = require('connect')
, router = connect.router
, methods = router.methods.concat(['del', 'all'])
, router = require('./router')
, methods = router.methods.concat('del', 'all')
, view = require('./view')
, url = require('url')
, utils = connect.utils;
/**
* Expose `HTTPServer`.
*/
exports = module.exports = HTTPServer;
/**
* Server proto.
*/
var app = HTTPServer.prototype;
/**
* Initialize a new `HTTPServer` with optional `middleware`.
*
@@ -24,7 +36,7 @@ var qs = require('qs')
* @api public
*/
var Server = exports = module.exports = function HTTPServer(middleware){
function HTTPServer(middleware){
connect.HTTPServer.call(this, []);
this.init(middleware);
};
@@ -33,7 +45,7 @@ var Server = exports = module.exports = function HTTPServer(middleware){
* Inherit from `connect.HTTPServer`.
*/
Server.prototype.__proto__ = connect.HTTPServer.prototype;
app.__proto__ = connect.HTTPServer.prototype;
/**
* Initialize the server.
@@ -42,21 +54,17 @@ Server.prototype.__proto__ = connect.HTTPServer.prototype;
* @api private
*/
Server.prototype.init = function(middleware){
app.init = function(middleware){
var self = this;
this.match = {};
this.lookup = {};
this.cache = {};
this.settings = {};
this.redirects = {};
this.isCallbacks = {};
this.viewHelpers = {};
this._locals = {};
this.dynamicViewHelpers = {};
this.errorHandlers = [];
// default "home" to /
this.set('home', '/');
// set "env" to NODE_ENV, defaulting to "development"
this.set('env', process.env.NODE_ENV || 'development');
// expose objects to each other
@@ -79,7 +87,7 @@ Server.prototype.init = function(middleware){
if (middleware) middleware.forEach(self.use.bind(self));
// use router, expose as app.get(), etc
var fn = router(function(app){ self.routes = app; });
var fn = router(function(app){ self.routes = app; }, this);
this.__defineGetter__('router', function(){
this.__usedRouter = true;
return fn;
@@ -98,7 +106,7 @@ Server.prototype.init = function(middleware){
// default production configuration
this.configure('production', function(){
this.enable('cache views');
this.enable('view cache');
});
// register error handlers on "listening"
@@ -106,6 +114,18 @@ Server.prototype.init = function(middleware){
this.on('listening', this.registerErrorHandlers.bind(this));
// route lookup methods
this.remove = function(url){
return self.remove.all(url);
};
this.match = function(url){
return self.match.all(url);
};
this.lookup = function(url){
return self.lookup.all(url);
};
methods.forEach(function(method){
self.match[method] = function(url){
return self.router.match(url, 'all' == method
@@ -113,6 +133,12 @@ Server.prototype.init = function(middleware){
: method);
};
self.remove[method] = function(url){
return self.router.remove(url, 'all' == method
? null
: method);
};
self.lookup[method] = function(path){
return self.router.lookup(path, 'all' == method
? null
@@ -125,7 +151,7 @@ Server.prototype.init = function(middleware){
* When using the vhost() middleware register error handlers.
*/
Server.prototype.onvhost = function(){
app.onvhost = function(){
this.registerErrorHandlers();
};
@@ -136,7 +162,7 @@ Server.prototype.onvhost = function(){
* @api public
*/
Server.prototype.registerErrorHandlers = function(){
app.registerErrorHandlers = function(){
this.errorHandlers.forEach(function(fn){
this.use(function(err, req, res, next){
fn.apply(this, arguments);
@@ -155,7 +181,7 @@ Server.prototype.registerErrorHandlers = function(){
* @api public
*/
Server.prototype.use = function(route, middleware){
app.use = function(route, middleware){
var app, home, handle;
if ('string' != typeof route) {
@@ -213,7 +239,7 @@ Server.prototype.use = function(route, middleware){
* @api public
*/
Server.prototype.mounted = function(fn){
app.mounted = function(fn){
this.__mounted = fn;
return this;
};
@@ -225,7 +251,7 @@ Server.prototype.mounted = function(fn){
* @api public
*/
Server.prototype.register = function(){
app.register = function(){
view.register.apply(this, arguments);
return this;
};
@@ -239,9 +265,9 @@ Server.prototype.register = function(){
* @api public
*/
Server.prototype.helpers =
Server.prototype.locals = function(obj){
utils.merge(this.viewHelpers, obj);
app.helpers =
app.locals = function(obj){
utils.merge(this._locals, obj);
return this;
};
@@ -254,7 +280,7 @@ Server.prototype.locals = function(obj){
* @api public
*/
Server.prototype.dynamicHelpers = function(obj){
app.dynamicHelpers = function(obj){
utils.merge(this.dynamicViewHelpers, obj);
return this;
};
@@ -302,7 +328,7 @@ Server.prototype.dynamicHelpers = function(obj){
* @api public
*/
Server.prototype.param = function(name, fn){
app.param = function(name, fn){
if (Array.isArray(name)) {
name.forEach(function(name){
this.param(name, fn);
@@ -323,7 +349,7 @@ Server.prototype.param = function(name, fn){
* @api public
*/
Server.prototype.error = function(fn){
app.error = function(fn){
this.errorHandlers.push(fn);
return this;
};
@@ -337,7 +363,7 @@ Server.prototype.error = function(fn){
* @api public
*/
Server.prototype.is = function(type, fn){
app.is = function(type, fn){
if (!fn) return this.isCallbacks[type];
this.isCallbacks[type] = fn;
return this;
@@ -353,7 +379,7 @@ Server.prototype.is = function(type, fn){
* @api public
*/
Server.prototype.set = function(setting, val){
app.set = function(setting, val){
if (val === undefined) {
if (this.settings.hasOwnProperty(setting)) {
return this.settings[setting];
@@ -374,7 +400,7 @@ Server.prototype.set = function(setting, val){
* @api public
*/
Server.prototype.enabled = function(setting){
app.enabled = function(setting){
return !!this.set(setting);
};
@@ -386,7 +412,7 @@ Server.prototype.enabled = function(setting){
* @api public
*/
Server.prototype.disabled = function(setting){
app.disabled = function(setting){
return !this.set(setting);
};
@@ -398,7 +424,7 @@ Server.prototype.disabled = function(setting){
* @api public
*/
Server.prototype.enable = function(setting){
app.enable = function(setting){
return this.set(setting, true);
};
@@ -410,7 +436,7 @@ Server.prototype.enable = function(setting){
* @api public
*/
Server.prototype.disable = function(setting){
app.disable = function(setting){
return this.set(setting, false);
};
@@ -423,7 +449,7 @@ Server.prototype.disable = function(setting){
* @api public
*/
Server.prototype.redirect = function(key, url){
app.redirect = function(key, url){
this.redirects[key] = url;
return this;
};
@@ -437,7 +463,7 @@ Server.prototype.redirect = function(key, url){
* @api public
*/
Server.prototype.configure = function(env, fn){
app.configure = function(env, fn){
if ('function' == typeof env) {
fn = env, env = 'all';
}
@@ -449,8 +475,8 @@ Server.prototype.configure = function(env, fn){
// Generate routing methods
function generateMethod(method){
Server.prototype[method] = function(path, fn){
methods.forEach(function(method){
app[method] = function(path){
var self = this;
// Lookup
@@ -461,34 +487,14 @@ function generateMethod(method){
}
// Ensure router is mounted
if (!this.__usedRouter) {
this.use(this.router);
}
// Route specific middleware support
if (arguments.length > 2) {
var args = Array.prototype.slice.call(arguments, 1);
fn = args.pop();
(function stack(middleware){
middleware.forEach(function(fn){
if (Array.isArray(fn)) {
stack(fn);
} else {
self[method](path, fn);
}
});
})(args);
}
if (!this.__usedRouter) this.use(this.router);
// Generate the route
this.routes[method](path, fn);
this.routes[method].apply(this, arguments);
return this;
};
return arguments.callee;
}
methods.forEach(generateMethod);
});
// Alias delete as "del"
Server.prototype.del = Server.prototype.delete;
app.del = app.delete;
+15 -3
Ver Arquivo
@@ -13,6 +13,18 @@ var connect = require('connect')
, HTTPServer = require('./http')
, https = require('https');
/**
* Expose `HTTPSServer`.
*/
exports = module.exports = HTTPSServer;
/**
* Server proto.
*/
var app = HTTPSServer.prototype;
/**
* Initialize a new `HTTPSServer` with the
* given `options`, and optional `middleware`.
@@ -22,7 +34,7 @@ var connect = require('connect')
* @api public
*/
var Server = exports = module.exports = function HTTPSServer(options, middleware){
function HTTPSServer(options, middleware){
connect.HTTPSServer.call(this, options, []);
this.init(middleware);
};
@@ -31,10 +43,10 @@ var Server = exports = module.exports = function HTTPSServer(options, middleware
* Inherit from `connect.HTTPSServer`.
*/
Server.prototype.__proto__ = connect.HTTPSServer.prototype;
app.__proto__ = connect.HTTPSServer.prototype;
// mixin HTTPServer methods
Object.keys(HTTPServer.prototype).forEach(function(method){
Server.prototype[method] = HTTPServer.prototype[method];
app[method] = HTTPServer.prototype[method];
});
+2 -2
Ver Arquivo
@@ -197,7 +197,7 @@ req.flash = function(type, msg){
, args = arguments
, formatters = this.app.flashFormatters || {};
formatters.__proto__ = flashFormatters;
msg = utils.miniMarkdown(utils.htmlEscape(msg));
msg = utils.miniMarkdown(utils.escape(msg));
msg = msg.replace(/%([a-zA-Z])/g, function(_, format){
var formatter = formatters[format];
if (formatter) return formatter(args[i++]);
@@ -269,7 +269,7 @@ req.is = function(type){
if ('*' == type[0] && type[1] == contentType[1]) return true;
if ('*' == type[1] && type[0] == contentType[0]) return true;
}
return ~contentType.indexOf(type);
return !! ~contentType.indexOf(type);
};
// Callback for isXMLHttpRequest / xhr
+17 -8
Ver Arquivo
@@ -17,8 +17,9 @@ var fs = require('fs')
, parseRange = require('./utils').parseRange
, res = http.ServerResponse.prototype
, send = connect.static.send
, join = require('path').join
, mime = require('mime');
, mime = require('mime')
, basename = path.basename
, join = path.join;
/**
* Send a response with the given `body` and optional `headers` and `status` code.
@@ -75,10 +76,12 @@ res.send = function(body, headers, status){
}
} else {
if (!this.header('Content-Type')) {
this.charset = this.charset || 'utf-8';
this.contentType('.json');
}
body = JSON.stringify(body);
if (this.req.query.callback && this.app.set('jsonp callback')) {
this.charset = this.charset || 'utf-8';
this.header('Content-Type', 'text/javascript');
body = this.req.query.callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
}
@@ -139,7 +142,7 @@ res.sendfile = function(path, options, fn){
options = {};
}
options.path = path;
options.path = encodeURIComponent(path);
options.callback = fn;
send(this.req, this, next, options);
};
@@ -176,8 +179,9 @@ res.contentType = function(type){
*/
res.attachment = function(filename){
if (filename) this.contentType(filename);
this.header('Content-Disposition', filename
? 'attachment; filename="' + path.basename(filename) + '"'
? 'attachment; filename="' + basename(filename) + '"'
: 'attachment');
return this;
};
@@ -211,7 +215,7 @@ res.download = function(path, filename, fn, fn2){
if (err) {
if (!sentHeader) self.removeHeader('Content-Disposition');
if (sentHeader) {
if (fn2) fn2(err);
fn2 && fn2(err);
} else if (fn) {
fn(err);
} else {
@@ -245,11 +249,15 @@ res.header = function(name, val){
* Clear cookie `name`.
*
* @param {String} name
* @param {Object} options
* @api public
*/
res.clearCookie = function(name){
this.cookie(name, '', { expires: new Date(1) });
res.clearCookie = function(name, options){
var opts = { expires: new Date(1) };
this.cookie(name, '', options
? utils.merge(options, opts)
: opts);
};
/**
@@ -404,7 +412,8 @@ res.local = function(name, val){
* @api public
*/
res.locals = function(obj){
res.locals =
res.helpers = function(obj){
if (obj) {
for (var key in obj) {
this.local(key, obj[key]);
+331
Ver Arquivo
@@ -0,0 +1,331 @@
/*!
* Express - router
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Module dependencies.
*/
var utils = require('../utils')
, parse = require('url').parse
, _methods = require('./methods')
, Route = require('./route');
/**
* Expose router.
*/
exports = module.exports = router;
/**
* Expose methods.
*/
exports.methods = _methods;
/**
* Provides Sinatra-like routing capabilities.
*
* @param {Function} fn
* @return {Function}
* @api private
*/
function router(fn, app){
var self = this
, methods = {}
, routes = {}
, params = {};
if (!fn) throw new Error('router provider requires a callback function');
// Generate method functions
_methods.forEach(function(method){
methods[method] = generateMethodFunction(method.toUpperCase());
});
// Alias del -> delete
methods.del = methods.delete;
// Apply callback to all methods
methods.all = function(){
var args = arguments;
_methods.forEach(function(name){
methods[name].apply(this, args);
});
return self;
};
// Register param callback
methods.param = function(name, fn){
params[name] = fn;
};
fn.call(this, methods);
function generateMethodFunction(name) {
var localRoutes = routes[name] = routes[name] || [];
return function(path, fn){
var keys = []
, middleware = [];
// slice middleware
if (arguments.length > 2) {
middleware = Array.prototype.slice.call(arguments, 1, arguments.length);
fn = middleware.pop();
middleware = utils.flatten(middleware);
}
if (!path) throw new Error(name + ' route requires a path');
if (!fn) throw new Error(name + ' route ' + path + ' requires a callback');
var options = { sensitive: app.enabled('case sensitive routes') };
var route = new Route(name, path, fn, options);
route.middleware = middleware;
localRoutes.push(route);
return self;
};
}
function router(req, res, next){
var route
, self = this;
(function pass(i){
if (route = match(req, routes, i)) {
var i = 0
, keys = route.keys;
req.params = route.params;
// Param preconditions
(function param(err) {
try {
var key = keys[i++]
, val = req.params[key]
, fn = params[key];
if ('route' == err) {
pass(req._route_index + 1);
// Error
} else if (err) {
next(err);
// Param has callback
} else if (fn) {
// Return style
if (1 == fn.length) {
req.params[key] = fn(val);
param();
// Middleware style
} else {
fn(req, res, param, val);
}
// Finished processing params
} else if (!key) {
// route middleware
i = 0;
(function nextMiddleware(err){
var fn = route.middleware[i++];
if ('route' == err) {
pass(req._route_index + 1);
} else if (err) {
next(err);
} else if (fn) {
fn(req, res, nextMiddleware);
} else {
route.callback.call(self, req, res, function(err){
if (err) {
next(err);
} else {
pass(req._route_index + 1);
}
});
}
})();
// More params
} else {
param();
}
} catch (err) {
next(err);
}
})();
} else if ('OPTIONS' == req.method) {
options(req, res, routes);
} else {
next();
}
})();
};
router.remove = function(path, method, ret){
var ret = ret || []
, route;
// method specific remove
if (method) {
method = method.toUpperCase();
if (routes[method]) {
for (var i = 0; i < routes[method].length; ++i) {
route = routes[method][i];
if (path == route.path) {
route.index = i;
routes[method].splice(i, 1);
ret.push(route);
--i;
}
}
}
// global remove
} else {
_methods.forEach(function(method){
router.remove(path, method, ret);
});
}
return ret;
};
router.lookup = function(path, method, ret){
ret = ret || [];
// method specific lookup
if (method) {
method = method.toUpperCase();
if (routes[method]) {
routes[method].forEach(function(route, i){
if (path == route.path) {
route.index = i;
ret.push(route);
}
});
}
// global lookup
} else {
_methods.forEach(function(method){
router.lookup(path, method, ret);
});
}
return ret;
};
router.match = function(url, method, ret){
var ret = ret || []
, i = 0
, route
, req;
// method specific matches
if (method) {
method = method.toUpperCase();
req = { url: url, method: method };
while (route = match(req, routes, i)) {
i = req._route_index + 1;
route.index = i;
ret.push(route);
}
// global matches
} else {
_methods.forEach(function(method){
router.match(url, method, ret);
});
}
return ret;
};
router.routes = routes;
return router;
}
/**
* Respond to OPTIONS.
*
* @param {ServerRequest} req
* @param {ServerResponse} req
* @param {Array} routes
* @api private
*/
function options(req, res, routes) {
var pathname = parse(req.url).pathname
, body = optionsFor(pathname, routes).join(',');
res.send(body, { Allow: body });
}
/**
* Return OPTIONS array for the given `path`, matching `routes`.
*
* @param {String} path
* @param {Array} routes
* @return {Array}
* @api private
*/
function optionsFor(path, routes) {
return _methods.filter(function(method){
var arr = routes[method.toUpperCase()];
for (var i = 0, len = arr.length; i < len; ++i) {
if (arr[i].regexp.test(path)) return true;
}
}).map(function(method){
return method.toUpperCase();
});
}
/**
* Attempt to match the given request to
* one of the routes. When successful
* a route function is returned.
*
* @param {ServerRequest} req
* @param {Object} routes
* @return {Function}
* @api private
*/
function match(req, routes, i) {
var captures
, method = req.method
, i = i || 0;
// pass HEAD to GET routes
if ('HEAD' == method) method = 'GET';
// routes for this method
if (routes = routes[method]) {
var url = parse(req.url)
, pathname = url.pathname;
// matching routes
for (var len = routes.length; i < len; ++i) {
var route = routes[i]
, fn = route.callback
, path = route.regexp
, keys = route.keys;
// match
if (captures = path.exec(pathname)) {
route.params = [];
for (var j = 1, l = captures.length; j < l; ++j) {
var key = keys[j-1],
val = 'string' == typeof captures[j]
? decodeURIComponent(captures[j])
: captures[j];
if (key) {
route.params[key] = val;
} else {
route.params.push(val);
}
}
req._route_index = i;
return route;
}
}
}
}
+70
Ver Arquivo
@@ -0,0 +1,70 @@
/*!
* Express - router - methods
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Hypertext Transfer Protocol -- HTTP/1.1
* http://www.ietf.org/rfc/rfc2616.txt
*/
var RFC2616 = ['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'];
/**
* HTTP Extensions for Distributed Authoring -- WEBDAV
* http://www.ietf.org/rfc/rfc2518.txt
*/
var RFC2518 = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK'];
/**
* Versioning Extensions to WebDAV
* http://www.ietf.org/rfc/rfc3253.txt
*/
var RFC3253 = ['VERSION-CONTROL', 'REPORT', 'CHECKOUT', 'CHECKIN', 'UNCHECKOUT', 'MKWORKSPACE', 'UPDATE', 'LABEL', 'MERGE', 'BASELINE-CONTROL', 'MKACTIVITY'];
/**
* Ordered Collections Protocol (WebDAV)
* http://www.ietf.org/rfc/rfc3648.txt
*/
var RFC3648 = ['ORDERPATCH'];
/**
* Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
* http://www.ietf.org/rfc/rfc3744.txt
*/
var RFC3744 = ['ACL'];
/**
* Web Distributed Authoring and Versioning (WebDAV) SEARCH
* http://www.ietf.org/rfc/rfc5323.txt
*/
var RFC5323 = ['SEARCH'];
/**
* PATCH Method for HTTP
* http://www.ietf.org/rfc/rfc5789.txt
*/
var RFC5789 = ['PATCH'];
/**
* Expose the methods.
*/
module.exports = [].concat(
RFC2616
, RFC2518
, RFC3253
, RFC3648
, RFC3744
, RFC5323
, RFC5789).map(function(method){
return method.toLowerCase();
});
+71
Ver Arquivo
@@ -0,0 +1,71 @@
/*!
* Express - router - Route
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
* MIT Licensed
*/
/**
* Expose `Route`.
*/
module.exports = Route;
/**
* Initialize `Route` with the given HTTP `method`, `path`,
* and callback `fn` and `options`.
*
* Options:
*
* - `sensitive` enable case-sensitive routes
*
* @param {String} method
* @param {String} path
* @param {Function} fn
* @param {Object} options.
* @api private
*/
function Route(method, path, fn, options) {
options = options || {};
this.callback = fn;
this.path = path;
this.regexp = normalize(path, this.keys = [], options.sensitive);
this.method = method;
}
/**
* Normalize the given path string,
* returning a regular expression.
*
* An empty array should be passed,
* which will contain the placeholder
* key names. For example "/user/:id" will
* then contain ["id"].
*
* @param {String|RegExp} path
* @param {Array} keys
* @param {Boolean} sensitive
* @return {RegExp}
* @api private
*/
function normalize(path, keys, sensitive) {
if (path instanceof RegExp) return path;
path = path
.concat('/?')
.replace(/\/\(/g, '(?:/')
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
keys.push(key);
slash = slash || '';
return ''
+ (optional ? '' : slash)
+ '(?:'
+ (optional ? slash : '')
+ (format || '') + (capture || '([^/]+?)') + ')'
+ (optional || '');
})
.replace(/([\/.])/g, '\\$1')
.replace(/\*/g, '(.+)');
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
}
+22 -59
Ver Arquivo
@@ -5,64 +5,6 @@
* MIT Licensed
*/
/**
* Module dependencies.
*/
var path = require('path')
, basename = path.basename
, dirname = path.dirname
, extname = path.extname;
/**
* Memory cache.
*/
var cache = {
basename: {}
, dirname: {}
, extname: {}
};
/**
* Cached basename.
*
* @param {String} path
* @return {String}
* @api private
*/
exports.basename = function(path){
return cache.basename[path]
|| (cache.basename[path] = basename(path));
};
/**
* Cached dirname.
*
* @param {String} path
* @return {String}
* @api private
*/
exports.dirname = function(path){
return cache.dirname[path]
|| (cache.dirname[path] = dirname(path));
};
/**
* Cached extname.
*
* @param {String} path
* @return {String}
* @api private
*/
exports.extname = function(path){
return cache.extname[path]
|| (cache.extname[path] = extname(path));
};
/**
* Merge object `b` with `a` giving precedence to
* values in object `a`.
@@ -88,6 +30,27 @@ exports.union = function(a, b){
return a;
};
/**
* Flatten the given `arr`.
*
* @param {Array} arr
* @return {Array}
* @api private
*/
exports.flatten = function(arr, ret){
var ret = ret || []
, len = arr.length;
for (var i = 0; i < len; ++i) {
if (Array.isArray(arr[i])) {
exports.flatten(arr[i], ret);
} else {
ret.push(arr[i]);
}
}
return ret;
};
/**
* Parse mini markdown implementation.
* The following conversions are supported,
@@ -117,7 +80,7 @@ exports.miniMarkdown = function(str){
* @api private
*/
exports.htmlEscape = function(html) {
exports.escape = function(html) {
return String(html)
.replace(/&/g, '&amp;')
.replace(/"/g, '&quot;')
+103 -55
Ver Arquivo
@@ -21,14 +21,6 @@ var path = require('path')
, http = require('http')
, res = http.ServerResponse.prototype;
/**
* Memory cache.
*
* @type Object
*/
var cache = {};
/**
* Expose constructors.
*/
@@ -41,6 +33,91 @@ exports = module.exports = View;
exports.register = View.register;
/**
* Lookup and compile `view` with cache support by supplying
* both the `cache` object and `cid` string,
* followed by `options` passed to `exports.lookup()`.
*
* @param {String} view
* @param {Object} cache
* @param {Object} cid
* @param {Object} options
* @return {View}
* @api private
*/
exports.compile = function(view, cache, cid, options){
if (cache && cid && cache[cid]) return cache[cid];
// lookup
view = exports.lookup(view, options);
// hints
if (!view.exists) {
if (options.hint) hintAtViewPaths(view.original, options);
var err = new Error('failed to locate view "' + view.original.view + '"');
err.view = view.original;
throw err;
}
// compile
view.fn = view.templateEngine.compile(view.contents, options);
cache[cid] = view;
return view;
};
/**
* Lookup `view`, returning an instanceof `View`.
*
* Options:
*
* - `root` root directory path
* - `defaultEngine` default template engine
* - `parentView` parent `View` object
* - `cache` cache object
* - `cacheid` optional cache id
*
* Lookup:
*
* - partial `_<name>`
* - any `<name>/index`
* - non-layout `../<name>/index`
* - any `<root>/<name>`
* - partial `<root>/_<name>`
*
* @param {String} view
* @param {Object} options
* @return {View}
* @api private
*/
exports.lookup = function(view, options){
var orig = view = new View(view, options);
// Try _ prefix ex: ./views/_<name>.jade
if (partial) {
view = new View(orig.prefixPath, options);
if (!view.exists) view = orig;
}
// Try index ex: ./views/user/index.jade
if (!view.exists) view = new View(orig.indexPath, options);
// Try ../<name>/index ex: ../user/index.jade
// when calling partial('user') within the same dir
if (!view.exists && !options.isLayout) view = new View(orig.upIndexPath, options);
// Try root ex: <root>/user.jade
if (!view.exists) view = new View(orig.rootPath, options);
// Try root _ prefix ex: <root>/_user.jade
if (!view.exists && partial) view = new View(view.prefixPath, options);
view.original = orig;
return view;
};
/**
* Partial render helper.
*
@@ -179,6 +256,7 @@ function renderPartial(res, view, options, parentLocals, parent){
res.partial = function(view, options, fn){
var app = this.app
, options = options || {}
, viewEngine = app.set('view engine')
, parent = {};
// accept callback as second argument
@@ -191,9 +269,7 @@ res.partial = function(view, options, fn){
parent.dirname = app.set('views') || process.cwd() + '/views';
// utilize "view engine" option
if (app.set('view engine')) {
parent.extension = '.' + app.set('view engine');
}
if (viewEngine) parent.extension = '.' + viewEngine;
// render the partial
try {
@@ -261,12 +337,16 @@ res._render = function(view, opts, fn, parent, sub){
var options = {}
, self = this
, app = this.app
, helpers = app.viewHelpers
, helpers = app._locals
, dynamicHelpers = app.dynamicViewHelpers
, viewOptions = app.set('view options')
, cacheViews = app.set('cache views')
, root = app.set('views') || process.cwd() + '/views';
// cache id
var cid = app.enabled('view cache')
? view + (parent ? ':' + parent.path : '')
: false;
// merge "view options"
if (viewOptions) merge(options, viewOptions);
@@ -282,6 +362,9 @@ res._render = function(view, opts, fn, parent, sub){
// status support
if (options.status) this.statusCode = options.status;
// capture attempts
options.attempts = [];
var partial = options.renderPartial
, layout = options.layout;
@@ -330,43 +413,10 @@ res._render = function(view, opts, fn, parent, sub){
return renderPartial(self, path, opts, options, view);
};
// cached view
if (cache[view]) {
view = cache[view];
options.filename = view.path;
// resolve view
} else {
var orig = view = new View(view, options);
// Try _ prefix ex: ./views/_<name>.jade
if (!view.exists) view = new View(orig.prefixPath, options);
// Try index ex: ./views/user/index.jade
if (!view.exists) view = new View(orig.indexPath, options);
// Try ../<name>/index ex: ../user/index.jade
// when calling partial('user') within the same dir
if (!view.exists && !options.isLayout) view = new View(orig.upIndexPath, options);
// Try root ex: <root>/user.jade
if (!view.exists) view = new View(orig.rootPath, options);
// Try root _ prefix ex: <root>/_user.jade
if (!view.exists && partial) view = new View(view.prefixPath, options);
// Does not exist
if (!view.exists) {
if (app.enabled('hints')) hintAtViewPaths(orig, options);
var err = new Error('failed to locate view "' + orig.view + '"');
err.view = orig;
throw err;
}
options.filename = view.path;
var engine = view.templateEngine;
view.fn = engine.compile(view.contents, options)
if (cacheViews) cache[orig.view] = view;
}
// View lookup
options.hint = app.enabled('hints');
view = exports.compile(view, app.cache, cid, options);
options.filename = view.path;
// layout helper
options.layout = function(path){
@@ -405,10 +455,8 @@ res._render = function(view, opts, fn, parent, sub){
function hintAtViewPaths(view, options) {
console.error();
console.error('failed to locate view "' + view.view + '", tried:');
console.error(' - ' + new View(view.path, options).path);
console.error(' - ' + new View(view.prefixPath, options).path);
console.error(' - ' + new View(view.indexPath, options).path);
if (!options.isLayout) console.error(' - ' + new View(view.upIndexPath, options).path);
if (options.isLayout) console.error(' - ' + new View(view.rootPath, options).path);
options.attempts.forEach(function(path){
console.error(' - %s', path);
});
console.error();
}
+15 -5
Ver Arquivo
@@ -9,13 +9,19 @@
* Module dependencies.
*/
var utils = require('../utils')
, extname = utils.extname
, dirname = utils.dirname
, basename = utils.basename
var path = require('path')
, extname = path.extname
, dirname = path.dirname
, basename = path.basename
, fs = require('fs')
, stat = fs.statSync;
/**
* Expose `View`.
*/
exports = module.exports = View;
/**
* Require cache.
*/
@@ -30,7 +36,7 @@ var cache = {};
* @api private
*/
var View = exports = module.exports = function View(view, options) {
function View(view, options) {
options = options || {};
this.view = view;
this.root = options.root;
@@ -43,6 +49,10 @@ var View = exports = module.exports = function View(view, options) {
this.name = this.basename.replace(this.extension, '');
this.path = this.resolvePath();
this.dirname = dirname(this.path);
if (options.attempts) {
if (!~options.attempts.indexOf(this.path))
options.attempts.push(this.path);
}
};
/**
+14 -2
Ver Arquivo
@@ -1,7 +1,7 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "2.2.2",
"version": "2.3.6",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
@@ -10,10 +10,22 @@
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
],
"dependencies": {
"connect": ">= 1.3.0 < 2.0.0",
"connect": ">= 1.4.1 < 2.0.0",
"mime": ">= 0.0.1",
"qs": ">= 0.0.6"
},
"devDependencies": {
"connect-form": "0.2.1",
"ejs": "0.4.2",
"expresso": "0.7.2",
"hamljs": "0.5.1",
"jade": "0.11.0",
"stylus": "0.13.0",
"should": "0.2.1",
"express-messages": "0.0.2",
"node-markdown": ">= 0.0.1",
"connect-redis": ">= 0.0.1"
},
"keywords": ["framework", "sinatra", "web", "rest", "restful"],
"repository": "git://github.com/visionmedia/express",
"main": "index",
Submodule support/connect deleted from 4b5a36540b
Submodule support/ejs deleted from 499c4815a5
Submodule support/haml deleted from 55bb6fdc79
Submodule support/jade deleted from bdeb8b9fb4
Submodule support/mime deleted from ade33a43be
Submodule support/qs deleted from 2b9796e54e
Submodule support/should deleted from 607f8734e8
+20 -162
Ver Arquivo
@@ -6,7 +6,8 @@
var express = require('express')
, connect = require('connect')
, assert = require('assert')
, should = require('should');
, should = require('should')
, Route = express.Route;
module.exports = {
'test inheritance': function(){
@@ -17,6 +18,7 @@ module.exports = {
'test constructor exports': function(){
express.should.have.property('HTTPServer');
express.should.have.property('HTTPSServer');
express.should.have.property('Route');
},
'test connect middleware autoloaders': function(){
@@ -435,173 +437,29 @@ module.exports = {
);
},
'test route middleware': function(){
var app = express.createServer();
function allow(role) {
return function(req, res, next) {
// this is totally not real, dont use this :)
// for tests only
if (req.headers['x-role'] == role) {
next();
} else {
res.send(401);
}
}
}
function restrictAge(age) {
return function(req, res, next){
if (req.headers['x-age'] >= age) {
next();
} else {
res.send(403);
}
}
'test routes with same callback': function(){
function handle(req, res) {
res.send('got ' + req.string);
}
app.get('/xxx', allow('member'), restrictAge(18), function(req, res){
res.send(200);
});
app.get('/booze', [allow('member')], restrictAge(18), function(req, res){
res.send(200);
});
app.get('/tobi', [allow('member')], [[restrictAge(18)]], function(req, res){
res.send(200);
});
['xxx', 'booze', 'tobi'].forEach(function(thing){
assert.response(app,
{ url: '/' + thing },
{ body: 'Unauthorized', status: 401 });
assert.response(app,
{ url: '/' + thing, headers: { 'X-Role': 'member' }},
{ body: 'Forbidden', status: 403 });
assert.response(app,
{ url: '/' + thing, headers: { 'X-Role': 'member', 'X-Age': 18 }},
{ body: 'OK', status: 200 });
});
},
'test named capture groups': function(){
var app = express.createServer();
app.get('/user/:id([0-9]{2,10})', function(req, res){
res.send('user ' + req.params.id);
});
app.get('/', function(req, res, next){
req.string = '/';
next();
}, handle);
app.get('/another', function(req, res, next){
req.string = '/another';
next();
}, handle);
assert.response(app,
{ url: '/user/12' },
{ body: 'user 12' });
{ url: '/' },
{ body: 'got /' });
assert.response(app,
{ url: '/user/ab' },
{ body: 'Cannot GET /user/ab' });
},
'test .param()': function(){
var app = express.createServer();
var users = [
{ name: 'tj' }
, { name: 'tobi' }
, { name: 'loki' }
, { name: 'jane' }
, { name: 'bandit' }
];
function integer(n){ return parseInt(n, 10); };
app.param(['to', 'from'], integer);
app.param('user', function(req, res, next, id){
if (req.user = users[id]) {
next();
} else {
next(new Error('failed to find user'));
}
});
app.get('/user/:user', function(req, res, next){
res.send('user ' + req.user.name);
});
app.get('/users/:from-:to', function(req, res, next){
var names = users.slice(req.params.from, req.params.to).map(function(user){
return user.name;
});
res.send('users ' + names.join(', '));
});
assert.response(app,
{ url: '/user/0' },
{ body: 'user tj' });
assert.response(app,
{ url: '/user/1' },
{ body: 'user tobi' });
assert.response(app,
{ url: '/users/0-3' },
{ body: 'users tj, tobi, loki' });
},
'test OPTIONS': function(){
var app = express.createServer();
app.get('/', function(){});
app.get('/user/:id', function(){});
app.put('/user/:id', function(){});
assert.response(app,
{ url: '/', method: 'OPTIONS' },
{ headers: { Allow: 'GET' }});
assert.response(app,
{ url: '/user/12', method: 'OPTIONS' },
{ headers: { Allow: 'GET,PUT' }});
},
'test app.lookup': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/edit', function(){});
app.get('/user').should.have.length(1);
app.get('/user/:id').should.have.length(1);
app.get('/user/:id/:op?').should.have.length(1);
app.put('/user/:id').should.have.length(1);
app.get('/user/:id/edit').should.have.length(1);
app.get('/').should.have.be.empty;
app.all('/user/:id').should.have.length(2);
app.lookup.get('/user').should.have.length(1);
app.lookup.get('/user/:id').should.have.length(1);
app.lookup.get('/user/:id/:op?').should.have.length(1);
app.lookup.put('/user/:id').should.have.length(1);
app.lookup.get('/user/:id/edit').should.have.length(1);
app.lookup.get('/').should.have.be.empty;
app.lookup.all('/user/:id').should.have.length(2);
},
'test app.match': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/edit', function(){});
app.match.get('/user').should.have.length(1);
app.match.get('/user/12').should.have.length(2);
app.match.get('/user/12/:op?').should.have.length(1);
app.match.put('/user/100').should.have.length(1);
app.match.get('/user/5/edit').should.have.length(2);
app.match.get('/').should.have.be.empty;
app.match.all('/user/123').should.have.length(3);
{ url: '/another' },
{ body: 'got /another' });
}
};
+1
Ver Arquivo
@@ -0,0 +1 @@
p two
+1
Ver Arquivo
@@ -0,0 +1 @@
p one
+1
Ver Arquivo
@@ -0,0 +1 @@
hello
+26 -12
Ver Arquivo
@@ -15,7 +15,7 @@ module.exports = {
app.get('/html', function(req, res){
res.send('<p>test</p>', { 'Content-Language': 'en' });
});
app.get('/json', function(req, res){
res.header('X-Foo', 'bar');
res.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
@@ -38,6 +38,10 @@ module.exports = {
res.send(404);
});
app.get('/status/text', function(req, res){
res.send('Oh noes!', 404);
});
app.get('/error', function(req, res){
res.send('Oh shit!', { 'Content-Type': 'text/plain' }, 500);
});
@@ -61,7 +65,7 @@ module.exports = {
assert.response(app,
{ url: '/bool' },
{ body: 'true'
, headers: { 'Content-Type': 'application/json' }});
, headers: { 'Content-Type': 'application/json; charset=utf-8' }});
assert.response(app,
{ url: '/html' },
@@ -76,7 +80,7 @@ module.exports = {
{ body: '{"foo":"bar"}'
, status: 201
, headers: {
'Content-Type': 'application/json'
'Content-Type': 'application/json; charset=utf-8'
, 'X-Foo': 'baz'
}});
@@ -85,7 +89,7 @@ module.exports = {
{ body: 'test({"foo":"bar"});'
, status: 201
, headers: {
'Content-Type': 'text/javascript'
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
@@ -93,7 +97,7 @@ module.exports = {
{ url: '/jsonp?callback=baz' },
{ body: 'baz({"foo":"bar"});'
, status: 201, headers: {
'Content-Type': 'text/javascript'
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
@@ -102,7 +106,7 @@ module.exports = {
{ body: 'invalid({"foo":"bar"});'
, status: 201
, headers: {
'Content-Type': 'text/javascript'
'Content-Type': 'text/javascript; charset=utf-8'
, 'X-Foo': 'baz'
}});
@@ -111,7 +115,7 @@ module.exports = {
{ body: '{"foo":"bar"}'
, status: 201
, headers: {
'Content-Type': 'application/json'
'Content-Type': 'application/json; charset=utf-8'
, 'X-Foo': 'baz'
}});
@@ -122,7 +126,11 @@ module.exports = {
'Content-Type': 'text/plain'
, 'X-Foo': 'bar'
}});
assert.response(app,
{ url: '/status/text' },
{ body: 'Oh noes!', status: 404 });
assert.response(app,
{ url: '/status' },
{ body: 'Not Found'
@@ -207,12 +215,14 @@ module.exports = {
assert.response(app,
{ url: '/javascripts/jquery.js' },
{ body: 'whatever'
, headers: { 'Content-Disposition': 'attachment; filename="jquery.js"' }});
, headers: { 'Content-Type': 'application/javascript'
, 'Content-Disposition': 'attachment; filename="jquery.js"' }});
assert.response(app,
{ url: '/style.css' },
{ body: 'some stylezzz'
, headers: { 'Content-Disposition': 'attachment' }});
, headers: { 'Content-Type': 'text/html; charset=utf-8'
, 'Content-Disposition': 'attachment' }});
},
'test #redirect()': function(){
@@ -542,6 +552,10 @@ module.exports = {
assert.equal(null, res.headers['content-disposition']);
});
assert.response(app,
{ url: '/some%20random%20text%20file.txt' },
{ body: 'hello' });
beforeExit(function(){
calls.should.equal(1);
});
@@ -568,7 +582,7 @@ module.exports = {
var app = express.createServer();
app.get('/', function(req, res){
res.clearCookie('rememberme');
res.clearCookie('rememberme', { path: '/foo' });
res.redirect('/');
});
@@ -576,7 +590,7 @@ module.exports = {
{ url: '/' },
function(res){
res.headers['set-cookie']
.should.eql(['rememberme=; expires=Thu, 01 Jan 1970 00:00:00 GMT']);
.should.eql(['rememberme=; path=/foo; expires=Thu, 01 Jan 1970 00:00:00 GMT']);
});
},
+267
Ver Arquivo
@@ -0,0 +1,267 @@
/**
* Module dependencies.
*/
var express = require('express')
, connect = require('connect')
, assert = require('assert')
, should = require('should')
, Route = express.Route;
module.exports = {
'test route middleware': function(beforeExit){
var app = express.createServer()
, calls = 0;
function allow(role) {
return function(req, res, next) {
// this is totally not real, dont use this :)
// for tests only
if (req.headers['x-role'] == role) {
next();
} else {
res.send(401);
}
}
}
function restrictAge(age) {
return function(req, res, next){
if (req.headers['x-age'] >= age) {
next();
} else {
res.send(403);
}
}
}
app.param('user', function(req, res, next, user){
++calls;
next();
});
app.get('/xxx', allow('member'), restrictAge(18), function(req, res){
res.send(200);
});
app.get('/booze', [allow('member')], restrictAge(18), function(req, res){
res.send(200);
});
app.get('/tobi', [allow('member')], [[restrictAge(18)]], function(req, res){
res.send(200);
});
app.get('/user/:user', [allow('member'), [[restrictAge(18)]]], function(req, res){
res.send(200);
});
['xxx', 'booze', 'tobi', 'user/tj'].forEach(function(thing){
assert.response(app,
{ url: '/' + thing },
{ body: 'Unauthorized', status: 401 });
assert.response(app,
{ url: '/' + thing, headers: { 'X-Role': 'member' }},
{ body: 'Forbidden', status: 403 });
assert.response(app,
{ url: '/' + thing, headers: { 'X-Role': 'member', 'X-Age': 18 }},
{ body: 'OK', status: 200 });
});
beforeExit(function(){
calls.should.equal(3);
});
},
'test named capture groups': function(){
var app = express.createServer();
app.get('/user/:id([0-9]{2,10})', function(req, res){
res.send('user ' + req.params.id);
});
assert.response(app,
{ url: '/user/12' },
{ body: 'user 12' });
assert.response(app,
{ url: '/user/ab' },
{ body: 'Cannot GET /user/ab' });
},
'test .param()': function(){
var app = express.createServer();
var users = [
{ name: 'tj' }
, { name: 'tobi' }
, { name: 'loki' }
, { name: 'jane' }
, { name: 'bandit' }
];
function integer(n){ return parseInt(n, 10); };
app.param(['to', 'from'], integer);
app.param('user', function(req, res, next, id){
if (req.user = users[id]) {
next();
} else {
next(new Error('failed to find user'));
}
});
app.get('/user/:user', function(req, res, next){
res.send('user ' + req.user.name);
});
app.get('/users/:from-:to', function(req, res, next){
var names = users.slice(req.params.from, req.params.to).map(function(user){
return user.name;
});
res.send('users ' + names.join(', '));
});
assert.response(app,
{ url: '/user/0' },
{ body: 'user tj' });
assert.response(app,
{ url: '/user/1' },
{ body: 'user tobi' });
assert.response(app,
{ url: '/users/0-3' },
{ body: 'users tj, tobi, loki' });
},
'test OPTIONS': function(){
var app = express.createServer();
app.get('/', function(){});
app.get('/user/:id', function(){});
app.put('/user/:id', function(){});
assert.response(app,
{ url: '/', method: 'OPTIONS' },
{ headers: { Allow: 'GET' }});
assert.response(app,
{ url: '/user/12', method: 'OPTIONS' },
{ headers: { Allow: 'GET,PUT' }});
},
'test app.lookup': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/edit', function(){});
var route = app.get('/user/:id')[0]
route.should.be.an.instanceof(Route);
route.callback.should.be.a('function');
route.path.should.equal('/user/:id');
route.regexp.should.be.an.instanceof(RegExp);
route.method.should.equal('GET');
route.index.should.equal(1);
route.keys.should.eql(['id']);
app.get('/user').should.have.length(1);
app.get('/user/:id').should.have.length(1);
app.get('/user/:id/:op?').should.have.length(1);
app.put('/user/:id').should.have.length(1);
app.get('/user/:id/edit').should.have.length(1);
app.get('/').should.have.be.empty;
app.all('/user/:id').should.have.length(2);
app.lookup.get('/user').should.have.length(1);
app.lookup.get('/user/:id').should.have.length(1);
app.lookup.get('/user/:id/:op?').should.have.length(1);
app.lookup.put('/user/:id').should.have.length(1);
app.lookup.get('/user/:id/edit').should.have.length(1);
app.lookup.get('/').should.have.be.empty;
app.lookup.all('/user/:id').should.have.length(2);
app.lookup('/user/:id').should.have.length(2);
},
'test app.remove': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user', function(){});
app.put('/user', function(){});
app.get('/user').should.have.length(2);
var removed = app.remove.get('/user');
removed.should.have.length(2);
var removed = app.remove.get('/user');
removed.should.have.length(0);
app.get('/user').should.have.length(0);
app.get('/user/:id', function(){});
app.put('/user/:id', function(){});
app.del('/user/:id', function(){});
app.remove.all('/user/:id').should.have.length(3);
app.remove.all('/user/:id').should.have.length(0);
app.get('/user/:id', function(){});
app.put('/user/:id', function(){});
app.del('/user/:id', function(){});
app.remove('/user/:id').should.have.length(3);
},
'test app.match': function(){
var app = express.createServer();
app.get('/user', function(){});
app.get('/user/:id', function(){});
app.get('/user/:id/:op?', function(){});
app.put('/user/:id', function(){});
app.get('/user/:id/edit', function(){});
var route = app.match.get('/user/12')[0];
route.should.be.an.instanceof(Route);
route.callback.should.be.a('function');
route.path.should.equal('/user/:id');
route.regexp.should.be.an.instanceof(RegExp);
route.method.should.equal('GET');
route.index.should.equal(2);
route.keys.should.eql(['id']);
route.params.id.should.equal('12');
app.match.get('/user').should.have.length(1);
app.match.get('/user/12').should.have.length(2);
app.match.get('/user/12/:op?').should.have.length(1);
app.match.put('/user/100').should.have.length(1);
app.match.get('/user/5/edit').should.have.length(2);
app.match.get('/').should.have.be.empty;
app.match.all('/user/123').should.have.length(3);
app.match('/user/123').should.have.length(3);
},
'test "case sensitive routes" setting': function(){
var app = express.createServer();
app.enable('case sensitive routes');
app.get('/account', function(req, res){
res.send('account');
});
app.get('/Account', function(req, res){
res.send('Account');
});
assert.response(app,
{ url: '/account' },
{ body: 'account' });
assert.response(app,
{ url: '/Account' },
{ body: 'Account' });
}
};
+9
Ver Arquivo
@@ -105,6 +105,7 @@ module.exports = {
'test #render()': function(){
var app = create();
app.set('view engine', 'jade');
app.register('haml', require('hamljs'));
app.get('/', function(req, res){
res.render('index.jade', { layout: false });
@@ -657,6 +658,14 @@ module.exports = {
assert.response(app,
{ url: '/error' },
{ status: 500 });
app.get('/underscore', function(req, res, next){
res.partial('foobar');
});
assert.response(app,
{ url: '/underscore' },
{ body: '<p>two</p>' });
},
'test #partial() with several calls': function(){