mirror of
https://github.com/expressjs/express.git
synced 2026-02-26 08:45:36 +00:00
Compare commits
416 Commits
2.0.0beta2
...
2.4.7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c04f85f93 | ||
|
|
77f885d4a0 | ||
|
|
c2fc9a83e8 | ||
|
|
d8b20bd2d5 | ||
|
|
d3c4fd91c9 | ||
|
|
00e44f6fc9 | ||
|
|
6692f911ab | ||
|
|
4f0b3f4684 | ||
|
|
cbf330c3db | ||
|
|
13010987b0 | ||
|
|
b078481cdb | ||
|
|
3888468a96 | ||
|
|
a6b70ceca4 | ||
|
|
45757a0f1a | ||
|
|
24a4a80ccd | ||
|
|
2fdd906a41 | ||
|
|
71cc6bac22 | ||
|
|
3a46660932 | ||
|
|
adca07a858 | ||
|
|
6b924bf1df | ||
|
|
eb88f160b6 | ||
|
|
01e6df759e | ||
|
|
1dc4e6fcb4 | ||
|
|
fff6951d94 | ||
|
|
1ebd49af75 | ||
|
|
8c2107e337 | ||
|
|
cddc5442f1 | ||
|
|
ac8cb270fe | ||
|
|
d54ee58f93 | ||
|
|
ce0fa0a3b2 | ||
|
|
e2f41147ed | ||
|
|
af3f7f0a65 | ||
|
|
a37003945b | ||
|
|
75361fb177 | ||
|
|
21f9b386db | ||
|
|
271cb16ecd | ||
|
|
803ec213d7 | ||
|
|
9e9042f1eb | ||
|
|
ec4c86f46d | ||
|
|
45f22ec602 | ||
|
|
0e1eb72058 | ||
|
|
4690b6cdf2 | ||
|
|
22204a5ce1 | ||
|
|
1ec16c0450 | ||
|
|
c72abc5293 | ||
|
|
93189ad0b6 | ||
|
|
c0aab36187 | ||
|
|
5ae994ee8f | ||
|
|
60d16eab77 | ||
|
|
fc60dfc1a6 | ||
|
|
45d149c146 | ||
|
|
4dfc1a69c3 | ||
|
|
0ae18bca60 | ||
|
|
2aaf0defe7 | ||
|
|
73ea5cd7ee | ||
|
|
e7f2d229ec | ||
|
|
87bc265817 | ||
|
|
796aaff295 | ||
|
|
8f87c50320 | ||
|
|
ee4471b345 | ||
|
|
6815feb8cf | ||
|
|
dbbe7be891 | ||
|
|
09c9452e5c | ||
|
|
b0e669ba00 | ||
|
|
f46ae9f3b2 | ||
|
|
9f2b344be8 | ||
|
|
6b47271679 | ||
|
|
6f7075be74 | ||
|
|
4b9cc3d698 | ||
|
|
3faa790b53 | ||
|
|
9477c9b516 | ||
|
|
b04be51848 | ||
|
|
9e4020efd3 | ||
|
|
6db19db665 | ||
|
|
1386f80ae5 | ||
|
|
e4342a7097 | ||
|
|
fda31b75f9 | ||
|
|
8ca0a45b33 | ||
|
|
ce2bcaef68 | ||
|
|
0db7f26ad3 | ||
|
|
35370da458 | ||
|
|
fe6c5832c2 | ||
|
|
e8c32df79c | ||
|
|
652e166462 | ||
|
|
af6385f8e4 | ||
|
|
f0277d3777 | ||
|
|
6bb100d7fa | ||
|
|
f13ea34de3 | ||
|
|
48a14a443a | ||
|
|
1820ea6f59 | ||
|
|
4d9647923e | ||
|
|
943e9b3a28 | ||
|
|
6b2ec50a0b | ||
|
|
7b813b95b6 | ||
|
|
cdaa2e78d7 | ||
|
|
add53d3222 | ||
|
|
f4f79d2217 | ||
|
|
aa36bc4516 | ||
|
|
9028cacfd1 | ||
|
|
40ccb595cd | ||
|
|
5606d08ecb | ||
|
|
1888d6fca1 | ||
|
|
5d16e6b302 | ||
|
|
96f7574bc1 | ||
|
|
490584c8bc | ||
|
|
0cbb1f661c | ||
|
|
3dc53e105a | ||
|
|
e2cdd760d8 | ||
|
|
4169202a41 | ||
|
|
835982c561 | ||
|
|
b67bacea18 | ||
|
|
3205ee7d75 | ||
|
|
ff7d5ff4e5 | ||
|
|
723774af27 | ||
|
|
c3fbd3fe10 | ||
|
|
d1d3871550 | ||
|
|
5462c8c7ec | ||
|
|
9536341e30 | ||
|
|
1bb798d963 | ||
|
|
91997e9c53 | ||
|
|
1393187040 | ||
|
|
6e69c880d9 | ||
|
|
59dcd03972 | ||
|
|
11482546a2 | ||
|
|
1ce43dd347 | ||
|
|
d1bfe137d4 | ||
|
|
9d7452cdc2 | ||
|
|
d9cee90efc | ||
|
|
175aa08500 | ||
|
|
c9ff6198d3 | ||
|
|
f026218c82 | ||
|
|
5bc86b9e29 | ||
|
|
5830ac9936 | ||
|
|
d7c6c9a9f9 | ||
|
|
9c87eed60e | ||
|
|
f15eb6d5ef | ||
|
|
5b33788359 | ||
|
|
11ec3ccd48 | ||
|
|
9d498ba3f1 | ||
|
|
15d4047180 | ||
|
|
44eae73843 | ||
|
|
d5b1c70731 | ||
|
|
e78dc18cfd | ||
|
|
4d122923e9 | ||
|
|
b1a7310263 | ||
|
|
d6ef90d98d | ||
|
|
85df59ac31 | ||
|
|
b789a28581 | ||
|
|
4068e7f444 | ||
|
|
80e9ffbf5d | ||
|
|
610fc92ca3 | ||
|
|
0f5dc9bdb2 | ||
|
|
c24b2faec5 | ||
|
|
c407f58dc2 | ||
|
|
380da0e202 | ||
|
|
9e5e7a1526 | ||
|
|
9016b5aaae | ||
|
|
45f168e873 | ||
|
|
799938683d | ||
|
|
7128f2d11f | ||
|
|
0634bf0b0d | ||
|
|
f9e48c2972 | ||
|
|
909960c0b3 | ||
|
|
8a7876f4d1 | ||
|
|
286c92b13b | ||
|
|
b9872a278f | ||
|
|
1b34fd7efa | ||
|
|
f05c351762 | ||
|
|
8323f19e96 | ||
|
|
565eda9ee5 | ||
|
|
7aea7194d1 | ||
|
|
2f68957c8c | ||
|
|
4ca848e526 | ||
|
|
31a8c7c19c | ||
|
|
f1c435e050 | ||
|
|
fac75a9bff | ||
|
|
4fe03ab223 | ||
|
|
c6122da59b | ||
|
|
131f658779 | ||
|
|
127f77964e | ||
|
|
9f2bd30dc7 | ||
|
|
6e633b31b4 | ||
|
|
1c65643488 | ||
|
|
388ad9067a | ||
|
|
f470f0bdc5 | ||
|
|
72384b0523 | ||
|
|
1b199b7d98 | ||
|
|
09b384ea44 | ||
|
|
56ae55f987 | ||
|
|
1c360a89ba | ||
|
|
8636dee13e | ||
|
|
70e6baf6fc | ||
|
|
3588c1eedc | ||
|
|
8d6f167a81 | ||
|
|
6106188347 | ||
|
|
eeb77541cd | ||
|
|
99b244b47c | ||
|
|
3043672448 | ||
|
|
0477a53c9f | ||
|
|
d9aa7c3bc9 | ||
|
|
986fac583b | ||
|
|
c6d76086e2 | ||
|
|
e2771364eb | ||
|
|
0d5a63798b | ||
|
|
7d15e2bf52 | ||
|
|
31fef407b6 | ||
|
|
6bef3ef891 | ||
|
|
b806846049 | ||
|
|
bc16020976 | ||
|
|
8afb905a43 | ||
|
|
53667728a8 | ||
|
|
5f0a854e29 | ||
|
|
e9ef3dd9cd | ||
|
|
f702884704 | ||
|
|
0cb866845d | ||
|
|
26483029db | ||
|
|
d2adcbdf67 | ||
|
|
d2f963db2a | ||
|
|
fc2bc1362f | ||
|
|
6ae45d0fd3 | ||
|
|
cc185a8c0e | ||
|
|
ae1078944c | ||
|
|
385a05dd10 | ||
|
|
2572a78648 | ||
|
|
470774cfba | ||
|
|
351f6abe4c | ||
|
|
53a16e1795 | ||
|
|
ff77c8b205 | ||
|
|
b33f38b109 | ||
|
|
b9596d7ce8 | ||
|
|
251175c025 | ||
|
|
fda1bc4630 | ||
|
|
83c2c176a9 | ||
|
|
9be5992f22 | ||
|
|
d8d23c0bf8 | ||
|
|
b2689fc40e | ||
|
|
a4cfde350f | ||
|
|
7374027457 | ||
|
|
63328c2177 | ||
|
|
c4e2ce23e5 | ||
|
|
dacad53b2e | ||
|
|
4ffd5280a7 | ||
|
|
74310fb464 | ||
|
|
8b2268cf38 | ||
|
|
fb655f4981 | ||
|
|
7208c33d72 | ||
|
|
4efb25d048 | ||
|
|
a3678cd7f6 | ||
|
|
393d38f1ab | ||
|
|
805b9ac3a9 | ||
|
|
379b9812be | ||
|
|
a9992b5647 | ||
|
|
b6c0a9b1b5 | ||
|
|
1d2dd2a375 | ||
|
|
63db694aa2 | ||
|
|
b6aca36ad9 | ||
|
|
8420ae93fd | ||
|
|
6722716fa7 | ||
|
|
658e064220 | ||
|
|
66b472e567 | ||
|
|
3d242a607e | ||
|
|
bc68e5e7a3 | ||
|
|
aa6858189c | ||
|
|
06fda62c9e | ||
|
|
5688ea650d | ||
|
|
f5c4e9d612 | ||
|
|
b9eda2a59d | ||
|
|
8c168b0971 | ||
|
|
9c20a50ee2 | ||
|
|
eac666574e | ||
|
|
e4c840f6b8 | ||
|
|
3afbcd0acf | ||
|
|
8f054dbcaf | ||
|
|
ccc39e5aa2 | ||
|
|
53d4da2a9c | ||
|
|
d9e7153fc9 | ||
|
|
dc02b0d5ae | ||
|
|
e0bc5711b8 | ||
|
|
957cf45fa1 | ||
|
|
f4487343df | ||
|
|
ca1bdb31e3 | ||
|
|
236a412459 | ||
|
|
759a57bdb6 | ||
|
|
1abb674a07 | ||
|
|
961146a287 | ||
|
|
573f940985 | ||
|
|
882916bb2e | ||
|
|
47d1c62732 | ||
|
|
d39293c025 | ||
|
|
2942dafdfd | ||
|
|
4e1aefa5b5 | ||
|
|
89383ecc57 | ||
|
|
08046f7692 | ||
|
|
885fb1fa92 | ||
|
|
6a58c71528 | ||
|
|
371d66ba2a | ||
|
|
25bddf3fb5 | ||
|
|
f6e9fb13f8 | ||
|
|
f0df8434e7 | ||
|
|
e4d3f239e5 | ||
|
|
bcc22dfa6f | ||
|
|
f614709a01 | ||
|
|
11250d22c9 | ||
|
|
4b4de29725 | ||
|
|
99b49d2718 | ||
|
|
6230ec55be | ||
|
|
0733d3c585 | ||
|
|
6f8370ff0e | ||
|
|
bc244ed07e | ||
|
|
41266aa8e4 | ||
|
|
45faee3e5b | ||
|
|
74ff735e10 | ||
|
|
97879f2b16 | ||
|
|
1a338251ad | ||
|
|
354dc768c1 | ||
|
|
8a0796cd94 | ||
|
|
cbc3b26584 | ||
|
|
d628583db8 | ||
|
|
058777be1e | ||
|
|
260d03a0c4 | ||
|
|
6dcf6f41cc | ||
|
|
799f790886 | ||
|
|
cb3c4b0ea9 | ||
|
|
798d255ba6 | ||
|
|
28ba9e8ac5 | ||
|
|
7888cb0506 | ||
|
|
5e284a20cc | ||
|
|
770357e727 | ||
|
|
673ba22555 | ||
|
|
fb38d9cfb7 | ||
|
|
dda89a57ec | ||
|
|
62df63d3a0 | ||
|
|
e71696cf34 | ||
|
|
b5d8d58670 | ||
|
|
14e6a667f5 | ||
|
|
0c324783ae | ||
|
|
5d6ce251ca | ||
|
|
92c04cee1d | ||
|
|
7fdf587a7b | ||
|
|
1e46218b09 | ||
|
|
c56fcd8fb9 | ||
|
|
319fbf7f64 | ||
|
|
bf06d9077c | ||
|
|
9d2bd29ee1 | ||
|
|
d11fa1f74e | ||
|
|
c824da0dab | ||
|
|
9362c83a33 | ||
|
|
0c38098f02 | ||
|
|
be7068f569 | ||
|
|
b122bf22e3 | ||
|
|
b7232f38f3 | ||
|
|
c1b72ac1b7 | ||
|
|
b9e0d15878 | ||
|
|
cf26cf7afc | ||
|
|
a75e60ae47 | ||
|
|
187dc5dd03 | ||
|
|
9c9e2afade | ||
|
|
fae1ba98c1 | ||
|
|
c3e632620a | ||
|
|
dd7158ac46 | ||
|
|
eefe51c7a7 | ||
|
|
bf596dc023 | ||
|
|
220d88d654 | ||
|
|
a254e64bdb | ||
|
|
1555b92fb8 | ||
|
|
d5b7a40b39 | ||
|
|
bd1ab7ab96 | ||
|
|
2ff991bfcf | ||
|
|
0b1378a539 | ||
|
|
723c908bd7 | ||
|
|
4874404701 | ||
|
|
4c1374840a | ||
|
|
5da01633fd | ||
|
|
cdbd8af527 | ||
|
|
a6fdc1bfd2 | ||
|
|
20b8facb05 | ||
|
|
909914f7af | ||
|
|
3f31ebc676 | ||
|
|
f3c068a90c | ||
|
|
90d7e193d1 | ||
|
|
c9f5bb6f17 | ||
|
|
9865a4c4f2 | ||
|
|
f12baf32d4 | ||
|
|
80f4d08e8b | ||
|
|
07c9cae923 | ||
|
|
d867cc9271 | ||
|
|
3a1fe1e295 | ||
|
|
3cacf050df | ||
|
|
1536d73d1b | ||
|
|
55143a9d44 | ||
|
|
c92e193916 | ||
|
|
d12452fc49 | ||
|
|
3d8400a40c | ||
|
|
1fe0aea0b0 | ||
|
|
5df2544883 | ||
|
|
acbf224277 | ||
|
|
d0d17a0d35 | ||
|
|
f7b53d33bc | ||
|
|
92be06874b | ||
|
|
f327455d9d | ||
|
|
54415bf2af | ||
|
|
bac62dfcd9 | ||
|
|
820b43c1f3 | ||
|
|
a5b69290d5 | ||
|
|
5c7a9c86f6 | ||
|
|
7f83f916f6 | ||
|
|
c15a949cc3 | ||
|
|
ce47f96570 | ||
|
|
57b035cd94 | ||
|
|
f1f126171c | ||
|
|
71e1bcd855 | ||
|
|
b5579b6307 | ||
|
|
110b0fe14a | ||
|
|
d7488bbb62 | ||
|
|
2007407e7b | ||
|
|
d152e7e780 |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,5 +6,8 @@ lib-cov
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
*.swp
|
||||
*.swo
|
||||
benchmarks/graphs
|
||||
testing.js
|
||||
node_modules/
|
||||
|
||||
30
.gitmodules
vendored
30
.gitmodules
vendored
@@ -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
|
||||
|
||||
260
History.md
260
History.md
@@ -1,4 +1,264 @@
|
||||
|
||||
2.4.7 / 2011-10-05
|
||||
==================
|
||||
|
||||
* Added mkdirp to express(1). Closes #795
|
||||
* Added simple _json-config_ example
|
||||
* Added shorthand for the parsed request's pathname via `req.path`
|
||||
* Changed connect dep to 1.7.x to fix npm issue...
|
||||
* Fixed `res.redirect()` __HEAD__ support. [reported by xerox]
|
||||
* Fixed `req.flash()`, only escape args
|
||||
* Fixed absolute path checking on windows. Closes #829 [reported by andrewpmckenzie]
|
||||
|
||||
2.4.6 / 2011-08-22
|
||||
==================
|
||||
|
||||
* Fixed multiple param callback regression. Closes #824 [reported by TroyGoode]
|
||||
|
||||
2.4.5 / 2011-08-19
|
||||
==================
|
||||
|
||||
* Added support for routes to handle errors. Closes #809
|
||||
* Added `app.routes.all()`. Closes #803
|
||||
* Added "basepath" setting to work in conjunction with reverse proxies etc.
|
||||
* Refactored `Route` to use a single array of callbacks
|
||||
* Added support for multiple callbacks for `app.param()`. Closes #801
|
||||
Closes #805
|
||||
* Changed: removed .call(self) for route callbacks
|
||||
* Dependency: `qs >= 0.3.1`
|
||||
* Fixed `res.redirect()` on windows due to `join()` usage. Closes #808
|
||||
|
||||
2.4.4 / 2011-08-05
|
||||
==================
|
||||
|
||||
* Fixed `res.header()` intention of a set, even when `undefined`
|
||||
* Fixed `*`, value no longer required
|
||||
* Fixed `res.send(204)` support. Closes #771
|
||||
|
||||
2.4.3 / 2011-07-14
|
||||
==================
|
||||
|
||||
* Added docs for `status` option special-case. Closes #739
|
||||
* Fixed `options.filename`, exposing the view path to template engines
|
||||
|
||||
2.4.2. / 2011-07-06
|
||||
==================
|
||||
|
||||
* Revert "removed jsonp stripping" for XSS
|
||||
|
||||
2.4.1 / 2011-07-06
|
||||
==================
|
||||
|
||||
* Added `res.json()` JSONP support. Closes #737
|
||||
* Added _extending-templates_ example. Closes #730
|
||||
* Added "strict routing" setting for trailing slashes
|
||||
* Added support for multiple envs in `app.configure()` calls. Closes #735
|
||||
* Changed: `res.send()` using `res.json()`
|
||||
* Changed: when cookie `path === null` don't default it
|
||||
* Changed; default cookie path to "home" setting. Closes #731
|
||||
* Removed _pids/logs_ creation from express(1)
|
||||
|
||||
2.4.0 / 2011-06-28
|
||||
==================
|
||||
|
||||
* Added chainable `res.status(code)`
|
||||
* Added `res.json()`, an explicit version of `res.send(obj)`
|
||||
* Added simple web-service example
|
||||
|
||||
2.3.12 / 2011-06-22
|
||||
==================
|
||||
|
||||
* \#express is now on freenode! come join!
|
||||
* Added `req.get(field, param)`
|
||||
* Added links to Japanese documentation, thanks @hideyukisaito!
|
||||
* Added; the `express(1)` generated app outputs the env
|
||||
* Added `content-negotiation` example
|
||||
* Dependency: connect >= 1.5.1 < 2.0.0
|
||||
* Fixed view layout bug. Closes #720
|
||||
* Fixed; ignore body on 304. Closes #701
|
||||
|
||||
2.3.11 / 2011-06-04
|
||||
==================
|
||||
|
||||
* Added `npm test`
|
||||
* Removed generation of dummy test file from `express(1)`
|
||||
* Fixed; `express(1)` adds express as a dep
|
||||
* Fixed; prune on `prepublish`
|
||||
|
||||
2.3.10 / 2011-05-27
|
||||
==================
|
||||
|
||||
* Added `req.route`, exposing the current route
|
||||
* Added _package.json_ generation support to `express(1)`
|
||||
* Fixed call to `app.param()` function for optional params. Closes #682
|
||||
|
||||
2.3.9 / 2011-05-25
|
||||
==================
|
||||
|
||||
* Fixed bug-ish with `../' in `res.partial()` calls
|
||||
|
||||
2.3.8 / 2011-05-24
|
||||
==================
|
||||
|
||||
* Fixed `app.options()`
|
||||
|
||||
2.3.7 / 2011-05-23
|
||||
==================
|
||||
|
||||
* Added route `Collection`, ex: `app.get('/user/:id').remove();`
|
||||
* Added support for `app.param(fn)` to define param logic
|
||||
* Removed `app.param()` support for callback with return value
|
||||
* Removed module.parent check from express(1) generated app. Closes #670
|
||||
* Refactored router. Closes #639
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
* Added second callback support for `res.download()` connection errors
|
||||
* Fixed `filename` option passing to template engine
|
||||
|
||||
2.2.1 / 2011-04-04
|
||||
==================
|
||||
|
||||
* Added `layout(path)` helper to change the layout within a view. Closes #610
|
||||
* Fixed `partial()` collection object support.
|
||||
Previously only anything with `.length` would work.
|
||||
When `.length` is present one must still be aware of holes,
|
||||
however now `{ collection: {foo: 'bar'}}` is valid, exposes
|
||||
`keyInCollection` and `keysInCollection`.
|
||||
|
||||
* Performance improved with better view caching
|
||||
* Removed `request` and `response` locals
|
||||
* Changed; errorHandler page title is now `Express` instead of `Connect`
|
||||
|
||||
2.2.0 / 2011-03-30
|
||||
==================
|
||||
|
||||
* Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606
|
||||
* Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606
|
||||
* Added `app.VERB(path)` as alias of `app.lookup.VERB()`.
|
||||
* Dependency `connect >= 1.2.0`
|
||||
|
||||
2.1.1 / 2011-03-29
|
||||
==================
|
||||
|
||||
* Added; expose `err.view` object when failing to locate a view
|
||||
* Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann]
|
||||
* Fixed; `res.send(undefined)` responds with 204 [aheckmann]
|
||||
|
||||
2.1.0 / 2011-03-24
|
||||
==================
|
||||
|
||||
* Added `<root>/_?<name>` partial lookup support. Closes #447
|
||||
* Added `request`, `response`, and `app` local variables
|
||||
* Added `settings` local variable, containing the app's settings
|
||||
* Added `req.flash()` exception if `req.session` is not available
|
||||
* Added `res.send(bool)` support (json response)
|
||||
* Fixed stylus example for latest version
|
||||
* Fixed; wrap try/catch around `res.render()`
|
||||
|
||||
2.0.0 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Fixed up index view path alternative.
|
||||
* Changed; `res.locals()` without object returns the locals
|
||||
|
||||
2.0.0rc3 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Added `res.locals(obj)` to compliment `res.local(key, val)`
|
||||
* Added `res.partial()` callback support
|
||||
* Fixed recursive error reporting issue in `res.render()`
|
||||
|
||||
2.0.0rc2 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Changed; `partial()` "locals" are now optional
|
||||
* Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01]
|
||||
* Fixed .filename view engine option [reported by drudge]
|
||||
* Fixed blog example
|
||||
* Fixed `{req,res}.app` reference when mounting [Ben Weaver]
|
||||
|
||||
2.0.0rc / 2011-03-14
|
||||
==================
|
||||
|
||||
* Fixed; expose `HTTPSServer` constructor
|
||||
* Fixed express(1) default test charset. Closes #579 [reported by secoif]
|
||||
* Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]
|
||||
|
||||
2.0.0beta3 / 2011-03-09
|
||||
==================
|
||||
|
||||
* Added support for `res.contentType()` literal
|
||||
The original `res.contentType('.json')`,
|
||||
`res.contentType('application/json')`, and `res.contentType('json')`
|
||||
will work now.
|
||||
* Added `res.render()` status option support back
|
||||
* Added charset option for `res.render()`
|
||||
* Added `.charset` support (via connect 1.0.4)
|
||||
* Added view resolution hints when in development and a lookup fails
|
||||
* Added layout lookup support relative to the page view.
|
||||
For example while rendering `./views/user/index.jade` if you create
|
||||
`./views/user/layout.jade` it will be used in favour of the root layout.
|
||||
* Fixed `res.redirect()`. RFC states absolute url [reported by unlink]
|
||||
* Fixed; default `res.send()` string charset to utf8
|
||||
* Removed `Partial` constructor (not currently used)
|
||||
|
||||
2.0.0beta2 / 2011-03-07
|
||||
==================
|
||||
|
||||
|
||||
29
Makefile
29
Makefile
@@ -1,42 +1,25 @@
|
||||
|
||||
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)
|
||||
TESTS = $(shell find test/*.test.js)
|
||||
|
||||
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
|
||||
$(TESTS)
|
||||
|
||||
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:
|
||||
|
||||
48
Readme.md
48
Readme.md
@@ -16,6 +16,27 @@
|
||||
|
||||
$ npm install express
|
||||
|
||||
or to access the `express(1)` executable install globally:
|
||||
|
||||
$ npm install -g express
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:
|
||||
|
||||
Create the app:
|
||||
|
||||
$ npm install -g express
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
|
||||
Install dependencies:
|
||||
|
||||
$ npm install -d
|
||||
|
||||
Start the server:
|
||||
|
||||
$ node app.js
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
@@ -54,12 +75,17 @@ 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 on freenode
|
||||
* [express-expose](http://github.com/visionmedia/express-expose) expose objects, functions, modules and more to client-side js with ease
|
||||
* [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
|
||||
* [express-params](https://github.com/visionmedia/express-params) param pre-condition functions
|
||||
* [express-mongoose](https://github.com/LearnBoost/express-mongoose) plugin for easy rendering of Mongoose async Query results
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito)
|
||||
* Screencast - [Introduction](http://bit.ly/eRYu0O)
|
||||
* Screencast - [View Partials](http://bit.ly/dU13Fx)
|
||||
* Screencast - [Route Specific Middleware](http://bit.ly/hX4IaH)
|
||||
@@ -71,6 +97,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)
|
||||
|
||||
96
bin/express
96
bin/express
@@ -5,13 +5,14 @@
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, exec = require('child_process').exec;
|
||||
, exec = require('child_process').exec
|
||||
, mkdirp = require('mkdirp');
|
||||
|
||||
/**
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
var version = '2.0.0beta2';
|
||||
var version = '2.4.6';
|
||||
|
||||
/**
|
||||
* Add session support.
|
||||
@@ -36,14 +37,15 @@ var templateEngine = 'jade';
|
||||
*/
|
||||
|
||||
var usage = ''
|
||||
+ '\x1b[1mUsage\x1b[0m: express [options] [PATH]\n'
|
||||
+ '\n'
|
||||
+ '\x1b[1mOptions\x1b[0m:\n'
|
||||
+ ' -s, --sessions Add session support\n'
|
||||
+ ' -t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade\n'
|
||||
+ ' -c, --css ENGINE Add stylesheet ENGINE support (less|sass|stylus). Defaults to plain css\n'
|
||||
+ ' -v, --version Output framework version\n'
|
||||
+ ' -h, --help Output help information\n'
|
||||
+ ' Usage: express [options] [path]\n'
|
||||
+ '\n'
|
||||
+ ' Options:\n'
|
||||
+ ' -s, --sessions add session support\n'
|
||||
+ ' -t, --template <engine> add template <engine> support (jade|ejs). default=jade\n'
|
||||
+ ' -c, --css <engine> add stylesheet <engine> support (less|sass|stylus). default=plain css\n'
|
||||
+ ' -v, --version output framework version\n'
|
||||
+ ' -h, --help output help information\n'
|
||||
;
|
||||
|
||||
/**
|
||||
@@ -142,37 +144,10 @@ var sass = [
|
||||
|
||||
var stylus = [
|
||||
'body'
|
||||
, ' padding 50px'
|
||||
, ' font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, ' padding: 50px'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, 'a'
|
||||
, ' color #00B7FF'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* App test template.
|
||||
*/
|
||||
|
||||
var appTest = [
|
||||
""
|
||||
, "// Run $ expresso"
|
||||
, ""
|
||||
, "/**"
|
||||
, " * Module dependencies."
|
||||
, " */"
|
||||
, ""
|
||||
, "var app = require('../app')"
|
||||
, " , assert = require('assert');"
|
||||
, "",
|
||||
, "module.exports = {"
|
||||
, " 'GET /': function(){"
|
||||
, " assert.response(app,"
|
||||
, " { url: '/' },"
|
||||
, " { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' }},"
|
||||
, " function(res){"
|
||||
, " assert.includes(res.body, '<title>Express</title>');"
|
||||
, " });"
|
||||
, " }"
|
||||
, "};"
|
||||
, ' color: #00B7FF'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
@@ -216,12 +191,8 @@ var app = [
|
||||
, ' });'
|
||||
, '});'
|
||||
, ''
|
||||
, '// Only listen on $ node app.js'
|
||||
, ''
|
||||
, 'if (!module.parent) {'
|
||||
, ' app.listen(3000);'
|
||||
, ' console.log("Express server listening on port %d", app.address().port)'
|
||||
, '}'
|
||||
, 'app.listen(3000);'
|
||||
, 'console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);'
|
||||
, ''
|
||||
].join('\n');
|
||||
|
||||
@@ -290,8 +261,6 @@ while (args.length) {
|
||||
|
||||
function createApplicationAt(path) {
|
||||
mkdir(path, function(){
|
||||
mkdir(path + '/pids');
|
||||
mkdir(path + '/logs');
|
||||
mkdir(path + '/public/javascripts');
|
||||
mkdir(path + '/public/images');
|
||||
mkdir(path + '/public/stylesheets', function(){
|
||||
@@ -321,9 +290,6 @@ function createApplicationAt(path) {
|
||||
break;
|
||||
}
|
||||
});
|
||||
mkdir(path + '/test', function(){
|
||||
write(path + '/test/app.test.js', appTest);
|
||||
});
|
||||
|
||||
// CSS Engine support
|
||||
switch (cssEngine) {
|
||||
@@ -346,19 +312,21 @@ function createApplicationAt(path) {
|
||||
// Template support
|
||||
app = app.replace(':TEMPLATE', templateEngine);
|
||||
|
||||
write(path + '/app.js', app);
|
||||
// package.json
|
||||
var json = '{\n';
|
||||
json += ' "name": "application-name"\n';
|
||||
json += ' , "version": "0.0.1"\n';
|
||||
json += ' , "private": true\n';
|
||||
json += ' , "dependencies": {\n';
|
||||
json += ' "express": "' + version + '"\n';
|
||||
if (cssEngine) json += ' , "' + cssEngine + '": ">= 0.0.1"\n';
|
||||
if (templateEngine) json += ' , "' + templateEngine + '": ">= 0.0.1"\n';
|
||||
json += ' }\n';
|
||||
json += '}';
|
||||
|
||||
// Suggestions
|
||||
process.on('exit', function(){
|
||||
if (cssEngine) {
|
||||
console.log(' - make sure you have installed %s: \x1b[33m$ npm install %s\x1b[0m'
|
||||
, cssEngine
|
||||
, cssEngine);
|
||||
}
|
||||
console.log(' - make sure you have installed %s: \x1b[33m$ npm install %s\x1b[0m'
|
||||
, templateEngine
|
||||
, templateEngine);
|
||||
});
|
||||
|
||||
write(path + '/package.json', json);
|
||||
write(path + '/app.js', app);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -431,9 +399,9 @@ function prompt(msg, fn) {
|
||||
*/
|
||||
|
||||
function mkdir(path, fn) {
|
||||
exec('mkdir -p ' + path, function(err){
|
||||
mkdirp(path, 0755, function(err){
|
||||
if (err) throw err;
|
||||
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
|
||||
console.log(' \033[36mcreate\033[0m : ' + path);
|
||||
fn && fn();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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\.
|
||||
@@ -1,7 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-25235225-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
@@ -190,11 +204,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 +237,7 @@
|
||||
|
||||
<p><a href="http://nodeknockout.com"><img src="images/apps/nodeko.png" alt="Node Knockout Competition Express" /></a></p>
|
||||
|
||||
<p><a href="http://widescript.com">Widescript</a> is an innovative app that helps you focus and interact with your texts - on your desktop, your couch or on the go.</p>
|
||||
<p><a href="http://widescript.com">Widescript</a> is an innovative app that helps you focus and interact with your texts – on your desktop, your couch or on the go.</p>
|
||||
|
||||
<p><a href="http://widescript.com"><img src="images/apps/widescript.png" alt="Widescript" /></a></p>
|
||||
|
||||
@@ -244,9 +253,7 @@
|
||||
|
||||
|
||||
<p>The applications shown above are not listed in any specific order. To have an application added or removed please contact <a href="http://github.com/visionmedia">TJ Holowaychuk</a>.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,82 +0,0 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "CONTRIB" "" "March 2011" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBcontrib\fR
|
||||
.
|
||||
.SS "Development Dependencies"
|
||||
Express development dependencies are stored within the \fI\./support\fR directory\. To update them execute:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ git submodule update \-\-init
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Running Tests"
|
||||
Express uses the Expresso \fIhttp://github\.com/visionmedia/expresso\fR TDD framework to write and run elegant test suites extremely fast\. To run all test suites simply execute:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To target specific suites we may specify the files via:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test TESTS=test/view\.test\.js
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To check test coverage run:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test\-cov
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Contributions"
|
||||
To accept a contribution, you should follow these guidelines:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
All tests \fImust\fR pass
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Your alterations or additions \fImust\fR include tests
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Your commit(s) should be \fIfocused\fR, do not commit once for several changes
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Do \fInot\fR alter release information such as the \fIversion\fR, or \fIHistory\.md\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Indents are \fI2\fR spaces\.
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Documentation"
|
||||
To contribute documentation edit the markdown files in \fI\./docs\fR, however do \fInot\fR run \fImake docs\fR, as they will be re\-built and published with each release\.
|
||||
@@ -1,7 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-25235225-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
@@ -190,20 +204,14 @@
|
||||
<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>
|
||||
<p>First install the dev dependencies by executing the following command in the repo’s directory:</p>
|
||||
|
||||
<pre><code>$ git submodule update --init
|
||||
<pre><code>$ npm install
|
||||
</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 +230,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 +243,11 @@ simply execute:</p>
|
||||
</ul>
|
||||
|
||||
|
||||
<h3 id="Documentation">Documentation</h3>
|
||||
<h3>Documentation</h3>
|
||||
|
||||
<p>To contribute documentation edit the markdown files in <em>./docs</em>, however
|
||||
do <em>not</em> run <em>make docs</em>, as they will be re-built and published with each release.</p>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-25235225-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
@@ -190,23 +204,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 +223,7 @@ application structure, this executable creates a maintainable base app.</p>
|
||||
-v, --version Output framework version
|
||||
-h, --help Output help information
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
2099
docs/guide.1
2099
docs/guide.1
File diff suppressed because it is too large
Load Diff
639
docs/guide.html
639
docs/guide.html
File diff suppressed because it is too large
Load Diff
308
docs/guide.md
308
docs/guide.md
@@ -1,8 +1,28 @@
|
||||
|
||||
### Installation
|
||||
|
||||
$ npm install express
|
||||
|
||||
or to access the `express(1)` executable install globally:
|
||||
|
||||
$ npm install -g express
|
||||
|
||||
## Quick Start
|
||||
|
||||
The quickest way to get started with express is to utilize the executable `express(1)` to generate an application as shown below:
|
||||
|
||||
Create the app:
|
||||
|
||||
$ npm install -g express
|
||||
$ express /tmp/foo && cd /tmp/foo
|
||||
|
||||
Install dependencies:
|
||||
|
||||
$ npm install -d
|
||||
|
||||
Start the server:
|
||||
|
||||
$ node app.js
|
||||
|
||||
### Creating A Server
|
||||
|
||||
To create an instance of the _express.HTTPServer_, simply invoke the _createServer()_ method. With our instance _app_ we can then define routes based on the HTTP verbs, in this example _app.get()_.
|
||||
@@ -47,10 +67,16 @@ otherwise the first call to _app.get()_, _app.post()_, etc will mount the routes
|
||||
|
||||
app.configure('production', function(){
|
||||
var oneYear = 31557600000;
|
||||
app.use(express.static({ root: __dirname + '/public', maxAge: oneYear }));
|
||||
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||
app.use(express.errorHandler());
|
||||
});
|
||||
|
||||
For similar environments you may also pass several env strings:
|
||||
|
||||
app.configure('stage', 'prod', function(){
|
||||
// config
|
||||
});
|
||||
|
||||
For internal and arbitrary settings Express provides the _set(key[, val])_, _enable(key)_, _disable(key)_ methods:
|
||||
|
||||
app.configure(function(){
|
||||
@@ -78,10 +104,14 @@ This is _very_ important, as many caching mechanisms are _only enabled_ when in
|
||||
|
||||
Express supports the following settings out of the box:
|
||||
|
||||
* _home_ Application base path used for _res.redirect()_ and transparently handling mounted apps.
|
||||
* _basepath_ Application base path used for _res.redirect()_ and transparently handling mounted apps.
|
||||
* _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
|
||||
* _strict routing_ When enabled trailing slashes are no longer ignored
|
||||
* _jsonp callback_ Enable _res.send()_ / _res.json()_ transparent jsonp support
|
||||
|
||||
### Routing
|
||||
|
||||
@@ -165,7 +195,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
|
||||
|
||||
@@ -234,7 +264,7 @@ passed to _express.createServer()_ as you would with a regular Connect server. F
|
||||
|
||||
Alternatively we can _use()_ them which is useful when adding middleware within _configure()_ blocks, in a progressive manor.
|
||||
|
||||
app.use(express.logger({ format: ':method :uri' }));
|
||||
app.use(express.logger({ format: ':method :url' }));
|
||||
|
||||
Typically with connect middleware you would _require('connect')_ like so:
|
||||
|
||||
@@ -247,6 +277,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 +375,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.
|
||||
@@ -372,7 +431,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;
|
||||
@@ -450,14 +509,6 @@ Doing so, as mentioned drastically improves our route readability, and allows us
|
||||
res.send('user ' + req.user.name);
|
||||
});
|
||||
|
||||
For simple cases such as route placeholder validation and coercion we can simple pass a callback which has an arity of 1 (accepts one argument). Any errors thrown will be passed to _next(err)_.
|
||||
|
||||
app.param('number', function(n){ return parseInt(n, 10); });
|
||||
|
||||
We may also apply the same callback to several placeholders, for example a route GET _/commits/:from-:to_ are both numbers, so we may define them as an array:
|
||||
|
||||
app.param(['from', 'to'], function(n){ return parseInt(n, 10); });
|
||||
|
||||
### View Rendering
|
||||
|
||||
View filenames take the form "<name>.<engine>", where <engine> is the name
|
||||
@@ -523,15 +574,15 @@ A good example of this is specifying custom _ejs_ opening and closing tags:
|
||||
### View Partials
|
||||
|
||||
The Express view system has built-in support for partials and collections, which are "mini" views representing a document fragment. For example rather than iterating
|
||||
in a view to display comments, we would use a partial with collection support:
|
||||
in a view to display comments, we could use partial collection:
|
||||
|
||||
partial('comment', { collection: comments });
|
||||
|
||||
If no other options are desired, we can omit the object and simply pass our array, which is equivalent to above:
|
||||
If no other options or local variables are desired, we can omit the object and simply pass our array, which is equivalent to above:
|
||||
|
||||
partial('comment', comments);
|
||||
|
||||
When using the partial collection support a few "magic" variables are provided
|
||||
When using the partial collection support a few "magic" locals are provided
|
||||
for free:
|
||||
|
||||
* _firstInCollection_ true if this is the first object
|
||||
@@ -572,13 +623,13 @@ Sessions support can be added by using Connect's _session_ middleware. To do so
|
||||
|
||||
By default the _session_ middleware uses the memory store bundled with Connect, however many implementations exist. For example [connect-redis](http://github.com/visionmedia/connect-redis) supplies a [Redis](http://code.google.com/p/redis/) session store and can be used as shown below:
|
||||
|
||||
var RedisStore = require('connect-redis');
|
||||
var RedisStore = require('connect-redis')(express);
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));
|
||||
|
||||
Now the _req.session_ and _req.sessionStore_ properties will be accessible to all routes and subsequent middleware. Properties on _req.session_ are automatically saved on a response, so for example if we wish to shopping cart data:
|
||||
|
||||
var RedisStore = require('connect-redis');
|
||||
var RedisStore = require('connect-redis')(express);
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: "keyboard cat", store: new RedisStore }));
|
||||
@@ -710,6 +761,17 @@ To utilize urlencoded request bodies, _req.body_
|
||||
should be an object. This can be done by using
|
||||
the _express.bodyParser middleware.
|
||||
|
||||
### req.get(field, param)
|
||||
|
||||
Get _field_'s _param_ value, defaulting to '' when the _param_
|
||||
or _field_ is not present.
|
||||
|
||||
req.get('content-disposition', 'filename');
|
||||
// => "something.png"
|
||||
|
||||
req.get('Content-Type', 'boundary');
|
||||
// => "--foo-bar-baz"
|
||||
|
||||
### req.flash(type[, msg])
|
||||
|
||||
Queue flash _msg_ of the given _type_.
|
||||
@@ -753,6 +815,24 @@ Get or set the response header _key_.
|
||||
res.header('Content-Length');
|
||||
// => 123
|
||||
|
||||
### res.charset
|
||||
|
||||
Sets the charset for subsequent `Content-Type` header fields. For example `res.send()` and `res.render()` default to "utf8", so we may explicitly set the charset before rendering a template:
|
||||
|
||||
res.charset = 'ISO-8859-1';
|
||||
res.render('users');
|
||||
|
||||
or before responding with `res.send()`:
|
||||
|
||||
res.charset = 'ISO-8859-1';
|
||||
res.send(str);
|
||||
|
||||
or with node's `res.end()`:
|
||||
|
||||
res.charset = 'ISO-8859-1';
|
||||
res.header('Content-Type', 'text/plain');
|
||||
res.end(str);
|
||||
|
||||
### res.contentType(type)
|
||||
|
||||
Sets the _Content-Type_ response header to the given _type_.
|
||||
@@ -761,6 +841,14 @@ Sets the _Content-Type_ response header to the given _type_.
|
||||
res.contentType(filename);
|
||||
// Content-Type is now "image/png"
|
||||
|
||||
A literal _Content-Type_ works as well:
|
||||
|
||||
res.contentType('application/json');
|
||||
|
||||
Or simply the extension without leading `.`:
|
||||
|
||||
res.contentType('json');
|
||||
|
||||
### res.attachment([filename])
|
||||
|
||||
Sets the _Content-Disposition_ response header to "attachment", with optional _filename_.
|
||||
@@ -790,7 +878,7 @@ Options may also be passed to the internal _fs.createReadStream()_ call, for exa
|
||||
// handle
|
||||
});
|
||||
|
||||
### res.download(file[, filename[, callback]])
|
||||
### res.download(file[, filename[, callback[, callback2]]])
|
||||
|
||||
Transfer the given _file_ as an attachment with optional alternative _filename_.
|
||||
|
||||
@@ -802,12 +890,20 @@ This is equivalent to:
|
||||
res.attachment(file);
|
||||
res.sendfile(file);
|
||||
|
||||
An optional callback may be supplied as either the second or third argument, which is passed to _res.sendfile()_:
|
||||
An optional callback may be supplied as either the second or third argument, which is passed to _res.sendfile()_. Within this callback you may still respond, as the header has not been sent.
|
||||
|
||||
res.download(path, 'expenses.doc', function(err){
|
||||
// handle
|
||||
});
|
||||
|
||||
An optional second callback, _callback2_ may be given to allow you to act on connection related errors, however you should not attempt to respond.
|
||||
|
||||
res.download(path, function(err){
|
||||
// error or finished
|
||||
}, function(err){
|
||||
// connection related error
|
||||
});
|
||||
|
||||
### res.send(body|status[, headers|status[, status]])
|
||||
|
||||
The _res.send()_ method is a high level response utility allowing you to pass
|
||||
@@ -827,6 +923,18 @@ it will not be set again.
|
||||
|
||||
Note that this method _end()_s the response, so you will want to use node's _res.write()_ for multiple writes or streaming.
|
||||
|
||||
### res.json(obj[, headers|status[, status]])
|
||||
|
||||
Send a JSON response with optional _headers_ and _status_. This method
|
||||
is ideal for JSON-only APIs, however _res.send(obj)_ will send JSON as
|
||||
well, though not ideal for cases when you want to send for example a string
|
||||
as JSON, since the default for _res.send(string)_ is text/html.
|
||||
|
||||
res.json(null);
|
||||
res.json({ user: 'tj' });
|
||||
res.json('oh noes!', 500);
|
||||
res.json('I dont have that', 404);
|
||||
|
||||
### res.redirect(url[, status])
|
||||
|
||||
Redirect to the given _url_ with a default response _status_ of 302.
|
||||
@@ -839,11 +947,12 @@ Redirect to the given _url_ with a default response _status_ of 302.
|
||||
|
||||
Express supports "redirect mapping", which by default provides _home_, and _back_.
|
||||
The _back_ map checks the _Referrer_ and _Referer_ headers, while _home_ utilizes
|
||||
the "home" setting and defaults to "/".
|
||||
the "basepath" setting and defaults to "/".
|
||||
|
||||
### res.cookie(name, val[, options])
|
||||
|
||||
Sets the given cookie _name_ to _val_, with options _httpOnly_, _secure_, _expires_ etc.
|
||||
Sets the given cookie _name_ to _val_, with options _httpOnly_, _secure_, _expires_ etc. The _path_ option defaults to the app's "basepath" setting, which
|
||||
is typically "/".
|
||||
|
||||
// "Remember me" for 15 minutes
|
||||
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
@@ -852,7 +961,7 @@ The _maxAge_ property may be used to set _expires_ relative to _Date.now()_ in m
|
||||
|
||||
res.cookie('rememberme', 'yes', { maxAge: 900000 });
|
||||
|
||||
To parse incoming _Cookie_ headers, use the _cookieDecoder_ middleware, which provides the _req.cookies_ object:
|
||||
To parse incoming _Cookie_ headers, use the _cookieParser_ middleware, which provides the _req.cookies_ object:
|
||||
|
||||
app.use(express.cookieParser());
|
||||
|
||||
@@ -860,9 +969,10 @@ To parse incoming _Cookie_ headers, use the _cookieDecoder_ middleware, which pr
|
||||
// use req.cookies.rememberme
|
||||
});
|
||||
|
||||
### res.clearCookie(name)
|
||||
### res.clearCookie(name[, options])
|
||||
|
||||
Clear cookie _name_ by setting "expires" far in the past.
|
||||
Clear cookie _name_ by setting "expires" far in the past. Much like
|
||||
_res.cookie()_ the _path_ option also defaults to the "basepath" setting.
|
||||
|
||||
res.clearCookie('rememberme');
|
||||
|
||||
@@ -877,6 +987,15 @@ The _options_ passed are the local variables as well, for example if we want to
|
||||
var user = { name: 'tj' };
|
||||
res.render('index', { layout: false, user: user });
|
||||
|
||||
This _options_ object is also considered an "options" object. For example
|
||||
when you pass the _status_ local, it's not only available to the view, it
|
||||
sets the response status to this number. This is also useful if a template
|
||||
engine accepts specific options, such as _debug_, or _compress_. Below
|
||||
is an example of how one might render an error page, passing the _status_ for
|
||||
display, as well as it setting _res.statusCode_.
|
||||
|
||||
res.render('error', { status: 500, message: 'Internal Server Error' });
|
||||
|
||||
### res.partial(view[, options])
|
||||
|
||||
Render _view_ partial with the given _options_. This method is always available
|
||||
@@ -912,7 +1031,7 @@ of _movie.director_ we could use _this.director_.
|
||||
partial('movie', { collection: movies, as: this });
|
||||
// In view: this.director
|
||||
|
||||
Another alternative is to "explode" the properties of the collection item into
|
||||
Another alternative is to "expand" the properties of the collection item into
|
||||
pseudo globals (local variables) by using _as: global_, which again is syntactic sugar:
|
||||
|
||||
partial('movie', { collection: movies, as: global });
|
||||
@@ -934,9 +1053,21 @@ This same logic applies to a single partial object usage:
|
||||
|
||||
When a non-collection (does _not_ have _.length_) is passed as the second argument, it is assumed to be the _object_, after which the object's local variable name is derived from the view name:
|
||||
|
||||
partial('movie', movie);
|
||||
var movie = new Movie('Nightmare Before Christmas', 'Tim Burton')
|
||||
partial('movie', movie)
|
||||
// => In view: movie.director
|
||||
|
||||
The exception of this, is when a "plain" object, aka "{}" or "new Object" is passed, which is considered an object with local variable. For example some may expect a "movie" local with the following, however since it is a plain object "director" and "title" are simply locals:
|
||||
|
||||
var movie = { title: 'Nightmare Before Christmas', director: 'Tim Burton' };
|
||||
partial('movie', movie)
|
||||
|
||||
For cases like this where passing a plain object is desired, simply assign it to a key, or use the `object` key which will use the filename-derived variable name. The examples below are equivalent:
|
||||
|
||||
partial('movie', { locals: { movie: movie }})
|
||||
partial('movie', { movie: movie })
|
||||
partial('movie', { object: movie })
|
||||
|
||||
This exact API can be utilized from within a route, to respond with a fragment via Ajax or WebSockets, for example we can render a collection of users directly from a route:
|
||||
|
||||
app.get('/users', function(req, res){
|
||||
@@ -969,6 +1100,15 @@ Get or set the given local variable _name_. The locals built up for a response a
|
||||
res.render('movie', { displayReviews: true });
|
||||
});
|
||||
|
||||
### res.locals(obj)
|
||||
|
||||
Assign several locals with the given _obj_. The following are equivalent:
|
||||
|
||||
res.local('foo', bar);
|
||||
res.local('bar', baz);
|
||||
|
||||
res.locals({ foo: bar, bar, baz });
|
||||
|
||||
### app.set(name[, val])
|
||||
|
||||
Apply an application level setting _name_ to _val_, or
|
||||
@@ -1083,17 +1223,24 @@ 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
|
||||
- `layout(path)` specify the layout from within a view
|
||||
|
||||
This method is aliased as _app.locals()_.
|
||||
|
||||
### app.dynamicHelpers(obj)
|
||||
|
||||
Registers dynamic view helpers. Dynamic view helpers
|
||||
@@ -1111,6 +1258,101 @@ All views would now have _session_ available so that session data can be accesse
|
||||
|
||||
<%= session.name %>
|
||||
|
||||
### app.lookup
|
||||
|
||||
The _app.lookup_ http methods returns an array of callback functions
|
||||
associated with the given _path_.
|
||||
|
||||
Suppose we define the following routes:
|
||||
|
||||
app.get('/user/:id', function(){});
|
||||
app.put('/user/:id', function(){});
|
||||
app.get('/user/:id/:op?', function(){});
|
||||
|
||||
We can utilize this lookup functionality to check which routes
|
||||
have been defined, which can be extremely useful for higher level
|
||||
frameworks built on Express.
|
||||
|
||||
app.lookup.get('/user/:id');
|
||||
// => [Function]
|
||||
|
||||
app.lookup.get('/user/:id/:op?');
|
||||
// => [Function]
|
||||
|
||||
app.lookup.put('/user/:id');
|
||||
// => [Function]
|
||||
|
||||
app.lookup.all('/user/:id');
|
||||
// => [Function, Function]
|
||||
|
||||
app.lookup.all('/hey');
|
||||
// => []
|
||||
|
||||
To alias _app.lookup.VERB()_, we can simply invoke _app.VERB()_
|
||||
without a callback, as a shortcut, for example the following are
|
||||
equivalent:
|
||||
|
||||
app.lookup.get('/user');
|
||||
app.get('/user');
|
||||
|
||||
Each function returned has the following properties:
|
||||
|
||||
var fn = app.get('/user/:id/:op?')[0];
|
||||
|
||||
fn.regexp
|
||||
// => /^\/user\/(?:([^\/]+?))(?:\/([^\/]+?))?\/?$/i
|
||||
|
||||
fn.keys
|
||||
// => ['id', 'op']
|
||||
|
||||
fn.path
|
||||
// => '/user/:id/:op?'
|
||||
|
||||
fn.method
|
||||
// => 'GET'
|
||||
|
||||
### app.match
|
||||
|
||||
The _app.match_ http methods return an array of callback functions
|
||||
which match the given _url_, which may include a query string etc. This
|
||||
is useful when you want reflect on which routes have the opportunity to
|
||||
respond.
|
||||
|
||||
Suppose we define the following routes:
|
||||
|
||||
app.get('/user/:id', function(){});
|
||||
app.put('/user/:id', function(){});
|
||||
app.get('/user/:id/:op?', function(){});
|
||||
|
||||
Our match against __GET__ will return two functions, since the _:op_
|
||||
in our second route is optional.
|
||||
|
||||
app.match.get('/user/1');
|
||||
// => [Function, Function]
|
||||
|
||||
This second call returns only the callback for _/user/:id/:op?_.
|
||||
|
||||
app.match.get('/user/23/edit');
|
||||
// => [Function]
|
||||
|
||||
We can also use _all()_ to disregard the http method:
|
||||
|
||||
app.match.all('/user/20');
|
||||
// => [Function, Function, Function]
|
||||
|
||||
Each function matched has the following properties:
|
||||
|
||||
var fn = app.match.get('/user/23/edit')[0];
|
||||
|
||||
fn.keys
|
||||
// => ['id', 'op']
|
||||
|
||||
fn.params
|
||||
// => { id: '23', op: 'edit' }
|
||||
|
||||
fn.method
|
||||
// => 'GET'
|
||||
|
||||
### app.mounted(fn)
|
||||
|
||||
Assign a callback _fn_ which is called when this _Server_ is passed to _Server#use()_.
|
||||
|
||||
126
docs/index.1
126
docs/index.1
@@ -1,126 +0,0 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "INDEX" "" "March 2011" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBindex\fR
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = express\.createServer();
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'Hello World\');
|
||||
});
|
||||
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Features"
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Robust routing
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Redirection helpers
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Dynamic view helpers
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Application level view options
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Content negotiation
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Application mounting
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Focus on high performance
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View rendering and partials support
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Environment based configuration
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Session based flash notifications
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Built on Connect \fIhttp://github\.com/senchalabs/connect\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Executable \fIexecutable\.html\fR for generating applications quickly
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
High test coverage
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Contributors"
|
||||
The following are the major contributors of Express (in no specific order)\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
TJ Holowaychuk (visionmedia \fIhttp://github\.com/visionmedia\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Ciaran Jessup (ciaranj \fIhttp://github\.com/ciaranj\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Aaron Heckmann (aheckmann \fIhttp://github\.com/aheckmann\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Guillermo Rauch (guille \fIhttp://github\.com/guille\fR)
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Third\-Party Modules"
|
||||
The following modules compliment or extend Express directly:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
express\-resource \fIhttp://github\.com/visionmedia/express\-resource\fR provides resourceful routing
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
express\-messages \fIhttp://github\.com/visionmedia/express\-messages\fR flash message notification rendering
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
express\-configure \fIhttp://github\.com/visionmedia/express\-configure\fR async configuration support (load settings from redis etc)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
express\-namespace \fIhttp://github\.com/visionmedia/express\-namespace\fR namespaced routing support
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "More Information"
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Google Group \fIhttp://groups\.google\.com/group/express\-js\fR for discussion
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Follow tjholowaychuk \fIhttp://twitter\.com/tjholowaychuk\fR on twitter for updates
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the Connect \fIhttp://senchalabs\.github\.com/connect\fR documentation
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the Connect Wiki \fIhttp://wiki\.github\.com/senchalabs/connect/\fR for contrib middleware
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the examples \fIhttp://github\.com/visionmedia/express/tree/master/examples/\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the source \fIhttp://github\.com/visionmedia/express\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the contrib guide \fIcontrib\.html\fR
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-25235225-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
@@ -190,11 +204,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 +213,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 +232,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,33 +244,32 @@ 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>
|
||||
<p>The following modules complement or extend Express directly:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://github.com/visionmedia/express-resource">express-resource</a> provides resourceful routing</li>
|
||||
<li><a href="http://github.com/visionmedia/express-messages">express-messages</a> flash message notification rendering</li>
|
||||
<li><a href="http://github.com/visionmedia/express-configure">express-configure</a> async configuration support (load settings from redis etc)</li>
|
||||
<li><a href="http://github.com/visionmedia/express-configuration">express-configure</a> async configuration support (load settings from redis etc)</li>
|
||||
<li><a href="http://github.com/visionmedia/express-namespace">express-namespace</a> namespaced routing support</li>
|
||||
<li><a href="http://github.com/visionmedia/express-expose">express-expose</a> expose objects, functions, modules and more to client-side js</li>
|
||||
<li><a href="https://github.com/visionmedia/express-params">express-params</a> app.param() extensions</li>
|
||||
<li><a href="https://github.com/LearnBoost/express-mongoose">express-mongoose</a> plugin for easy rendering of Mongoose async Query results</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2 id="More-Information">More Information</h2>
|
||||
<h2>More Information</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
|
||||
<li>#express on freenode</li>
|
||||
<li>Follow <a href="http://twitter.com/tjholowaychuk">tjholowaychuk</a> on twitter for updates</li>
|
||||
<li>View the <a href="http://senchalabs.github.com/connect">Connect</a> documentation</li>
|
||||
<li>View the <a href="http://wiki.github.com/senchalabs/connect/">Connect Wiki</a> for contrib middleware</li>
|
||||
<li>View the <a href="http://github.com/visionmedia/express/tree/master/examples/">examples</a></li>
|
||||
<li>View the <a href="http://github.com/visionmedia/express">source</a></li>
|
||||
<li>View the <a href="contrib.html">contrib guide</a></li>
|
||||
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
|
||||
<li>Visit the <a href="http://github.com/visionmedia/express/wiki">Wiki</a></li>
|
||||
<li><a href="http://hideyukisaito.com/doc/expressjs/">日本語ドキュメンテーション</a> by <a href="https://github.com/hideyukisaito">hideyukisaito</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -34,19 +34,20 @@ The following are the major contributors of Express (in no specific order).
|
||||
|
||||
## Third-Party Modules
|
||||
|
||||
The following modules compliment or extend Express directly:
|
||||
The following modules complement 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
|
||||
* [express-expose](http://github.com/visionmedia/express-expose) expose objects, functions, modules and more to client-side js
|
||||
* [express-params](https://github.com/visionmedia/express-params) app.param() extensions
|
||||
* [express-mongoose](https://github.com/LearnBoost/express-mongoose) plugin for easy rendering of Mongoose async Query results
|
||||
|
||||
## More Information
|
||||
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* \#express on freenode
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* View the [Connect](http://senchalabs.github.com/connect) documentation
|
||||
* View the [Connect Wiki](http://wiki.github.com/senchalabs/connect/) for contrib middleware
|
||||
* View the [examples](http://github.com/visionmedia/express/tree/master/examples/)
|
||||
* View the [source](http://github.com/visionmedia/express)
|
||||
* View the [contrib guide](contrib.html)
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito)
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-25235225-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
|
||||
272
docs/migrate.1
272
docs/migrate.1
@@ -1,272 +0,0 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "MIGRATE" "" "March 2011" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBmigrate\fR
|
||||
.
|
||||
.SS "Express 1\.x to 2\.x Migration"
|
||||
.
|
||||
.SS "HTTPS"
|
||||
Creating an HTTPS server is simply, simply pass the TLS options to \fIexpress\.createServer()\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = express\.createServer({
|
||||
key: \.\.\.
|
||||
, cert: \.\.\.
|
||||
});
|
||||
|
||||
app\.listen(443);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.header() Referrer"
|
||||
Previously if anyone was doing something similar to:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
req\.headers\.referrer || req\.headers\.referer
|
||||
req\.header(\'Referrer\') || req\.header(\'Referer\')
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
With the new special\-case we may now simply use \fIReferrer\fR which will return either if defined:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
req\.header(\'Referrer\')
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.local(name, val)"
|
||||
Previously all local variables had to be passed to \fIres\.render()\fR, or either \fIapp\.helpers()\fR or \fIapp\.dynamicHelpers()\fR, now we may do this at the request\-level progressively\. The \fIres\.local()\fR method accepts a \fIname\fR and \fIval\fR, however the locals passed to \fIres\.render()\fR will take precedence\.
|
||||
.
|
||||
.P
|
||||
For example we may utilize this feature to create locals in middleware:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
function loadUser(req, res, next) {
|
||||
User\.get(req\.params\.id, function(err, user){
|
||||
res\.local(\'user\', user);
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
app\.get(\'/user/:id\', loadUser, function(req, res){
|
||||
res\.render(\'user\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.param(name[, defaultValue])"
|
||||
Previously only \fIname\fR was accepted, so some of you may have been doing the following:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var id = req\.param(\'id\') || req\.user\.id;
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
The new \fIdefaultValue\fR argument can handle this nicely:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var id = req\.param(\'id\', req\.user\.id);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.helpers() / app\.locals()"
|
||||
\fIapp\.locals()\fR is now an alias of \fIapp\.helpers()\fR, as helpers makes more sense for functions\.
|
||||
.
|
||||
.SS "req\.accepts(type)"
|
||||
\fIreq\.accepts()\fR now accepts extensions:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
// Accept: text/html
|
||||
req\.accepts(\'html\');
|
||||
req\.accepts(\'\.html\');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req\.accepts(\'html\');
|
||||
req\.accepts(\'text/*\');
|
||||
req\.accepts(\'text/plain\');
|
||||
req\.accepts(\'application/json\');
|
||||
// => true
|
||||
|
||||
req\.accepts(\'image/png\');
|
||||
req\.accepts(\'png\');
|
||||
// => false
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.cookie()"
|
||||
Previously only directly values could be passed, so for example:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.cookie(\'rememberme\', \'yes\', { expires: new Date(Date\.now() + 900000) });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
However now we have the alternative \fImaxAge\fR property which may be used to set \fIexpires\fR relative to \fIDate\.now()\fR in milliseconds, so our example above can now become:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.cookie(\'rememberme\', \'yes\', { maxAge: 900000 });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.download() / res\.sendfile()"
|
||||
Both of these methods now utilize Connect\'s static file server behind the scenes (actually the previous Express code was ported to Connect 1\.0)\. With this change comes a change to the callback as well\. Previously the \fIpath\fR and \fIstream\fR were passed, however now only an \fIerror\fR is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete\. The callback remains optional:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.download(\'/path/to/file\');
|
||||
|
||||
res\.download(\'/path/to/file\', function(err){
|
||||
if (err) {
|
||||
console\.error(err);
|
||||
} else {
|
||||
console\.log(\'transferred\');
|
||||
}
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
The \fIstream threshold\fR setting was removed\.
|
||||
.
|
||||
.SS "res\.render()"
|
||||
Previously locals were passed as a separate key:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.render(\'user\', { layout: false, locals: { user: user }});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
In Express 2\.0 both the locals and the options are one in the same, meaning you cannot have a local variable named \fIlayout\fR as it is reserved for express, however this cleans up the API:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.render(\'user\', { layout: false, user: user });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.partial()"
|
||||
Express 2\.0 adds the \fIres\.partial()\fR method, helpful for rendering partial fragments over WebSockets or Ajax requests etc\. The API is identical to the \fIpartial()\fR calls within views\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
// render a collection of comments
|
||||
res\.partial(\'comment\', [comment1, comment2]);
|
||||
|
||||
// render a single comment
|
||||
res\.partial(\'comment\', comment);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Template Engine Compliance"
|
||||
To comply with Express previously engines needed the following signature:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
engine\.render(str, options, function(err){});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now they must export a \fIcompile()\fR function, returning a function which when called with local variables will render the template\. This allows Express to cache the compiled function in memory during production\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var fn = engine\.compile(str, options);
|
||||
fn(locals);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "View Partial Lookup"
|
||||
Previously partials were loaded relative to the now removed \fIview partials\fR directory setting, or by default \fIviews/partials\fR, now they are relative to the view calling them, read more on view lookup \fIguide\.html#View\-Lookup\fR\.
|
||||
.
|
||||
.SS "Mime Types"
|
||||
Express and Connect now utilize the \fImime\fR module in npm, so to add more use:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
require(\'mime\')\.define({ \'foo/bar\': [\'foo\', \'bar\'] });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-25235225-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
@@ -190,14 +204,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 +218,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 +231,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 +249,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 +261,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 +286,7 @@
|
||||
// => false
|
||||
</code></pre>
|
||||
|
||||
<h3 id="res-cookie-">res.cookie()</h3>
|
||||
<h3>res.cookie()</h3>
|
||||
|
||||
<p> Previously only directly values could be passed, so for example:</p>
|
||||
|
||||
@@ -289,9 +298,9 @@
|
||||
<pre><code>res.cookie('rememberme', 'yes', { maxAge: 900000 });
|
||||
</code></pre>
|
||||
|
||||
<h3 id="res-download-res-sendfile-">res.download() / res.sendfile()</h3>
|
||||
<h3>res.download() / res.sendfile()</h3>
|
||||
|
||||
<p> Both of these methods now utilize Connect's static file server behind the scenes (actually the previous Express code was ported to Connect 1.0). With this change comes a change to the callback as well. Previously the <em>path</em> and <em>stream</em> were passed, however now only an <em>error</em> is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete. The callback remains optional:</p>
|
||||
<p> Both of these methods now utilize Connect’s static file server behind the scenes (actually the previous Express code was ported to Connect 1.0). With this change comes a change to the callback as well. Previously the <em>path</em> and <em>stream</em> were passed, however now only an <em>error</em> is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete. The callback remains optional:</p>
|
||||
|
||||
<pre><code> res.download('/path/to/file');
|
||||
|
||||
@@ -306,7 +315,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 +327,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 +338,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 a single object consisting of both the options and the locals. Previously with Express 1.x you may pass <em>user</em> to a partial, along with <em>date</em> like so:</p>
|
||||
|
||||
<pre><code> partial('user', { object: user, locals: { date: new Date }})
|
||||
</code></pre>
|
||||
|
||||
<p>or perhaps if you preferred not to use the inferred name <em>user</em> you may used a local for this as well:</p>
|
||||
|
||||
<pre><code> partial('user', { locals: { user: user, date: new Date }})
|
||||
</code></pre>
|
||||
|
||||
<p> With recent changes to Express 2.x the object passed is now both, so the following is valid for the <em>object</em> option and locals:</p>
|
||||
|
||||
<pre><code> partial('user', { object: user, date: new Date })
|
||||
</code></pre>
|
||||
|
||||
<p> Or the following which is equivalent, however the local var name is explicitly set to <em>user</em> instead of deduced from the filename.</p>
|
||||
|
||||
<pre><code> partial('user', { user: user, date: new Date })
|
||||
</code></pre>
|
||||
|
||||
<p> When a “basic” object aka <em>{}</em> or <em>new Object</em> is passed, it is considered options, otherwise it is considered the <em>object</em>. The following are equivalent:</p>
|
||||
|
||||
<pre><code> partial('user', user);
|
||||
partial('user', { object: user });
|
||||
</code></pre>
|
||||
|
||||
<h3>Template Engine Compliance</h3>
|
||||
|
||||
<p> To comply with Express previously engines needed the following signature:</p>
|
||||
|
||||
@@ -342,19 +379,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>
|
||||
<p> Previously partials were loaded relative to the now removed <em>view partials</em> directory setting, or by default <em>views/partials</em>, now they are relative to the view calling them, read more on <a href="guide.html#view-lookup">view lookup</a>.</p>
|
||||
|
||||
<h3 id="Mime-Types">Mime Types</h3>
|
||||
<h3>Mime Types</h3>
|
||||
|
||||
<p> Express and Connect now utilize the <em>mime</em> module in npm, so to add more use:</p>
|
||||
|
||||
<pre><code> require('mime').define({ 'foo/bar': ['foo', 'bar'] });
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
<h3>static() middleware</h3>
|
||||
|
||||
<p> Previously named <code>staticProvider()</code>, the now <code>static()</code> middleware takes a single directory path, followed by options.</p>
|
||||
|
||||
<pre><code> app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||
</code></pre>
|
||||
|
||||
<p>Previously when using options the <code>root</code> option would be used for this:</p>
|
||||
|
||||
<pre><code> app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -121,6 +121,29 @@ However now we have the alternative _maxAge_ property which may be used to set _
|
||||
// render a single comment
|
||||
res.partial('comment', comment);
|
||||
|
||||
### partial() locals
|
||||
|
||||
Both _res.partial()_ and the _partial()_ functions accept a single object consisting of both the options and the locals. Previously with Express 1.x you may pass _user_ to a partial, along with _date_ like so:
|
||||
|
||||
partial('user', { object: user, locals: { date: new Date }})
|
||||
|
||||
or perhaps if you preferred not to use the inferred name _user_ you may used a local for this as well:
|
||||
|
||||
partial('user', { locals: { user: user, date: new Date }})
|
||||
|
||||
With recent changes to Express 2.x the object passed is now both, so the following is valid for the _object_ option and locals:
|
||||
|
||||
partial('user', { object: user, date: new Date })
|
||||
|
||||
Or the following which is equivalent, however the local var name is explicitly set to _user_ instead of deduced from the filename.
|
||||
|
||||
partial('user', { user: user, date: new Date })
|
||||
|
||||
When a "basic" object aka _{}_ or _new Object_ is passed, it is considered options, otherwise it is considered the _object_. The following are equivalent:
|
||||
|
||||
partial('user', user);
|
||||
partial('user', { object: user });
|
||||
|
||||
### Template Engine Compliance
|
||||
|
||||
To comply with Express previously engines needed the following signature:
|
||||
@@ -134,10 +157,21 @@ However now we have the alternative _maxAge_ property which may be used to set _
|
||||
|
||||
### View Partial Lookup
|
||||
|
||||
Previously partials were loaded relative to the now removed _view partials_ directory setting, or by default _views/partials_, now they are relative to the view calling them, read more on [view lookup](guide.html#View-Lookup).
|
||||
Previously partials were loaded relative to the now removed _view partials_ directory setting, or by default _views/partials_, now they are relative to the view calling them, read more on [view lookup](guide.html#view-lookup).
|
||||
|
||||
### Mime Types
|
||||
|
||||
Express and Connect now utilize the _mime_ module in npm, so to add more use:
|
||||
|
||||
require('mime').define({ 'foo/bar': ['foo', 'bar'] });
|
||||
require('mime').define({ 'foo/bar': ['foo', 'bar'] });
|
||||
|
||||
### static() middleware
|
||||
|
||||
Previously named `staticProvider()`, the now `static()` middleware takes a single directory path, followed by options.
|
||||
|
||||
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||
|
||||
Previously when using options the `root` option would be used for this:
|
||||
|
||||
app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));
|
||||
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
.\" generated with Ronn/v0.7.3
|
||||
.\" http://github.com/rtomayko/ronn/tree/0.7.3
|
||||
.
|
||||
.TH "SCREENCASTS" "" "March 2011" "" ""
|
||||
.
|
||||
.SH "NAME"
|
||||
\fBscreencasts\fR
|
||||
.
|
||||
.SS "Introduction"
|
||||
This introduction screencast covers the basics of Express, and how to get started with your first application\.
|
||||
.
|
||||
.P
|
||||
.
|
||||
.SS "View Partials"
|
||||
In this screencast we work with partials to display a collection of users using the Jade \fIhttp://jade\-lang\.com\fR template engine, and learn about view path resolution\.
|
||||
.
|
||||
.P
|
||||
.
|
||||
.SS "Route Specific Middleware"
|
||||
In the screencast below we learn about the benefits of route\-specific middleware\.
|
||||
.
|
||||
.P
|
||||
.
|
||||
.SS "Route Param Preconditions"
|
||||
Learn about route parameter (\fI/user/:id\fR) pre\-conditions, providing automated validation, and loading of data via the named route param segments\.
|
||||
.
|
||||
.P
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
|
||||
var _gaq = _gaq || [];
|
||||
_gaq.push(['_setAccount', 'UA-25235225-1']);
|
||||
_gaq.push(['_trackPageview']);
|
||||
|
||||
(function() {
|
||||
var ga = document.createElement('script'); ga.type = 'text/javascript'; ga.async = true;
|
||||
ga.src = ('https:' == document.location.protocol ? 'https://ssl' : 'http://www') + '.google-analytics.com/ga.js';
|
||||
var s = document.getElementsByTagName('script')[0]; s.parentNode.insertBefore(ga, s);
|
||||
})();
|
||||
|
||||
</script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
@@ -190,37 +204,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>
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -38,16 +35,15 @@ var users = {
|
||||
tj: {
|
||||
name: 'tj'
|
||||
, salt: 'randomly-generated-salt'
|
||||
, pass: md5('foobar' + 'randomly-generated-salt')
|
||||
, pass: hash('foobar', 'randomly-generated-salt')
|
||||
}
|
||||
};
|
||||
|
||||
// Used to generate a hash of the plain-text password + salt
|
||||
|
||||
function md5(str) {
|
||||
return crypto.createHash('md5').update(str).digest('hex');
|
||||
function hash(msg, key) {
|
||||
return crypto.createHmac('sha256', key).update(msg).digest('hex');
|
||||
}
|
||||
|
||||
// Authenticate using our plain-object database of doom!
|
||||
|
||||
function authenticate(name, pass, fn) {
|
||||
@@ -55,9 +51,9 @@ function authenticate(name, pass, fn) {
|
||||
// query the db for the given username
|
||||
if (!user) return fn(new Error('cannot find user'));
|
||||
// apply the same algorithm to the POSTed password, applying
|
||||
// the md5 against the pass / salt, if there is a match we
|
||||
// the hash against the pass / salt, if there is a match we
|
||||
// found the user
|
||||
if (user.pass == md5(pass + user.salt)) return fn(null, user);
|
||||
if (user.pass == hash(pass, user.salt)) return fn(null, user);
|
||||
// Otherwise password is invalid
|
||||
fn(new Error('invalid password'));
|
||||
}
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, messages = require('express-contrib/messages');
|
||||
, messages = require('express-messages');
|
||||
|
||||
var app = module.exports = express.createServer();
|
||||
|
||||
@@ -22,8 +19,8 @@ app.mounted(function(other){
|
||||
console.log('ive been mounted!');
|
||||
});
|
||||
|
||||
// Flash message helper provided by express-contrib
|
||||
// $ npm install express-contrib
|
||||
// Flash message helper provided by express-messages
|
||||
// $ npm install express-messages
|
||||
|
||||
app.dynamicHelpers({
|
||||
messages: messages
|
||||
|
||||
@@ -10,10 +10,10 @@ h1 Blog
|
||||
| It looks like you have no posts!
|
||||
p
|
||||
| Click
|
||||
a(href=base + '/post/add') here
|
||||
a(href=base + '/post/add') here
|
||||
| to create a post. Login
|
||||
| as
|
||||
em "admin"
|
||||
em "admin"
|
||||
| and
|
||||
em "express"
|
||||
| .
|
||||
|
||||
47
examples/content-negotiation/app.js
Normal file
47
examples/content-negotiation/app.js
Normal file
@@ -0,0 +1,47 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
var users = [
|
||||
{ name: 'tobi' }
|
||||
, { name: 'loki' }
|
||||
, { name: 'jane' }
|
||||
];
|
||||
|
||||
function provides(type) {
|
||||
return function(req, res, next){
|
||||
if (req.accepts(type)) return next();
|
||||
next('route');
|
||||
}
|
||||
}
|
||||
|
||||
// curl http://localhost:3000/users -H "Accept: application/json"
|
||||
|
||||
app.get('/users', provides('json'), function(req, res){
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
// curl http://localhost:3000/users -H "Accept: text/html"
|
||||
|
||||
app.get('/users', provides('html'), function(req, res){
|
||||
res.send('<ul>' + users.map(function(user){
|
||||
return '<li>' + user.name + '</li>';
|
||||
}).join('\n') + '</ul>');
|
||||
});
|
||||
|
||||
// curl http://localhost:3000/users -H "Accept: text/plain"
|
||||
|
||||
app.get('/users', function(req, res, next){
|
||||
res.contentType('txt');
|
||||
res.send(users.map(function(user){
|
||||
return user.name;
|
||||
}).join(', '));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -14,7 +11,7 @@ var app = express.createServer(
|
||||
express.favicon(),
|
||||
|
||||
// Custom logger format
|
||||
express.logger({ format: '\x1b[1m:method\x1b[0m \x1b[33m:url\x1b[0m :response-time' }),
|
||||
express.logger({ format: '\x1b[36m:method\x1b[0m \x1b[90m:url\x1b[0m :response-time' }),
|
||||
|
||||
// Provides req.cookies
|
||||
express.cookieParser(),
|
||||
@@ -39,9 +36,8 @@ app.get('/forget', function(req, res){
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
if (req.body.remember) {
|
||||
res.cookie('remember', '1', { path: '/', expires: new Date(Date.now() + 900000), httpOnly: true });
|
||||
}
|
||||
var minute = 60000;
|
||||
if (req.body.remember) res.cookie('remember', 1, { maxAge: minute });
|
||||
res.redirect('back');
|
||||
});
|
||||
|
||||
|
||||
@@ -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
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -9,13 +9,15 @@ require.paths.unshift(__dirname + '/../../support');
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// Serve default connect favicon
|
||||
app.use(express.favicon());
|
||||
|
||||
// Logger is placed below favicon, so favicon.ico
|
||||
// requests will not be logged
|
||||
app.use(express.logger({ format: '":method :url" :status' }));
|
||||
app.use(express.logger('":method :url" :status'));
|
||||
|
||||
// "app.router" positions our routes
|
||||
// specifically above the middleware
|
||||
@@ -23,59 +25,38 @@ app.use(express.logger({ format: '":method :url" :status' }));
|
||||
|
||||
app.use(app.router);
|
||||
|
||||
// When no more middleware require execution, aka
|
||||
// our router is finished and did not respond, we
|
||||
// can assume that it is "not found". Instead of
|
||||
// letting Connect deal with this, we define our
|
||||
// custom middleware here to simply pass a NotFound
|
||||
// exception
|
||||
// Since this is the last non-error-handling
|
||||
// middleware use()d, we assume 404, as nothing else
|
||||
// responded.
|
||||
|
||||
app.use(function(req, res, next){
|
||||
next(new NotFound(req.url));
|
||||
// the status option, or res.statusCode = 404
|
||||
// are equivalent, however with the option we
|
||||
// get the "status" local available as well
|
||||
res.render('404', { status: 404, url: req.url });
|
||||
});
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
// error-handling middleware, take the same form
|
||||
// as regular middleware, however they require an
|
||||
// arity of 4, aka the signature (err, req, res, next).
|
||||
// when connect has an error, it will invoke ONLY error-handling
|
||||
// middleware.
|
||||
|
||||
// Provide our app with the notion of NotFound exceptions
|
||||
// If we were to next() here any remaining non-error-handling
|
||||
// middleware would then be executed, or if we next(err) to
|
||||
// continue passing the error, only error-handling middleware
|
||||
// would remain being executed, however here
|
||||
// we simply respond with an error page.
|
||||
|
||||
function NotFound(path){
|
||||
this.name = 'NotFound';
|
||||
if (path) {
|
||||
Error.call(this, 'Cannot find ' + path);
|
||||
this.path = path;
|
||||
} else {
|
||||
Error.call(this, 'Not Found');
|
||||
}
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Error.prototype`.
|
||||
*/
|
||||
|
||||
NotFound.prototype.__proto__ = Error.prototype;
|
||||
|
||||
// We can call app.error() several times as shown below.
|
||||
// Here we check for an instanceof NotFound and show the
|
||||
// 404 page, or we pass on to the next error handler.
|
||||
|
||||
// These handlers could potentially be defined within
|
||||
// configure() blocks to provide introspection when
|
||||
// in the development environment.
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res.render('404.jade', { status: 404, error: err });
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// Here we assume all errors as 500 for the simplicity of
|
||||
// this demo, however you can choose whatever you like
|
||||
|
||||
app.error(function(err, req, res){
|
||||
res.render('500.jade', { status: 500, error: err });
|
||||
app.use(function(err, req, res, next){
|
||||
// we may use properties of the error object
|
||||
// here and next(err) appropriately, or if
|
||||
// we possibly recovered from the error, simply next().
|
||||
res.render('500', {
|
||||
status: err.status || 500
|
||||
, error: err
|
||||
});
|
||||
});
|
||||
|
||||
// Routes
|
||||
@@ -84,8 +65,14 @@ app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound(req.url);
|
||||
app.get('/404', function(req, res, next){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/403', function(req, res, next){
|
||||
var err = new Error('not allowed!');
|
||||
err.status = 403;
|
||||
next(err);
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res, next){
|
||||
|
||||
@@ -1,4 +1 @@
|
||||
- if (error.path)
|
||||
h2 Cannot find #{error.path}
|
||||
- else
|
||||
h2 Page Not Found
|
||||
h2 Cannot find #{url}
|
||||
@@ -5,4 +5,7 @@ ul
|
||||
a(href="/500") 500
|
||||
li
|
||||
| visit
|
||||
a(href="/404") 404
|
||||
a(href="/404") 404
|
||||
li
|
||||
| visit
|
||||
a(href='/403') 403
|
||||
@@ -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');
|
||||
66
examples/extending-templates/app.js
Normal file
66
examples/extending-templates/app.js
Normal file
@@ -0,0 +1,66 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Register ejs as .html
|
||||
|
||||
app.register('.html', require('ejs'));
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@sencha.com' }
|
||||
, { name: 'ciaran', email: 'ciaranj@gmail.com' }
|
||||
, { name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
];
|
||||
|
||||
// dynamic helpers are simply functions that are invoked
|
||||
// per request (once), passed both the request and response
|
||||
// objects. These can be used for request-specific
|
||||
// details within a view, such telling the layout which
|
||||
// scripts to include.
|
||||
app.dynamicHelpers({
|
||||
// by simply returning an object here
|
||||
// we can set it's properties such as "page.title"
|
||||
// within a view, and it remains specific to that request,
|
||||
// so it would be valid to do:
|
||||
// page.title = user.name + "'s account"
|
||||
page: function() {
|
||||
return {};
|
||||
},
|
||||
|
||||
// the scripts array here is assigned once,
|
||||
// so by returning a closure, we can use script(path)
|
||||
// in a template, instead of something like
|
||||
// scripts.push(path).
|
||||
script: function(req){
|
||||
req._scripts = [];
|
||||
return function(path){
|
||||
req._scripts.push(path);
|
||||
}
|
||||
},
|
||||
|
||||
// to expose our scripts array for iteration within
|
||||
// our views (typically the layout), we simply return it
|
||||
// here, and since composite types are mutable, it will
|
||||
// contain all of the paths pushed with the helper above.
|
||||
scripts: function(req){
|
||||
return req._scripts;
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
11
examples/extending-templates/views/layout.html
Normal file
11
examples/extending-templates/views/layout.html
Normal file
@@ -0,0 +1,11 @@
|
||||
<html>
|
||||
<head>
|
||||
<title><%- page.title %></title>
|
||||
<% for (var i in scripts) { %>
|
||||
<script src="<%= scripts[i] %>"></script>
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
8
examples/extending-templates/views/users/index.html
Normal file
8
examples/extending-templates/views/users/index.html
Normal file
@@ -0,0 +1,8 @@
|
||||
<% page.title = 'Users' %>
|
||||
<% script('/javascripts/jquery.js') %>
|
||||
<% script('/javascripts/users.js') %>
|
||||
|
||||
<h1>Users</h1>
|
||||
<ul id="users">
|
||||
<%- partial('user', users) %>
|
||||
</ul>
|
||||
1
examples/extending-templates/views/users/user.html
Normal file
1
examples/extending-templates/views/users/user.html
Normal file
@@ -0,0 +1 @@
|
||||
<li><%= user.name %> <<%= user.email %>></li>
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -15,10 +12,12 @@ var pub = __dirname + '/public';
|
||||
// Auto-compile sass to css with "compiler"
|
||||
// and then serve with connect's staticProvider
|
||||
|
||||
var app = express.createServer(
|
||||
express.compiler({ src: pub, enable: ['sass'] })
|
||||
, express.static(pub)
|
||||
);
|
||||
var app = express.createServer();
|
||||
app.use(express.logger('dev'));
|
||||
app.use(express.compiler({ src: pub, enable: ['sass'] }));
|
||||
app.use(app.router);
|
||||
app.use(express.static(pub));
|
||||
app.use(express.errorHandler({ dump: true, stack: true }));
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
@@ -29,17 +28,30 @@ app.set('views', __dirname + '/views');
|
||||
// (although you can still mix and match)
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
function User(name, email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@vision-media.ca' }
|
||||
, { name: 'ciaran', email: 'ciaranj@gmail.com' }
|
||||
, { name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
new User('tj', 'tj@vision-media.ca')
|
||||
, new User('ciaran', 'ciaranj@gmail.com')
|
||||
, new User('aaron', 'aaron.heckmann+github@gmail.com')
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
app.get('/users/callback', function(req, res){
|
||||
// a callback is also accepted
|
||||
res.partial('users/user', users, function(err, html){
|
||||
if (err) throw err;
|
||||
res.send(html);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/users', function(req, res){
|
||||
// we can use res.partial() as if
|
||||
// we were in a view, utilizing the same api
|
||||
@@ -48,7 +60,11 @@ app.get('/users', function(req, res){
|
||||
});
|
||||
|
||||
app.get('/users/list', function(req, res){
|
||||
res.partial('users/list', { object: users });
|
||||
// use "object" to utilize the name deduced from
|
||||
// the view filename. The examples below are equivalent
|
||||
|
||||
//res.partial('users/list', { object: users });
|
||||
res.partial('users/list', { list: users });
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
|
||||
3
examples/jade/views/header.jade
Normal file
3
examples/jade/views/header.jade
Normal file
@@ -0,0 +1,3 @@
|
||||
head
|
||||
title Jade Example
|
||||
link(rel="stylesheet", href="/stylesheets/style.css")
|
||||
@@ -1,6 +1,4 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Jade Example
|
||||
link(rel="stylesheet", href="/stylesheets/style.css")
|
||||
include header
|
||||
body!= body
|
||||
26
examples/json-config/app.js
Normal file
26
examples/json-config/app.js
Normal file
@@ -0,0 +1,26 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
app = express.createServer();
|
||||
|
||||
// load the config for this environment (NODE_ENV)
|
||||
|
||||
var config = require('./config')[app.settings.env];
|
||||
|
||||
// apply settings
|
||||
|
||||
for (var key in config) app.set(key, config[key]);
|
||||
|
||||
// apply middleware
|
||||
|
||||
config.middleware.forEach(app.use.bind(app));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index', { layout: false });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
19
examples/json-config/config.js
Normal file
19
examples/json-config/config.js
Normal file
@@ -0,0 +1,19 @@
|
||||
|
||||
// ok so it's not JSON, but close enough :)
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
exports.development = {
|
||||
'view engine': 'jade'
|
||||
, 'views': __dirname + '/views'
|
||||
, 'title': 'My Site'
|
||||
, 'middleware': [
|
||||
express.logger('dev')
|
||||
, app.router
|
||||
, express.static(__dirname + '/public')
|
||||
]
|
||||
};
|
||||
|
||||
exports.production = {
|
||||
|
||||
};
|
||||
4
examples/json-config/views/index.jade
Normal file
4
examples/json-config/views/index.jade
Normal file
@@ -0,0 +1,4 @@
|
||||
html
|
||||
body
|
||||
h1 #{settings.title}
|
||||
p Simple example
|
||||
32
examples/layout-control/app.js
Normal file
32
examples/layout-control/app.js
Normal file
@@ -0,0 +1,32 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// set default layout, usually "layout"
|
||||
app.set('view options', { layout: 'layouts/default' });
|
||||
|
||||
// Set our default template engine to "ejs"
|
||||
// which prevents the need for extensions
|
||||
// (although you can still mix and match)
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('pages/default');
|
||||
});
|
||||
|
||||
app.get('/alternate', function(req, res){
|
||||
// note that we do not explicitly
|
||||
// state the layout here, the view does,
|
||||
// although we could do it here as well.
|
||||
res.render('pages/alternate');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
6
examples/layout-control/views/layouts/alternate.ejs
Normal file
6
examples/layout-control/views/layouts/alternate.ejs
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Alternate Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
6
examples/layout-control/views/layouts/default.ejs
Normal file
6
examples/layout-control/views/layouts/default.ejs
Normal file
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Default Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
2
examples/layout-control/views/pages/alternate.ejs
Normal file
2
examples/layout-control/views/pages/alternate.ejs
Normal file
@@ -0,0 +1,2 @@
|
||||
<% layout('layouts/alternate') %>
|
||||
<h1>Page</h1>
|
||||
1
examples/layout-control/views/pages/default.ejs
Normal file
1
examples/layout-control/views/pages/default.ejs
Normal file
@@ -0,0 +1 @@
|
||||
<h1>Page</h1>
|
||||
@@ -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];
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -14,7 +14,7 @@ exports.boot = function(app){
|
||||
// App settings and middleware
|
||||
|
||||
function bootApplication(app) {
|
||||
app.use(express.logger({ format: ':method :url :status' }));
|
||||
app.use(express.logger(':method :url :status'));
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieParser());
|
||||
@@ -45,6 +45,7 @@ function bootApplication(app) {
|
||||
},
|
||||
|
||||
hasMessages: function(req){
|
||||
if (!req.session) return false;
|
||||
return Object.keys(req.session.flash || {}).length;
|
||||
},
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -28,13 +25,8 @@ var ninja = {
|
||||
};
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('ninjas', { ninja: ninja });
|
||||
res.render('ninja', { ninja: ninja });
|
||||
});
|
||||
|
||||
app.get('/li', function(req, res){
|
||||
res.partial('li', { object: 'Testing', as: 'value' });
|
||||
});
|
||||
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
h1= ninja.name
|
||||
|
||||
// file, partial name, and partial object all match ('summary')
|
||||
// the partial filename prefix '_' is completely optional
|
||||
#summary!= partial('summary', ninja.summary)
|
||||
// the partial filename prefix '_' is completely optional.
|
||||
|
||||
// In this case we need to specify ninja.summary as the object
|
||||
// option, since it is a "plain" object Express cannot otherwise
|
||||
// tell if it is intended to be locals, or THE summary object
|
||||
#summary!= partial('summary', { object: ninja.summary })
|
||||
|
||||
// file, partial name = '_weapon', resolves to 'weapon' object within partial
|
||||
#weapons
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
44
examples/say/app.js
Normal file
44
examples/say/app.js
Normal file
@@ -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);
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
@@ -1,28 +1,30 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
// $ npm install connect-redis
|
||||
var RedisStore = require('connect-redis');
|
||||
// pass the express to the connect redis module
|
||||
// allowing it to inherit from express.session.Store
|
||||
var RedisStore = require('connect-redis')(express);
|
||||
|
||||
var app = express.createServer(
|
||||
express.logger(),
|
||||
var app = express.createServer();
|
||||
|
||||
// Required by session() middleware
|
||||
express.cookieDecoder(),
|
||||
app.use(express.favicon());
|
||||
|
||||
// Populates:
|
||||
// - req.session
|
||||
// - req.sessionStore
|
||||
// - req.sessionID (or req.session.id)
|
||||
express.session({ secret: 'keyboard cat', store: new RedisStore })
|
||||
);
|
||||
// request logging
|
||||
app.use(express.logger());
|
||||
|
||||
// required to parse the session cookie
|
||||
app.use(express.cookieParser());
|
||||
|
||||
// Populates:
|
||||
// - req.session
|
||||
// - req.sessionStore
|
||||
// - req.sessionID (or req.session.id)
|
||||
|
||||
app.use(express.session({ secret: 'keyboard cat', store: new RedisStore }));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
var body = '';
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -18,11 +15,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
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
121
examples/web-service/app.js
Normal file
121
examples/web-service/app.js
Normal file
@@ -0,0 +1,121 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// configuration
|
||||
|
||||
// if we wanted to supply more than JSON, we could
|
||||
// use something similar to the content-negotiation
|
||||
// example.
|
||||
|
||||
// here we validate the API key,
|
||||
// by mounting this middleware to /api/v1
|
||||
// meaning only paths prefixed with "/api/v1"
|
||||
// will cause this middleware to be invoked
|
||||
|
||||
app.use('/api/v1', function(req, res, next){
|
||||
var key = req.query['api-key'];
|
||||
|
||||
// key isnt present
|
||||
if (!key) return next(new Error('api key required'));
|
||||
|
||||
// key is invalid
|
||||
if (!~apiKeys.indexOf(key)) return next(new Error('invalid api key'));
|
||||
|
||||
// all good, store req.key for route access
|
||||
req.key = key;
|
||||
next();
|
||||
});
|
||||
|
||||
// position our routes above the error handling middleware,
|
||||
// and below our API middleware, since we want the API validation
|
||||
// to take place BEFORE our routes
|
||||
app.use(app.router);
|
||||
|
||||
// middleware with an arity of 4 are considered
|
||||
// error handling middleware. When you next(err)
|
||||
// it will be passed through the defined middleware
|
||||
// in order, but ONLY those with an arity of 4, ignoring
|
||||
// regular middleware.
|
||||
app.use(function(err, req, res, next){
|
||||
// whatever you want here, feel free to populate
|
||||
// properties on `err` to treat it differently in here,
|
||||
// or when you next(err) set res.statusCode= etc.
|
||||
res.send({ error: err.message }, 500);
|
||||
});
|
||||
|
||||
// our custom JSON 404 middleware. Since it's placed last
|
||||
// it will be the last middleware called, if all others
|
||||
// invoke next() and do not respond.
|
||||
app.use(function(req, res){
|
||||
res.send({ error: "Lame, can't find that" }, 404);
|
||||
});
|
||||
|
||||
/**
|
||||
* Generate our unique identifier.
|
||||
*/
|
||||
|
||||
function uid() {
|
||||
return [
|
||||
Math.random() * 0xffff | 0
|
||||
, Math.random() * 0xffff | 0
|
||||
, Math.random() * 0xffff | 0
|
||||
, Date.now()
|
||||
].join('-');
|
||||
}
|
||||
|
||||
// map of valid api keys, typically mapped to
|
||||
// account info with some sort of database like redis.
|
||||
// api keys do _not_ serve as authentication, merely to
|
||||
// track API usage or help prevent malicious behavior etc.
|
||||
|
||||
var apiKeys = [uid(), uid(), uid()];
|
||||
|
||||
console.log('valid keys:\n ', apiKeys.join('\n '));
|
||||
|
||||
// these two objects will serve as our faux database
|
||||
|
||||
var repos = [
|
||||
{ name: 'express', url: 'http://github.com/visionmedia/express' }
|
||||
, { name: 'stylus', url: 'http://github.com/learnboost/stylus' }
|
||||
, { name: 'cluster', url: 'http://github.com/learnboost/cluster' }
|
||||
];
|
||||
|
||||
var users = [
|
||||
{ name: 'tobi' }
|
||||
, { name: 'loki' }
|
||||
, { name: 'jane' }
|
||||
];
|
||||
|
||||
var userRepos = {
|
||||
tobi: [repos[0], repos[1]]
|
||||
, loki: [repos[1]]
|
||||
, jane: [repos[2]]
|
||||
};
|
||||
|
||||
// we now can assume the api key is valid,
|
||||
// and simply expose the data
|
||||
|
||||
app.get('/api/v1/users', function(req, res, next){
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
app.get('/api/v1/repos', function(req, res, next){
|
||||
res.send(repos);
|
||||
});
|
||||
|
||||
app.get('/api/v1/user/:name/repos', function(req, res, next){
|
||||
var name = req.params.name
|
||||
, user = userRepos[name];
|
||||
|
||||
if (user) res.send(user);
|
||||
else next();
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
@@ -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.0.0beta2';
|
||||
exports.version = '2.4.7';
|
||||
|
||||
/**
|
||||
* Shortcut for `new Server(...)`.
|
||||
@@ -46,16 +47,19 @@ exports.createServer = function(options){
|
||||
};
|
||||
|
||||
/**
|
||||
* Expose `HTTPServer`.
|
||||
* Expose constructors.
|
||||
*/
|
||||
|
||||
exports.HTTPServer = HTTPServer;
|
||||
exports.HTTPSServer = HTTPSServer;
|
||||
exports.Route = Route;
|
||||
|
||||
/**
|
||||
* View extensions.
|
||||
*/
|
||||
|
||||
require('./view');
|
||||
exports.View =
|
||||
exports.view = require('./view');
|
||||
|
||||
/**
|
||||
* Response extensions.
|
||||
@@ -68,3 +72,8 @@ require('./response');
|
||||
*/
|
||||
|
||||
require('./request');
|
||||
|
||||
// Error handler title
|
||||
|
||||
exports.errorHandler.title = 'Express';
|
||||
|
||||
|
||||
367
lib/http.js
367
lib/http.js
@@ -11,12 +11,26 @@
|
||||
|
||||
var qs = require('qs')
|
||||
, connect = require('connect')
|
||||
, router = connect.router
|
||||
, methods = router.methods.concat(['del', 'all'])
|
||||
, router = require('./router')
|
||||
, Router = require('./router')
|
||||
, view = require('./view')
|
||||
, toArray = require('./utils').toArray
|
||||
, methods = router.methods.concat('del', 'all')
|
||||
, 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 +38,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 +47,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,19 +56,16 @@ Server.prototype.__proto__ = connect.HTTPServer.prototype;
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Server.prototype.init = function(middleware){
|
||||
app.init = function(middleware){
|
||||
var self = this;
|
||||
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
|
||||
@@ -76,28 +87,96 @@ Server.prototype.init = function(middleware){
|
||||
// apply middleware
|
||||
if (middleware) middleware.forEach(self.use.bind(self));
|
||||
|
||||
// use router, expose as app.get(), etc
|
||||
var fn = router(function(app){ self.routes = app; });
|
||||
// router
|
||||
this.routes = new Router(this);
|
||||
this.__defineGetter__('router', function(){
|
||||
this.__usedRouter = true;
|
||||
return fn;
|
||||
return self.routes.middleware;
|
||||
});
|
||||
|
||||
// default locals
|
||||
this.locals({
|
||||
settings: this.settings
|
||||
, app: this
|
||||
});
|
||||
|
||||
// default development configuration
|
||||
this.configure('development', function(){
|
||||
this.enable('hints');
|
||||
});
|
||||
|
||||
// default production configuration
|
||||
this.configure('production', function(){
|
||||
this.enable('cache views');
|
||||
this.enable('view cache');
|
||||
});
|
||||
|
||||
// register error handlers on "listening"
|
||||
// so that they disregard definition position.
|
||||
this.on('listening', this.registerErrorHandlers.bind(this));
|
||||
|
||||
// route manipulation methods
|
||||
methods.forEach(function(method){
|
||||
self.lookup[method] = function(path){
|
||||
return self.routes.lookup(method, path);
|
||||
};
|
||||
|
||||
self.match[method] = function(path){
|
||||
return self.routes.match(method, path);
|
||||
};
|
||||
|
||||
self.remove[method] = function(path){
|
||||
return self.routes.lookup(method, path).remove();
|
||||
};
|
||||
});
|
||||
|
||||
// del -> delete
|
||||
self.lookup.del = self.lookup.delete;
|
||||
self.match.del = self.match.delete;
|
||||
self.remove.del = self.remove.delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove routes matching the given `path`.
|
||||
*
|
||||
* @param {Route} path
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.remove = function(path){
|
||||
return this.routes.lookup('all', path).remove();
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup routes defined with a path
|
||||
* equivalent to `path`.
|
||||
*
|
||||
* @param {Stirng} path
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.lookup = function(path){
|
||||
return this.routes.lookup('all', path);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup routes matching the given `url`.
|
||||
*
|
||||
* @param {Stirng} url
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.match = function(url){
|
||||
return this.routes.match('all', url);
|
||||
};
|
||||
|
||||
/**
|
||||
* When using the vhost() middleware register error handlers.
|
||||
*/
|
||||
|
||||
Server.prototype.onvhost = function(){
|
||||
app.onvhost = function(){
|
||||
this.registerErrorHandlers();
|
||||
};
|
||||
|
||||
@@ -108,7 +187,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);
|
||||
@@ -127,26 +206,38 @@ Server.prototype.registerErrorHandlers = function(){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.use = function(route, middleware){
|
||||
var app, home;
|
||||
app.use = function(route, middleware){
|
||||
var app, base, handle;
|
||||
|
||||
if ('string' != typeof route) {
|
||||
middleware = route, route = '/';
|
||||
}
|
||||
|
||||
// express app
|
||||
if (middleware.handle && middleware.set) app = middleware;
|
||||
|
||||
// restore .app property on req and res
|
||||
if (app) {
|
||||
app.route = route;
|
||||
middleware = function(req, res, next) {
|
||||
var orig = req.app;
|
||||
app.handle(req, res, function(err){
|
||||
req.app = res.app = orig;
|
||||
next(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
connect.HTTPServer.prototype.use.call(this, route, middleware);
|
||||
|
||||
// mounted an app
|
||||
if (middleware.handle) {
|
||||
app = middleware;
|
||||
// express app
|
||||
if (app.set) {
|
||||
home = app.set('home');
|
||||
if ('/' == home) home = '';
|
||||
app.set('home', app.route + home);
|
||||
app.parent = this;
|
||||
}
|
||||
// mounted hook
|
||||
// mounted an app, invoke the hook
|
||||
// and adjust some settings
|
||||
if (app) {
|
||||
base = this.set('basepath') || this.route;
|
||||
if ('/' == base) base = '';
|
||||
base = base + (app.set('basepath') || app.route);
|
||||
app.set('basepath', base);
|
||||
app.parent = this;
|
||||
if (app.__mounted) app.__mounted.call(app, this);
|
||||
}
|
||||
|
||||
@@ -174,7 +265,7 @@ Server.prototype.use = function(route, middleware){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.mounted = function(fn){
|
||||
app.mounted = function(fn){
|
||||
this.__mounted = fn;
|
||||
return this;
|
||||
};
|
||||
@@ -186,7 +277,7 @@ Server.prototype.mounted = function(fn){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.register = function(){
|
||||
app.register = function(){
|
||||
view.register.apply(this, arguments);
|
||||
return this;
|
||||
};
|
||||
@@ -200,9 +291,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;
|
||||
};
|
||||
|
||||
@@ -215,63 +306,92 @@ Server.prototype.locals = function(obj){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.dynamicHelpers = function(obj){
|
||||
app.dynamicHelpers = function(obj){
|
||||
utils.merge(this.dynamicViewHelpers, obj);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map the given param placeholder `name`(s) to the given callback `fn`.
|
||||
* Map the given param placeholder `name`(s) to the given callback(s).
|
||||
*
|
||||
* Param mapping is used to provide pre-conditions to routes
|
||||
* which us normalized placeholders. For example ":user_id" may
|
||||
* attempt to load the user from the database, where as ":num" may
|
||||
* pass the value through `parseInt(num, 10)`.
|
||||
* which us normalized placeholders. This callback has the same
|
||||
* signature as regular middleware, for example below when ":userId"
|
||||
* is used this function will be invoked in an attempt to load the user.
|
||||
*
|
||||
* When the callback function accepts only a single argument, the
|
||||
* value of placeholder is passed:
|
||||
* app.param('userId', function(req, res, next, id){
|
||||
* User.find(id, function(err, user){
|
||||
* if (err) {
|
||||
* next(err);
|
||||
* } else if (user) {
|
||||
* req.user = user;
|
||||
* next();
|
||||
* } else {
|
||||
* next(new Error('failed to load user'));
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* app.param('page', function(n){ return parseInt(n, 10); });
|
||||
* Passing a single function allows you to map logic
|
||||
* to the values passed to `app.param()`, for example
|
||||
* this is useful to provide coercion support in a concise manner.
|
||||
*
|
||||
* After which "/users/:page" would automatically provide us with
|
||||
* an integer for `req.params.page`. If desired we could use the callback
|
||||
* signature shown below, and immediately `next(new Error('invalid page'))`
|
||||
* when `parseInt` fails.
|
||||
* The following example maps regular expressions to param values
|
||||
* ensuring that they match, otherwise passing control to the next
|
||||
* route:
|
||||
*
|
||||
* Alternatively the callback may accept the request, response, next, and
|
||||
* the value, acting like middlware:
|
||||
* app.param(function(name, regexp){
|
||||
* if (regexp instanceof RegExp) {
|
||||
* return function(req, res, next, val){
|
||||
* var captures;
|
||||
* if (captures = regexp.exec(String(val))) {
|
||||
* req.params[name] = captures;
|
||||
* next();
|
||||
* } else {
|
||||
* next('route');
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* app.param('userId', function(req, res, next, id){
|
||||
* User.find(id, function(err, user){
|
||||
* if (err) {
|
||||
* next(err);
|
||||
* } else if (user) {
|
||||
* req.user = user;
|
||||
* next();
|
||||
* } else {
|
||||
* next(new Error('failed to load user'));
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
* We can now use it as shown below, where "/commit/:commit" expects
|
||||
* that the value for ":commit" is at 5 or more digits. The capture
|
||||
* groups are then available as `req.params.commit` as we defined
|
||||
* in the function above.
|
||||
*
|
||||
* Now every time ":userId" is present, the associated user object
|
||||
* will be loaded and assigned before the route handler is invoked.
|
||||
* app.param('commit', /^\d{5,}$/);
|
||||
*
|
||||
* @param {String|Array} name
|
||||
* For more of this useful functionality take a look
|
||||
* at [express-params](http://github.com/visionmedia/express-params).
|
||||
*
|
||||
* @param {String|Array|Function} name
|
||||
* @param {Function} fn
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.param = function(name, fn){
|
||||
app.param = function(name, fn){
|
||||
var self = this
|
||||
, fns = [].slice.call(arguments, 1);
|
||||
|
||||
// array
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function(name){
|
||||
this.param(name, fn);
|
||||
}, this);
|
||||
fns.forEach(function(fn){
|
||||
self.param(name, fn);
|
||||
});
|
||||
});
|
||||
// param logic
|
||||
} else if ('function' == typeof name) {
|
||||
this.routes.param(name);
|
||||
// single
|
||||
} else {
|
||||
if (':' == name[0]) name = name.substr(1);
|
||||
this.routes.param(name, fn);
|
||||
fns.forEach(function(fn){
|
||||
self.routes.param(name, fn);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
@@ -284,7 +404,7 @@ Server.prototype.param = function(name, fn){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.error = function(fn){
|
||||
app.error = function(fn){
|
||||
this.errorHandlers.push(fn);
|
||||
return this;
|
||||
};
|
||||
@@ -298,7 +418,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;
|
||||
@@ -314,7 +434,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];
|
||||
@@ -335,7 +455,7 @@ Server.prototype.set = function(setting, val){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.enabled = function(setting){
|
||||
app.enabled = function(setting){
|
||||
return !!this.set(setting);
|
||||
};
|
||||
|
||||
@@ -347,7 +467,7 @@ Server.prototype.enabled = function(setting){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.disabled = function(setting){
|
||||
app.disabled = function(setting){
|
||||
return !this.set(setting);
|
||||
};
|
||||
|
||||
@@ -359,7 +479,7 @@ Server.prototype.disabled = function(setting){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.enable = function(setting){
|
||||
app.enable = function(setting){
|
||||
return this.set(setting, true);
|
||||
};
|
||||
|
||||
@@ -371,7 +491,7 @@ Server.prototype.enable = function(setting){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.disable = function(setting){
|
||||
app.disable = function(setting){
|
||||
return this.set(setting, false);
|
||||
};
|
||||
|
||||
@@ -384,65 +504,80 @@ Server.prototype.disable = function(setting){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.redirect = function(key, url){
|
||||
app.redirect = function(key, url){
|
||||
this.redirects[key] = url;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configure callback for the given `env`.
|
||||
* Configure callback for zero or more envs,
|
||||
* when no env is specified that callback will
|
||||
* be invoked for all environments. Any combination
|
||||
* can be used multiple times, in any order desired.
|
||||
*
|
||||
* @param {String} env
|
||||
* Examples:
|
||||
*
|
||||
* app.configure(function(){
|
||||
* // executed for all envs
|
||||
* });
|
||||
*
|
||||
* app.configure('stage', function(){
|
||||
* // executed staging env
|
||||
* });
|
||||
*
|
||||
* app.configure('stage', 'production', function(){
|
||||
* // executed for stage and production
|
||||
* });
|
||||
*
|
||||
* @param {String} env...
|
||||
* @param {Function} fn
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Server.prototype.configure = function(env, fn){
|
||||
if ('function' == typeof env) {
|
||||
fn = env, env = 'all';
|
||||
}
|
||||
if ('all' == env || env == this.settings.env) {
|
||||
fn.call(this);
|
||||
}
|
||||
app.configure = function(env, fn){
|
||||
var envs = 'all'
|
||||
, args = toArray(arguments);
|
||||
fn = args.pop();
|
||||
if (args.length) envs = args;
|
||||
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
|
||||
return this;
|
||||
};
|
||||
|
||||
// Generate routing methods
|
||||
/**
|
||||
* Delegate `.VERB(...)` calls to `.route(VERB, ...)`.
|
||||
*/
|
||||
|
||||
function generateMethod(method){
|
||||
Server.prototype[method] = function(path, fn){
|
||||
var self = this;
|
||||
methods.forEach(function(method){
|
||||
app[method] = function(path){
|
||||
if (1 == arguments.length) return this.routes.lookup(method, path);
|
||||
var args = [method].concat(toArray(arguments));
|
||||
if (!this.__usedRouter) this.use(this.router);
|
||||
return this.routes._route.apply(this.routes, args);
|
||||
}
|
||||
});
|
||||
|
||||
// Ensure router is mounted
|
||||
if (!this.__usedRouter) {
|
||||
this.use(this.router);
|
||||
}
|
||||
/**
|
||||
* Special-cased "all" method, applying the given route `path`,
|
||||
* middleware, and callback to _every_ HTTP method.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} ...
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
// 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);
|
||||
}
|
||||
app.all = function(path){
|
||||
var args = arguments;
|
||||
if (1 == args.length) return this.routes.lookup('all', path);
|
||||
methods.forEach(function(method){
|
||||
if ('all' == method) return;
|
||||
app[method].apply(this, args);
|
||||
}, this);
|
||||
return this;
|
||||
};
|
||||
|
||||
// Generate the route
|
||||
this.routes[method](path, fn);
|
||||
return this;
|
||||
};
|
||||
return arguments.callee;
|
||||
}
|
||||
// del -> delete alias
|
||||
|
||||
methods.forEach(generateMethod);
|
||||
app.del = app.delete;
|
||||
|
||||
// Alias delete as "del"
|
||||
|
||||
Server.prototype.del = Server.prototype.delete;
|
||||
|
||||
18
lib/https.js
18
lib/https.js
@@ -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];
|
||||
});
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
var http = require('http')
|
||||
, req = http.IncomingMessage.prototype
|
||||
, utils = require('./utils')
|
||||
, parse = require('url').parse
|
||||
, mime = require('mime');
|
||||
|
||||
/**
|
||||
@@ -65,6 +66,39 @@ req.header = function(name, defaultValue){
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get `field`'s `param` value, defaulting to ''.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* req.get('content-disposition', 'filename');
|
||||
* // => "something.png"
|
||||
*
|
||||
* @param {String} field
|
||||
* @param {String} param
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.get = function(field, param){
|
||||
var val = this.header(field);
|
||||
if (!val) return '';
|
||||
var regexp = new RegExp(param + ' *= *(?:"([^"]+)"|([^;]+))', 'i');
|
||||
if (!regexp.exec(val)) return '';
|
||||
return RegExp.$1 || RegExp.$2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Short-hand for `require('url').parse(req.url).pathname`.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('path', function(){
|
||||
return parse(this.url).pathname;
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the _Accept_ header is present, and includes the given `type`.
|
||||
*
|
||||
@@ -106,16 +140,14 @@ req.accepts = function(type){
|
||||
return true;
|
||||
} else if (type) {
|
||||
// allow "html" vs "text/html" etc
|
||||
if (type.indexOf('/') < 0) {
|
||||
type = mime.lookup(type);
|
||||
}
|
||||
if (!~type.indexOf('/')) type = mime.lookup(type);
|
||||
|
||||
// check if we have a direct match
|
||||
if (~accept.indexOf(type)) return true;
|
||||
|
||||
// check if we have type/*
|
||||
type = type.split('/')[0] + '/*';
|
||||
return accept.indexOf(type) >= 0;
|
||||
return !!~accept.indexOf(type);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -140,7 +172,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,16 +222,17 @@ 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
|
||||
, args = arguments
|
||||
, formatters = this.app.flashFormatters || {};
|
||||
formatters.__proto__ = flashFormatters;
|
||||
msg = utils.miniMarkdown(utils.htmlEscape(msg));
|
||||
msg = utils.miniMarkdown(msg);
|
||||
msg = msg.replace(/%([a-zA-Z])/g, function(_, format){
|
||||
var formatter = formatters[format];
|
||||
if (formatter) return formatter(args[i++]);
|
||||
if (formatter) return formatter(utils.escape(args[i++]));
|
||||
});
|
||||
return (msgs[type] = msgs[type] || []).push(msg);
|
||||
} else if (type) {
|
||||
@@ -268,7 +301,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
|
||||
|
||||
188
lib/response.js
188
lib/response.js
@@ -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.
|
||||
@@ -51,7 +52,7 @@ res.send = function(body, headers, status){
|
||||
status = status || this.statusCode;
|
||||
|
||||
// allow 0 args as 204
|
||||
if (!arguments.length) body = status = 204;
|
||||
if (!arguments.length || undefined === body) status = 204;
|
||||
|
||||
// determine content type
|
||||
switch (typeof body) {
|
||||
@@ -63,30 +64,25 @@ res.send = function(body, headers, status){
|
||||
break;
|
||||
case 'string':
|
||||
if (!this.header('Content-Type')) {
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.contentType('.html');
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
if (body instanceof Buffer) {
|
||||
if (Buffer.isBuffer(body)) {
|
||||
if (!this.header('Content-Type')) {
|
||||
this.contentType('.bin');
|
||||
}
|
||||
} else {
|
||||
if (!this.header('Content-Type')) {
|
||||
this.contentType('.json');
|
||||
}
|
||||
body = JSON.stringify(body);
|
||||
if (this.req.query.callback && this.app.set('jsonp callback')) {
|
||||
this.header('Content-Type', 'text/javascript');
|
||||
body = this.req.query.callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
|
||||
}
|
||||
return this.json(body, headers, status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// populate Content-Length
|
||||
if (!this.header('Content-Length')) {
|
||||
this.header('Content-Length', body instanceof Buffer
|
||||
if (undefined !== body && !this.header('Content-Length')) {
|
||||
this.header('Content-Length', Buffer.isBuffer(body)
|
||||
? body.length
|
||||
: Buffer.byteLength(body));
|
||||
}
|
||||
@@ -101,14 +97,62 @@ res.send = function(body, headers, status){
|
||||
}
|
||||
|
||||
// strip irrelevant headers
|
||||
if (204 === status) {
|
||||
if (204 == status || 304 == status) {
|
||||
this.removeHeader('Content-Type');
|
||||
this.removeHeader('Content-Length');
|
||||
body = '';
|
||||
}
|
||||
|
||||
// respond
|
||||
this.statusCode = status;
|
||||
this.end('HEAD' == this.req.method ? undefined : body);
|
||||
this.end('HEAD' == this.req.method ? null : body);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send JSON response with `obj`, optional `headers`, and optional `status`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.json(null);
|
||||
* res.json({ user: 'tj' });
|
||||
* res.json('oh noes!', 500);
|
||||
* res.json('I dont have that', 404);
|
||||
*
|
||||
* @param {Mixed} obj
|
||||
* @param {Object|Number} headers or status
|
||||
* @param {Number} status
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.json = function(obj, headers, status){
|
||||
var body = JSON.stringify(obj)
|
||||
, callback = this.req.query.callback
|
||||
, jsonp = this.app.enabled('jsonp callback');
|
||||
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.header('Content-Type', 'application/json');
|
||||
|
||||
if (callback && jsonp) {
|
||||
this.header('Content-Type', 'text/javascript');
|
||||
body = callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
|
||||
}
|
||||
|
||||
return this.send(body, headers, status);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set status `code`.
|
||||
*
|
||||
* @param {Number} code
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.status = function(code){
|
||||
this.statusCode = code;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -128,6 +172,7 @@ res.send = function(body, headers, status){
|
||||
*/
|
||||
|
||||
res.sendfile = function(path, options, fn){
|
||||
var next = this.req.next;
|
||||
options = options || {};
|
||||
|
||||
// support function as second arg
|
||||
@@ -136,9 +181,9 @@ res.sendfile = function(path, options, fn){
|
||||
options = {};
|
||||
}
|
||||
|
||||
options.path = path;
|
||||
options.path = encodeURIComponent(path);
|
||||
options.callback = fn;
|
||||
send(this.req, this, this.req.next, options);
|
||||
send(this.req, this, next, options);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -173,40 +218,50 @@ 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;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`, with optional
|
||||
* `filename` as an attachment and optional callback `fn(err)`.
|
||||
* `filename` as an attachment and optional callback `fn(err)`,
|
||||
* and optional `fn2(err)` which is invoked when an error has
|
||||
* occurred after header has been sent.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String|Function} filename or fn
|
||||
* @param {Function} fn
|
||||
* @return {Type}
|
||||
* @param {Function} fn2
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.download = function(path, filename, fn){
|
||||
res.download = function(path, filename, fn, fn2){
|
||||
var self = this;
|
||||
|
||||
// support callback as second arg
|
||||
if ('function' == typeof filename) {
|
||||
fn2 = fn;
|
||||
fn = filename;
|
||||
filename = null;
|
||||
}
|
||||
|
||||
// transfer the file
|
||||
this.attachment(filename || path).sendfile(path, function(err){
|
||||
if (err) self.removeHeader('Content-Disposition');
|
||||
if (fn) return fn(err);
|
||||
var sentHeader = self._header;
|
||||
if (err) {
|
||||
self.req.next('ENOENT' == err.code
|
||||
? null
|
||||
: err);
|
||||
if (!sentHeader) self.removeHeader('Content-Disposition');
|
||||
if (sentHeader) {
|
||||
fn2 && fn2(err);
|
||||
} else if (fn) {
|
||||
fn(err);
|
||||
} else {
|
||||
self.req.next(err);
|
||||
}
|
||||
} else if (fn) {
|
||||
fn();
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -216,28 +271,29 @@ res.download = function(path, filename, fn){
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} val
|
||||
* @return {String}
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.header = function(name, val){
|
||||
if (val === undefined) {
|
||||
return this.getHeader(name);
|
||||
} else {
|
||||
this.setHeader(name, val);
|
||||
return val;
|
||||
}
|
||||
if (1 == arguments.length) return this.getHeader(name);
|
||||
this.setHeader(name, val);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -246,6 +302,7 @@ res.clearCookie = function(name){
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` max-age in milliseconds, converted to `expires`
|
||||
* - `path` defaults to the "basepath" setting which is typically "/"
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -264,6 +321,7 @@ res.clearCookie = function(name){
|
||||
res.cookie = function(name, val, options){
|
||||
options = options || {};
|
||||
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
|
||||
if (undefined === options.path) options.path = this.app.set('basepath');
|
||||
var cookie = utils.serializeCookie(name, val, options);
|
||||
this.header('Set-Cookie', cookie);
|
||||
};
|
||||
@@ -275,8 +333,8 @@ res.cookie = function(name, val, options){
|
||||
* The given `url` can also be the name of a mapped url, for
|
||||
* example by default express supports "back" which redirects
|
||||
* to the _Referrer_ or _Referer_ headers or the application's
|
||||
* "home" setting. Express also supports "home" out of the box,
|
||||
* which can be set via `app.set('home', '/blog');`, and defaults
|
||||
* "basepath" setting. Express also supports "basepath" out of the box,
|
||||
* which can be set via `app.set('basepath', '/blog');`, and defaults
|
||||
* to '/'.
|
||||
*
|
||||
* Redirect Mapping:
|
||||
@@ -315,34 +373,42 @@ res.cookie = function(name, val, options){
|
||||
|
||||
res.redirect = function(url, status){
|
||||
var app = this.app
|
||||
, base = app.set('home') || '/'
|
||||
, req = this.req
|
||||
, base = app.set('basepath') || app.route
|
||||
, status = status || 302
|
||||
, head = 'HEAD' == req.method
|
||||
, body;
|
||||
|
||||
// Setup redirect map
|
||||
var map = {
|
||||
back: this.req.header('Referrer', base)
|
||||
back: req.header('Referrer', base)
|
||||
, home: base
|
||||
};
|
||||
|
||||
// Support custom redirect map
|
||||
map.__proto__ = this.app.redirects;
|
||||
map.__proto__ = app.redirects;
|
||||
|
||||
// Attempt mapped redirect
|
||||
var mapped = 'function' == typeof map[url]
|
||||
? map[url](this.req, this)
|
||||
? map[url](req, this)
|
||||
: map[url];
|
||||
|
||||
// Perform redirect
|
||||
url = mapped || url;
|
||||
|
||||
// Respect mount-point
|
||||
if (app.route && !~url.indexOf('://')) {
|
||||
url = join(app.route, url);
|
||||
}
|
||||
// Relative
|
||||
if (!~url.indexOf('://')) {
|
||||
// Respect mount-point
|
||||
if ('/' != base && 0 != url.indexOf(base)) url = base + url;
|
||||
|
||||
// Absolute
|
||||
var host = req.headers.host
|
||||
, tls = req.connection.encrypted;
|
||||
url = 'http' + (tls ? 's' : '') + '://' + host + url;
|
||||
}
|
||||
|
||||
// Support text/{plain,html} by default
|
||||
if (this.req.accepts('html')) {
|
||||
if (req.accepts('html')) {
|
||||
body = '<p>' + http.STATUS_CODES[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
|
||||
this.header('Content-Type', 'text/html');
|
||||
} else {
|
||||
@@ -353,7 +419,7 @@ res.redirect = function(url, status){
|
||||
// Respond
|
||||
this.statusCode = status;
|
||||
this.header('Location', url);
|
||||
this.end(body);
|
||||
this.end(head ? null : body);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -367,8 +433,28 @@ res.redirect = function(url, status){
|
||||
*/
|
||||
|
||||
res.local = function(name, val){
|
||||
this.locals = this.locals || {};
|
||||
this._locals = this._locals || {};
|
||||
return undefined === val
|
||||
? this.locals[name]
|
||||
: this.locals[name] = val;
|
||||
? this._locals[name]
|
||||
: this._locals[name] = val;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign several locals with the given `obj`,
|
||||
* or return the locals.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {Object|Undefined}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.locals =
|
||||
res.helpers = function(obj){
|
||||
if (obj) {
|
||||
for (var key in obj) {
|
||||
this.local(key, obj[key]);
|
||||
}
|
||||
} else {
|
||||
return this._locals;
|
||||
}
|
||||
};
|
||||
|
||||
53
lib/router/collection.js
Normal file
53
lib/router/collection.js
Normal file
@@ -0,0 +1,53 @@
|
||||
|
||||
/*!
|
||||
* Express - router - Collection
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Expose `Collection`.
|
||||
*/
|
||||
|
||||
module.exports = Collection;
|
||||
|
||||
/**
|
||||
* Initialize a new route `Collection`
|
||||
* with the given `router`.
|
||||
*
|
||||
* @param {Router} router
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Collection(router) {
|
||||
Array.apply(this, arguments);
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Array.prototype`.
|
||||
*/
|
||||
|
||||
Collection.prototype.__proto__ = Array.prototype;
|
||||
|
||||
/**
|
||||
* Remove the routes in this collection.
|
||||
*
|
||||
* @return {Collection} of routes removed
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Collection.prototype.remove = function(){
|
||||
var router = this.router
|
||||
, len = this.length
|
||||
, ret = new Collection(this.router);
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (router.remove(this[i])) {
|
||||
ret.push(this[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
398
lib/router/index.js
Normal file
398
lib/router/index.js
Normal file
@@ -0,0 +1,398 @@
|
||||
|
||||
/*!
|
||||
* Express - Router
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Route = require('./route')
|
||||
, Collection = require('./collection')
|
||||
, utils = require('../utils')
|
||||
, parse = require('url').parse
|
||||
, toArray = utils.toArray;
|
||||
|
||||
/**
|
||||
* Expose `Router` constructor.
|
||||
*/
|
||||
|
||||
exports = module.exports = Router;
|
||||
|
||||
/**
|
||||
* Expose HTTP methods.
|
||||
*/
|
||||
|
||||
var methods = exports.methods = require('./methods');
|
||||
|
||||
/**
|
||||
* Initialize a new `Router` with the given `app`.
|
||||
*
|
||||
* @param {express.HTTPServer} app
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Router(app) {
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.routes = {};
|
||||
this.params = {};
|
||||
this._params = [];
|
||||
|
||||
this.middleware = function(req, res, next){
|
||||
self._dispatch(req, res, next);
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Register a param callback `fn` for the given `name`.
|
||||
*
|
||||
* @param {String|Function} name
|
||||
* @param {Function} fn
|
||||
* @return {Router} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.param = function(name, fn){
|
||||
// param logic
|
||||
if ('function' == typeof name) {
|
||||
this._params.push(name);
|
||||
return;
|
||||
}
|
||||
|
||||
// apply param functions
|
||||
var params = this._params
|
||||
, len = params.length
|
||||
, ret;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (ret = params[i](name, fn)) {
|
||||
fn = ret;
|
||||
}
|
||||
}
|
||||
|
||||
// ensure we end up with a
|
||||
// middleware function
|
||||
if ('function' != typeof fn) {
|
||||
throw new Error('invalid param() call for ' + name + ', got ' + fn);
|
||||
}
|
||||
|
||||
(this.params[name] = this.params[name] || []).push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return a `Collection` of all routes defined.
|
||||
*
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.all = function(){
|
||||
return this.find(function(){
|
||||
return true;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the given `route`, returns
|
||||
* a bool indicating if the route was present
|
||||
* or not.
|
||||
*
|
||||
* @param {Route} route
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.remove = function(route){
|
||||
var routes = this.routes[route.method]
|
||||
, len = routes.length;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (route == routes[i]) {
|
||||
routes.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return routes with route paths matching `path`.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.lookup = function(method, path){
|
||||
return this.find(function(route){
|
||||
return path == route.path
|
||||
&& (route.method == method
|
||||
|| method == 'all');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return routes with regexps that match the given `url`.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} url
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.match = function(method, url){
|
||||
return this.find(function(route){
|
||||
return route.match(url)
|
||||
&& (route.method == method
|
||||
|| method == 'all');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Find routes based on the return value of `fn`
|
||||
* which is invoked once per route.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.find = function(fn){
|
||||
var len = methods.length
|
||||
, ret = new Collection(this)
|
||||
, method
|
||||
, routes
|
||||
, route;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
method = methods[i];
|
||||
routes = this.routes[method];
|
||||
if (!routes) continue;
|
||||
for (var j = 0, jlen = routes.length; j < jlen; ++j) {
|
||||
route = routes[j];
|
||||
if (fn(route)) ret.push(route);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Route dispatcher aka the route "middleware".
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {ServerResponse} res
|
||||
* @param {Function} next
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._dispatch = function(req, res, next){
|
||||
var params = this.params
|
||||
, self = this;
|
||||
|
||||
// route dispatch
|
||||
(function pass(i, err){
|
||||
var paramCallbacks
|
||||
, paramIndex = 0
|
||||
, paramVal
|
||||
, route
|
||||
, keys
|
||||
, key
|
||||
, ret;
|
||||
|
||||
// match next route
|
||||
function nextRoute(err) {
|
||||
pass(req._route_index + 1, err);
|
||||
}
|
||||
|
||||
// match route
|
||||
req.route = route = self._match(req, i);
|
||||
|
||||
// implied OPTIONS
|
||||
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
|
||||
|
||||
// no route
|
||||
if (!route) return next(err);
|
||||
|
||||
// we have a route
|
||||
// start at param 0
|
||||
req.params = route.params;
|
||||
keys = route.keys;
|
||||
i = 0;
|
||||
|
||||
// param callbacks
|
||||
function param(err) {
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
paramVal = key && req.params[key.name];
|
||||
paramCallbacks = key && params[key.name];
|
||||
|
||||
try {
|
||||
if ('route' == err) {
|
||||
nextRoute();
|
||||
} else if (err) {
|
||||
i = 0;
|
||||
callbacks(err);
|
||||
} else if (paramCallbacks && undefined !== paramVal) {
|
||||
paramCallback();
|
||||
} else if (key) {
|
||||
param();
|
||||
} else {
|
||||
i = 0;
|
||||
callbacks();
|
||||
}
|
||||
} catch (err) {
|
||||
param(err);
|
||||
}
|
||||
};
|
||||
|
||||
param(err);
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
if (err || !fn) return param(err);
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
}
|
||||
|
||||
// invoke route callbacks
|
||||
function callbacks(err) {
|
||||
var fn = route.callbacks[i++];
|
||||
try {
|
||||
if ('route' == err) {
|
||||
nextRoute();
|
||||
} else if (err && fn) {
|
||||
if (fn.length < 4) return callbacks(err);
|
||||
fn(err, req, res, callbacks);
|
||||
} else if (fn) {
|
||||
fn(req, res, callbacks);
|
||||
} else {
|
||||
nextRoute(err);
|
||||
}
|
||||
} catch (err) {
|
||||
callbacks(err);
|
||||
}
|
||||
}
|
||||
})(0);
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to __OPTIONS__ method.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {ServerResponse} res
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._options = function(req, res){
|
||||
var path = parse(req.url).pathname
|
||||
, body = this._optionsFor(path).join(',');
|
||||
res.send(body, { Allow: body });
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an array of HTTP verbs or "options" for `path`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._optionsFor = function(path){
|
||||
var self = this;
|
||||
return methods.filter(function(method){
|
||||
var routes = self.routes[method];
|
||||
if (!routes || 'options' == method) return;
|
||||
for (var i = 0, len = routes.length; i < len; ++i) {
|
||||
if (routes[i].match(path)) return true;
|
||||
}
|
||||
}).map(function(method){
|
||||
return method.toUpperCase();
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Attempt to match a route for `req`
|
||||
* starting from offset `i`.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {Number} i
|
||||
* @return {Route}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._match = function(req, i){
|
||||
var method = req.method.toLowerCase()
|
||||
, url = parse(req.url)
|
||||
, path = url.pathname
|
||||
, routes = this.routes
|
||||
, captures
|
||||
, route
|
||||
, keys;
|
||||
|
||||
// pass HEAD to GET routes
|
||||
if ('head' == method) method = 'get';
|
||||
|
||||
// routes for this method
|
||||
if (routes = routes[method]) {
|
||||
|
||||
// matching routes
|
||||
for (var len = routes.length; i < len; ++i) {
|
||||
route = routes[i];
|
||||
if (captures = route.match(path)) {
|
||||
keys = route.keys;
|
||||
route.params = [];
|
||||
|
||||
// params from capture groups
|
||||
for (var j = 1, jlen = captures.length; j < jlen; ++j) {
|
||||
var key = keys[j-1]
|
||||
, val = 'string' == typeof captures[j]
|
||||
? decodeURIComponent(captures[j])
|
||||
: captures[j];
|
||||
if (key) {
|
||||
route.params[key.name] = val;
|
||||
} else {
|
||||
route.params.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
// all done
|
||||
req._route_index = i;
|
||||
return route;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Route `method`, `path`, and one or more callbacks.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @param {Function} callback...
|
||||
* @return {Router} for chaining
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._route = function(method, path, callbacks){
|
||||
var app = this.app
|
||||
, callbacks = utils.flatten(toArray(arguments, 2));
|
||||
|
||||
// ensure path was given
|
||||
if (!path) throw new Error('app.' + method + '() requires a path');
|
||||
|
||||
// create the route
|
||||
var route = new Route(method, path, callbacks, {
|
||||
sensitive: app.enabled('case sensitive routes')
|
||||
, strict: app.enabled('strict routing')
|
||||
});
|
||||
|
||||
// add it
|
||||
(this.routes[method] = this.routes[method] || [])
|
||||
.push(route);
|
||||
return this;
|
||||
};
|
||||
70
lib/router/methods.js
Normal file
70
lib/router/methods.js
Normal file
@@ -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();
|
||||
});
|
||||
88
lib/router/route.js
Normal file
88
lib/router/route.js
Normal file
@@ -0,0 +1,88 @@
|
||||
|
||||
/*!
|
||||
* 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 an array of `callbacks` and `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `sensitive` enable case-sensitive routes
|
||||
* - `strict` enable strict matching for trailing slashes
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @param {Array} callbacks
|
||||
* @param {Object} options.
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Route(method, path, callbacks, options) {
|
||||
options = options || {};
|
||||
this.path = path;
|
||||
this.method = method;
|
||||
this.callbacks = callbacks;
|
||||
this.regexp = normalize(path
|
||||
, this.keys = []
|
||||
, options.sensitive
|
||||
, options.strict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this route matches `path` and return captures made.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Route.prototype.match = function(path){
|
||||
return this.regexp.exec(path);
|
||||
};
|
||||
|
||||
/**
|
||||
* 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
|
||||
* @param {Boolean} strict
|
||||
* @return {RegExp}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function normalize(path, keys, sensitive, strict) {
|
||||
if (path instanceof RegExp) return path;
|
||||
path = path
|
||||
.concat(strict ? '' : '/?')
|
||||
.replace(/\/\(/g, '(?:/')
|
||||
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
|
||||
keys.push({ name: key, optional: !! optional });
|
||||
slash = slash || '';
|
||||
return ''
|
||||
+ (optional ? '' : slash)
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
|
||||
+ (optional || '');
|
||||
})
|
||||
.replace(/([\/.])/g, '\\$1')
|
||||
.replace(/\*/g, '(.*)');
|
||||
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
|
||||
}
|
||||
95
lib/utils.js
95
lib/utils.js
@@ -6,61 +6,16 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var path = require('path')
|
||||
, basename = path.basename
|
||||
, dirname = path.dirname
|
||||
, extname = path.extname;
|
||||
|
||||
/**
|
||||
* Memory cache.
|
||||
*/
|
||||
|
||||
var cache = {
|
||||
basename: {}
|
||||
, dirname: {}
|
||||
, extname: {}
|
||||
};
|
||||
|
||||
/**
|
||||
* Cached basename.
|
||||
* Check if `path` looks absolute.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {String}
|
||||
* @return {Boolean}
|
||||
* @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));
|
||||
exports.isAbsolute = function(path){
|
||||
if ('/' == path[0]) return true;
|
||||
if (':' == path[1] && '\\' == path[2]) return true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -88,6 +43,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 +93,7 @@ exports.miniMarkdown = function(str){
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.htmlEscape = function(html) {
|
||||
exports.escape = function(html) {
|
||||
return String(html)
|
||||
.replace(/&/g, '&')
|
||||
.replace(/"/g, '"')
|
||||
@@ -157,3 +133,20 @@ exports.parseRange = function(size, str){
|
||||
});
|
||||
return valid ? arr : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fast alternative to `Array.prototype.slice.call()`.
|
||||
*
|
||||
* @param {Arguments} args
|
||||
* @param {Number} n
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.toArray = function(args, i){
|
||||
var arr = []
|
||||
, len = args.length
|
||||
, i = i || 0;
|
||||
for (; i < len; ++i) arr.push(args[i]);
|
||||
return arr;
|
||||
};
|
||||
|
||||
365
lib/view.js
365
lib/view.js
@@ -15,26 +15,17 @@ var path = require('path')
|
||||
, basename = path.basename
|
||||
, utils = require('connect').utils
|
||||
, View = require('./view/view')
|
||||
, Partial = require('./view/partial')
|
||||
, partial = require('./view/partial')
|
||||
, union = require('./utils').union
|
||||
, merge = utils.merge
|
||||
, http = require('http')
|
||||
, res = http.ServerResponse.prototype;
|
||||
|
||||
/**
|
||||
* Memory cache.
|
||||
*
|
||||
* @type Object
|
||||
*/
|
||||
|
||||
var cache = {};
|
||||
|
||||
/**
|
||||
* Expose constructors.
|
||||
*/
|
||||
|
||||
exports = module.exports = View;
|
||||
exports.Partial = Partial;
|
||||
|
||||
/**
|
||||
* Export template engine registrar.
|
||||
@@ -42,73 +33,193 @@ exports.Partial = Partial;
|
||||
|
||||
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
|
||||
options.filename = view.path;
|
||||
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)
|
||||
, partial = options.isPartial
|
||||
, layout = options.isLayout;
|
||||
|
||||
// Try _ prefix ex: ./views/_<name>.jade
|
||||
// taking precedence over the direct path
|
||||
if (partial) {
|
||||
view = new View(orig.prefixPath, options);
|
||||
if (!view.exists) view = orig;
|
||||
}
|
||||
|
||||
// Try index ex: ./views/user/index.jade
|
||||
if (!layout && !view.exists) view = new View(orig.indexPath, options);
|
||||
|
||||
// Try ../<name>/index ex: ../user/index.jade
|
||||
// when calling partial('user') within the same dir
|
||||
if (!layout && !view.exists) 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.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function renderPartial(res, view, options, locals, parent){
|
||||
// Inherit parent view extension when not present
|
||||
if (parent && !~view.indexOf('.')) {
|
||||
view += parent.extension;
|
||||
}
|
||||
function renderPartial(res, view, options, parentLocals, parent){
|
||||
var collection, object, locals;
|
||||
|
||||
// Allow collection to be passed as second param
|
||||
if (options) {
|
||||
if ('length' in options) {
|
||||
options = { collection: options };
|
||||
} else if (!options.collection && !options.locals && !options.object) {
|
||||
options = { object: options };
|
||||
// collection
|
||||
if (options.collection) {
|
||||
collection = options.collection;
|
||||
delete options.collection;
|
||||
} else if ('length' in options) {
|
||||
collection = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// locals
|
||||
if (options.locals) {
|
||||
locals = options.locals;
|
||||
delete options.locals;
|
||||
}
|
||||
|
||||
// object
|
||||
if ('Object' != options.constructor.name) {
|
||||
object = options;
|
||||
options = {};
|
||||
} else if (undefined != options.object) {
|
||||
object = options.object;
|
||||
delete options.object;
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
// Inherit locals from parent
|
||||
union(options, locals);
|
||||
union(options, parentLocals);
|
||||
|
||||
// Merge locals
|
||||
if (options.locals) {
|
||||
merge(options, options.locals);
|
||||
}
|
||||
if (locals) merge(options, locals);
|
||||
|
||||
// Partials dont need layouts
|
||||
options.renderPartial = true;
|
||||
options.isPartial = true;
|
||||
options.layout = false;
|
||||
|
||||
// Deduce name from view path
|
||||
var name = options.as || Partial.resolveObjectName(view);
|
||||
var name = options.as || partial.resolveObjectName(view);
|
||||
|
||||
// Render partial
|
||||
function render(){
|
||||
if (options.object) {
|
||||
if (object) {
|
||||
if ('string' == typeof name) {
|
||||
options[name] = options.object;
|
||||
options[name] = object;
|
||||
} else if (name === global) {
|
||||
merge(options, options.object);
|
||||
} else {
|
||||
options.scope = options.object;
|
||||
merge(options, object);
|
||||
}
|
||||
}
|
||||
return res.render(view, options, null, parent);
|
||||
return res.render(view, options, null, parent, true);
|
||||
}
|
||||
|
||||
// Collection support
|
||||
var collection = options.collection;
|
||||
if (collection) {
|
||||
var len = collection.length
|
||||
, buf = '';
|
||||
delete options.collection;
|
||||
, buf = ''
|
||||
, keys
|
||||
, key
|
||||
, val;
|
||||
|
||||
options.collectionLength = len;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
var val = collection[i];
|
||||
options.firstInCollection = i === 0;
|
||||
options.indexInCollection = i;
|
||||
options.lastInCollection = i === len - 1;
|
||||
options.object = val;
|
||||
buf += render();
|
||||
|
||||
if ('number' == typeof len || Array.isArray(collection)) {
|
||||
for (var i = 0; i < len; ++i) {
|
||||
val = collection[i];
|
||||
options.firstInCollection = i == 0;
|
||||
options.indexInCollection = i;
|
||||
options.lastInCollection = i == len - 1;
|
||||
object = val;
|
||||
buf += render();
|
||||
}
|
||||
} else {
|
||||
keys = Object.keys(collection);
|
||||
len = keys.length;
|
||||
options.collectionLength = len;
|
||||
options.collectionKeys = keys;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
key = keys[i];
|
||||
val = collection[key];
|
||||
options.keyInCollection = key;
|
||||
options.firstInCollection = i == 0;
|
||||
options.indexInCollection = i;
|
||||
options.lastInCollection = i == len - 1;
|
||||
object = val;
|
||||
buf += render();
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
} else {
|
||||
return render();
|
||||
@@ -116,7 +227,9 @@ function renderPartial(res, view, options, locals, parent){
|
||||
};
|
||||
|
||||
/**
|
||||
* Render `view` partial with the given `options`.
|
||||
* Render `view` partial with the given `options`. Optionally a
|
||||
* callback `fn(err, str)` may be passed instead of writing to
|
||||
* the socket.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
@@ -131,26 +244,48 @@ function renderPartial(res, view, options, locals, parent){
|
||||
* For example _video.html_ will have a object _video_ available to it.
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object|Array} options, collection, or object
|
||||
* @param {Object|Array|Function} options, collection, callback, or object
|
||||
* @param {Function} fn
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.partial = function(view, options){
|
||||
res.partial = function(view, options, fn){
|
||||
var app = this.app
|
||||
, options = options || {}
|
||||
, viewEngine = app.set('view engine')
|
||||
, parent = {};
|
||||
|
||||
// accept callback as second argument
|
||||
if ('function' == typeof options) {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// root "views" option
|
||||
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.engine = viewEngine;
|
||||
|
||||
// render the partial
|
||||
try {
|
||||
var str = renderPartial(this, view, options, null, parent);
|
||||
} catch (err) {
|
||||
if (fn) {
|
||||
fn(err);
|
||||
} else {
|
||||
this.req.next(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
var str = renderPartial(this, view, options, null, parent);
|
||||
this.send(str);
|
||||
// callback or transfer
|
||||
if (fn) {
|
||||
fn(null, str);
|
||||
} else {
|
||||
this.send(str);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -162,6 +297,7 @@ res.partial = function(view, options){
|
||||
*
|
||||
* - `scope` Template evaluation context (the value of `this`)
|
||||
* - `debug` Output debugging information
|
||||
* - `status` Response status code
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object|Function} options or callback function
|
||||
@@ -169,25 +305,49 @@ res.partial = function(view, options){
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.render = function(view, opts, fn, parent){
|
||||
res.render = function(view, opts, fn, parent, sub){
|
||||
// support callback function as second arg
|
||||
if (typeof opts === 'function') {
|
||||
if ('function' == typeof opts) {
|
||||
fn = opts, opts = null;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
return this._render(view, opts, fn, parent, sub);
|
||||
} catch (err) {
|
||||
// callback given
|
||||
if (fn) {
|
||||
fn(err);
|
||||
// unwind to root call to prevent multiple callbacks
|
||||
} else if (sub) {
|
||||
throw err;
|
||||
// root template, next(err)
|
||||
} else {
|
||||
this.req.next(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// private render()
|
||||
|
||||
res._render = function(view, opts, fn, parent, sub){
|
||||
var options = {}
|
||||
, self = this
|
||||
, app = this.app
|
||||
, helpers = app.viewHelpers
|
||||
, helpers = app._locals
|
||||
, dynamicHelpers = app.dynamicViewHelpers
|
||||
, viewOptions = app.set('view options')
|
||||
, cacheTemplates = 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);
|
||||
|
||||
// merge res.locals
|
||||
if (this.locals) merge(options, this.locals);
|
||||
// merge res._locals
|
||||
if (this._locals) merge(options, this._locals);
|
||||
|
||||
// merge render() options
|
||||
if (opts) merge(options, opts);
|
||||
@@ -195,10 +355,13 @@ res.render = function(view, opts, fn, parent){
|
||||
// merge render() .locals
|
||||
if (opts && opts.locals) merge(options, opts.locals);
|
||||
|
||||
// Defaults
|
||||
var self = this
|
||||
, root = app.set('views') || process.cwd() + '/views'
|
||||
, partial = options.renderPartial
|
||||
// status support
|
||||
if (options.status) this.statusCode = options.status;
|
||||
|
||||
// capture attempts
|
||||
options.attempts = [];
|
||||
|
||||
var partial = options.isPartial
|
||||
, layout = options.layout;
|
||||
|
||||
// Layout support
|
||||
@@ -218,32 +381,8 @@ res.render = function(view, opts, fn, parent){
|
||||
// "view engine" setting
|
||||
options.defaultEngine = app.set('view engine');
|
||||
|
||||
// Populate view
|
||||
// TODO: move this logic into the protos
|
||||
var orig = view = partial
|
||||
? new Partial(view, options)
|
||||
: new View(view, options);
|
||||
|
||||
// Ensure view exists, otherwise try _ prefix
|
||||
if (!view.exists) {
|
||||
view = partial
|
||||
? new Partial(view.prefixPath, options)
|
||||
: new View(view.prefixPath, options);
|
||||
}
|
||||
|
||||
// Ensure view _ prefix exists, otherwise try index
|
||||
if (!view.exists) {
|
||||
view = partial
|
||||
? new Partial(orig.indexPath, options)
|
||||
: new View(orig.indexPath, options);
|
||||
}
|
||||
|
||||
// Ensure view exists, or try ../index
|
||||
if (!view.exists && !options.isLayout) {
|
||||
view = partial
|
||||
? new Partial(orig.upIndexPath, options)
|
||||
: new View(orig.upIndexPath, options);
|
||||
}
|
||||
// charset option
|
||||
if (options.charset) this.charset = options.charset;
|
||||
|
||||
// Dynamic helper support
|
||||
if (false !== options.dynamicHelpers) {
|
||||
@@ -270,37 +409,49 @@ res.render = function(view, opts, fn, parent){
|
||||
return renderPartial(self, path, opts, options, view);
|
||||
};
|
||||
|
||||
function error(err) {
|
||||
if (fn) {
|
||||
fn(err);
|
||||
} else {
|
||||
self.req.next(err);
|
||||
}
|
||||
}
|
||||
// View lookup
|
||||
options.hint = app.enabled('hints');
|
||||
view = exports.compile(view, app.cache, cid, options);
|
||||
|
||||
// Attempt render
|
||||
try {
|
||||
var engine = view.templateEngine
|
||||
, template = cacheTemplates
|
||||
? cache[view.path] || (cache[view.path] = engine.compile(view.contents, options))
|
||||
: engine.compile(view.contents, options)
|
||||
, str = template.call(options.scope, options);
|
||||
} catch (err) {
|
||||
return error(err);
|
||||
}
|
||||
// layout helper
|
||||
options.layout = function(path){
|
||||
layout = path;
|
||||
};
|
||||
|
||||
// Layout support
|
||||
// render
|
||||
var str = view.fn.call(options.scope, options);
|
||||
|
||||
// layout expected
|
||||
if (layout) {
|
||||
options.isLayout = true;
|
||||
options.layout = false;
|
||||
options.body = str;
|
||||
options.relative = false;
|
||||
self.render(layout, options, fn, view);
|
||||
this.render(layout, options, fn, view, true);
|
||||
// partial return
|
||||
} else if (partial) {
|
||||
return str;
|
||||
// render complete, and
|
||||
// callback given
|
||||
} else if (fn) {
|
||||
fn(null, str);
|
||||
// respond
|
||||
} else {
|
||||
this.send(str);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Hint at view path resolution, outputting the
|
||||
* paths that Express has tried.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function hintAtViewPaths(view, options) {
|
||||
console.error();
|
||||
console.error('failed to locate view "' + view.view + '", tried:');
|
||||
options.attempts.forEach(function(path){
|
||||
console.error(' - %s', path);
|
||||
});
|
||||
console.error();
|
||||
}
|
||||
@@ -5,38 +5,12 @@
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var View = require('./view')
|
||||
, basename = require('path').basename;
|
||||
|
||||
/**
|
||||
* Memory cache.
|
||||
*/
|
||||
|
||||
var cache = {};
|
||||
|
||||
/**
|
||||
* Initialize a new `Partial` for the given `view` and `options`.
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var Partial = exports = module.exports = function Partial(view, options) {
|
||||
options = options || {};
|
||||
View.call(this, view, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `View.prototype`.
|
||||
*/
|
||||
|
||||
Partial.prototype.__proto__ = View.prototype;
|
||||
|
||||
/**
|
||||
* Resolve partial object name from the view path.
|
||||
*
|
||||
|
||||
@@ -9,25 +9,26 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('../utils')
|
||||
, extname = utils.extname
|
||||
, dirname = utils.dirname
|
||||
, basename = utils.basename
|
||||
var path = require('path')
|
||||
, utils = require('../utils')
|
||||
, extname = path.extname
|
||||
, dirname = path.dirname
|
||||
, basename = path.basename
|
||||
, fs = require('fs')
|
||||
, stat = fs.statSync;
|
||||
|
||||
/**
|
||||
* Memory cache.
|
||||
* Expose `View`.
|
||||
*/
|
||||
|
||||
exports = module.exports = View;
|
||||
|
||||
/**
|
||||
* Require cache.
|
||||
*/
|
||||
|
||||
var cache = {};
|
||||
|
||||
/**
|
||||
* Existance cache.
|
||||
*/
|
||||
|
||||
var exists = {};
|
||||
|
||||
/**
|
||||
* Initialize a new `View` with the given `view` path and `options`.
|
||||
*
|
||||
@@ -36,9 +37,8 @@ var exists = {};
|
||||
* @api private
|
||||
*/
|
||||
|
||||
var View = exports = module.exports = function View(view, options) {
|
||||
function View(view, options) {
|
||||
options = options || {};
|
||||
// TODO: more caching
|
||||
this.view = view;
|
||||
this.root = options.root;
|
||||
this.relative = false !== options.relative;
|
||||
@@ -47,8 +47,13 @@ var View = exports = module.exports = function View(view, options) {
|
||||
this.basename = basename(view);
|
||||
this.engine = this.resolveEngine();
|
||||
this.extension = '.' + this.engine;
|
||||
this.name = this.basename.replace(this.extension, '');
|
||||
this.path = this.resolvePath();
|
||||
this.dirname = dirname(this.path);
|
||||
if (options.attempts) {
|
||||
if (!~options.attempts.indexOf(this.path))
|
||||
options.attempts.push(this.path);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -59,16 +64,11 @@ var View = exports = module.exports = function View(view, options) {
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('exists', function(){
|
||||
var path = this.path;
|
||||
if (null != exists[path]) {
|
||||
return exists[path];
|
||||
} else {
|
||||
try {
|
||||
stat(path);
|
||||
return exists[path] = true;
|
||||
} catch (err) {
|
||||
return exists[path] = false;
|
||||
}
|
||||
try {
|
||||
stat(this.path);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -100,7 +100,7 @@ View.prototype.resolvePath = function(){
|
||||
// Implicit engine
|
||||
if (!~this.basename.indexOf('.')) path += this.extension;
|
||||
// Absolute
|
||||
if ('/' == path[0]) return path;
|
||||
if (utils.isAbsolute(path)) return path;
|
||||
// Relative to parent
|
||||
if (this.relative && this.parent) return this.parent.dirname + '/' + path;
|
||||
// Relative to root
|
||||
@@ -134,6 +134,18 @@ View.prototype.__defineGetter__('templateEngine', function(){
|
||||
return cache[ext] || (cache[ext] = require(this.engine));
|
||||
});
|
||||
|
||||
/**
|
||||
* Return root path alternative.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('rootPath', function(){
|
||||
this.relative = false;
|
||||
return this.resolvePath();
|
||||
});
|
||||
|
||||
/**
|
||||
* Return index path alternative.
|
||||
*
|
||||
@@ -148,14 +160,14 @@ View.prototype.__defineGetter__('indexPath', function(){
|
||||
});
|
||||
|
||||
/**
|
||||
* Return ../index path alternative.
|
||||
* Return ../<name>/index path alternative.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('upIndexPath', function(){
|
||||
return this.dirname + '/index' + this.extension;
|
||||
return this.dirname + '/../' + this.name + '/index' + this.extension;
|
||||
});
|
||||
|
||||
/**
|
||||
|
||||
24
package.json
24
package.json
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "express",
|
||||
"description": "Sinatra inspired web development framework",
|
||||
"version": "2.0.0beta2",
|
||||
"version": "2.4.7",
|
||||
"author": "TJ Holowaychuk <tj@vision-media.ca>",
|
||||
"contributors": [
|
||||
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
|
||||
@@ -10,12 +10,30 @@
|
||||
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
|
||||
],
|
||||
"dependencies": {
|
||||
"connect": ">= 1.0.1 < 2.0.0",
|
||||
"connect": "1.7.x",
|
||||
"mime": ">= 0.0.1",
|
||||
"qs": ">= 0.0.6"
|
||||
"qs": ">= 0.3.1",
|
||||
"mkdirp": "0.0.7"
|
||||
},
|
||||
"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",
|
||||
"bin": { "express": "./bin/express" },
|
||||
"scripts": {
|
||||
"test": "make test",
|
||||
"prepublish" : "npm prune"
|
||||
},
|
||||
"engines": { "node": ">= 0.4.1 < 0.5.0" }
|
||||
}
|
||||
Submodule support/connect deleted from 2796a2fdeb
Submodule support/connect-form deleted from ccefcd28db
Submodule support/ejs deleted from 673e6f23cb
Submodule support/expresso deleted from 2c8759f147
Submodule support/formidable deleted from 63347d825b
Submodule support/haml deleted from 4122210f38
Submodule support/jade deleted from 3c002865b7
Submodule support/mime deleted from ade33a43be
Submodule support/qs deleted from 2b9796e54e
Submodule support/should deleted from 607f8734e8
@@ -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');
|
||||
}
|
||||
@@ -6,13 +6,20 @@
|
||||
var express = require('express')
|
||||
, connect = require('connect')
|
||||
, assert = require('assert')
|
||||
, should = require('should');
|
||||
, should = require('should')
|
||||
, Route = express.Route;
|
||||
|
||||
module.exports = {
|
||||
'test inheritance': function(){
|
||||
var server = express.createServer();
|
||||
server.should.be.an.instanceof(connect.HTTPServer);
|
||||
},
|
||||
|
||||
'test constructor exports': function(){
|
||||
express.should.have.property('HTTPServer');
|
||||
express.should.have.property('HTTPSServer');
|
||||
express.should.have.property('Route');
|
||||
},
|
||||
|
||||
'test connect middleware autoloaders': function(){
|
||||
express.errorHandler.should.equal(connect.errorHandler);
|
||||
@@ -183,12 +190,6 @@ module.exports = {
|
||||
{ body: 'Internal Server Error' });
|
||||
},
|
||||
|
||||
'test error() with route-specific middleware': function(){
|
||||
var app = express.createServer();
|
||||
|
||||
|
||||
},
|
||||
|
||||
'test next()': function(){
|
||||
var app = express.createServer();
|
||||
|
||||
@@ -232,10 +233,9 @@ module.exports = {
|
||||
|
||||
'test #configure()': function(beforeExit){
|
||||
var calls = [];
|
||||
process.env.NODE_ENV = 'development';
|
||||
var server = express.createServer();
|
||||
server.set('env', 'development');
|
||||
|
||||
// Config blocks
|
||||
var ret = server.configure(function(){
|
||||
assert.equal(this, server, 'Test context of configure() is the server');
|
||||
calls.push('any');
|
||||
@@ -296,31 +296,56 @@ module.exports = {
|
||||
{ url: '/' },
|
||||
{ body: 'first route last' });
|
||||
},
|
||||
|
||||
|
||||
'test #configure() multiple envs': function(){
|
||||
var app = express.createServer();
|
||||
app.set('env', 'prod');
|
||||
var calls = [];
|
||||
|
||||
app.configure('stage', 'prod', function(){
|
||||
calls.push('stage/prod');
|
||||
});
|
||||
|
||||
app.configure('prod', function(){
|
||||
calls.push('prod');
|
||||
});
|
||||
|
||||
calls.should.eql(['stage/prod', 'prod']);
|
||||
},
|
||||
|
||||
'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(){
|
||||
@@ -342,15 +367,27 @@ module.exports = {
|
||||
{ url: '/', method: 'POST', data: 'name=tj', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }},
|
||||
{ body: '{"name":"tj"}' });
|
||||
},
|
||||
|
||||
|
||||
'test "basepath" setting': function(){
|
||||
var app = express.createServer();
|
||||
|
||||
app.set('basepath', '/shop');
|
||||
|
||||
app.get('/redirect', function(req, res){
|
||||
res.redirect('/cart');
|
||||
});
|
||||
|
||||
assert.response(app,
|
||||
{ url: '/redirect', headers: { Host: 'foo.com' }},
|
||||
{ headers: { Location: 'http://foo.com/shop/cart' }});
|
||||
},
|
||||
|
||||
'test mounting': function(){
|
||||
var called
|
||||
, app = express.createServer()
|
||||
, blog = express.createServer()
|
||||
, map = express.createServer()
|
||||
, reg = connect.createServer();
|
||||
|
||||
map.set('home', '/map');
|
||||
|
||||
map.mounted(function(parent){
|
||||
called = true;
|
||||
@@ -371,9 +408,8 @@ module.exports = {
|
||||
blog.set('test').should.equal('parent setting');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
app.set('home').should.equal('/');
|
||||
blog.set('home').should.equal('/blog');
|
||||
map.set('home').should.equal('/contact/map');
|
||||
blog.set('basepath').should.equal('/blog');
|
||||
map.set('basepath').should.equal('/contact');
|
||||
res.send('main app');
|
||||
});
|
||||
|
||||
@@ -401,132 +437,67 @@ module.exports = {
|
||||
{ url: '/regular' },
|
||||
{ body: 'hey' });
|
||||
},
|
||||
|
||||
'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 .app property after returning control to parent': function() {
|
||||
var app = express.createServer()
|
||||
, blog = express.createServer();
|
||||
|
||||
// Mounted servers did not restore `req.app` and `res.app` when
|
||||
// passing control back to parent via `out()` in `#handle()`.
|
||||
|
||||
blog.get('/', function(req, res, next){
|
||||
req.app.should.equal(blog);
|
||||
res.app.should.equal(blog);
|
||||
next();
|
||||
});
|
||||
|
||||
app.use(blog);
|
||||
|
||||
app.use(function(req, res, next) {
|
||||
res.send((res.app === app) ? 'restored' : 'not-restored');
|
||||
});
|
||||
|
||||
assert.response(app,
|
||||
{ url: '/' },
|
||||
{ body: 'restored' }
|
||||
);
|
||||
},
|
||||
|
||||
'test 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' });
|
||||
{ url: '/another' },
|
||||
{ body: 'got /another' });
|
||||
},
|
||||
|
||||
'test .param()': function(){
|
||||
'invalid chars': 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('/:name', function(req, res, next){
|
||||
res.send('invalid');
|
||||
});
|
||||
|
||||
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' }});
|
||||
{ url: '/%a0' },
|
||||
{ status: 500 });
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user