Comparar commits

..

267 Commits

Autor SHA1 Mensagem Data
Tj Holowaychuk 003599cbda Release 1.0.0rc4 2010-10-14 03:50:10 -07:00
Tj Holowaychuk bacfad37a9 Updated connect submodule 2010-10-14 03:41:41 -07:00
Tj Holowaychuk 6ef6ba7b8a Fix up app.register() docs. Closes #450
thank ya
2010-10-14 03:03:18 -07:00
Dav Glass 93c0da0367 Added tests for enable/disable of settings 2010-10-12 12:58:59 -07:00
Dav Glass 268d19e8a3 Added config option app.enable('jsonp callback') support 2010-10-12 12:58:59 -07:00
Dav Glass a383b302c2 Added callback query check on response.send to autowrap JSON objects for simple webservice implementations 2010-10-12 12:58:59 -07:00
Tj Holowaychuk 95ebbf2608 Updated jade submodule 2010-10-12 11:27:41 -07:00
Tj Holowaychuk f880574c78 Example typo 2010-10-12 10:09:20 -07:00
Tj Holowaychuk 350c310906 Fixed test typo 2010-10-12 09:49:15 -07:00
Tj Holowaychuk 5026f7686e Updated connect submodule 2010-10-12 09:48:49 -07:00
Tj Holowaychuk ab3337ddf5 Regenerated guide 2010-10-12 08:46:22 -07:00
Tj Holowaychuk c9268f2e3d Doc typo 2010-10-12 08:46:05 -07:00
Tj Holowaychuk fa01388f97 Updated connect-form submodule 2010-10-12 08:30:36 -07:00
Tj Holowaychuk d8dc89ac7b Updated jade submodule 2010-10-12 08:29:59 -07:00
Tj Holowaychuk a659fd1fa9 Added partial() support for array-like collections. Closes #434 2010-10-11 08:37:42 -07:00
Tj Holowaychuk 27fa3ba34c Default env to "development" again [thanks aheckmann] 2010-10-11 08:17:52 -07:00
Tj Holowaychuk 663f42948e Added support for swappable querystring parsers 2010-10-08 11:38:59 -07:00
Tj Holowaychuk baca8f0cdb Added guillermo to package.json contrib 2010-10-07 06:50:29 -07:00
Tj Holowaychuk c5c3a82b23 Removed another sys require 2010-10-07 06:45:48 -07:00
Tj Holowaychuk c0617864e1 Removed sys require 2010-10-07 06:45:14 -07:00
Tj Holowaychuk d9ef9ab497 Merge branch 'perf/cache-dynamicHelpers' 2010-10-07 06:34:49 -07:00
Tj Holowaychuk 65d9f783e1 Added test for dynamic helper cache 2010-10-07 06:32:35 -07:00
Tj Holowaychuk f70232d1b0 Implemented dynamic helper cache. Closes #439 2010-10-07 06:29:58 -07:00
Tj Holowaychuk b083aed245 Doc cleanup 2010-10-07 05:57:27 -07:00
Tj Holowaychuk 0faac20d53 Regenerated docs 2010-10-07 05:53:14 -07:00
Tj Holowaychuk cef14ca7cd Fixed docs 2010-10-07 05:53:02 -07:00
Tj Holowaychuk 090debe0df Regenerated docs 2010-10-07 05:49:35 -07:00
Tj Holowaychuk 296f1270ee Ordering docs 2010-10-07 05:49:19 -07:00
Tj Holowaychuk 334a18a114 Regenerated docs 2010-10-07 05:46:39 -07:00
Tj Holowaychuk dd4ddae082 Added route middleware docs 2010-10-07 05:46:26 -07:00
Tj Holowaychuk 4983e967bc Added route-middleware example 2010-10-07 05:28:09 -07:00
Tj Holowaychuk 0b5e2f14ed Updated ejs submodule 2010-10-07 04:28:11 -07:00
Tj Holowaychuk 70102aecb5 Fixed mvc example 2010-10-07 04:15:34 -07:00
Tj Holowaychuk ae75153561 Merge branch 'features/route-middleware' 2010-10-07 03:49:58 -07:00
Tj Holowaychuk dd23d60513 Recursively apply route middleware 2010-10-07 03:49:38 -07:00
Tj Holowaychuk db95481b76 Added another route middleware test 2010-10-07 03:46:20 -07:00
Tj Holowaychuk 3e0da535b9 Implemented route middleware with arrays 2010-10-07 03:45:23 -07:00
Tj Holowaychuk c74602551e Added test for multiple route middleware 2010-10-07 03:40:54 -07:00
Tj Holowaychuk 1722e2c7a5 Implemented route middleware support 2010-10-07 03:30:39 -07:00
Tj Holowaychuk 8a845e2951 Auth example using route middleware 2010-10-07 03:21:07 -07:00
Tj Holowaychuk 72765bada0 Typo in auth example 2010-10-07 03:16:40 -07:00
Tj Holowaychuk b380f180cc Links 2010-10-07 02:36:34 -07:00
Tj Holowaychuk f1799799a1 Regenerated guide 2010-10-06 08:26:01 -07:00
Tj Holowaychuk 4bc8a8673d More session docs 2010-10-06 08:25:49 -07:00
Tj Holowaychuk cbdd907393 Added logger to pages example 2010-10-05 06:31:21 -07:00
Tj Holowaychuk aa7ff3ef47 typo 2010-10-05 06:27:17 -07:00
Tj Holowaychuk ebdf6434eb Fixing up examples a bit 2010-10-05 06:14:19 -07:00
Tj Holowaychuk 9b7470d204 Exposing ./support libs to examples 2010-10-05 06:08:53 -07:00
Tj Holowaychuk 9f3ab8683c Added log lines to more examples 2010-10-05 06:01:26 -07:00
Tj Holowaychuk fa464aca90 Updated jade submodule 2010-10-05 05:56:42 -07:00
Tj Holowaychuk 2724ce368a Rebuilt docs 2010-10-04 11:17:29 -07:00
Tj Holowaychuk 89f7cbe877 Added session usage docs. Closes #443 2010-10-04 11:16:46 -07:00
Tj Holowaychuk fab9af2206 Merge branch 'range' 2010-10-04 11:06:15 -07:00
Tj Holowaychuk 4d01b4fa59 Handling invalid ranges 2010-10-04 08:36:57 -07:00
Tj Holowaychuk c3365aa37e Merge branch 'master' into range 2010-10-04 08:14:30 -07:00
Tj Holowaychuk 426e80d4c3 Updated jade submodule 2010-10-04 08:04:00 -07:00
Tj Holowaychuk 30a282d126 Updated connect submodule 2010-10-04 08:03:51 -07:00
Tj Holowaychuk 0a96621e3e Docs for layout option 2010-10-04 08:01:17 -07:00
Tj Holowaychuk d9d30abe7a Updated docs to use NODE_ENV 2010-10-01 16:33:27 -07:00
Tj Holowaychuk 03b56d8140 Implement NODE_ENV, EXPRESS_ENV is now deprecated
spark, connect, and express will from now on utilize NODE_ENV.
EXPRESS_ENV support will remain until 1.0
2010-10-01 16:31:32 -07:00
Tj Holowaychuk 1f79ce4e96 Updated connect submodule 2010-10-01 16:26:09 -07:00
Tj Holowaychuk 8d80f23a0f antialiased text 2010-10-01 07:53:56 -07:00
Tj Holowaychuk d89b951451 HTTP verb docs. Closes #415 2010-10-01 07:51:19 -07:00
Tj Holowaychuk 1906a38ec8 express(1) app using 2 spaces instead of 4 2010-09-26 08:24:48 -07:00
Tj Holowaychuk 8d91a3e3b8 Updated jade submodule 2010-09-23 09:24:56 -07:00
Tj Holowaychuk 3324259977 Updated connect submodule 2010-09-23 09:24:52 -07:00
Tj Holowaychuk ec2db2d541 Fixed mvc example listen() call 2010-09-22 16:08:38 -07:00
Tj Holowaychuk 5cf248c7f3 Merge branch 'auth-example' 2010-09-22 12:03:28 -07:00
Tj Holowaychuk 3876a566ff Added authentication example
very raw and minimal but gives people and idea
2010-09-22 12:03:18 -07:00
Tj Holowaychuk 334e6cc618 Started auth example 2010-09-22 10:36:15 -07:00
Tj Holowaychuk 64033fcfc2 Extended utils.parseRange() 2010-09-22 09:34:05 -07:00
Tj Holowaychuk fbe3af4719 Added utils.parseRange() tests 2010-09-22 09:29:25 -07:00
Tj Holowaychuk 699ed4ee85 Started naive Range support 2010-09-22 09:06:11 -07:00
Tj Holowaychuk 903f67a154 Started utils.parseRange() 2010-09-22 08:48:22 -07:00
Tj Holowaychuk 9a89dab9e0 Added Range test 2010-09-22 08:41:20 -07:00
Tj Holowaychuk 435a2565b8 Added Accept-Ranges: bytes 2010-09-22 08:29:48 -07:00
Tj Holowaychuk 8267b8da57 Added res.sendfile() Accept-Ranges test 2010-09-22 08:29:22 -07:00
Tj Holowaychuk 8b5773c246 Multipart typo 2010-09-21 12:38:42 -07:00
Tj Holowaychuk fd4c88d202 Updated docs 2010-09-21 09:21:11 -07:00
Tj Holowaychuk 9b26623aa5 View scope now defaults to the request
No longer the response, as this is more useful for obtaining urls etc
2010-09-21 09:20:04 -07:00
Tj Holowaychuk b115ec7c14 "context" option is no more, use "scope" 2010-09-21 09:18:22 -07:00
Tj Holowaychuk a2e6e2a06a Pass true for res.sendfile() callback when streamed
This is potentially useful since we can no longer
respond, however with readFile() we can respond with a 500
page or similar
2010-09-21 08:51:07 -07:00
Tj Holowaychuk f0eab06a8b Updated docs 2010-09-20 09:54:01 -07:00
Tj Holowaychuk b6c839d693 Release 1.0.0rc3 2010-09-20 09:50:38 -07:00
Tj Holowaychuk 177a724d58 res.sendfile() responding with 403 on malicious path 2010-09-20 09:16:01 -07:00
Tj Holowaychuk 949803dcf6 Merge branch 'downloads' 2010-09-20 09:06:46 -07:00
Tj Holowaychuk b763a88e60 Docs for "stream threshold" setting 2010-09-20 09:06:43 -07:00
Tj Holowaychuk 18c7f164a8 res.sendfile() using sys.pump() when >= "stream threshold" 2010-09-20 08:41:08 -07:00
Tj Holowaychuk e037ff6bba Added Content-Length assertion for res.sendfile() 2010-09-20 08:23:16 -07:00
Tj Holowaychuk 95bfda1ff0 res.sendfile() using fs.stat() 2010-09-20 08:19:21 -07:00
Tj Holowaychuk b9992e0b45 Refactored res.sendfile() 2010-09-20 08:18:38 -07:00
Tj Holowaychuk f6ba793885 Merge branch 'download-example' 2010-09-18 10:25:05 -07:00
Tj Holowaychuk bbe65a8ea3 Fixed res.download() bug; when an error occurs remove Content-Disposition
Previously if you tried to show a 404 page (or similar)
on a failed res.download(), the browser would still prompt for download
2010-09-18 10:25:00 -07:00
Tj Holowaychuk 20af7371d0 Added download example 2010-09-18 10:21:37 -07:00
Tj Holowaychuk a33fb0c439 Added widescript link 2010-09-14 15:56:21 -07:00
Tj Holowaychuk 87003c8b5b Fixed tests for node --version 0.2.1 2010-09-14 08:18:13 -07:00
Tj Holowaychuk ddd48ee79f Added LICENSE as requested by davglass 2010-09-14 07:59:30 -07:00
Tj Holowaychuk a9819df3ee res.send() HEAD support 2010-09-13 12:05:28 -07:00
Tj Holowaychuk 15dffb22aa Updated connect submodule 2010-09-13 11:58:49 -07:00
Tj Holowaychuk 6ef5f1ff62 Merge branch 'integration' 2010-09-13 11:24:33 -07:00
Tj Holowaychuk acc28c68a4 Refactored app.set() 2010-09-13 11:24:20 -07:00
Aaron Heckmann 6c2da60eb0 add test for inherited mounted app settings 2010-09-13 11:20:49 -07:00
Aaron Heckmann 490081077d mounted apps settings now inherited from parent app 2010-09-13 11:20:49 -07:00
Tj Holowaychuk f4a08092da Merge branch 'guide' 2010-09-13 11:14:55 -07:00
Tj Holowaychuk 0116efd4a3 Removed manpage crap from site 2010-09-13 11:14:41 -07:00
Tj Holowaychuk 9719a24f8a Toggling menus 2010-09-13 11:11:47 -07:00
Tj Holowaychuk 3a7d5a099b TOC sections 2010-09-13 11:03:17 -07:00
Tj Holowaychuk 27dba655fc Uncommented cookie assertions previously failing 2010-09-13 10:11:28 -07:00
Tj Holowaychuk a25dd387bf Refactored res.redirect() 2010-09-13 10:10:03 -07:00
Tj Holowaychuk a89767cc16 Cookie docs 2010-09-13 09:52:33 -07:00
Tj Holowaychuk a13df5b68d Added test for res.clearCookie() 2010-09-13 09:44:42 -07:00
Tj Holowaychuk 6a689504ff Fixed res.cookie() test 2010-09-13 09:44:02 -07:00
Tj Holowaychuk 6521d2bcfb Merge branch 'features/cookie' 2010-09-13 09:43:37 -07:00
Tj Holowaychuk 26d55b15de Added cookies example 2010-09-13 09:43:13 -07:00
Tj Holowaychuk 041b7e8a29 Added res.cookie() and res.clearCookie() 2010-09-13 09:43:07 -07:00
Tj Holowaychuk 6eeb425e7a Updated jade submodule 2010-09-11 13:09:39 -07:00
Tj Holowaychuk d2d0eb829e Updated jade submodule 2010-09-10 13:19:14 -07:00
Tj Holowaychuk 058505b664 Updated connect submodule 2010-09-10 13:19:11 -07:00
Tj Holowaychuk 8adecedb40 Added test to confirm view helper usage w/ ejs 2010-09-08 10:49:29 -07:00
Tj Holowaychuk 838a51db21 Merge branch 'master' of github.com:visionmedia/express 2010-09-06 14:06:50 -07:00
Tj Holowaychuk 4d4dfc27fc Updated connect submodule 2010-09-06 14:06:35 -07:00
Tj Holowaychuk 1444439cf3 Wiki 2010-09-06 11:37:48 -07:00
Tj Holowaychuk e64fc8d5ab node dep 2010-09-06 11:03:33 -07:00
Tj Holowaychuk 9a31f772d9 Added contrib link 2010-09-06 10:59:56 -07:00
Tj Holowaychuk 12876ee4ce Added res.send() as 204 in the guide 2010-09-06 10:15:32 -07:00
Tj Holowaychuk a4a349da45 Stripping Content-Length / Content-Type when 204 2010-09-06 10:10:59 -07:00
Tj Holowaychuk ecbc3cae71 res.send() as 204. Closes #419 2010-09-06 10:05:39 -07:00
Tj Holowaychuk 57609c87fb Merge branch 'features/render-status' 2010-09-06 09:44:46 -07:00
Tj Holowaychuk bb2e24e028 Added docs for render() status / headers 2010-09-06 09:44:32 -07:00
Tj Holowaychuk 5cafb26e93 Fixed pages example w/ render() status 2010-09-06 09:43:17 -07:00
Tj Holowaychuk f74ef7b293 Added render() headers option 2010-09-06 09:42:00 -07:00
Tj Holowaychuk 7ef8f531e7 Added render() status option support. Closes #425 2010-09-06 09:38:23 -07:00
Tj Holowaychuk afb65c7b19 Added render() status test 2010-09-06 09:36:22 -07:00
Tj Holowaychuk 74a0673663 Better app.helper() test 2010-09-06 09:11:27 -07:00
Tj Holowaychuk 20fae6e512 Updated jade submodule 2010-09-02 17:08:08 -07:00
Tj Holowaychuk 509cd242f7 Updated connect submodule 2010-09-02 17:06:50 -07:00
Tj Holowaychuk f01ec516eb Updated connect submodule 2010-09-02 09:50:04 -07:00
Tj Holowaychuk 394b1000d2 Updated ejs submodule 2010-08-30 17:46:16 -07:00
Tj Holowaychuk e0b9fba788 Updated jade submodule 2010-08-30 17:46:13 -07:00
Tj Holowaychuk 378ee29cc2 Updated connect submodule 2010-08-30 16:14:06 -07:00
Tj Holowaychuk 02bcef71dd Updated jade submodule 2010-08-27 13:47:48 -07:00
Tj Holowaychuk 248e7c8003 Updated connect submodule 2010-08-27 13:47:41 -07:00
Tj Holowaychuk a2e93ebd56 Fixed Connect links 2010-08-27 12:40:33 -07:00
Tj Holowaychuk 21a9454de1 Merge branch 'confirmation' 2010-08-27 12:38:23 -07:00
Tj Holowaychuk ec286eed08 Added confirmation for express(1) app generation. Closes #391 2010-08-27 12:38:19 -07:00
Tj Holowaychuk f16978d1fe Added flash formatter docs 2010-08-27 12:17:57 -07:00
Tj Holowaychuk fa3adc7606 Merge branch 'flash-formatting' 2010-08-27 12:15:23 -07:00
Tj Holowaychuk 192144fc5d Added extending of flash formatters via app.flashFormatters 2010-08-27 12:15:18 -07:00
Tj Holowaychuk a4d155c5e4 Added flash formatter support. Closes #411 2010-08-27 12:09:55 -07:00
Tj Holowaychuk 6c56e9d199 Added failing assertion for flash formatters 2010-08-27 11:59:29 -07:00
Tj Holowaychuk bb18f87a98 Merge branch 'fix-toc' 2010-08-27 10:13:13 -07:00
Tj Holowaychuk dda6a31381 Fixed TOC. Closes #404 [thanks weaver] 2010-08-27 10:13:08 -07:00
Tj Holowaychuk 6e25e18e98 express(1) now outputting listening msg, and express.MIDDLEWARE vs connect.MIDDLWARE 2010-08-24 13:52:26 -07:00
Tj Holowaychuk ffd978c24f Updated node --version support 2010-08-24 13:35:40 -07:00
Tj Holowaychuk 4c4af03a5a Doc typos 2010-08-24 13:22:52 -07:00
Tj Holowaychuk c0739bb8b7 Fixed bug messing with error handlers when listenFD() is called instead of listen(). [thanks guillermo]
programs such as "spark" call listenFD() to call listen(1) with
a file descriptor, BUT Express was only proxying listen()
to register these handlers. Thanks for catching this bug guillermo!
2010-08-24 13:21:28 -07:00
Tj Holowaychuk 5add7cb0b1 Merge branch 'mvc' 2010-08-24 13:06:53 -07:00
Tj Holowaychuk 99f49f1061 Fixed MVC 500 error example 2010-08-24 13:06:50 -07:00
Tj Holowaychuk 401eaca9ab Mvc example typo 2010-08-24 12:58:30 -07:00
Tj Holowaychuk da784fe02b Added 500 page to mvc example 2010-08-24 12:56:34 -07:00
Tj Holowaychuk d507b29559 Added .json format support to the mvc example 2010-08-24 12:10:29 -07:00
Tj Holowaychuk 35ca13b38d Merge branch 'form' 2010-08-24 10:49:56 -07:00
Tj Holowaychuk 04b92a31a8 Added keepExtensions: true to multipart example 2010-08-24 10:49:52 -07:00
Tj Holowaychuk dd311d759d Better 404 Not Found example 2010-08-20 21:39:35 -07:00
Tj Holowaychuk b504ca9f0a Merged mvc example. Closes #405 2010-08-20 18:09:56 -07:00
Tj Holowaychuk bd61c5c721 Fixed hasMessages dynamic helper 2010-08-20 18:04:01 -07:00
Tj Holowaychuk 24694b0047 mvc styling 2010-08-20 18:03:12 -07:00
Tj Holowaychuk 5258154285 Added 404 page to mvc example 2010-08-20 17:58:29 -07:00
Tj Holowaychuk 221f993b46 Misc refactoring of mvc example 2010-08-20 17:53:55 -07:00
Tj Holowaychuk ae5ea70b93 Editing of email for mvc example as well 2010-08-20 17:29:01 -07:00
Tj Holowaychuk f59ffbeb63 Implemented simple user editing 2010-08-20 17:27:21 -07:00
Tj Holowaychuk ce55275634 ejs parsing *.html for mvc example 2010-08-20 17:11:30 -07:00
Tj Holowaychuk 20c5892f9a More work on mvc example 2010-08-20 17:08:10 -07:00
Tj Holowaychuk 9231245a3f Started mvc example 2010-08-20 16:50:23 -07:00
Tj Holowaychuk cbfed20e6f Updated ejs submodule 2010-08-20 16:23:49 -07:00
Tj Holowaychuk 640274ed52 Merge branch 'redirect-bodies' 2010-08-20 12:30:06 -07:00
Tj Holowaychuk 1507a39a2b res.redirect() using res.send()
Allows for setting of arbitrary headers via res.header()
2010-08-20 12:21:29 -07:00
Tj Holowaychuk e6405653d8 Added res.redirect() Content-Type of text/plain for now 2010-08-20 12:19:08 -07:00
Tj Holowaychuk be1541a646 Added res.redirect() response bodies 2010-08-20 12:17:52 -07:00
Tj Holowaychuk f37f9bf5e3 Updated connect submodule 2010-08-20 08:42:18 -07:00
Tj Holowaychuk a5c39ca8c8 Merge branch 'set-cookie' 2010-08-20 08:41:10 -07:00
Tj Holowaychuk cb7e7b2a74 Pass res.send() headers through res.header() 2010-08-20 08:41:06 -07:00
Tj Holowaychuk 2e8e705bab Fixed multiple Set-Cookie headers via res.header(). Closes #402 2010-08-20 08:29:21 -07:00
Tj Holowaychuk ab3812432b Updated expresso submodule 2010-08-19 11:44:42 -07:00
Tj Holowaychuk d2acb36e3b Updated connect submodule 2010-08-19 11:44:40 -07:00
Tj Holowaychuk 601743ff7a Added boilerplate example 2010-08-18 17:41:06 -07:00
Tj Holowaychuk 7eca74ac05 Updated node --version in readme 2010-08-17 16:45:42 -07:00
Tj Holowaychuk 02b4ec04bb Doc typos 2010-08-17 16:08:02 -07:00
Tj Holowaychuk 8ead9014d0 Regenerated docs again 2010-08-17 13:15:54 -07:00
Tj Holowaychuk 4920e82dd6 Regenerated docs 2010-08-17 13:12:21 -07:00
Tj Holowaychuk 3150253661 Release 1.0.0rc2 2010-08-17 13:09:17 -07:00
Tj Holowaychuk baa423b45a Updated connect submodule 2010-08-16 13:49:33 -07:00
Tj Holowaychuk 77e5d8ea96 Added app.register() docs 2010-08-15 10:20:33 -07:00
Tj Holowaychuk 9a8166d649 Added app.register() for template engine mapping. Closes #390
This is not exactly what you requested, but
I think this is a better approach. With old Express
I used to have "foo.ejs.html" "foo.haml.html" etc,
but that is super verbose.

This will allow for non-authors to support non-compliant
engines without modifying the original module, as well
as having things like .html render with jade
2010-08-15 10:13:47 -07:00
Tj Holowaychuk da8c0cd2db Removed clearCache()
its useless ATM
2010-08-15 09:54:51 -07:00
Tj Holowaychuk fc2711b543 Fixed bug with undefined path 2010-08-12 14:57:36 -07:00
Tj Holowaychuk b4327bd36c Merge branch 'render-callback' 2010-08-11 16:29:10 -07:00
Tj Holowaychuk f7f75923aa Added res.render() callback support as second argument
This previously was not valid:

    res.render(foo, function(){});
2010-08-11 16:28:24 -07:00
Tj Holowaychuk 0b4388bc5b Added test to confirm res.render() callback with layouts 2010-08-11 16:25:31 -07:00
Tj Holowaychuk f08883071f Updated app list 2010-08-11 10:11:20 -07:00
Tj Holowaychuk bb8a2ee3a6 Updated expresso submodule 2010-08-11 09:32:59 -07:00
Tj Holowaychuk 6bfa7595d8 Added callback support to res.download() 2010-08-09 16:11:00 -07:00
Tj Holowaychuk 8308e501cd Fixed res.sendfile() error handling, defer via next()
No need to be so specific here.
2010-08-09 16:04:41 -07:00
Tj Holowaychuk 25909c0d9d Added callback support for res.sendfile() 2010-08-09 16:00:23 -07:00
Tj Holowaychuk 2844b1ac38 Fixed test labels 2010-08-09 15:57:27 -07:00
Tj Holowaychuk 6fd62b1cee No need to pass params in app.redirect() 2010-08-09 09:19:44 -07:00
Tj Holowaychuk 1579f53a61 Updated connect submodule 2010-08-09 08:50:33 -07:00
Tj Holowaychuk 7af7851952 Fixed res.download() test 2010-08-09 08:06:01 -07:00
Tj Holowaychuk 5dbd1a6dcc Merge branch 'format-example' 2010-08-09 08:03:00 -07:00
Tj Holowaychuk 24881537a1 Fixed format example 2010-08-09 08:02:38 -07:00
Tj Holowaychuk 631c29b26b Updated connect submodule 2010-08-09 07:57:45 -07:00
Tj Holowaychuk ab249d4032 Added format example 2010-08-09 07:46:29 -07:00
Tj Holowaychuk 5bfa3f6274 Merge branch 'helper-precedence' 2010-08-09 07:32:20 -07:00
Tj Holowaychuk 82ee25dfaa Added test helper local precedence test 2010-08-09 07:32:12 -07:00
Tj Holowaychuk fd9a24553f Added another dynamicHelper example to flash example 2010-08-09 07:22:33 -07:00
Tj Holowaychuk ae1c20fad9 Fixed flash example. Closes #392 2010-08-09 07:19:55 -07:00
Tj Holowaychuk 734f777ff9 Added failing test for view helper precedence 2010-08-06 16:13:21 -07:00
Tj Holowaychuk 498115d5d9 Merge branch 'fix-error' 2010-08-06 09:04:08 -07:00
Tj Holowaychuk 033ff06b68 More app.error() docs 2010-08-06 09:04:04 -07:00
Tj Holowaychuk c145a8e1a1 Changed "pages" example to show error() handlers defined above routes 2010-08-06 09:01:09 -07:00
Tj Holowaychuk ae5b0dbc29 Fixed issue preventing error handlers from being defined anywhere. Closes #387
Now we can define them in configure() callbacks etc
2010-08-06 08:59:16 -07:00
Tj Holowaychuk 95aa957c80 Altered app.error() tests to reflect issue #387 2010-08-06 08:48:17 -07:00
Tj Holowaychuk 7dff9ddeeb Merge branch 'flash-example' 2010-08-05 17:12:33 -07:00
Tj Holowaychuk b4e755747f Fixed dynamic helper in flash example 2010-08-05 17:12:18 -07:00
Tj Holowaychuk 348fa402be Started flash example 2010-08-05 16:51:07 -07:00
Tj Holowaychuk 344218ff3d Updated docs 2010-08-05 16:19:06 -07:00
Tj Holowaychuk 90a1a2febf Updated examples to use express.<middleware> 2010-08-05 16:10:22 -07:00
Tj Holowaychuk e234a24894 Re-exporting connect middleware auto-loaders 2010-08-05 15:56:33 -07:00
Tj Holowaychuk d9beb75bd6 Updated connect submodule 2010-08-05 15:51:27 -07:00
Tj Holowaychuk dadea8c83d Merge branch 'fix-layouts' 2010-08-05 13:57:02 -07:00
Tj Holowaychuk 31d40e5509 Updated connect submodule 2010-08-05 13:32:22 -07:00
Tj Holowaychuk a917e84b51 Fixed latest test 2010-08-05 11:59:58 -07:00
Tj Holowaychuk f30b497206 Added failing layout test 2010-08-05 11:47:35 -07:00
Tj Holowaychuk 706e32c20b Merge branch 'app-level-view-options' 2010-08-05 09:46:03 -07:00
Tj Holowaychuk 4d98552c37 Docs for "view options" 2010-08-05 09:45:56 -07:00
Tj Holowaychuk 5311bd3f4e Added app level view options
via the "view options" setting
2010-08-05 09:41:26 -07:00
Tj Holowaychuk 8d716fb7ba Added test for "view options" setting 2010-08-05 09:31:14 -07:00
Tj Holowaychuk 01b3caaf49 Updated ejs submodule 2010-08-05 09:21:47 -07:00
Tj Holowaychuk cd73c0b9ca Updated expresso submodule 2010-08-05 08:48:25 -07:00
Tj Holowaychuk 7ec5230402 make install-docs creating PREFIX/share/man/man1 when not present 2010-08-04 09:45:16 -07:00
Tj Holowaychuk 9dbe026f83 make install creating ~/.node_libraries when not present 2010-08-04 09:43:58 -07:00
Tj Holowaychuk 3fae10da3e Merge branch 'integration' 2010-08-04 09:13:25 -07:00
Tj Holowaychuk d775154b92 Added "partials" setting to docs 2010-08-04 09:13:21 -07:00
Tj Holowaychuk 6c306b3542 Fixed "partials" setting 2010-08-04 09:10:58 -07:00
Nick Poulden 58b46ffeac Optionally set partials directory 2010-08-04 09:04:50 -07:00
Tj Holowaychuk 0bb7b7c60e Updated connect submodule 2010-08-04 08:35:16 -07:00
Tj Holowaychuk 58adf9a385 Updated jade submodule 2010-08-04 08:33:18 -07:00
Tj Holowaychuk d6ba968ace Updated expresso 2010-08-04 07:56:26 -07:00
Tj Holowaychuk 1706721a62 Updated ejs 2010-08-04 07:56:11 -07:00
Tj Holowaychuk 7f88bd2eaf Added isLayout property
Aid introspection for another project I am working on
2010-08-03 15:52:20 -07:00
Tj Holowaychuk b819366265 Updated docs 2010-08-03 14:53:35 -07:00
Tj Holowaychuk 47ff7d9dba Fixed doc typo 2010-08-03 14:53:31 -07:00
Tj Holowaychuk e5f5254877 Added default expresso tests to express(1) generated app. Closes #384 2010-08-03 09:57:24 -07:00
Tj Holowaychuk 4778f728f9 Updated ejs submodule 2010-08-02 10:37:03 -07:00
Tj Holowaychuk e80e4573be Updated jade submodule 2010-08-02 09:04:50 -07:00
Tj Holowaychuk fdf0f3f282 Fixed multipart app example 2010-08-02 07:35:38 -07:00
Tj Holowaychuk a81fce47d0 Updated docs 2010-07-31 07:18:35 -07:00
Tj Holowaychuk 2cd6989b11 Fixed some inline comments 2010-07-31 07:18:29 -07:00
Tj Holowaychuk 21abb0d205 Fixed readme version of node 2010-07-30 18:09:51 -07:00
Tj Holowaychuk 5e1a271ca2 Fixed res.render() callback when a layout is used [thanks guillermo] 2010-07-30 16:52:35 -07:00
Tj Holowaychuk 868e4c41e1 Added storify link 2010-07-30 12:51:50 -07:00
Tj Holowaychuk a6e23bb713 Fixed connect dependency version 2010-07-30 10:50:54 -07:00
Tj Holowaychuk 446406dd62 Removed --growl from make test 2010-07-30 10:47:00 -07:00
Tj Holowaychuk 091f9f7eae Test typo 2010-07-30 10:45:35 -07:00
Tj Holowaychuk 10b1e06263 Updated docs 2010-07-30 10:41:12 -07:00
Tj Holowaychuk a8865a51fd Misc docs 2010-07-30 10:29:58 -07:00
Tj Holowaychuk 32343297fc Updated docs 2010-07-30 10:21:33 -07:00
82 arquivos alterados com 4460 adições e 933 exclusões
+57
Ver Arquivo
@@ -1,4 +1,61 @@
1.0.0rc4 / 2010-10-14
==================
* Added _NODE_ENV_ support, _EXPRESS_ENV_ is deprecated and will be removed in 1.0.0
* Added route-middleware support (very helpful, see the [docs](http://expressjs.com/guide.html#Route-Middleware))
* Added _jsonp callback_ setting to enable/disable jsonp autowrapping [Dav Glass]
* Added callback query check on response.send to autowrap JSON objects for simple webservice implementations [Dav Glass]
* Added `partial()` support for array-like collections. Closes #434
* Added support for swappable querystring parsers
* Added session usage docs. Closes #443
* Added dynamic helper caching. Closes #439 [suggested by maritz]
* Added authentication example
* Added basic Range support to `res.sendfile()` (and `res.download()` etc)
* Changed; `express(1)` generated app using 2 spaces instead of 4
* Default env to "development" again [aheckmann]
* Removed _context_ option is no more, use "scope"
* Fixed; exposing _./support_ libs to examples so they can run without installs
* Fixed mvc example
1.0.0rc3 / 2010-09-20
==================
* Added confirmation for `express(1)` app generation. Closes #391
* Added extending of flash formatters via `app.flashFormatters`
* Added flash formatter support. Closes #411
* Added streaming support to `res.sendfile()` using `sys.pump()` when >= "stream threshold"
* Added _stream threshold_ setting for `res.sendfile()`
* Added `res.send()` __HEAD__ support
* Added `res.clearCookie()`
* Added `res.cookie()`
* Added `res.render()` headers option
* Added `res.redirect()` response bodies
* Added `res.render()` status option support. Closes #425 [thanks aheckmann]
* Fixed `res.sendfile()` responding with 403 on malicious path
* Fixed `res.download()` bug; when an error occurs remove _Content-Disposition_
* Fixed; mounted apps settings now inherit from parent app [aheckmann]
* Fixed; stripping Content-Length / Content-Type when 204
* Fixed `res.send()` 204. Closes #419
* Fixed multiple _Set-Cookie_ headers via `res.header()`. Closes #402
* Fixed bug messing with error handlers when `listenFD()` is called instead of `listen()`. [thanks guillermo]
1.0.0rc2 / 2010-08-17
==================
* Added `app.register()` for template engine mapping. Closes #390
* Added `res.render()` callback support as second argument (no options)
* Added callback support to `res.download()`
* Added callback support for `res.sendfile()`
* Added support for middleware access via `express.middlewareName()` vs `connect.middlewareName()`
* Added "partials" setting to docs
* Added default expresso tests to `express(1)` generated app. Closes #384
* Fixed `res.sendfile()` error handling, defer via `next()`
* Fixed `res.render()` callback when a layout is used [thanks guillermo]
* Fixed; `make install` creating ~/.node_libraries when not present
* Fixed issue preventing error handlers from being defined anywhere. Closes #387
1.0.0rc / 2010-07-28
==================
+22
Ver Arquivo
@@ -0,0 +1,22 @@
(The MIT License)
Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca>
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+7 -3
Ver Arquivo
@@ -13,6 +13,7 @@ HTMLDOCS =$(DOCS:.md=.html)
install: install-docs
@mkdir -p $(PREFIX)/bin
@mkdir -p $(LIB_PREFIX)
cp -f bin/express $(PREFIX)/bin/express
cp -fr lib/express $(LIB_PREFIX)/express
@@ -29,14 +30,14 @@ uninstall-support:
cd support/jade && $(MAKE) uninstall
install-docs:
@mkdir -p $(PREFIX)/share/man/man1
cp -f docs/executable.1 $(PREFIX)/share/man/man1/express.1
uninstall-docs:
rm -f $(PREFIX)/share/man/man1/express.1
test:
@CONNECT_ENV=test ./support/expresso/bin/expresso \
--growl \
@NODE_ENV=test ./support/expresso/bin/expresso \
-I lib \
-I support/connect/lib \
-I support/haml/lib \
@@ -44,6 +45,7 @@ test:
-I support/ejs/lib \
$(TESTFLAGS) \
test/*.test.js
test-cov:
@TESTFLAGS=--cov $(MAKE) test
@@ -52,7 +54,9 @@ docs: docs/api.html $(MANPAGES) $(HTMLDOCS)
@./support/toc.js docs/guide.html
docs/api.html: lib/express/*.js
dox --title Express \
dox \
--private \
--title Express \
--desc "High performance web framework for [node](http://nodejs.org)." \
$(shell find lib/express/* -type f) > $@
+11 -7
Ver Arquivo
@@ -2,7 +2,7 @@
# Express
Insanely fast (and small) server-side JavaScript web development framework
built on [node](http://nodejs.org) and [Connect](http://github.com/extjs/Connect).
built on [node](http://nodejs.org) and [Connect](http://github.com/senchalabs/connect).
var app = express.createServer();
@@ -39,7 +39,9 @@ git clone, first update the submodules:
* Environment based configuration
* Session based flash notifications
* Built on [Connect](http://github.com/senchalabs/connect)
* High test coverage
* Executable for generating applications quickly
* Application level view options
Via Connect:
@@ -64,19 +66,21 @@ The following are the major contributors of Express (in no specific order).
## More Information
* Express [Contrib](http://github.com/visionmedia/express-contrib) repo for additional functionality
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
* [Google Group](http://groups.google.com/group/express-js) for discussion
* [JavaScript Extensions &amp; Utilities](http://github.com/visionmedia/ext.js)
* [JavaScript Sass](http://github.com/visionmedia/sass.js)
* [JavaScript Haml](http://github.com/visionmedia/haml.js)
* [JavaScript Jade](http://github.com/visionmedia/jade) Haml successor
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
## Node Compatibility
The latest release of Express is compatible with node --version:
v0.1.100
v0.2.3
and connect --version:
0.2.6
## License
(The MIT License)
+125 -19
Ver Arquivo
@@ -12,7 +12,13 @@ var sys = require('sys'),
* Framework version.
*/
var version = '1.0.0rc';
var version = '1.0.0rc4';
/**
* stdin stream.
*/
var stdin;
/**
* Usage documentation.
@@ -58,6 +64,32 @@ var jadeIndex = [
'p Welcome to #{title}'
].join('\n');
/**
* App test template.
*/
var appTest = [
"",
"// Run $ expresso",
"",
"/**",
" * Module dependencies.",
" */",
"",
"var app = require('../app');",
"",
"module.exports = {",
" 'GET /': function(assert){",
" assert.response(app,",
" { url: '/' },",
" { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' }},",
" function(res){",
" assert.includes(res.body, '<title>Express</title>');",
" });",
" }",
"};",
].join('\n');
/**
* App template.
*/
@@ -68,41 +100,45 @@ var app = [
' * Module dependencies.',
' */',
'',
'var express = require(\'express\'),',
' connect = require(\'connect\');',
'var express = require(\'express\');',
'',
'var app = express.createServer();',
'var app = module.exports = express.createServer();',
'',
'// Configuration',
'',
'app.configure(function(){',
' app.set(\'views\', __dirname + \'/views\');',
' app.use(connect.bodyDecoder());',
' app.use(connect.methodOverride());',
' app.use(connect.compiler({ src: __dirname + \'/public\', enable: [\'less\'] }));',
' app.use(app.router);',
' app.use(connect.staticProvider(__dirname + \'/public\'));',
' app.set(\'views\', __dirname + \'/views\');',
' app.use(express.bodyDecoder());',
' app.use(express.methodOverride());',
' app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'less\'] }));',
' app.use(app.router);',
' app.use(express.staticProvider(__dirname + \'/public\'));',
'});',
'',
'app.configure(\'development\', function(){',
' app.use(connect.errorHandler({ dumpExceptions: true, showStack: true })); ',
' app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); ',
'});',
'',
'app.configure(\'production\', function(){',
' app.use(connect.errorHandler()); ',
' app.use(express.errorHandler()); ',
'});',
'',
'// Routes',
'',
'app.get(\'/\', function(req, res){',
' res.render(\'index.jade\', {',
' locals: {',
' title: \'Express\'',
' }',
' });',
' res.render(\'index.jade\', {',
' locals: {',
' title: \'Express\'',
' }',
' });',
'});',
'',
'app.listen(3000);',
'// Only listen on $ node app.js',
'',
'if (!module.parent) {',
' app.listen(3000);',
' console.log("Express server listening on port %d", app.address().port)',
'}',
''
].join('\n');
@@ -129,6 +165,29 @@ while (args.length) {
// Generate application
(function createApplication(path) {
emptyDirectory(path, function(empty){
if (empty) {
createApplicationAt(path);
} else {
confirm('destination is not empty, continue? ', function(ok){
if (ok) {
stdin.destroy();
createApplicationAt(path);
} else {
abort('aborting');
}
});
}
});
})(path);
/**
* Create application at the given directory `path`.
*
* @param {String} path
*/
function createApplicationAt(path) {
mkdir(path, function(){
mkdir(path + '/pids');
mkdir(path + '/logs');
@@ -141,9 +200,26 @@ while (args.length) {
write(path + '/views/layout.jade', jadeLayout);
write(path + '/views/index.jade', jadeIndex);
});
mkdir(path + '/test', function(){
write(path + '/test/app.test.js', appTest);
});
write(path + '/app.js', app);
});
})(path);
}
/**
* Check if the given directory `path` is empty.
*
* @param {String} path
* @param {Function} fn
*/
function emptyDirectory(path, fn) {
fs.readdir(path, function(err, files){
if (err && err.errno !== process.ENOENT) throw err;
fn(!files || !files.length);
});
}
/**
* echo str > path.
@@ -157,6 +233,36 @@ function write(path, str) {
sys.puts(' create : ' + path);
}
/**
* Prompt confirmation with the given `msg`.
*
* @param {String} msg
* @param {Function} fn
*/
function confirm(msg, fn) {
prompt(msg, function(val){
fn(/^ *y(es)?/i.test(val));
});
}
/**
* Prompt input with the given `msg` and callback `fn`.
*
* @param {String} msg
* @param {Function} fn
*/
function prompt(msg, fn) {
stdin = stdin || process.openStdin();
sys[msg[msg.length - 1] == ' ' ? 'print' : 'puts'](msg);
stdin.setEncoding('ascii');
stdin.addListener('data', function(data){
fn(data);
stdin.removeListener('data', arguments.callee);
});
}
/**
* Mkdir -p.
*
+697 -335
Ver Arquivo
Diferenças do arquivo suprimidas por serem muito extensas Carregar Diff
+8 -3
Ver Arquivo
@@ -1,8 +1,11 @@
.\" generated with Ronn/v0.6.6
.\" http://github.com/rtomayko/ronn/
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "CONTRIB" "" "July 2010" "" ""
.
.SH "NAME"
\fBcontrib\fR
.
.SS "Development Dependencies"
Express development dependencies are stored within the \fI\./support\fR directory\. To update them execute:
.
@@ -74,4 +77,6 @@ Do \fInot\fR alter release information such as the \fIversion\fR, or \fIHistory\
Indents are \fI4\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\.
+36 -31
Ver Arquivo
@@ -2,32 +2,7 @@
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(function(){
var done;
$(document).scroll(function(event){
if (!done) {
$('#toc').animate({
top: 0
});
}
});
});
</script>
<style>
#header {
position: absolute;
top: 10px;
left: 0;
padding: 12px 0;
text-indent: 40px;
width: 100%;
border-top: 1px solid rgba(0,0,0,0.7);
border-bottom: 1px solid rgba(0,0,0,0.7);
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
text-align: left;
color: #fff;
}
#tagline {
margin-left: 75px;
margin-bottom: 30px;
@@ -41,8 +16,7 @@
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
@@ -65,13 +39,17 @@
a:hover {
opacity: 0.8; }
h1, h2, h3 {
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
@@ -93,6 +71,12 @@
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
@@ -123,17 +107,18 @@
#container {
margin: 0 auto;
padding-top: 110px;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 60px;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
@@ -171,12 +156,23 @@
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="header"><strong>Sencha</strong> labs</div>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
@@ -191,6 +187,10 @@
<li><a href="migrate.html">1.x Migration</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>
<p>Express development dependencies are stored within the <em>./support</em> directory. To
@@ -231,6 +231,11 @@ simply execute:</p>
</ul>
<h3 id="Documentation">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>
+5 -2
Ver Arquivo
@@ -1,8 +1,11 @@
.\" generated with Ronn/v0.6.6
.\" http://github.com/rtomayko/ronn/
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "EXECUTABLE" "" "July 2010" "" ""
.
.SH "NAME"
\fBexecutable\fR
.
.SH "Synopsis"
.
.nf
+31 -31
Ver Arquivo
@@ -2,32 +2,7 @@
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(function(){
var done;
$(document).scroll(function(event){
if (!done) {
$('#toc').animate({
top: 0
});
}
});
});
</script>
<style>
#header {
position: absolute;
top: 10px;
left: 0;
padding: 12px 0;
text-indent: 40px;
width: 100%;
border-top: 1px solid rgba(0,0,0,0.7);
border-bottom: 1px solid rgba(0,0,0,0.7);
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
text-align: left;
color: #fff;
}
#tagline {
margin-left: 75px;
margin-bottom: 30px;
@@ -41,8 +16,7 @@
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
@@ -65,13 +39,17 @@
a:hover {
opacity: 0.8; }
h1, h2, h3 {
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
@@ -93,6 +71,12 @@
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
@@ -123,17 +107,18 @@
#container {
margin: 0 auto;
padding-top: 110px;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 60px;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
@@ -171,12 +156,23 @@
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="header"><strong>Sencha</strong> labs</div>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
@@ -191,6 +187,10 @@
<li><a href="migrate.html">1.x Migration</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>executable</code>
</p>
<h2 id="Synopsis">Synopsis</h2>
<pre><code>express [-h|--help] [-v|--version] [PATH]
+569 -56
Ver Arquivo
@@ -1,7 +1,10 @@
.\" generated with Ronn/v0.6.6
.\" http://github.com/rtomayko/ronn/
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "GUIDE" "" "July 2010" "" ""
.TH "GUIDE" "" "October 2010" "" ""
.
.SH "NAME"
\fBguide\fR
.
.SS "Installation"
curl:
@@ -74,18 +77,18 @@ In the example below we only \fIdumpExceptions\fR, and respond with exception st
.nf
app\.configure(function(){
app\.use(connect\.methodOverride());
app\.use(connect\.bodyDecoder());
app\.use(express\.methodOverride());
app\.use(express\.bodyDecoder());
app\.use(app\.router);
app\.use(connect\.staticProvider(__dirname + \'/public\'));
app\.use(express\.staticProvider(__dirname + \'/public\'));
});
app\.configure(\'development\', function(){
app\.use(connect\.errorHandler({ dumpExceptions: true, showStack: true }));
app\.use(express\.errorHandler({ dumpExceptions: true, showStack: true }));
});
app\.configure(\'production\', function(){
app\.use(connect\.errorHandler());
app\.use(express\.errorHandler());
});
.
.fi
@@ -102,7 +105,7 @@ For internal and arbitrary settings Express provides the \fIset(key[, val])\fR,
app\.configure(function(){
app\.set(\'views\', __dirname + \'/views\');
app\.set(\'views\');
// => \"\.\.\. views directory \.\.\.\"
// => "\.\.\. views directory \.\.\."
app\.enable(\'some feature\');
// same as app\.set(\'some feature\', true);
@@ -116,13 +119,13 @@ app\.configure(function(){
.IP "" 0
.
.P
To alter the environment we can set the \fICONNECT_ENV\fR environment variable, or more specifically \fIEXPRESS_ENV\fR, for example:
To alter the environment we can set the \fINODE_ENV\fR environment variable, for example:
.
.IP "" 4
.
.nf
$ EXPRESS_ENV=production node app\.js
$ NODE_ENV=production node app\.js
.
.fi
.
@@ -144,7 +147,13 @@ Express supports the following settings out of the box:
\fIview engine\fR Default view engine name for views rendered without extensions
.
.IP "\(bu" 4
\fIreload views\fR Reloads altered views, by default watches for \fImtime\fR changes with with a 500 millisecond interval\.
\fIview options\fR An object specifying global view options
.
.IP "\(bu" 4
\fIpartials\fR Root view partials directory defaulting to \fIviews\fR/partials\.
.
.IP "\(bu" 4
\fIstream threshold\fR Bytesize indicating when a file should be streamed for \fIres\.sendfile()\fR using \fIfs\.ReadStream()\fR and \fIsys\.pump()\fR\.
.
.IP "" 0
.
@@ -170,7 +179,7 @@ A route is simple a string which is compiled to a \fIRegExp\fR internally\. For
.
.nf
\\/user\\/([^\\/]+)\\/?
\e/user\e/([^\e/]+)\e/?
.
.fi
.
@@ -183,7 +192,7 @@ Regular expression literals may also be passed for complex uses\. Since capture
.
.nf
app\.get(/^\\/users?(?:\\/(\\d+)(?:\\\.\\\.(\\d+))?)?/, function(req, res){
app\.get(/^\e/users?(?:\e/(\ed+)(?:\e\.\e\.(\ed+))?)?/, function(req, res){
res\.send(req\.params);
});
.
@@ -203,9 +212,9 @@ Curl requests against the previously defined route:
$ curl http://dev:3000/users
[null,null]
$ curl http://dev:3000/users/1
[\"1\",null]
["1",null]
$ curl http://dev:3000/users/1\.\.15
[\"1\",\"15\"]
["1","15"]
.
.fi
.
@@ -218,30 +227,30 @@ Below are some route examples, and the associated paths that they may consume:
.
.nf
\"/user/:id\"
"/user/:id"
/user/12
\"/users/:id?\"
"/users/:id?"
/users/5
/users
\"/files/*\"
"/files/*"
/files/jquery\.js
/files/javascripts/jquery\.js
\"/file/*\.*\"
"/file/*\.*"
/files/jquery\.js
/files/javascripts/jquery\.js
\"/user/:id/:operation?\"
"/user/:id/:operation?"
/user/1
/user/1/edit
\"/products\.:format\"
"/products\.:format"
/products\.json
/products\.xml
\"/products\.:format?\"
"/products\.:format?"
/products\.json
/products\.xml
/products
@@ -251,13 +260,13 @@ Below are some route examples, and the associated paths that they may consume:
.IP "" 0
.
.SS "Passing Route Control"
We may pass control to the next \fImatching\fR route, by calling the \fIthird\fR argument, the \fInext()\fR function\. When a match cannot be made, control is passed back to Connect, and middleware continue to be invoked\.
We may pass control to the next \fImatching\fR route, by calling the \fIthird\fR argument, the \fInext()\fR function\. When a match cannot be made, control is passed back to Connect, and middleware continue to be invoked\. The same is true for several routes which have the same path defined, they will simply be executed in order until one does \fInot\fR call \fInext()\fR\.
.
.IP "" 4
.
.nf
app\.get(\'/users/:id?\', function(req, res){
app\.get(\'/users/:id?\', function(req, res, next){
var id = req\.params\.id;
if (id) {
// do something
@@ -275,19 +284,18 @@ app\.get(\'/users\', function(req, res){
.IP "" 0
.
.SS "Middleware"
The Express \fIPlugin\fR is no more! middleware via Connect \fIhttp://github\.com/extjs/Connect\fR can be passed to \fIexpress\.createServer()\fR as you would with a regular Connect server\. For example:
Middleware via Connect \fIhttp://github\.com/senchalabs/connect\fR can be passed to \fIexpress\.createServer()\fR as you would with a regular Connect server\. For example:
.
.IP "" 4
.
.nf
var connect = require(\'connect\'),
express = require(\'express\');
var express = require(\'express\');
var app = express\.createServer(
connect\.logger(),
connect\.bodyDecoder()
);
express\.logger(),
express\.bodyDecoder()
);
.
.fi
.
@@ -300,7 +308,236 @@ Alternatively we can \fIuse()\fR them which is useful when adding middleware wit
.
.nf
app\.use(connect\.logger({ format: \':method :uri\' }));
app\.use(express\.logger({ format: \':method :uri\' }));
.
.fi
.
.IP "" 0
.
.P
Typically with connect middleware you would \fIrequire(\'connect\')\fR like so:
.
.IP "" 4
.
.nf
var connect = require(\'connect\');
app\.use(connect\.logger());
.
.fi
.
.IP "" 0
.
.P
This is somewhat annoying, so express re\-exports these middleware properties, however they are \fIidentical\fR:
.
.IP "" 4
.
.nf
app\.use(express\.logger());
.
.fi
.
.IP "" 0
.
.SS "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\.
.
.P
Typically async data retrieval might look similar to below, where we take the \fI:id\fR parameter, and attempt loading a user\.
.
.IP "" 4
.
.nf
app\.get(\'/user/:id\', function(req, res, next){
loadUser(req\.params\.id, function(err, user){
if (err) return next(err);
res\.send(\'Viewing user \' + user\.name);
});
});
.
.fi
.
.IP "" 0
.
.P
To keep things DRY and to increase readability we can apply this logic within a middleware\. As you can see below, abstracting this logic into middleware allows us to reuse it, and clean up our route at the same time\.
.
.IP "" 4
.
.nf
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req\.params\.id];
if (user) {
req\.user = user;
next();
} else {
next(new Error(\'Failed to load user \' + req\.params\.id));
}
}
app\.get(\'/user/:id\', loadUser, function(req, res){
res\.send(\'Viewing user \' + req\.user\.name);
});
.
.fi
.
.IP "" 0
.
.P
Multiple route middleware can be applied, and will be executed sequentially to apply further logic such as restricting access to a user account\. In the example below only the authenticated user may edit his/her account\.
.
.IP "" 4
.
.nf
function andRestrictToSelf(req, res, next) {
req\.authenticatedUser\.id == req\.user\.id
? next()
: next(new Error(\'Unauthorized\'));
}
app\.get(\'/user/:id/edit\', loadUser, andRestrictToSelf, function(req, res){
res\.send(\'Editing user \' + req\.user\.name);
});
.
.fi
.
.IP "" 0
.
.P
Keeping in mind that middleware are simply functions, we can define function that \fIreturns\fR the middleware in order to create a more expressive and flexible solution as shown below\.
.
.IP "" 4
.
.nf
function andRestrictTo(role) {
return function(req, res, next) {
req\.authenticatedUser\.role == role
? next()
: next(new Error(\'Unauthorized\'));
}
}
app\.del(\'/user/:id\', loadUser, andRestrictTo(\'admin\'), function(req, res){
res\.send(\'Deleted user \' + req\.user\.name);
});
.
.fi
.
.IP "" 0
.
.P
Commonly used "stacks" of middleware can be passed as an array (\fIapplied recursively\fR), which can be mixed and matched to any degree\.
.
.IP "" 4
.
.nf
var a = [middleware1, middleware2]
, b = [middleware3, middleware4]
, all = [a, b];
app\.get(\'/foo\', a, function(){});
app\.get(\'/bar\', a, function(){});
app\.get(\'/\', a, middleware3, middleware4, function(){});
app\.get(\'/\', a, b, function(){});
app\.get(\'/\', all, function(){});
.
.fi
.
.IP "" 0
.
.P
For this example in full, view the route middleware example \fIhttp://github\.com/visionmedia/express/blob/master/examples/route\-middleware/app\.js\fR in the repository\.
.
.SS "HTTP Methods"
We have seen \fIapp\.get()\fR a few times, however Express also exposes other familiar HTTP verbs in the same manor, such as \fIapp\.post()\fR, \fIapp\.del()\fR, etc\.
.
.P
A common example for \fIPOST\fR usage, is when "submitting" a form\. Below we simply set our form method to "post" in our html, and control will be given to the route we have defined below it\.
.
.IP "" 4
.
.nf
<form method="post" action="/">
<input type="text" name="user[name]" />
<input type="text" name="user[email]" />
<input type="submit" value="Submit" />
</form>
.
.fi
.
.IP "" 0
.
.P
By default Express does not know what to do with this request body, so we should add the \fIbodyDecoder\fR middleware, which will parse \fIapplication/x\-www\-form\-urlencoded\fR request bodies and place the variables in \fIreq\.body\fR\. We can do this by "using" the middleware as shown below:
.
.IP "" 4
.
.nf
app\.use(express\.bodyDecoder());
.
.fi
.
.IP "" 0
.
.P
Our route below will now have access to the \fIreq\.body\.user\fR object which will contain the \fIname\fR and \fIemail\fR properties when defined\.
.
.IP "" 4
.
.nf
app\.post(\'/\', function(req, res){
console\.log(req\.body\.user);
res\.redirect(\'back\');
});
.
.fi
.
.IP "" 0
.
.P
When using methods such as \fIPUT\fR with a form, we can utilize a hidden input named \fI_method\fR, which can be used to alter the HTTP method\. To do so we first need the \fImethodOverride\fR middleware, which should be placed below \fIbodyDecoder\fR so that it can utilize it\'s \fIreq\.body\fR containing the form values\.
.
.IP "" 4
.
.nf
app\.use(express\.bodyDecoder());
app\.use(express\.methodOverride());
.
.fi
.
.IP "" 0
.
.P
The reason that these are not always defaults, is simply because these are not required for Express to be fully functional\. Depending on the needs of your application, you may not need these at all, your methods such as \fIPUT\fR and \fIDELETE\fR can still be accessed by clients which can use them directly, although \fImethodOverride\fR provides a great solution for forms\. Below shows what the usage of \fIPUT\fR might look like:
.
.IP "" 4
.
.nf
<form method="post" action="/">
<input type="hidden" name="_method" value="put" />
<input type="text" name="user[name]" />
<input type="text" name="user[email]" />
<input type="submit" value="Submit" />
</form>
app\.put(\'/\', function(){
console\.log(req\.body\.user);
res\.redirect(\'back\');
});
.
.fi
.
@@ -336,6 +573,9 @@ app\.get(\'/500\', function(req, res){
.P
We can call \fIapp\.error()\fR several times as shown below\. Here we check for an instanceof \fINotFound\fR and show the 404 page, or we pass on to the next error handler\.
.
.P
Note that these handlers can be defined anywhere, as they will be placed below the route handlers on \fIlisten()\fR\. This allows for definition within \fIconfigure()\fR blocks so we can handle exceptions in different ways based on the environment\.
.
.IP "" 4
.
.nf
@@ -372,13 +612,13 @@ app\.error(function(err, req, res){
.IP "" 0
.
.P
Our apps could also utilize the Connect \fIerrorHandler\fR middleware to report on exceptions\. For example if we wish to output exceptions in \"development\" mode to \fIstderr\fR we can use:
Our apps could also utilize the Connect \fIerrorHandler\fR middleware to report on exceptions\. For example if we wish to output exceptions in "development" mode to \fIstderr\fR we can use:
.
.IP "" 4
.
.nf
app\.use(connect\.errorHandler({ dumpExceptions: true }));
app\.use(express\.errorHandler({ dumpExceptions: true }));
.
.fi
.
@@ -391,7 +631,7 @@ Also during development we may want fancy html pages to show exceptions that are
.
.nf
app\.use(connect\.errorHandler({ showStack: true, dumpExceptions: true }));
app\.use(express\.errorHandler({ showStack: true, dumpExceptions: true }));
.
.fi
.
@@ -401,7 +641,7 @@ app\.use(connect\.errorHandler({ showStack: true, dumpExceptions: true }));
The \fIerrorHandler\fR middleware also responds with \fIjson\fR if \fIAccept: application/json\fR is present, which is useful for developing apps that rely heavily on client\-side JavaScript\.
.
.SS "View Rendering"
View filenames take the form \fINAME\fR\.\fIENGINE\fR, where \fIENGINE\fR is the name of the module that will be required\. For example the view \fIlayout\.ejs\fR will tell the view system to \fIrequire(\'ejs\')\fR, the module being loaded must (currently) export the method \fIexports\.render(str, options)\fR to comply with Express, however with will likely be extensible in the future\.
View filenames take the form \fINAME\fR\.\fIENGINE\fR, where \fIENGINE\fR is the name of the module that will be required\. For example the view \fIlayout\.ejs\fR will tell the view system to \fIrequire(\'ejs\')\fR, the module being loaded must export the method \fIexports\.render(str, options)\fR to comply with Express, however \fIapp\.register()\fR can be used to map engines to file extensions, so that for example "foo\.html" can be rendered by jade\.
.
.P
Below is an example using Haml\.js \fIhttp://github\.com/visionmedia/haml\.js\fR to render \fIindex\.html\fR, and since we do not use \fIlayout: false\fR the rendered contents of \fIindex\.html\fR will be passed as the \fIbody\fR local variable in \fIlayout\.haml\fR\.
@@ -472,8 +712,91 @@ res\.render(\'another\-page\.ejs\');
.
.IP "" 0
.
.P
Express also provides the \fIview options\fR setting, which is applied each time a view is rendered, so for example if you rarely use layouts you may set:
.
.IP "" 4
.
.nf
app\.set(\'view options\', {
layout: false
});
.
.fi
.
.IP "" 0
.
.P
Which can then be overridden within the \fBres\.render()\fR call if need be:
.
.IP "" 4
.
.nf
res\.render(\'myview\.ejs\', { layout: true });
.
.fi
.
.IP "" 0
.
.P
When an alternate layout is required, we may also specify a path\. For example if we have \fIview engine\fR set to \fIjade\fR and a file named \fI\./views/mylayout\.jade\fR we can simply pass:
.
.IP "" 4
.
.nf
res\.render(\'page\', { layout: \'mylayout\' });
.
.fi
.
.IP "" 0
.
.P
Otherwise we must specify the extension:
.
.IP "" 4
.
.nf
res\.render(\'page\', { layout: \'mylayout\.jade\' });
.
.fi
.
.IP "" 0
.
.P
These paths may also be absolute:
.
.IP "" 4
.
.nf
res\.render(\'page\', { layout: __dirname + \'/\.\./\.\./mylayout\.jade\' });
.
.fi
.
.IP "" 0
.
.P
A good example of this is specifying custom \fIejs\fR opening and closing tags:
.
.IP "" 4
.
.nf
app\.set(\'view options\', {
open: \'{{\',
close: \'}}\'
});
.
.fi
.
.IP "" 0
.
.SS "View Partials"
The Express view system has built\-in support for partials and collections, which are sort of \"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:
The Express view system has built\-in support for partials and collections, which are sort of "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:
.
.IP "" 4
.
@@ -512,7 +835,7 @@ partial(\'comments\', comments);
.IP "" 0
.
.P
When using the partial collection support a few \"magic\" variables are provided for free:
When using the partial collection support a few "magic" variables are provided for free:
.
.IP "\(bu" 4
\fIfirstInCollection\fR True if this is the first object
@@ -521,7 +844,7 @@ When using the partial collection support a few \"magic\" variables are provided
\fIindexInCollection\fR Index of the object in the collection
.
.IP "\(bu" 4
_lastInCollection _ True if this is the last object
\fIlastInCollection\fR True if this is the last object
.
.IP "" 0
.
@@ -529,16 +852,82 @@ _lastInCollection _ True if this is the last object
Below are a few template engines commonly used with Express:
.
.IP "\(bu" 4
Jade \fIhttp://github\.com/visionmedia/jade\fR haml\.js successor
Jade \fIhttp://jade\-lang\.com\fR haml\.js successor
.
.IP "\(bu" 4
Haml \fIhttp://github\.com/visionmedia/haml\.js\fR indented templates
Haml \fIhttp://github\.com/visionmedia/haml\.js\fR pythonic indented templates
.
.IP "\(bu" 4
EJS \fIhttp://github\.com/visionmedia/ejs\fR Embedded JavaScript
.
.IP "" 0
.
.SS "Session Support"
Sessions support can be added by using Connect\'s \fIsession\fR middleware\. To do so we also need the \fIcookieDecoder\fR middleware place above it, which will parse and populate cookie data to \fIreq\.cookies\fR\.
.
.IP "" 4
.
.nf
app\.use(express\.cookieDecoder());
app\.use(express\.session());
.
.fi
.
.IP "" 0
.
.P
By default the \fIsession\fR middleware uses the memory store bundled with Connect, however many implementations exist\. For example connect\-redis \fIhttp://github\.com/visionmedia/connect\-redis\fR supplies a Redis \fIhttp://code\.google\.com/p/redis/\fR session store and can be used as shown below:
.
.IP "" 4
.
.nf
var RedisStore = require(\'connect\-redis\');
app\.use(express\.cookieDecoder());
app\.use(express\.session({ store: new RedisStore }));
.
.fi
.
.IP "" 0
.
.P
Now the \fIreq\.session\fR and \fIreq\.sessionStore\fR properties will be accessible to all routes and subsequent middleware\. Properties on \fIreq\.session\fR are automatically saved on a response, so for example if we wish to shopping cart data:
.
.IP "" 4
.
.nf
var RedisStore = require(\'connect\-redis\');
app\.use(express\.cookieDecoder());
app\.use(express\.session({ store: new RedisStore }));
app\.use(express\.bodyDecoder());
app\.post(\'/add\-to\-cart\', function(req, res){
// Perhaps we posted several items with a form
// (use the bodyDecoder() middleware for this)
var items = req\.body\.items;
req\.session\.items = items;
res\.redirect(\'back\');
});
app\.get(\'/add\-to\-cart\', function(req, res){
// When redirected back to GET /add\-to\-cart
// we could check req\.session\.items && req\.session\.items\.length
// to print out a message
if (req\.session\.items && req\.session\.items\.length) {
req\.flash(\'info\', \'You have %s items in your cart\', req\.session\.items\.length);
}
res\.render(\'shopping\-cart\');
});
.
.fi
.
.IP "" 0
.
.P
The \fIreq\.session\fR object also has methods such as \fISession#touch()\fR, \fISession#destroy()\fR, \fISession#regenerate()\fR among others to maintain and manipulate sessions\. For more information view the Connect Session \fIhttp://senchalabs\.github\.com/connect/session\.html\fR documentation\.
.
.SS "req\.header(key[, defaultValue])"
Get the case\-insensitive request header \fIkey\fR, with optional \fIdefaultValue\fR:
.
@@ -558,7 +947,7 @@ req\.header(\'Accept\', \'*/*\');
Check if the \fIAccept\fR header is present, and includes the given \fItype\fR\.
.
.P
When the \fIAccept\fR header is not present \fItrue\fR is returned\. Otherwise the given \fItype\fR is matched by an exact match, and then subtypes\. You may pass the subtype such as \"html\" which is then converted internally to \"text/html\" using the mime lookup table\.
When the \fIAccept\fR header is not present \fItrue\fR is returned\. Otherwise the given \fItype\fR is matched by an exact match, and then subtypes\. You may pass the subtype such as "html" which is then converted internally to "text/html" using the mime lookup table\.
.
.IP "" 4
.
@@ -598,7 +987,7 @@ Checks urlencoded body params (\fIreq\.body\fR), ex: id=12
.IP "" 0
.
.P
To utilize urlencoded request bodies, \fIreq\.body\fR should be an object\. This can be done by using the \fIconnect\.bodyDecoder\fR middleware\.
To utilize urlencoded request bodies, \fIreq\.body\fR should be an object\. This can be done by using the \fIexpress\.bodyDecoder\fR middleware\.
.
.SS "req\.flash(type[, msg])"
Queue flash \fImsg\fR of the given \fItype\fR\.
@@ -625,6 +1014,19 @@ req\.flash();
.
.IP "" 0
.
.P
Flash notification message may also utilize formatters, by default only the %s string formatter is available:
.
.IP "" 4
.
.nf
req\.flash(\'info\', \'email delivery to _%s_ from _%s_ failed\.\', toUser, fromUser);
.
.fi
.
.IP "" 0
.
.SS "req\.isXMLHttpRequest"
Also aliased as \fIreq\.xhr\fR, this getter checks the \fIX\-Requested\-With\fR header to see if it was issued by an \fIXMLHttpRequest\fR:
.
@@ -668,14 +1070,14 @@ Sets the \fIContent\-Type\fR response header to the given \fItype\fR\.
var filename = \'path/to/image\.png\';
res\.contentType(filename);
// res\.headers[\'Content\-Type\'] is now \"image/png\"
// res\.headers[\'Content\-Type\'] is now "image/png"
.
.fi
.
.IP "" 0
.
.SS "res\.attachment([filename])"
Sets the \fIContent\-Disposition\fR response header to \"attachment\", with optional \fIfilename\fR\.
Sets the \fIContent\-Disposition\fR response header to "attachment", with optional \fIfilename\fR\.
.
.IP "" 4
.
@@ -701,7 +1103,26 @@ res\.sendfile(\'path/to/my\.file\');
.IP "" 0
.
.P
This is \fInot\fR a substitution for Connect\'s \fIstaticProvider\fR middleware, it does not support HTTP caching, and does not perform any security checks\. This method is utilized by \fIres\.download()\fR to transfer static files, and allows you do to so from outside of the public directory, so suitable security checks should be applied\.
This method accepts a callback which when given will be called on an exception, as well as when the transfer has completed\. When a callback is not given, and the file has \fBnot\fR been streamed, \fInext(err)\fR will be called on an exception\.
.
.IP "" 4
.
.nf
res\.sendfile(path, function(err, path){
if (err) {
// handle the error
} else {
console\.log(\'transferred %s\', path);
}
});
.
.fi
.
.IP "" 0
.
.P
When the filesize exceeds the \fIstream threshold\fR (defaulting to 32k), the file will be streamed using \fIfs\.ReadStream\fR and \fIsys\.pump()\fR\.
.
.SS "res\.download(file[, filename])"
Transfer the given \fIfile\fR as an attachment with optional alternative \fIfilename\fR\.
@@ -738,6 +1159,7 @@ The \fBres\.send()\fR method is a high level response utility allowing you to pa
.
.nf
res\.send(); // 204
res\.send(new Buffer(\'wahoo\'));
res\.send({ some: \'json\' });
res\.send(\'<p>some html</p>\');
@@ -770,7 +1192,51 @@ res\.redirect(\'back\');
.IP "" 0
.
.P
Express supports \"redirect mapping\", which by default provides \fIhome\fR, and \fIback\fR\. The \fIback\fR map checks the \fIReferrer\fR and \fIReferer\fR headers, while \fIhome\fR utilizes the \"home\" setting and defaults to \"/\"\.
Express supports "redirect mapping", which by default provides \fIhome\fR, and \fIback\fR\. The \fIback\fR map checks the \fIReferrer\fR and \fIReferer\fR headers, while \fIhome\fR utilizes the "home" setting and defaults to "/"\.
.
.SS "res\.cookie(name, val[, options])"
Sets the given cookie \fIname\fR to \fIval\fR, with \fIoptions\fR such as "httpOnly: true", "expires", "secure" etc\.
.
.IP "" 4
.
.nf
// "Remember me" for 15 minutes
res\.cookie(\'rememberme\', \'yes\', { expires: new Date(Date\.now() + 900000), httpOnly: true });
.
.fi
.
.IP "" 0
.
.P
To parse incoming \fICookie\fR headers, use the \fIcookieDecoder\fR middleware, which provides the \fIreq\.cookies\fR object:
.
.IP "" 4
.
.nf
app\.use(express\.cookieDecoder());
app\.get(\'/\', function(req, res){
// use req\.cookies\.rememberme
});
.
.fi
.
.IP "" 0
.
.SS "res\.clearCookie(name)"
Clear cookie \fIname\fR by setting "expires" far in the past\.
.
.IP "" 4
.
.nf
res\.clearCookie(\'rememberme\');
.
.fi
.
.IP "" 0
.
.SS "res\.render(view[, options[, fn]])"
Render \fIview\fR with the given \fIoptions\fR and optional callback \fIfn\fR\. When a callback function is given a response will \fInot\fR be made automatically, however otherwise a response of \fI200\fR and \fItext/html\fR is given\.
@@ -779,16 +1245,19 @@ Render \fIview\fR with the given \fIoptions\fR and optional callback \fIfn\fR\.
Most engines accept one or more of the following options, both haml \fIhttp://github\.com/visionmedia/haml\.js\fR and jade \fIhttp://github\.com/visionmedia/jade\fR accept all:
.
.IP "\(bu" 4
\fIcontext|scope\fR Template evaluation context (\fIthis\fR)
\fIscope\fR Template evaluation context (value of \fIthis\fR)
.
.IP "\(bu" 4
\fIlocals\fR Object containing local variables
.
.IP "\(bu" 4
\fIcache\fR Cache intermediate JavaScript in memory (the default in \fIproduction\fR mode)
\fIdebug\fR Output debugging information
.
.IP "\(bu" 4
\fIdebug\fR Output debugging information
\fIstatus\fR Response status code, defaults to 200
.
.IP "\(bu" 4
\fIheaders\fR Response headers object
.
.IP "" 0
.
@@ -832,7 +1301,7 @@ partial(\'movie\', movies);
.IP "" 0
.
.P
To change the local from \fImovie\fR to \fIvideo\fR we can use the \"as\" option:
To change the local from \fImovie\fR to \fIvideo\fR we can use the "as" option:
.
.IP "" 4
.
@@ -860,7 +1329,7 @@ partial(\'movie\', { collection: movies, as: this });
.IP "" 0
.
.P
Another alternative is to \"explode\" the properties of the collection item into pseudo globals (local variables) by using \fIas: global\fR, which again is syntactic sugar:
Another alternative is to "explode" the properties of the collection item into pseudo globals (local variables) by using \fIas: global\fR, which again is syntactic sugar:
.
.IP "" 4
.
@@ -880,9 +1349,9 @@ Apply an application level setting \fIname\fR to \fIval\fR, or get the value of
.
.nf
app\.set(\'reload views\', 200);
app\.set(\'reload views\');
// => 200
app\.set(\'views\', __dirname + \'/views\');
app\.set(\'views\');
// => \.\.\.path\.\.\.
.
.fi
.
@@ -1073,7 +1542,7 @@ var app = express\.createServer(),
blog\.mounted(function(parent){
// parent is app
// \"this\" is blog
// "this" is blog
});
app\.use(blog);
@@ -1082,6 +1551,50 @@ app\.use(blog);
.
.IP "" 0
.
.SS "app\.register(ext, exports)"
Register the given template engine \fIexports\fR as \fIext\fR\. For example we may wish to map "\.html" files to jade:
.
.IP "" 4
.
.nf
app\.register(\'\.html\', require(\'jade\'));
.
.fi
.
.IP "" 0
.
.P
This is also useful for libraries that may not match extensions correctly\. For example my haml\.js library is installed from npm as "hamljs" so instead of layout\.hamljs, we can register the engine as "\.haml":
.
.IP "" 4
.
.nf
app\.register(\'\.haml\', require(\'haml\-js\'));
.
.fi
.
.IP "" 0
.
.P
For engines that do not comply with the Express specification, we can also wrap their api this way\.
.
.IP "" 4
.
.nf
app\.register(\'\.foo\', {
render: function(str, options) {
// perhaps their api is
// foo\.toHTML(str, options);
}
});
.
.fi
.
.IP "" 0
.
.SS "app\.listen([port[, host]])"
Bind the app server to the given \fIport\fR, which defaults to 3000\. When \fIhost\fR is omitted all connections will be accepted via \fIINADDR_ANY\fR\.
.
+387 -93
Ver Arquivo
@@ -2,32 +2,7 @@
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(function(){
var done;
$(document).scroll(function(event){
if (!done) {
$('#toc').animate({
top: 0
});
}
});
});
</script>
<style>
#header {
position: absolute;
top: 10px;
left: 0;
padding: 12px 0;
text-indent: 40px;
width: 100%;
border-top: 1px solid rgba(0,0,0,0.7);
border-bottom: 1px solid rgba(0,0,0,0.7);
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
text-align: left;
color: #fff;
}
#tagline {
margin-left: 75px;
margin-bottom: 30px;
@@ -41,8 +16,7 @@
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
@@ -65,13 +39,17 @@
a:hover {
opacity: 0.8; }
h1, h2, h3 {
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
@@ -93,6 +71,12 @@
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
@@ -123,17 +107,18 @@
#container {
margin: 0 auto;
padding-top: 110px;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 60px;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
@@ -171,12 +156,23 @@
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="header"><strong>Sencha</strong> labs</div>
<div id="wrapper">
<div id="container"><ul id="toc">
<li><a href="#Installation">Installation</a></li>
@@ -186,34 +182,46 @@
<li><a href="#Routing">Routing</a></li>
<li><a href="#Passing-Route-Control">Passing Route Control</a></li>
<li><a href="#Middleware">Middleware</a></li>
<li><a href="#Route-Middleware">Route Middleware</a></li>
<li><a href="#HTTP-Methods">HTTP Methods</a></li>
<li><a href="#Error-Handling">Error Handling</a></li>
<li><a href="#View-Rendering">View Rendering</a></li>
<li><a href="#View-Partials">View Partials</a></li>
<li><a href="#Template-Engines">Template Engines</a></li>
<li><a href="#req-header-key-defaultValue-">req.header()</a></li>
<li><a href="#req-accepts-type-">req.accepts()</a></li>
<li><a href="#req-param-name-">req.param()</a></li>
<li><a href="#req-flash-type-msg-">req.flash()</a></li>
<li><a href="#req-isXMLHttpRequest">req.isXMLHttpRequest</a></li>
<li><a href="#res-header-key-val-">res.header()</a></li>
<li><a href="#res-contentType-type-">res.contentType()</a></li>
<li><a href="#res-attachment-filename-">res.attachment()</a></li>
<li><a href="#res-sendfile-path-">res.sendfile()</a></li>
<li><a href="#res-download-file-filename-">res.download()</a></li>
<li><a href="#res-send-body-status-headers-status-status-">res.send()</a></li>
<li><a href="#res-redirect-url-status-">res.redirect()</a></li>
<li><a href="#res-render-view-options-fn-">res.render()</a></li>
<li><a href="#res-partial-view-options-">res.partial()</a></li>
<li><a href="#app-set-name-val-">app.set()</a></li>
<li><a href="#app-enable-name-">app.enable()</a></li>
<li><a href="#app-disable-name-">app.disable()</a></li>
<li><a href="#app-configure-env-function-function-">app.configure()</a></li>
<li><a href="#app-redirect-name-val-">app.redirect()</a></li>
<li><a href="#app-error-function-">app.error()</a></li>
<li><a href="#app-helpers-obj-">app.helpers()</a></li>
<li><a href="#app-dynamicHelpers-obj-">app.dynamicHelpers()</a></li>
<li><a href="#app-mounted-fn-">app.mounted()</a></li>
<li><a href="#app-listen-port-host-">app.listen()</a></li>
<li><a href="#Session-Support">Session Support</a></li>
<li><a href="#" class="toggle">+</a> <a class="section-title" href="#">Request</a><ul class="section" id="section-Request">
<li><a href="#req-header-key-defaultValue-">header()</a></li>
<li><a href="#req-accepts-type-">accepts()</a></li>
<li><a href="#req-param-name-">param()</a></li>
<li><a href="#req-flash-type-msg-">flash()</a></li>
<li><a href="#req-isXMLHttpRequest">isXMLHttpRequest</a></li>
</ul></li>
<li><a href="#" class="toggle">+</a> <a class="section-title" href="#">Response</a><ul class="section" id="section-Response">
<li><a href="#res-header-key-val-">header()</a></li>
<li><a href="#res-contentType-type-">contentType()</a></li>
<li><a href="#res-attachment-filename-">attachment()</a></li>
<li><a href="#res-sendfile-path-">sendfile()</a></li>
<li><a href="#res-download-file-filename-">download()</a></li>
<li><a href="#res-send-body-status-headers-status-status-">send()</a></li>
<li><a href="#res-redirect-url-status-">redirect()</a></li>
<li><a href="#res-cookie-name-val-options-">cookie()</a></li>
<li><a href="#res-clearCookie-name-">clearCookie()</a></li>
<li><a href="#res-render-view-options-fn-">render()</a></li>
<li><a href="#res-partial-view-options-">partial()</a></li>
</ul></li>
<li><a href="#" class="toggle">+</a> <a class="section-title" href="#">Server</a><ul class="section" id="section-Server">
<li><a href="#app-set-name-val-">set()</a></li>
<li><a href="#app-enable-name-">enable()</a></li>
<li><a href="#app-disable-name-">disable()</a></li>
<li><a href="#app-configure-env-function-function-">configure()</a></li>
<li><a href="#app-redirect-name-val-">redirect()</a></li>
<li><a href="#app-error-function-">error()</a></li>
<li><a href="#app-helpers-obj-">helpers()</a></li>
<li><a href="#app-dynamicHelpers-obj-">dynamicHelpers()</a></li>
<li><a href="#app-mounted-fn-">mounted()</a></li>
<li><a href="#app-register-ext-exports-">register()</a></li>
<li><a href="#app-listen-port-host-">listen()</a></li>
</ul></li>
</ul>
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
<p id="tagline">
@@ -227,6 +235,10 @@
<li><a href="migrate.html">1.x Migration</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>guide</code>
</p>
<h3 id="Installation">Installation</h3>
<p>curl:</p>
@@ -274,18 +286,18 @@ Note the use of <em>app.router</em>, which can (optionally) be used to mount the
otherwise the first call to <em>app.{get,put,del,post}()</em> will mount the routes.</p>
<pre><code>app.configure(function(){
app.use(connect.methodOverride());
app.use(connect.bodyDecoder());
app.use(express.methodOverride());
app.use(express.bodyDecoder());
app.use(app.router);
app.use(connect.staticProvider(__dirname + '/public'));
app.use(express.staticProvider(__dirname + '/public'));
});
app.configure('development', function(){
app.use(connect.errorHandler({ dumpExceptions: true, showStack: true }));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(connect.errorHandler());
app.use(express.errorHandler());
});
</code></pre>
@@ -304,10 +316,9 @@ app.configure('production', function(){
});
</code></pre>
<p>To alter the environment we can set the <em>CONNECT_ENV</em> environment variable,
or more specifically <em>EXPRESS_ENV</em>, for example:</p>
<p>To alter the environment we can set the <em>NODE_ENV</em> environment variable, for example:</p>
<pre><code>$ EXPRESS_ENV=production node app.js
<pre><code>$ NODE_ENV=production node app.js
</code></pre>
<h3 id="Settings">Settings</h3>
@@ -319,8 +330,9 @@ or more specifically <em>EXPRESS_ENV</em>, for example:</p>
<li><em>home</em> Application base path used for <em>res.redirect()</em> and transparently handling mounted apps.</li>
<li><em>views</em> Root views directory defaulting to <strong>CWD/views</strong></li>
<li><em>view engine</em> Default view engine name for views rendered without extensions</li>
<li><em>reload views</em> Reloads altered views, by default watches for <em>mtime</em> changes with
with a 500 millisecond interval.</li>
<li><em>view options</em> An object specifying global view options</li>
<li><em>partials</em> Root view partials directory defaulting to <em>views</em>/partials.</li>
<li><em>stream threshold</em> Bytesize indicating when a file should be streamed for <em>res.sendfile()</em> using <em>fs.ReadStream()</em> and <em>sys.pump()</em>.</li>
</ul>
@@ -398,9 +410,9 @@ may consume:</p>
<p>We may pass control to the next <em>matching</em> route, by calling the <em>third</em> argument,
the <em>next()</em> function. When a match cannot be made, control is passed back to Connect,
and middleware continue to be invoked.</p>
and middleware continue to be invoked. The same is true for several routes which have the same path defined, they will simply be executed in order until one does <em>not</em> call <em>next()</em>.</p>
<pre><code>app.get('/users/:id?', function(req, res){
<pre><code>app.get('/users/:id?', function(req, res, next){
var id = req.params.id;
if (id) {
// do something
@@ -416,21 +428,154 @@ app.get('/users', function(req, res){
<h3 id="Middleware">Middleware</h3>
<p>The Express <em>Plugin</em> is no more! middleware via <a href="http://github.com/extjs/Connect">Connect</a> can be
<p>Middleware via <a href="http://github.com/senchalabs/connect">Connect</a> can be
passed to <em>express.createServer()</em> as you would with a regular Connect server. For example:</p>
<pre><code>var connect = require('connect'),
express = require('express');
<pre><code> var express = require('express');
var app = express.createServer(
connect.logger(),
connect.bodyDecoder()
);
express.logger(),
express.bodyDecoder()
);
</code></pre>
<p>Alternatively we can <em>use()</em> them which is useful when adding middleware within <em>configure()</em> blocks:</p>
<pre><code>app.use(connect.logger({ format: ':method :uri' }));
<pre><code>app.use(express.logger({ format: ':method :uri' }));
</code></pre>
<p>Typically with connect middleware you would <em>require('connect')</em> like so:</p>
<pre><code>var connect = require('connect');
app.use(connect.logger());
</code></pre>
<p>This is somewhat annoying, so express re-exports these middleware properties, however they are <em>identical</em>:</p>
<pre><code>app.use(express.logger());
</code></pre>
<h3 id="Route-Middleware">Route Middleware</h3>
<p>Routes may utilize route-specific middleware by passing one or more additional callbacks (or arrays) to the method. This feature is extremely useful for restricting access, loading data used by the route etc.</p>
<p>Typically async data retrieval might look similar to below, where we take the <em>:id</em> parameter, and attempt loading a user.</p>
<pre><code>app.get('/user/:id', function(req, res, next){
loadUser(req.params.id, function(err, user){
if (err) return next(err);
res.send('Viewing user ' + user.name);
});
});
</code></pre>
<p>To keep things DRY and to increase readability we can apply this logic within a middleware. As you can see below, abstracting this logic into middleware allows us to reuse it, and clean up our route at the same time.</p>
<pre><code>function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' + req.params.id));
}
}
app.get('/user/:id', loadUser, function(req, res){
res.send('Viewing user ' + req.user.name);
});
</code></pre>
<p>Multiple route middleware can be applied, and will be executed sequentially to apply further logic such as restricting access to a user account. In the example below only the authenticated user may edit his/her account.</p>
<pre><code>function andRestrictToSelf(req, res, next) {
req.authenticatedUser.id == req.user.id
? next()
: next(new Error('Unauthorized'));
}
app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
res.send('Editing user ' + req.user.name);
});
</code></pre>
<p>Keeping in mind that middleware are simply functions, we can define function that <em>returns</em> the middleware in order to create a more expressive and flexible solution as shown below.</p>
<pre><code>function andRestrictTo(role) {
return function(req, res, next) {
req.authenticatedUser.role == role
? next()
: next(new Error('Unauthorized'));
}
}
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
res.send('Deleted user ' + req.user.name);
});
</code></pre>
<p>Commonly used "stacks" of middleware can be passed as an array (<em>applied recursively</em>), which can be mixed and matched to any degree.</p>
<pre><code>var a = [middleware1, middleware2]
, b = [middleware3, middleware4]
, all = [a, b];
app.get('/foo', a, function(){});
app.get('/bar', a, function(){});
app.get('/', a, middleware3, middleware4, function(){});
app.get('/', a, b, function(){});
app.get('/', all, function(){});
</code></pre>
<p>For this example in full, view the <a href="http://github.com/visionmedia/express/blob/master/examples/route-middleware/app.js">route middleware example</a> in the repository.</p>
<h3 id="HTTP-Methods">HTTP Methods</h3>
<p>We have seen <em>app.get()</em> a few times, however Express also exposes other familiar HTTP verbs in the same manor, such as <em>app.post()</em>, <em>app.del()</em>, etc.</p>
<p> A common example for <em>POST</em> usage, is when "submitting" a form. Below we simply set our form method to "post" in our html, and control will be given to the route we have defined below it.</p>
<pre><code> &lt;form method="post" action="/"&gt;
&lt;input type="text" name="user[name]" /&gt;
&lt;input type="text" name="user[email]" /&gt;
&lt;input type="submit" value="Submit" /&gt;
&lt;/form&gt;
</code></pre>
<p>By default Express does not know what to do with this request body, so we should add the <em>bodyDecoder</em> middleware, which will parse <em>application/x-www-form-urlencoded</em> request bodies and place the variables in <em>req.body</em>. We can do this by "using" the middleware as shown below:</p>
<pre><code>app.use(express.bodyDecoder());
</code></pre>
<p>Our route below will now have access to the <em>req.body.user</em> object which will contain the <em>name</em> and <em>email</em> properties when defined.</p>
<pre><code>app.post('/', function(req, res){
console.log(req.body.user);
res.redirect('back');
});
</code></pre>
<p>When using methods such as <em>PUT</em> with a form, we can utilize a hidden input named <em>_method</em>, which can be used to alter the HTTP method. To do so we first need the <em>methodOverride</em> middleware, which should be placed below <em>bodyDecoder</em> so that it can utilize it's <em>req.body</em> containing the form values.</p>
<pre><code>app.use(express.bodyDecoder());
app.use(express.methodOverride());
</code></pre>
<p>The reason that these are not always defaults, is simply because these are not required for Express to be fully functional. Depending on the needs of your application, you may not need these at all, your methods such as <em>PUT</em> and <em>DELETE</em> can still be accessed by clients which can use them directly, although <em>methodOverride</em> provides a great solution for forms. Below shows what the usage of <em>PUT</em> might look like:</p>
<pre><code>&lt;form method="post" action="/"&gt;
&lt;input type="hidden" name="_method" value="put" /&gt;
&lt;input type="text" name="user[name]" /&gt;
&lt;input type="text" name="user[email]" /&gt;
&lt;input type="submit" value="Submit" /&gt;
&lt;/form&gt;
app.put('/', function(){
console.log(req.body.user);
res.redirect('back');
});
</code></pre>
<h3 id="Error-Handling">Error Handling</h3>
@@ -460,6 +605,11 @@ app.get('/500', function(req, res){
Here we check for an instanceof <em>NotFound</em> and show the
404 page, or we pass on to the next error handler.</p>
<p>Note that these handlers can be defined anywhere, as they
will be placed below the route handlers on <em>listen()</em>. This
allows for definition within <em>configure()</em> blocks so we can
handle exceptions in different ways based on the environment.</p>
<pre><code>app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.jade');
@@ -485,13 +635,13 @@ this demo, however you can choose whatever you like</p>
to report on exceptions. For example if we wish to output exceptions
in "development" mode to <em>stderr</em> we can use:</p>
<pre><code>app.use(connect.errorHandler({ dumpExceptions: true }));
<pre><code>app.use(express.errorHandler({ dumpExceptions: true }));
</code></pre>
<p>Also during development we may want fancy html pages to show exceptions
that are passed or thrown, so we can set <em>showStack</em> to true:</p>
<pre><code>app.use(connect.errorHandler({ showStack: true, dumpExceptions: true }));
<pre><code>app.use(express.errorHandler({ showStack: true, dumpExceptions: true }));
</code></pre>
<p>The <em>errorHandler</em> middleware also responds with <em>json</em> if <em>Accept: application/json</em>
@@ -501,9 +651,8 @@ is present, which is useful for developing apps that rely heavily on client-side
<p>View filenames take the form <em>Express</em>.<em>ENGINE</em>, where <em>ENGINE</em> is the name
of the module that will be required. For example the view <em>layout.ejs</em> will
tell the view system to <em>require('ejs')</em>, the module being loaded must (currently)
export the method <em>exports.render(str, options)</em> to comply with Express, however
with will likely be extensible in the future.</p>
tell the view system to <em>require('ejs')</em>, the module being loaded must export the method <em>exports.render(str, options)</em> to comply with Express, however
<em>app.register()</em> can be used to map engines to file extensions, so that for example "foo.html" can be rendered by jade.</p>
<p>Below is an example using <a href="http://github.com/visionmedia/haml.js">Haml.js</a> to render <em>index.html</em>,
and since we do not use <em>layout: false</em> the rendered contents of <em>index.html</em> will be passed as
@@ -538,6 +687,41 @@ mix and match template engines:</p>
<pre><code>res.render('another-page.ejs');
</code></pre>
<p>Express also provides the <em>view options</em> setting, which is applied each time a view is rendered, so for example if you rarely use layouts you may set:</p>
<pre><code>app.set('view options', {
layout: false
});
</code></pre>
<p>Which can then be overridden within the <code>res.render()</code> call if need be:</p>
<pre><code>res.render('myview.ejs', { layout: true });
</code></pre>
<p>When an alternate layout is required, we may also specify a path. For example if we have <em>view engine</em> set to <em>jade</em> and a file named <em>./views/mylayout.jade</em> we can simply pass:</p>
<pre><code>res.render('page', { layout: 'mylayout' });
</code></pre>
<p>Otherwise we must specify the extension:</p>
<pre><code>res.render('page', { layout: 'mylayout.jade' });
</code></pre>
<p>These paths may also be absolute:</p>
<pre><code>res.render('page', { layout: __dirname + '/../../mylayout.jade' });
</code></pre>
<p>A good example of this is specifying custom <em>ejs</em> opening and closing tags:</p>
<pre><code>app.set('view options', {
open: '{{',
close: '}}'
});
</code></pre>
<h3 id="View-Partials">View Partials</h3>
<p>The Express view system has built-in support for partials and collections, which are
@@ -565,7 +749,7 @@ for free:</p>
<ul>
<li><em>firstInCollection</em> True if this is the first object</li>
<li><em>indexInCollection</em> Index of the object in the collection</li>
<li>_lastInCollection _ True if this is the last object</li>
<li><em>lastInCollection</em> True if this is the last object</li>
</ul>
@@ -574,12 +758,55 @@ for free:</p>
<p>Below are a few template engines commonly used with Express:</p>
<ul>
<li><a href="http://github.com/visionmedia/jade">Jade</a> haml.js successor</li>
<li><a href="http://github.com/visionmedia/haml.js">Haml</a> indented templates</li>
<li><a href="http://jade-lang.com">Jade</a> haml.js successor</li>
<li><a href="http://github.com/visionmedia/haml.js">Haml</a> pythonic indented templates</li>
<li><a href="http://github.com/visionmedia/ejs">EJS</a> Embedded JavaScript</li>
</ul>
<h3 id="Session-Support">Session Support</h3>
<p>Sessions support can be added by using Connect's <em>session</em> middleware. To do so we also need the <em>cookieDecoder</em> middleware place above it, which will parse and populate cookie data to <em>req.cookies</em>.</p>
<pre><code>app.use(express.cookieDecoder());
app.use(express.session());
</code></pre>
<p>By default the <em>session</em> middleware uses the memory store bundled with Connect, however many implementations exist. For example <a href="http://github.com/visionmedia/connect-redis">connect-redis</a> supplies a <a href="http://code.google.com/p/redis/">Redis</a> session store and can be used as shown below:</p>
<pre><code>var RedisStore = require('connect-redis');
app.use(express.cookieDecoder());
app.use(express.session({ store: new RedisStore }));
</code></pre>
<p>Now the <em>req.session</em> and <em>req.sessionStore</em> properties will be accessible to all routes and subsequent middleware. Properties on <em>req.session</em> are automatically saved on a response, so for example if we wish to shopping cart data:</p>
<pre><code>var RedisStore = require('connect-redis');
app.use(express.cookieDecoder());
app.use(express.session({ store: new RedisStore }));
app.use(express.bodyDecoder());
app.post('/add-to-cart', function(req, res){
// Perhaps we posted several items with a form
// (use the bodyDecoder() middleware for this)
var items = req.body.items;
req.session.items = items;
res.redirect('back');
});
app.get('/add-to-cart', function(req, res){
// When redirected back to GET /add-to-cart
// we could check req.session.items &amp;&amp; req.session.items.length
// to print out a message
if (req.session.items &amp;&amp; req.session.items.length) {
req.flash('info', 'You have %s items in your cart', req.session.items.length);
}
res.render('shopping-cart');
});
</code></pre>
<p>The <em>req.session</em> object also has methods such as <em>Session#touch()</em>, <em>Session#destroy()</em>, <em>Session#regenerate()</em> among others to maintain and manipulate sessions. For more information view the <a href="http://senchalabs.github.com/connect/session.html">Connect Session</a> documentation.</p>
<h3 id="req-header-key-defaultValue-">req.header(key[, defaultValue])</h3>
<p>Get the case-insensitive request header <em>key</em>, with optional <em>defaultValue</em>:</p>
@@ -627,7 +854,7 @@ req.accepts('png');
<p>To utilize urlencoded request bodies, <em>req.body</em>
should be an object. This can be done by using
the <em>connect.bodyDecoder</em> middleware.</p>
the <em>express.bodyDecoder</em> middleware.</p>
<h3 id="req-flash-type-msg-">req.flash(type[, msg])</h3>
@@ -648,6 +875,11 @@ req.flash();
// =&gt; { error: ['email delivery failed'], info: [] }
</code></pre>
<p>Flash notification message may also utilize formatters, by default only the %s string formatter is available:</p>
<pre><code>req.flash('info', 'email delivery to _%s_ from _%s_ failed.', toUser, fromUser);
</code></pre>
<h3 id="req-isXMLHttpRequest">req.isXMLHttpRequest</h3>
<p>Also aliased as <em>req.xhr</em>, this getter checks the <em>X-Requested-With</em> header
@@ -694,10 +926,18 @@ res.header('Content-Length');
<pre><code>res.sendfile('path/to/my.file');
</code></pre>
<p>This is <em>not</em> a substitution for Connect's <em>staticProvider</em> middleware, it does not
support HTTP caching, and does not perform any security checks. This method is utilized
by <em>res.download()</em> to transfer static files, and allows you do to so from outside of
the public directory, so suitable security checks should be applied.</p>
<p>This method accepts a callback which when given will be called on an exception, as well as when the transfer has completed. When a callback is not given, and the file has <strong>not</strong> been streamed, <em>next(err)</em> will be called on an exception.</p>
<pre><code>res.sendfile(path, function(err, path){
if (err) {
// handle the error
} else {
console.log('transferred %s', path);
}
});
</code></pre>
<p>When the filesize exceeds the <em>stream threshold</em> (defaulting to 32k), the file will be streamed using <em>fs.ReadStream</em> and <em>sys.pump()</em>.</p>
<h3 id="res-download-file-filename-">res.download(file[, filename])</h3>
@@ -719,7 +959,8 @@ res.sendfile(file);
objects to respond with json, strings for html, arbitrary _Buffer_s or numbers for status
code based responses. The following are all valid uses:</p>
<pre><code> res.send(new Buffer('wahoo'));
<pre><code> res.send(); // 204
res.send(new Buffer('wahoo'));
res.send({ some: 'json' });
res.send('&lt;p&gt;some html&lt;/p&gt;');
res.send('Sorry, cant find that', 404);
@@ -746,6 +987,30 @@ res.redirect('back');
The <em>back</em> map checks the <em>Referrer</em> and <em>Referer</em> headers, while <em>home</em> utilizes
the "home" setting and defaults to "/".</p>
<h3 id="res-cookie-name-val-options-">res.cookie(name, val[, options])</h3>
<p>Sets the given cookie <em>name</em> to <em>val</em>, with <em>options</em> such as "httpOnly: true", "expires", "secure" etc.</p>
<pre><code>// "Remember me" for 15 minutes
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000), httpOnly: true });
</code></pre>
<p>To parse incoming <em>Cookie</em> headers, use the <em>cookieDecoder</em> middleware, which provides the <em>req.cookies</em> object:</p>
<pre><code>app.use(express.cookieDecoder());
app.get('/', function(req, res){
// use req.cookies.rememberme
});
</code></pre>
<h3 id="res-clearCookie-name-">res.clearCookie(name)</h3>
<p>Clear cookie <em>name</em> by setting "expires" far in the past.</p>
<pre><code>res.clearCookie('rememberme');
</code></pre>
<h3 id="res-render-view-options-fn-">res.render(view[, options[, fn]])</h3>
<p>Render <em>view</em> with the given <em>options</em> and optional callback <em>fn</em>.
@@ -756,10 +1021,11 @@ automatically, however otherwise a response of <em>200</em> and <em>text/html</e
both <a href="http://github.com/visionmedia/haml.js">haml</a> and <a href="http://github.com/visionmedia/jade">jade</a> accept all:</p>
<ul>
<li><em>context|scope</em> Template evaluation context (<em>this</em>)</li>
<li><em>scope</em> Template evaluation context (value of <em>this</em>)</li>
<li><em>locals</em> Object containing local variables</li>
<li><em>cache</em> Cache intermediate JavaScript in memory (the default in <em>production</em> mode)</li>
<li><em>debug</em> Output debugging information</li>
<li><em>status</em> Response status code, defaults to 200</li>
<li><em>headers</em> Response headers object</li>
</ul>
@@ -816,9 +1082,9 @@ pseudo globals (local variables) by using <em>as: global</em>, which again is sy
<p>Apply an application level setting <em>name</em> to <em>val</em>, or
get the value of <em>name</em> when <em>val</em> is not present:</p>
<pre><code>app.set('reload views', 200);
app.set('reload views');
// =&gt; 200
<pre><code>app.set('views', __dirname + '/views');
app.set('views');
// =&gt; ...path...
</code></pre>
<h3 id="app-enable-name-">app.enable(name)</h3>
@@ -941,6 +1207,34 @@ blog.mounted(function(parent){
app.use(blog);
</code></pre>
<h3 id="app-register-ext-exports-">app.register(ext, exports)</h3>
<p>Register the given template engine <em>exports</em>
as <em>ext</em>. For example we may wish to map ".html"
files to jade:</p>
<pre><code> app.register('.html', require('jade'));
</code></pre>
<p>This is also useful for libraries that may not
match extensions correctly. For example my haml.js
library is installed from npm as "hamljs" so instead
of layout.hamljs, we can register the engine as ".haml":</p>
<pre><code> app.register('.haml', require('haml-js'));
</code></pre>
<p>For engines that do not comply with the Express
specification, we can also wrap their api this way.</p>
<pre><code> app.register('.foo', {
render: function(str, options) {
// perhaps their api is
// foo.toHTML(str, options);
}
});
</code></pre>
<h3 id="app-listen-port-host-">app.listen([port[, host]])</h3>
<p>Bind the app server to the given <em>port</em>, which defaults to 3000. When <em>host</em> is omitted all
+287 -31
Ver Arquivo
@@ -42,18 +42,18 @@ Note the use of _app.router_, which can (optionally) be used to mount the applic
otherwise the first call to _app.{get,put,del,post}()_ will mount the routes.
app.configure(function(){
app.use(connect.methodOverride());
app.use(connect.bodyDecoder());
app.use(express.methodOverride());
app.use(express.bodyDecoder());
app.use(app.router);
app.use(connect.staticProvider(__dirname + '/public'));
app.use(express.staticProvider(__dirname + '/public'));
});
app.configure('development', function(){
app.use(connect.errorHandler({ dumpExceptions: true, showStack: true }));
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
});
app.configure('production', function(){
app.use(connect.errorHandler());
app.use(express.errorHandler());
});
For internal and arbitrary settings Express provides the _set(key[, val])_, _enable(key)_, _disable(key)_ methods:
@@ -70,10 +70,9 @@ For internal and arbitrary settings Express provides the _set(key[, val])_, _ena
// same as app.set('some feature', false);
});
To alter the environment we can set the _CONNECT_ENV_ environment variable,
or more specifically _EXPRESS_ENV_, for example:
To alter the environment we can set the _NODE_ENV_ environment variable, for example:
$ EXPRESS_ENV=production node app.js
$ NODE_ENV=production node app.js
### Settings
@@ -83,6 +82,9 @@ Express supports the following settings out of the box:
* _home_ 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
* _partials_ Root view partials directory defaulting to _views_/partials.
* _stream threshold_ Bytesize indicating when a file should be streamed for _res.sendfile()_ using _fs.ReadStream()_ and _sys.pump()_.
### Routing
@@ -153,9 +155,9 @@ may consume:
We may pass control to the next _matching_ route, by calling the _third_ argument,
the _next()_ function. When a match cannot be made, control is passed back to Connect,
and middleware continue to be invoked.
and middleware continue to be invoked. The same is true for several routes which have the same path defined, they will simply be executed in order until one does _not_ call _next()_.
app.get('/users/:id?', function(req, res){
app.get('/users/:id?', function(req, res, next){
var id = req.params.id;
if (id) {
// do something
@@ -170,20 +172,141 @@ and middleware continue to be invoked.
### Middleware
The Express _Plugin_ is no more! middleware via [Connect](http://github.com/extjs/Connect) can be
Middleware via [Connect](http://github.com/senchalabs/connect) can be
passed to _express.createServer()_ as you would with a regular Connect server. For example:
var connect = require('connect'),
express = require('express');
var express = require('express');
var app = express.createServer(
connect.logger(),
connect.bodyDecoder()
);
express.logger(),
express.bodyDecoder()
);
Alternatively we can _use()_ them which is useful when adding middleware within _configure()_ blocks:
app.use(connect.logger({ format: ':method :uri' }));
app.use(express.logger({ format: ':method :uri' }));
Typically with connect middleware you would _require('connect')_ like so:
var connect = require('connect');
app.use(connect.logger());
This is somewhat annoying, so express re-exports these middleware properties, however they are _identical_:
app.use(express.logger());
### 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.
Typically async data retrieval might look similar to below, where we take the _:id_ parameter, and attempt loading a user.
app.get('/user/:id', function(req, res, next){
loadUser(req.params.id, function(err, user){
if (err) return next(err);
res.send('Viewing user ' + user.name);
});
});
To keep things DRY and to increase readability we can apply this logic within a middleware. As you can see below, abstracting this logic into middleware allows us to reuse it, and clean up our route at the same time.
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' + req.params.id));
}
}
app.get('/user/:id', loadUser, function(req, res){
res.send('Viewing user ' + req.user.name);
});
Multiple route middleware can be applied, and will be executed sequentially to apply further logic such as restricting access to a user account. In the example below only the authenticated user may edit his/her account.
function andRestrictToSelf(req, res, next) {
req.authenticatedUser.id == req.user.id
? next()
: next(new Error('Unauthorized'));
}
app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
res.send('Editing user ' + req.user.name);
});
Keeping in mind that middleware are simply functions, we can define function that _returns_ the middleware in order to create a more expressive and flexible solution as shown below.
function andRestrictTo(role) {
return function(req, res, next) {
req.authenticatedUser.role == role
? next()
: next(new Error('Unauthorized'));
}
}
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
res.send('Deleted user ' + req.user.name);
});
Commonly used "stacks" of middleware can be passed as an array (_applied recursively_), which can be mixed and matched to any degree.
var a = [middleware1, middleware2]
, b = [middleware3, middleware4]
, all = [a, b];
app.get('/foo', a, function(){});
app.get('/bar', a, function(){});
app.get('/', a, middleware3, middleware4, function(){});
app.get('/', a, b, function(){});
app.get('/', all, function(){});
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.
### 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.
A common example for _POST_ usage, is when "submitting" a form. Below we simply set our form method to "post" in our html, and control will be given to the route we have defined below it.
<form method="post" action="/">
<input type="text" name="user[name]" />
<input type="text" name="user[email]" />
<input type="submit" value="Submit" />
</form>
By default Express does not know what to do with this request body, so we should add the _bodyDecoder_ middleware, which will parse _application/x-www-form-urlencoded_ request bodies and place the variables in _req.body_. We can do this by "using" the middleware as shown below:
app.use(express.bodyDecoder());
Our route below will now have access to the _req.body.user_ object which will contain the _name_ and _email_ properties when defined.
app.post('/', function(req, res){
console.log(req.body.user);
res.redirect('back');
});
When using methods such as _PUT_ with a form, we can utilize a hidden input named _\_method_, which can be used to alter the HTTP method. To do so we first need the _methodOverride_ middleware, which should be placed below _bodyDecoder_ so that it can utilize it's _req.body_ containing the form values.
app.use(express.bodyDecoder());
app.use(express.methodOverride());
The reason that these are not always defaults, is simply because these are not required for Express to be fully functional. Depending on the needs of your application, you may not need these at all, your methods such as _PUT_ and _DELETE_ can still be accessed by clients which can use them directly, although _methodOverride_ provides a great solution for forms. Below shows what the usage of _PUT_ might look like:
<form method="post" action="/">
<input type="hidden" name="_method" value="put" />
<input type="text" name="user[name]" />
<input type="text" name="user[email]" />
<input type="submit" value="Submit" />
</form>
app.put('/', function(){
console.log(req.body.user);
res.redirect('back');
});
### Error Handling
@@ -211,6 +334,11 @@ 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.
Note that these handlers can be defined anywhere, as they
will be placed below the route handlers on _listen()_. This
allows for definition within _configure()_ blocks so we can
handle exceptions in different ways based on the environment.
app.error(function(err, req, res, next){
if (err instanceof NotFound) {
res.render('404.jade');
@@ -234,12 +362,12 @@ Our apps could also utilize the Connect _errorHandler_ middleware
to report on exceptions. For example if we wish to output exceptions
in "development" mode to _stderr_ we can use:
app.use(connect.errorHandler({ dumpExceptions: true }));
app.use(express.errorHandler({ dumpExceptions: true }));
Also during development we may want fancy html pages to show exceptions
that are passed or thrown, so we can set _showStack_ to true:
app.use(connect.errorHandler({ showStack: true, dumpExceptions: true }));
app.use(express.errorHandler({ showStack: true, dumpExceptions: true }));
The _errorHandler_ middleware also responds with _json_ if _Accept: application/json_
is present, which is useful for developing apps that rely heavily on client-side JavaScript.
@@ -248,9 +376,8 @@ is present, which is useful for developing apps that rely heavily on client-side
View filenames take the form _NAME_._ENGINE_, where _ENGINE_ is the name
of the module that will be required. For example the view _layout.ejs_ will
tell the view system to _require('ejs')_, the module being loaded must (currently)
export the method _exports.render(str, options)_ to comply with Express, however
with will likely be extensible in the future.
tell the view system to _require('ejs')_, the module being loaded must export the method _exports.render(str, options)_ to comply with Express, however
_app.register()_ can be used to map engines to file extensions, so that for example "foo.html" can be rendered by jade.
Below is an example using [Haml.js](http://github.com/visionmedia/haml.js) to render _index.html_,
and since we do not use _layout: false_ the rendered contents of _index.html_ will be passed as
@@ -280,6 +407,35 @@ mix and match template engines:
res.render('another-page.ejs');
Express also provides the _view options_ setting, which is applied each time a view is rendered, so for example if you rarely use layouts you may set:
app.set('view options', {
layout: false
});
Which can then be overridden within the `res.render()` call if need be:
res.render('myview.ejs', { layout: true });
When an alternate layout is required, we may also specify a path. For example if we have _view engine_ set to _jade_ and a file named _./views/mylayout.jade_ we can simply pass:
res.render('page', { layout: 'mylayout' });
Otherwise we must specify the extension:
res.render('page', { layout: 'mylayout.jade' });
These paths may also be absolute:
res.render('page', { layout: __dirname + '/../../mylayout.jade' });
A good example of this is specifying custom _ejs_ opening and closing tags:
app.set('view options', {
open: '{{',
close: '}}'
});
### View Partials
The Express view system has built-in support for partials and collections, which are
@@ -303,16 +459,56 @@ for free:
* _firstInCollection_ True if this is the first object
* _indexInCollection_ Index of the object in the collection
* _lastInCollection _ True if this is the last object
* _lastInCollection_ True if this is the last object
### Template Engines
Below are a few template engines commonly used with Express:
* [Jade](http://github.com/visionmedia/jade) haml.js successor
* [Haml](http://github.com/visionmedia/haml.js) indented templates
* [Jade](http://jade-lang.com) haml.js successor
* [Haml](http://github.com/visionmedia/haml.js) pythonic indented templates
* [EJS](http://github.com/visionmedia/ejs) Embedded JavaScript
### Session Support
Sessions support can be added by using Connect's _session_ middleware. To do so we also need the _cookieDecoder_ middleware place above it, which will parse and populate cookie data to _req.cookies_.
app.use(express.cookieDecoder());
app.use(express.session());
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');
app.use(express.cookieDecoder());
app.use(express.session({ 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');
app.use(express.cookieDecoder());
app.use(express.session({ store: new RedisStore }));
app.use(express.bodyDecoder());
app.post('/add-to-cart', function(req, res){
// Perhaps we posted several items with a form
// (use the bodyDecoder() middleware for this)
var items = req.body.items;
req.session.items = items;
res.redirect('back');
});
app.get('/add-to-cart', function(req, res){
// When redirected back to GET /add-to-cart
// we could check req.session.items && req.session.items.length
// to print out a message
if (req.session.items && req.session.items.length) {
req.flash('info', 'You have %s items in your cart', req.session.items.length);
}
res.render('shopping-cart');
});
The _req.session_ object also has methods such as _Session#touch()_, _Session#destroy()_, _Session#regenerate()_ among others to maintain and manipulate sessions. For more information view the [Connect Session](http://senchalabs.github.com/connect/session.html) documentation.
### req.header(key[, defaultValue])
Get the case-insensitive request header _key_, with optional _defaultValue_:
@@ -355,7 +551,7 @@ Return the value of param _name_ when present.
To utilize urlencoded request bodies, _req.body_
should be an object. This can be done by using
the _connect.bodyDecoder_ middleware.
the _express.bodyDecoder_ middleware.
### req.flash(type[, msg])
@@ -375,6 +571,10 @@ Queue flash _msg_ of the given _type_.
req.flash();
// => { error: ['email delivery failed'], info: [] }
Flash notification message may also utilize formatters, by default only the %s string formatter is available:
req.flash('info', 'email delivery to _%s_ from _%s_ failed.', toUser, fromUser);
### req.isXMLHttpRequest
Also aliased as _req.xhr_, this getter checks the _X-Requested-With_ header
@@ -416,10 +616,17 @@ Used by `res.download()` to transfer an arbitrary file.
res.sendfile('path/to/my.file');
This is _not_ a substitution for Connect's _staticProvider_ middleware, it does not
support HTTP caching, and does not perform any security checks. This method is utilized
by _res.download()_ to transfer static files, and allows you do to so from outside of
the public directory, so suitable security checks should be applied.
This method accepts a callback which when given will be called on an exception, as well as when the transfer has completed. When a callback is not given, and the file has __not__ been streamed, _next(err)_ will be called on an exception.
res.sendfile(path, function(err, path){
if (err) {
// handle the error
} else {
console.log('transferred %s', path);
}
});
When the filesize exceeds the _stream threshold_ (defaulting to 32k), the file will be streamed using _fs.ReadStream_ and _sys.pump()_.
### res.download(file[, filename])
@@ -439,6 +646,7 @@ The `res.send()` method is a high level response utility allowing you to pass
objects to respond with json, strings for html, arbitrary _Buffer_s or numbers for status
code based responses. The following are all valid uses:
res.send(); // 204
res.send(new Buffer('wahoo'));
res.send({ some: 'json' });
res.send('<p>some html</p>');
@@ -464,6 +672,27 @@ 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 "/".
### res.cookie(name, val[, options])
Sets the given cookie _name_ to _val_, with _options_ such as "httpOnly: true", "expires", "secure" etc.
// "Remember me" for 15 minutes
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000), httpOnly: true });
To parse incoming _Cookie_ headers, use the _cookieDecoder_ middleware, which provides the _req.cookies_ object:
app.use(express.cookieDecoder());
app.get('/', function(req, res){
// use req.cookies.rememberme
});
### res.clearCookie(name)
Clear cookie _name_ by setting "expires" far in the past.
res.clearCookie('rememberme');
### res.render(view[, options[, fn]])
Render _view_ with the given _options_ and optional callback _fn_.
@@ -473,9 +702,11 @@ automatically, however otherwise a response of _200_ and _text/html_ is given.
Most engines accept one or more of the following options,
both [haml](http://github.com/visionmedia/haml.js) and [jade](http://github.com/visionmedia/jade) accept all:
- _context|scope_ Template evaluation context (_this_)
- _scope_ Template evaluation context (value of _this_)
- _locals_ Object containing local variables
- _debug_ Output debugging information
- _status_ Response status code, defaults to 200
- _headers_ Response headers object
### res.partial(view[, options])
@@ -632,6 +863,31 @@ Assign a callback _fn_ which is called when this _Server_ is passed to _Server#u
app.use(blog);
### app.register(ext, exports)
Register the given template engine _exports_
as _ext_. For example we may wish to map ".html"
files to jade:
app.register('.html', require('jade'));
This is also useful for libraries that may not
match extensions correctly. For example my haml.js
library is installed from npm as "hamljs" so instead
of layout.hamljs, we can register the engine as ".haml":
app.register('.haml', require('haml-js'));
For engines that do not comply with the Express
specification, we can also wrap their api this way.
app.register('.foo', {
render: function(str, options) {
// perhaps their api is
// return foo.toHTML(str, options);
}
});
### app.listen([port[, host]])
Bind the app server to the given _port_, which defaults to 3000. When _host_ is omitted all
+39 -3
Ver Arquivo
@@ -1,7 +1,10 @@
.\" generated with Ronn/v0.6.6
.\" http://github.com/rtomayko/ronn/
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "INDEX" "" "July 2010" "" ""
.TH "INDEX" "" "October 2010" "" ""
.
.SH "NAME"
\fBindex\fR
.
.IP "" 4
.
@@ -31,6 +34,9 @@ Redirection helpers
Dynamic view helpers
.
.IP "\(bu" 4
Application level view options
.
.IP "\(bu" 4
Content negotiation
.
.IP "\(bu" 4
@@ -51,6 +57,9 @@ 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"
@@ -98,10 +107,37 @@ View the source \fIhttp://github\.com/visionmedia/express\fR
.SH "Apps Using Express"
.
.IP "\(bu" 4
LearnBoost \fIhttp://learnboost\.com\fR \- Online gradebook software
.
.IP "\(bu" 4
Markup\.IO \fIhttp://markup\.io/\fR \- Draw on any website to share ideas
.
.IP "\(bu" 4
wtfjs \fIhttp://wtfjs\.com/\fR \- JavaScript WTFs :)
.
.IP "\(bu" 4
Node Knockout \fIhttp://nodeknockout\.com/\fR \- node knockout competition site
.
.IP "\(bu" 4
Node News \fIhttp://nodejs\.se/\fR \- node news aggregator
.
.IP "\(bu" 4
Code Shelver \fIhttp://codeshelver\.com/\fR \- GitHub watch list app
.
.IP "\(bu" 4
Clickdummy \fIhttp://clickdummy\.net/\fR \- Fast prototyping for designers
.
.IP "\(bu" 4
E\-Resistible \fIhttp://e\-resistible\.co\.uk\fR \- Online takeaway ordering app
.
.IP "\(bu" 4
Storify \fIhttp://storify\.com\fR \- The future of publishing
.
.IP "\(bu" 4
Ogre \fIhttp://ogre\.adc4gis\.com/\fR \- Translates spatial files into GeoJSON
.
.IP "\(bu" 4
Widescript \fIhttp://widescript\.com/\fR \- A digital reading app targeted to people who study or make use of reference material
.
.IP "" 0
+42 -31
Ver Arquivo
@@ -2,32 +2,7 @@
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(function(){
var done;
$(document).scroll(function(event){
if (!done) {
$('#toc').animate({
top: 0
});
}
});
});
</script>
<style>
#header {
position: absolute;
top: 10px;
left: 0;
padding: 12px 0;
text-indent: 40px;
width: 100%;
border-top: 1px solid rgba(0,0,0,0.7);
border-bottom: 1px solid rgba(0,0,0,0.7);
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
text-align: left;
color: #fff;
}
#tagline {
margin-left: 75px;
margin-bottom: 30px;
@@ -41,8 +16,7 @@
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
@@ -65,13 +39,17 @@
a:hover {
opacity: 0.8; }
h1, h2, h3 {
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
@@ -93,6 +71,12 @@
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
@@ -123,17 +107,18 @@
#container {
margin: 0 auto;
padding-top: 110px;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 60px;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
@@ -171,12 +156,23 @@
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="header"><strong>Sencha</strong> labs</div>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
@@ -191,6 +187,10 @@
<li><a href="migrate.html">1.x Migration</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){
@@ -206,6 +206,7 @@ app.listen(3000);
<li>Robust routing</li>
<li>Redirection helpers</li>
<li>Dynamic view helpers</li>
<li>Application level view options</li>
<li>Content negotiation</li>
<li>Focus on high performance</li>
<li>View rendering and partials support</li>
@@ -213,6 +214,7 @@ app.listen(3000);
<li>Session based flash notifications</li>
<li>Built on <a href="http://github.com/senchalabs/connect">Connect</a></li>
<li><a href="executable.html">Executable</a> for generating applications quickly</li>
<li>High test coverage</li>
</ul>
@@ -244,8 +246,17 @@ app.listen(3000);
<h2 id="Apps-Using-Express">Apps Using Express</h2>
<ul>
<li><a href="http://learnboost.com">LearnBoost</a> - Online gradebook software</li>
<li><a href="http://markup.io/">Markup.IO</a> - Draw on any website to share ideas</li>
<li><a href="http://wtfjs.com/">wtfjs</a> - JavaScript WTFs :)</li>
<li><a href="http://nodeknockout.com/">Node Knockout</a> - node knockout competition site</li>
<li><a href="http://nodejs.se/">Node News</a> - node news aggregator</li>
<li><a href="http://codeshelver.com/">Code Shelver</a> - GitHub watch list app</li>
<li><a href="http://clickdummy.net/">Clickdummy</a> - Fast prototyping for designers</li>
<li><a href="http://e-resistible.co.uk">E-Resistible</a> - Online takeaway ordering app</li>
<li><a href="http://storify.com">Storify</a> - The future of publishing</li>
<li><a href="http://ogre.adc4gis.com/">Ogre</a> - Translates spatial files into GeoJSON</li>
<li><a href="http://widescript.com/">Widescript</a> - A digital reading app targeted to people who study or make use of reference material</li>
</ul>
+8 -1
Ver Arquivo
@@ -12,6 +12,7 @@
* Robust routing
* Redirection helpers
* Dynamic view helpers
* Application level view options
* Content negotiation
* Focus on high performance
* View rendering and partials support
@@ -19,6 +20,7 @@
* Session based flash notifications
* Built on [Connect](http://github.com/senchalabs/connect)
* [Executable](executable.html) for generating applications quickly
* High test coverage
## Contributors
@@ -41,9 +43,14 @@ The following are the major contributors of Express (in no specific order).
## Apps Using Express
* [LearnBoost](http://learnboost.com) - Online gradebook software
* [Markup.IO](http://markup.io/) - Draw on any website to share ideas
* [wtfjs](http://wtfjs.com/) - JavaScript WTFs :)
* [Node Knockout](http://nodeknockout.com/) - node knockout competition site
* [Node News](http://nodejs.se/) - node news aggregator
* [Code Shelver](http://codeshelver.com/) - GitHub watch list app
* [Clickdummy](http://clickdummy.net/) - Fast prototyping for designers
* [E-Resistible](e-resistible.co.uk) - Online takeaway ordering app
* [E-Resistible](http://e-resistible.co.uk) - Online takeaway ordering app
* [Storify](http://storify.com) - The future of publishing
* [Ogre](http://ogre.adc4gis.com/) - Translates spatial files into GeoJSON
* [Widescript](http://widescript.com/) - A digital reading app targeted to people who study or make use of reference material
+27 -31
Ver Arquivo
@@ -2,32 +2,7 @@
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(function(){
var done;
$(document).scroll(function(event){
if (!done) {
$('#toc').animate({
top: 0
});
}
});
});
</script>
<style>
#header {
position: absolute;
top: 10px;
left: 0;
padding: 12px 0;
text-indent: 40px;
width: 100%;
border-top: 1px solid rgba(0,0,0,0.7);
border-bottom: 1px solid rgba(0,0,0,0.7);
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
text-align: left;
color: #fff;
}
#tagline {
margin-left: 75px;
margin-bottom: 30px;
@@ -41,8 +16,7 @@
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
@@ -65,13 +39,17 @@
a:hover {
opacity: 0.8; }
h1, h2, h3 {
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
@@ -93,6 +71,12 @@
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
@@ -123,17 +107,18 @@
#container {
margin: 0 auto;
padding-top: 110px;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 60px;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
@@ -171,12 +156,23 @@
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="header"><strong>Sencha</strong> labs</div>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
+12 -12
Ver Arquivo
@@ -1,7 +1,10 @@
.\" generated with Ronn/v0.6.6
.\" http://github.com/rtomayko/ronn/
.\" generated with Ronn/v0.7.3
.\" http://github.com/rtomayko/ronn/tree/0.7.3
.
.TH "MIGRATE" "" "July 2010" "" ""
.TH "MIGRATE" "" "October 2010" "" ""
.
.SH "NAME"
\fBmigrate\fR
.
.SS "Built On Connect"
Express 1\.x is written to run on\-top of the Connect \fIhttp://extjs\.github\.com/Connect\fR middlware framework, thus the \fIPlugin\fR has been replaced by Connect\'s middleware\. By abstracting our middleware to Connect we allow additional community frameworks to develop robust, high\-level frameworks using the same technologies as Express\.
@@ -74,12 +77,10 @@ Which we can now \fIuse()\fR within our app, or pass to the \fIexpress\.createSe
.
.nf
var connect = require(\'connect\');
var app = express\.createServer(
connect\.logger(),
connect\.methodOverride(),
connect\.cookieDecoder()
express\.logger(),
express\.methodOverride(),
express\.cookieDecoder()
);
.
.fi
@@ -93,12 +94,11 @@ or:
.
.nf
var connect = require(\'connect\');
var app = express\.createServer();
app\.use(connect\.logger());
app\.use(connect\.methodOverride());
app\.use(connect\.cookieDecoder());
app\.use(express\.logger());
app\.use(express\.methodOverride());
app\.use(express\.cookieDecoder());
.
.fi
.
+39 -42
Ver Arquivo
@@ -2,32 +2,7 @@
<head>
<title>Express - node web framework</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
<script>
$(function(){
var done;
$(document).scroll(function(event){
if (!done) {
$('#toc').animate({
top: 0
});
}
});
});
</script>
<style>
#header {
position: absolute;
top: 10px;
left: 0;
padding: 12px 0;
text-indent: 40px;
width: 100%;
border-top: 1px solid rgba(0,0,0,0.7);
border-bottom: 1px solid rgba(0,0,0,0.7);
background: rgba(255,255,255,0.1) url(http://www.sencha.com/favicon.ico) no-repeat 15px 50%;
text-align: left;
color: #fff;
}
#tagline {
margin-left: 75px;
margin-bottom: 30px;
@@ -41,8 +16,7 @@
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
font-size: 14px;
line-height: 1.5;
-webkit-text-stroke: 1px rgba(0, 0, 0, 0.1);
-moz-text-stroke: 1px rgba(0, 0, 0, 0.1);
-webkit-font-smoothing: antialiased;
background: url(images/bg.jpg) 50% 0 no-repeat;
color: #8b8b8b; }
@@ -65,13 +39,17 @@
a:hover {
opacity: 0.8; }
h1, h2, h3 {
h1, h2, h3, h4 {
margin: 45px 0 0 0;
color: white;
text-shadow: 1px 2px 2px rgba(0,0,0,0.6); }
h3 {
font-size: 18px; }
h4 {
margin-left: 10px;
font-size: 14px;
}
pre {
margin: 20px 10px;
@@ -93,6 +71,12 @@
margin: 0;
padding: 2px 0;
list-style: square; }
ul li ul {
margin: 0;
padding-left: 12px;
}
.man-name, #Express { display:none; }
.sect {
margin-left: 40px; }
@@ -123,17 +107,18 @@
#container {
margin: 0 auto;
padding-top: 110px;
padding-top: 80px;
width: 550px; }
#toc {
position: fixed;
top: 60px;
top: 0;
left: 0;
margin: 0 0 0 15px;
padding: 15px;
height: 100%;
background: rgba(0,0,0,0.2);
overflow: auto;
border-right: 1px solid rgba(255,255,255,0.05);
}
#toc li {
@@ -171,12 +156,23 @@
-webkit-box-shadow: 1px 1px 1px rgba(0,0,0,0.4);
-moz-box-shadow: 1px 1px 1px rgba(0,0,0,0.4); }
</style>
<script>
$(function(){
$('.section').hide();
$('.toggle, a.section-title').toggle(function(){
$(this).siblings('ul').fadeIn(300);
return false;
}, function(){
$(this).siblings('ul').fadeOut(300);
return false;
});
});
</script>
</head>
<body>
<a href='http://github.com/visionmedia/express'>
<img alt='Fork me on GitHub' id='ribbon' src='http://s3.amazonaws.com/github/ribbons/forkme_right_white_ffffff.png' />
</a>
<div id="header"><strong>Sencha</strong> labs</div>
<div id="wrapper">
<div id="container">
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
@@ -191,6 +187,10 @@
<li><a href="migrate.html">1.x Migration</a></li>
</ul>
<div class='mp'>
<h2 id="Express">Express</h2>
<p class="man-name">
<code>migrate</code>
</p>
<h3 id="Built-On-Connect">Built On Connect</h3>
<p>Express 1.x is written to run on-top of the <a href="http://extjs.github.com/Connect">Connect</a> middlware
@@ -246,23 +246,20 @@ use(Cookie);
<p>Which we can now <em>use()</em> within our app, or pass to the <em>express.createServer()</em> method:</p>
<pre><code>var connect = require('connect');
var app = express.createServer(
connect.logger(),
connect.methodOverride(),
connect.cookieDecoder()
<pre><code>var app = express.createServer(
express.logger(),
express.methodOverride(),
express.cookieDecoder()
);
</code></pre>
<p>or:</p>
<pre><code>var connect = require('connect');
var app = express.createServer();
<pre><code>var app = express.createServer();
app.use(connect.logger());
app.use(connect.methodOverride());
app.use(connect.cookieDecoder());
app.use(express.logger());
app.use(express.methodOverride());
app.use(express.cookieDecoder());
</code></pre>
<p>For documentation on creating Connect middleware visit <a href="http://extjs.github.com/Connect/#Middleware-Authoring">Middleware Authoring</a>.</p>
+6 -9
Ver Arquivo
@@ -51,22 +51,19 @@ similar to below:
Which we can now _use()_ within our app, or pass to the _express.createServer()_ method:
var connect = require('connect');
var app = express.createServer(
connect.logger(),
connect.methodOverride(),
connect.cookieDecoder()
express.logger(),
express.methodOverride(),
express.cookieDecoder()
);
or:
var connect = require('connect');
var app = express.createServer();
app.use(connect.logger());
app.use(connect.methodOverride());
app.use(connect.cookieDecoder());
app.use(express.logger());
app.use(express.methodOverride());
app.use(express.cookieDecoder());
For documentation on creating Connect middleware visit [Middleware Authoring](http://extjs.github.com/Connect/#Middleware-Authoring).
+126
Ver Arquivo
@@ -0,0 +1,126 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('../../lib/express'),
crypto = require('crypto');
var app = express.createServer();
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.bodyDecoder());
app.use(express.cookieDecoder());
app.use(express.session());
// Message helper, ideally we would use req.flash()
// however this is more light-weight for an example
app.dynamicHelpers({
message: function(req){
var err = req.session.error,
msg = req.session.success;
delete req.session.error;
delete req.session.success;
if (err) return '<p class="msg error">' + err + '</p>';
if (msg) return '<p class="msg success">' + msg + '</p>';
}
});
// Generate a salt for the user to prevent rainbow table attacks
var users = {
tj: {
name: 'tj',
salt: 'randomly-generated-salt',
pass: md5('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');
}
// Authenticate using our plain-object database of doom!
function authenticate(name, pass, fn) {
var user = users[name];
// 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
// found the user
if (user.pass == md5(pass + user.salt)) return fn(null, user);
// Otherwise password is invalid
fn(new Error('invalid password'));
}
function restrict(req, res, next) {
if (req.session.user) {
next();
} else {
req.session.error = 'Access denied!';
res.redirect('/login');
}
}
function accessLogger(req, res, next) {
console.log('/restricted accessed by %s', req.session.user.name);
next();
}
app.get('/', function(req, res){
res.redirect('/login');
});
app.get('/restricted', restrict, accessLogger, function(req, res){
res.send('Wahoo! restricted area');
});
app.get('/logout', function(req, res){
// destroy the user's session to log them out
// will be re-created next request
req.session.destroy(function(){
res.redirect('home');
});
});
app.get('/login', function(req, res){
if (req.session.user) {
req.session.success = 'Authenticated as ' + req.session.user.name
+ ' click to <a href="/logout">logout</a>. '
+ ' You may now access <a href="/restricted">/restricted</a>.';
}
res.render('login');
});
app.post('/login', function(req, res){
authenticate(req.body.username, req.body.password, function(err, user){
if (user) {
// Regenerate session when signing in
// to prevent fixation
req.session.regenerate(function(){
// Store the user's primary key
// in the session store to be retrieved,
// or in this case the entire user object
req.session.user = user;
res.redirect('back');
});
} else {
req.session.error = 'Authentication failed, please check your '
+ ' username and password.'
+ ' (use "tj" and "foobar")';
res.redirect('back');
}
});
});
app.listen(3000);
console.log('Express started on port 3000');
+21
Ver Arquivo
@@ -0,0 +1,21 @@
<!DOCTYPE html>
<html>
<head>
<title>Authentication Example</title>
<style>
body {
padding: 50px;
font: 13px Helvetica, Arial, sans-serif;
}
.error {
color: red
}
.success {
color: green;
}
</style>
</head>
<body>
<%- body %>
</body>
</html>
+16
Ver Arquivo
@@ -0,0 +1,16 @@
<h1>Login</h1>
<%- message %>
Try accessing <a href="/restricted">/restricted</a>.
<form method="post" action="/login">
<p>
<label>Username:</label>
<input type="text" name="username">
</p>
<p>
<label>Password:</label>
<input type="text" name="password">
</p>
<p>
<input type="submit" value="Login">
</p>
</form>
+5 -1
Ver Arquivo
@@ -1,4 +1,7 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
@@ -18,4 +21,5 @@ require('./contact');
// be "mounted" to another at the given route.
app.use('/blog', require('./blog'));
app.listen(3000);
app.listen(3000);
console.log('Express started on port 3000');
+46
Ver Arquivo
@@ -0,0 +1,46 @@
/**
* Module dependencies.
*/
var express = require('./../../lib/express');
var app = express.createServer(
// Place default Connect favicon above logger so it is not in
// the logging output
express.favicon(),
// Custom logger format
express.logger({ format: '\x1b[1m:method\x1b[0m \x1b[33m:url\x1b[0m :response-time' }),
// Provides req.cookies
express.cookieDecoder(),
// Parses x-www-form-urlencoded request bodies (and json)
express.bodyDecoder()
);
app.get('/', function(req, res){
if (req.cookies.remember) {
res.send('Remembered :). Click to <a href="/forget">forget</a>!.');
} else {
res.send('<form method="post"><p>Check to <label>'
+ '<input type="checkbox" name="remember"/> remember me</label> '
+ '<input type="submit" value="Submit"/>.</p></form>');
}
});
app.get('/forget', function(req, res){
res.clearCookie('remember');
res.redirect('back');
});
app.post('/', function(req, res){
if (req.body.remember) {
res.cookie('remember', '1', { path: '/', expires: new Date(Date.now() + 900000), httpOnly: true });
}
res.redirect('back');
});
app.listen(3000);
console.log('Express started on port 3000');
+32
Ver Arquivo
@@ -0,0 +1,32 @@
/**
* Module dependencies.
*/
var express = require('./../../lib/express');
var app = express.createServer();
app.get('/', function(req, res){
res.send('<ul>'
+ '<li>Download <a href="/files/amazing.txt">amazing.txt</a>.</li>'
+ '<li>Download <a href="/files/missing.txt">missing.txt</a>.</li>'
+ '</ul>');
});
app.get('/files/*', function(req, res){
var file = req.params[0];
res.download(__dirname + '/files/' + file);
});
app.error(function(err, req, res, next){
if (process.ENOENT == err.errno) {
res.send('Cant find that file, sorry!');
} else {
// Not a 404
next(err);
}
});
app.listen(3000);
console.log('Express started on port 3000');
+1
Ver Arquivo
@@ -0,0 +1 @@
wow supes cool are you glad you downloaded this?
+3
Ver Arquivo
@@ -1,4 +1,7 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
+2 -3
Ver Arquivo
@@ -3,8 +3,7 @@
* Module dependencies.
*/
var express = require('./../../lib/express'),
connect = require('connect');
var express = require('./../../lib/express');
var app = express.createServer();
@@ -21,6 +20,6 @@ app.get('/next', function(req, res, next){
// The errorHandler middleware in this case will dump exceptions to stderr
// as well as show the stack trace in responses, currently handles text/plain,
// text/html, and application/json responses to aid in development
app.use('/', connect.errorHandler({ dumpExceptions: true, showStack: true }));
app.use('/', express.errorHandler({ dumpExceptions: true, showStack: true }));
app.listen(3000);
+78
Ver Arquivo
@@ -0,0 +1,78 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('../../lib/express');
// App with session support
var app = express.createServer(
express.cookieDecoder(),
express.session()
);
// View settings
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
// Dynamic helpers are functions which are executed
// on each view render, unless dynamicHelpers is false.
// So for example we do not need to call messages() in our
// template, "messages" will be populated with the return
// value of this function.
app.dynamicHelpers({
messages: function(req, res){
// In the case of flash messages
// we return a function, allowing
// flash messages to only be flushed
// when called, otherwise every request
// will flush flash messages regardless.
return function(){
// Grab the flash messages
var messages = req.flash();
// We will render the "messages.ejs" partial
return res.partial('messages', {
// Our target object is our messages
object: messages,
// We want it to be named "types" in the partial
// since they are keyed like this:
// { info: ['foo'], error: ['bar']}
as: 'types',
// Pass a local named "hasMessages" so we can easily
// check if we have any messages at all
locals: { hasMessages: Object.keys(messages).length },
// We dont want dynamicHelpers in this partial, as
// it would cause infinite recursion
dynamicHelpers: false
});
}
}
});
app.dynamicHelpers({
// Another dynamic helper example. Since dynamic
// helpers resolve at view rendering time, we can
// "inject" the "page" local variable per request
// providing us with the request url.
page: function(req, res){
return req.url;
}
});
app.get('/', function(req, res){
// Not very realistic notifications but illustrates usage
req.flash('info', 'email queued');
req.flash('info', 'email sent');
req.flash('error', 'delivery failed');
res.render('index');
});
app.listen(3000);
console.log('Express app started on port 3000');
+3
Ver Arquivo
@@ -0,0 +1,3 @@
<h1>Flash Message Example</h1>
<p>on page <%- page %></p>
<%- messages() %>
+5
Ver Arquivo
@@ -0,0 +1,5 @@
<html>
<body>
<%- body %>
</body>
</html>
+12
Ver Arquivo
@@ -0,0 +1,12 @@
<% if (hasMessages) { %>
<div id="messages">
<% for (var type in types) { %>
<h2><%= type %> messages</h2>
<ul id="<%= type %>">
<% types[type].forEach(function(msg){ %>
<li><%= msg %></li>
<% }) %>
</ul>
<% } %>
</div>
<% } %>
+6 -6
Ver Arquivo
@@ -4,7 +4,6 @@
*/
var express = require('./../../lib/express'),
connect = require('connect'),
sys = require('sys');
var app = express.createServer();
@@ -12,19 +11,19 @@ var app = express.createServer();
// Here we use the bodyDecoder middleware
// to parse urlencoded request bodies
// which populates req.body
app.use(connect.bodyDecoder());
app.use(express.bodyDecoder());
// The methodOverride middleware allows us
// to set a hidden input of _method to an arbitrary
// HTTP method to support app.put(), app.del() etc
app.use(connect.methodOverride());
app.use(express.methodOverride());
// Required by session
app.use(connect.cookieDecoder());
app.use(express.cookieDecoder());
// Required by req.flash() for persistent
// notifications
app.use(connect.session());
app.use(express.session());
app.get('/', function(req, res){
// get ?name=foo
@@ -76,4 +75,5 @@ app.put('/', function(req, res){
res.redirect('/?name=' + req.body.name);
});
app.listen(3000);
app.listen(3000);
console.log('Express app started on port 3000');
+63
Ver Arquivo
@@ -0,0 +1,63 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var app = express.createServer();
// Fake items
var items = [
{ name: 'foo' },
{ name: 'bar' },
{ name: 'baz' }
];
// Routes
app.get('/', function(req, res, next){
res.send('Visit /item/2');
});
app.get('/item/:id.:format?', function(req, res, next){
var id = req.params.id,
format = req.params.format,
item = items[id];
// Ensure item exists
if (item) {
// Serve the format
switch (format) {
case 'json':
// Detects json
res.send(item);
break;
case 'xml':
// Set contentType as xml then sends
// the string
var xml = ''
+ '<items>'
+ '<item>' + item.name + '</item>'
+ '</items>';
res.contentType('.xml');
res.send(xml);
break;
case 'html':
default:
// By default send some hmtl
res.send('<h1>' + item.name + '</h1>');
}
} else {
// We could simply pass route control and potentially 404
// by calling next(), or pass an exception like below.
next(new Error('Item ' + id + ' does not exist'));
}
});
// Middleware
app.use(express.errorHandler({ showStack: true }));
app.listen(3000);
console.log('Express app started on port 3000');
+7 -4
Ver Arquivo
@@ -1,10 +1,12 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('./../../lib/express'),
connect = require('connect'),
http = require('http');
var app = express.createServer();
@@ -85,7 +87,7 @@ app.get('/repos/*', function(req, res, next){
(function fetchData(name){
// We have a user name
if (name) {
console.log('... fetching %s', name);
console.log('... fetching \x1b[33m%s\x1b[0m', name);
request('/repos/show/' + name, function(err, user){
if (err) {
next(err)
@@ -110,7 +112,8 @@ app.get('/repos/*', function(req, res, next){
});
// Serve statics from ./public
app.use(connect.staticProvider(__dirname + '/public'));
app.use(express.staticProvider(__dirname + '/public'));
// Listen on port 3000
app.listen(3000);
app.listen(3000);
console.log('Express app started on port 3000');
+8 -5
Ver Arquivo
@@ -1,10 +1,12 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('./../../lib/express'),
connect = require('connect');
var express = require('./../../lib/express');
// Path to our public directory
@@ -14,8 +16,8 @@ var pub = __dirname + '/public';
// and then serve with connect's staticProvider
var app = express.createServer(
connect.compiler({ src: pub, enable: ['sass'] }),
connect.staticProvider(pub)
express.compiler({ src: pub, enable: ['sass'] }),
express.staticProvider(pub)
);
// Optional since express defaults to CWD/views
@@ -41,4 +43,5 @@ app.get('/', function(req, res){
});
});
app.listen(3000);
app.listen(3000);
console.log('Express app started on port 3000');
+13 -8
Ver Arquivo
@@ -1,28 +1,30 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('./../../lib/express'),
form = require('./../../support/connect-form'),
connect = require('connect'),
var express = require('../../lib/express'),
form = require('connect-form'),
sys = require('sys');
var app = express.createServer(
// connect-form (http://github.com/visionmedia/connect-form)
// middleware uses the formidable middleware to parse urlencoded
// and multipart form data
form()
form({ keepExtensions: true })
);
app.get('/', function(req, res){
res.send('<form method="post" enctype="form-data/multipart">'
res.send('<form method="post" enctype="multipart/form-data">'
+ '<p>Image: <input type="file" name="image" /></p>'
+ '<p><input type="submit" value="Upload" /></p>'
+ '</form>');
});
app.post('/', function(req, res, params, next){
app.post('/', function(req, res, next){
// connect-form adds the req.form object
// we can (optionally) define onComplete, passing
@@ -31,7 +33,9 @@ app.post('/', function(req, res, params, next){
if (err) {
next(err);
} else {
sys.puts('\nuploaded ' + files.image.filename);
console.log('\nuploaded %s to %s',
files.image.filename,
files.image.path);
res.redirect('back');
}
});
@@ -44,4 +48,5 @@ app.post('/', function(req, res, params, next){
});
});
app.listen(3000);
app.listen(3000);
console.log('Express app started on port 3000');
+16
Ver Arquivo
@@ -0,0 +1,16 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var app = express.createServer();
require('./mvc').boot(app);
app.listen(3000);
console.log('Express app started on port 3000');
+9
Ver Arquivo
@@ -0,0 +1,9 @@
module.exports = {
// /
index: function(req, res){
res.render();
}
};
+55
Ver Arquivo
@@ -0,0 +1,55 @@
// Fake user database for example
var users = [
{ id: 0, name: 'TJ', email: 'tj@vision-media.ca' },
{ id: 1, name: 'Simon', email: 'simon@vision-media.ca' }
];
function get(id, fn) {
if (users[id]) {
fn(null, users[id]);
} else {
fn(new Error('User ' + id + ' does not exist'));
}
}
module.exports = {
// /users
index: function(req, res){
res.render(users);
},
// /users/:id
show: function(req, res, next){
get(req.params.id, function(err, user){
if (err) return next(err);
res.render(user);
});
},
// /users/:id/edit
edit: function(req, res, next){
get(req.params.id, function(err, user){
if (err) return next(err);
res.render(user);
});
},
// PUT /users/:id
update: function(req, res, next){
var id = req.params.id;
get(id, function(err){
if (err) return next(err);
var user = users[id] = req.body.user;
user.id = id;
req.flash('info', 'Successfully updated _' + user.name + '_.');
res.redirect('back');
});
}
};
+151
Ver Arquivo
@@ -0,0 +1,151 @@
/**
* Module dependencies.
*/
var fs = require('fs'),
express = require('../../lib/express');
exports.boot = function(app){
bootApplication(app);
bootControllers(app);
};
// App settings and middleware
function bootApplication(app) {
app.use(express.logger({ format: ':method :url :status' }));
app.use(express.bodyDecoder());
app.use(express.methodOverride());
app.use(express.cookieDecoder());
app.use(express.session());
app.use(app.router);
app.use(express.staticProvider(__dirname + '/public'));
// Example 500 page
app.error(function(err, req, res){
console.dir(err)
res.render('500');
});
// Example 404 page via simple Connect middleware
app.use(function(req, res){
res.render('404');
});
// Setup ejs views as default, with .html as the extension
app.set('views', __dirname + '/views');
app.register('.html', require('ejs'));
app.set('view engine', 'html');
// Some dynamic view helpers
app.dynamicHelpers({
request: function(req){
return req;
},
hasMessages: function(req){
return Object.keys(req.session.flash || {}).length;
},
messages: function(req){
return function(){
var msgs = req.flash();
return Object.keys(msgs).reduce(function(arr, type){
return arr.concat(msgs[type]);
}, []);
}
}
});
}
// Bootstrap controllers
function bootControllers(app) {
fs.readdir(__dirname + '/controllers', function(err, files){
if (err) throw err;
files.forEach(function(file){
bootController(app, file);
});
});
}
// Example (simplistic) controller support
function bootController(app, file) {
var name = file.replace('.js', ''),
actions = require('./controllers/' + name),
plural = name + 's', // realistically we would use an inflection lib
prefix = '/' + plural;
// Special case for "app"
if (name == 'app') {
prefix = '/';
}
Object.keys(actions).map(function(action){
var fn = controllerAction(name, plural, action, actions[action]);
switch(action) {
case 'index':
app.get(prefix, fn);
break;
case 'show':
app.get(prefix + '/:id.:format?', fn);
break;
case 'add':
app.get(prefix + '/:id/add', fn);
break;
case 'create':
app.post(prefix + '/:id', fn);
break;
case 'edit':
app.get(prefix + '/:id/edit', fn);
break;
case 'update':
app.put(prefix + '/:id', fn);
break;
case 'destroy':
app.del(prefix + '/:id', fn);
break;
}
});
}
// Proxy res.render() to add some magic
function controllerAction(name, plural, action, fn) {
return function(req, res, next){
var render = res.render,
format = req.params.format,
path = __dirname + '/views/' + name + '/' + action + '.html';
res.render = function(obj, options, fn){
res.render = render;
// Template path
if (typeof obj === 'string') {
return res.render(obj, options, fn);
}
// Format support
if (action == 'show' && format) {
if (format === 'json') {
return res.send(obj);
} else {
throw new Error('unsupported format "' + format + '"');
}
}
// Render template
res.render = render;
options = options || {};
options.locals = options.locals || {};
// Expose obj as the "users" or "user" local
if (action == 'index') {
options.locals[plural] = obj;
} else {
options.locals[name] = obj;
}
return res.render(path, options, fn);
};
fn.apply(this, arguments);
};
}
+24
Ver Arquivo
@@ -0,0 +1,24 @@
body {
padding: 30px;
font: 12px/1.4 "Helvetica Nueue", "Lucida Grande", Arial, sans-serif;
}
a {
color: #00aaff;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
#messages {
width: 95%;
margin-bottom: 15px;
padding: 10px;
border: 1px solid #00DD00;
-webkit-box-shadow: 1px 1px 4px rgba(0,0,0,0.2);
}
#messages li {
list-style: none;
}
#messages em {
font-weight: bold;
}
+2
Ver Arquivo
@@ -0,0 +1,2 @@
<h1>Cannot find <%= request.url %></h1>
<p>Sorry we failed to locate this page or resource.</p>
+2
Ver Arquivo
@@ -0,0 +1,2 @@
<h1>Internal Server Error</h1>
<p>Sorry an error has occurred, please try refreshing the page or navigate back to <a href="/">home</a>.</p>
+10
Ver Arquivo
@@ -0,0 +1,10 @@
<h1>Application Index</h1>
<p>Try visiting one of the following urls:</p>
<ul>
<li><a href="/users">/users</a></li>
<li><a href="/users/1">/users/1</a></li>
<li><a href="/users/1.json">/users/1.json</a></li>
<li><a href="/users/1/edit">/users/1/edit</a></li>
<li><a href="/accounts">/accounts (404, non existent)</a></li>
<li><a href="/users/1.foo">/users/1.foo (500, unsupported format)</a></li>
</ul>
+9
Ver Arquivo
@@ -0,0 +1,9 @@
<html>
<head>
<title>Express - MVC Example</title>
<link rel="stylesheet" href="/style.css" />
</head>
<body>
<%- body %>
</body>
</html>
+7
Ver Arquivo
@@ -0,0 +1,7 @@
<% if (hasMessages) { %>
<ul id="messages">
<% messages().forEach(function(msg){ %>
<li><%- msg %></li>
<% }) %>
</ul>
<% } %>
+8
Ver Arquivo
@@ -0,0 +1,8 @@
<h1>Editing user <%= user.name %></h1>
<%- partial('messages') %>
<form method="post" action="/users/<%= user.id %>">
<input type="hidden" name="_method" value="put" />
<p>Name: <input type="text" name="user[name]", value="<%= user.name %>" /></p>
<p>Email: <input type="text" name="user[email]", value="<%= user.email %>" /></p>
<p><input type="submit" value="Update" /></p>
</form>
+7
Ver Arquivo
@@ -0,0 +1,7 @@
<h1>Users</h1>
<%- partial('messages') %>
<ul>
<% users.forEach(function(user){ %>
<li><a href="/users/<%= user.id %>"><%= user.name %></a></li>
<% }) %>
</ul>
+7
Ver Arquivo
@@ -0,0 +1,7 @@
<h1>Viewing user #<%= user.id %></h1>
<%- partial('messages') %>
<ul>
<li><%- user.name %></li>
<li><%- user.email %></li>
<li><a href="<%= user.id %>/edit">Edit</a> this user</li>
</ul>
+64 -19
Ver Arquivo
@@ -1,4 +1,7 @@
// Expose modules in ./support for demo purposes
require.paths.unshift(__dirname + '/../../support');
/**
* Module dependencies.
*/
@@ -8,37 +11,63 @@ var express = require('./../../lib/express');
var app = express.createServer(),
sys = require('sys');
// 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.router" positions our routes
// specifically above the middleware
// assigned below
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
app.use(function(req, res, next){
next(new NotFound(req.url));
});
app.set('views', __dirname + '/views');
// Provide our app with the notion of NotFound exceptions
function NotFound(msg){
function NotFound(path){
this.name = 'NotFound';
Error.call(this, msg);
if (path) {
Error.call(this, 'Cannot find ' + path);
this.path = path;
} else {
Error.call(this, 'Not Found');
}
Error.captureStackTrace(this, arguments.callee);
}
sys.inherits(NotFound, Error);
app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/404', function(req, res){
throw new NotFound;
});
app.get('/500', function(req, res){
throw new Error('keyboard cat!');
});
// 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');
res.render('404.jade', {
status: 404,
locals: {
error: err
}
});
} else {
next(err);
}
@@ -49,10 +78,26 @@ app.error(function(err, req, res, next){
app.error(function(err, req, res){
res.render('500.jade', {
locals: {
error: err
}
status: 500,
locals: {
error: err
}
});
});
app.listen(3000);
// Routes
app.get('/', function(req, res){
res.render('index.jade');
});
app.get('/404', function(req, res){
throw new NotFound;
});
app.get('/500', function(req, res, next){
next(new Error('keyboard cat!'));
});
app.listen(3000);
console.log('Express app started on port 3000');
+4 -1
Ver Arquivo
@@ -1 +1,4 @@
h2 Page Not Found
- if (error.path)
h2 Cannot find #{error.path}
- else
h2 Page Not Found
+4 -3
Ver Arquivo
@@ -31,7 +31,7 @@ var users = [
{ name: 'tobi' }
];
// Fake model
// Fake controller
var User = {
index: function(req, res){
@@ -47,7 +47,7 @@ var User = {
res.send(destroyed ? 'destroyed' : 'Cannot find user');
},
range: function(req, res, a, b){
res.send(users.slice(a, b));
res.send(users.slice(a, b+1));
}
};
@@ -71,4 +71,5 @@ app.get('/', function(req, res){
].join('\n'));
});
app.listen(3000);
app.listen(3000);
console.log('Express app started on port 3000');
+86
Ver Arquivo
@@ -0,0 +1,86 @@
/**
* Module dependencies.
*/
var express = require('../../lib/express');
var app = express.createServer();
// Example requests:
// curl http://localhost:3000/user/0
// curl http://localhost:3000/user/0/edit
// curl http://localhost:3000/user/1
// curl http://localhost:3000/user/1/edit (unauthorized since this is not you)
// curl -X DELETE http://localhost:3000/user/0 (unauthorized since you are not an admin)
// Dummy users
var users = [
{ id: 0, name: 'tj', email: 'tj@vision-media.ca', role: 'member' },
{ id: 1, name: 'ciaran', email: 'ciaranj@gmail.com', role: 'member' },
{ id: 2, name: 'aaron', email: 'aaron.heckmann+github@gmail.com', role: 'admin' }
];
function loadUser(req, res, next) {
// You would fetch your user from the db
var user = users[req.params.id];
if (user) {
req.user = user;
next();
} else {
next(new Error('Failed to load user ' + req.params.id));
}
}
function andRestrictToSelf(req, res, next) {
// If our authenticated user is the user we are viewing
// then everything is fine :)
if (req.authenticatedUser.id == req.user.id) {
next();
} else {
// You may want to implement specific exceptions
// such as UnauthorizedError or similar so that you
// can handle these in app.error() specifically
// (view ./examples/pages for this)
next(new Error('Unauthorized'));
}
}
function andRestrictTo(role) {
return function(req, res, next) {
if (req.authenticatedUser.role == role) {
next();
} else {
next(new Error('Unauthorized'));
}
}
}
// Middleware for faux authentication
// you would of course implement something real,
// but this illustrates how an authenticated user
// may interacte with middleware
app.use(function(req, res, next){
req.authenticatedUser = users[0];
next();
});
app.get('/', function(req, res){
res.redirect('/user/0');
});
app.get('/user/:id', loadUser, function(req, res){
res.send('Viewing user ' + req.user.name);
});
app.get('/user/:id/edit', loadUser, andRestrictToSelf, function(req, res){
res.send('Editing user ' + req.user.name);
});
app.del('/user/:id', loadUser, andRestrictTo('admin'), function(req, res){
res.send('Deleted user ' + req.user.name);
});
app.listen(3000);
console.log('Express app started on port 3000');
+5 -6
Ver Arquivo
@@ -3,20 +3,19 @@
* Module dependencies.
*/
var express = require('./../../lib/express'),
connect = require('connect');
var express = require('./../../lib/express');
var app = express.createServer(
connect.logger(),
express.logger(),
// Required by session() middleware
connect.cookieDecoder(),
express.cookieDecoder(),
// Populates:
// - req.session
// - req.sessionStore
// - req.sessionHash (the SID fingerprint)
connect.session()
// - req.sessionID
express.session()
);
app.get('/', function(req, res){
+12 -2
Ver Arquivo
@@ -5,11 +5,21 @@
* MIT Licensed
*/
/**
* Re-export connect auto-loaders.
*
* This prevents the need to `require('connect')` in order
* to access core middleware, so for example `express.logger()` instead
* of `require('connect').logger()`.
*/
var exports = module.exports = require('connect').middleware;
/**
* Framework version.
*/
exports.version = '1.0.0rc';
exports.version = '1.0.0rc4';
/**
* Module dependencies.
@@ -34,5 +44,5 @@ exports.createServer = function(){
*/
require('./view');
require('./request');
require('./response');
require('./request');
+30
Ver Arquivo
@@ -13,6 +13,18 @@ var http = require('http'),
utils = require('./utils'),
mime = require('connect/utils').mime;
/**
* Default flash formatters.
*
* @type Object
*/
var flashFormatters = exports.flashFormatters = {
s: function(val){
return String(val);
}
};
/**
* Return request header or optional default.
*
@@ -142,6 +154,16 @@ http.IncomingMessage.prototype.param = function(name){
* req.flash();
* // => { error: ['email delivery failed'], info: [] }
*
* Formatting:
*
* Flash notifications also support arbitrary formatting support.
* For example you may pass variable arguments to `req.flash()`
* and use the %s specifier to be replaced by the associated argument:
*
* req.flash('info', 'email has been sent to %s.', userName);
*
* To add custom formatters use the `exports.flashFormatters` object.
*
* @param {String} type
* @param {String} msg
* @return {Array|Object|Number}
@@ -151,7 +173,15 @@ http.IncomingMessage.prototype.param = function(name){
http.IncomingMessage.prototype.flash = function(type, msg){
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 = msg.replace(/%([a-zA-Z])/g, function(_, format){
var formatter = formatters[format];
if (formatter) return formatter(args[i++]);
});
return (msgs[type] = msgs[type] || []).push(msg);
} else if (type) {
var arr = msgs[type];
+155 -27
Ver Arquivo
@@ -12,15 +12,26 @@
var fs = require('fs'),
http = require('http'),
path = require('path'),
pump = require('sys').pump,
utils = require('connect/utils'),
mime = require('connect/utils').mime,
parseRange = require('./utils').parseRange,
Buffer = require('buffer').Buffer;
/**
* Header fields supporting multiple values.
*
* @type Array
*/
var multiple = ['Set-Cookie'];
/**
* Send a response with the given `body` and optional `headers` and `status` code.
*
* Examples:
*
* res.send();
* res.send(new Buffer('wahoo'));
* res.send({ some: 'json' });
* res.send('<p>some html</p>');
@@ -44,7 +55,11 @@ http.ServerResponse.prototype.send = function(body, headers, status){
// Defaults
status = status || 200;
headers = headers || {};
// Allow 0 args as 204
if (!arguments.length) {
body = status = 204;
}
// Determine content type
switch (typeof body) {
@@ -69,6 +84,9 @@ http.ServerResponse.prototype.send = function(body, headers, status){
this.contentType('.json');
}
body = JSON.stringify(body);
if (this.req.query.callback && this.app.settings['jsonp callback']) {
body = this.req.query.callback + '(' + body + ');';
}
}
break;
}
@@ -81,31 +99,91 @@ http.ServerResponse.prototype.send = function(body, headers, status){
}
// Merge headers passed
utils.merge(this.headers, headers);
if (headers) {
var fields = Object.keys(headers);
for (var i = 0, len = fields.length; i < len; ++i) {
var field = fields[i];
this.header(field, headers[field]);
}
}
// Strip irrelevant headers
if (204 === status) {
delete this.headers['Content-Type'];
delete this.headers['Content-Length'];
}
// Respond
this.writeHead(status, this.headers);
this.end(body);
this.end('HEAD' == this.req.method ? undefined : body);
};
/**
* Transfer the given `file`. Automatically sets the _Content-Type_
* response header, and responds with 404 / 500 appropriately.
* Transfer the file at the given `path`. Automatically sets
* the _Content-Type_ response header via `res.contentType()`.
*
* @param {String} file
* The given callback `fn` is invoked when an error occurs,
* passing it as the first argument, or when the file is transferred,
* passing the path as the second argument.
*
* When the filesize is >= "stream threshold" (defaulting to 32k), the
* file will be streamed using an `fs.ReadStream` and `sys.pump()`.
*
* @param {String} path
* @param {Function} fn
* @api public
*/
http.ServerResponse.prototype.sendfile = function(file){
var self = this;
fs.readFile(file, function(err, buf){
if (err) {
self.send(err.errno === process.ENOENT
? 404
: 500);
http.ServerResponse.prototype.sendfile = function(path, fn){
var self = this,
streamThreshold = this.app.set('stream threshold') || 32 * 1024,
ranges = self.req.headers.range;
if (~path.indexOf('..')) this.send(403);
function error(err) {
delete self.headers['Content-Disposition'];
if (fn) {
fn(err, path);
} else {
self.contentType(file);
self.send(buf);
self.req.next(err);
}
}
fs.stat(path, function(err, stat){
if (err) return error(err);
if (stat.size >= streamThreshold) {
var status = 200;
if (ranges) ranges = parseRange(stat.size, ranges);
if (ranges) {
var stream = fs.createReadStream(path, ranges[0]),
start = ranges[0].start,
end = ranges[0].end;
status = 206;
self.header('Content-Range', 'bytes '
+ start
+ '-'
+ end
+ '/'
+ stat.size);
self.header('Content-Length', end - start + 1);
} else {
var stream = fs.createReadStream(path);
self.header('Content-Length', stat.size);
}
self.contentType(path);
self.header('Accept-Ranges', 'bytes');
self.writeHead(status, self.headers);
pump(stream, self, function(err){
fn && fn(err, path, true);
});
} else {
fs.readFile(path, function(err, buf){
if (err) return error(err);
self.contentType(path);
self.send(buf);
fn && fn(null, path);
});
}
});
};
@@ -144,21 +222,30 @@ http.ServerResponse.prototype.attachment = function(filename){
};
/**
* Transfer the given `file` with optional `filename` as an attachment.
* Transfer the file at the given `path`, with optional
* `filename` as an attachment. Once transferred, or if an
* error occurs `fn` is called with the error and path.
*
* @param {String} file
* @param {String} path
* @param {String} filename
* @param {Function} fn
* @return {Type}
* @api public
*/
http.ServerResponse.prototype.download = function(file, filename){
this.attachment(filename || file).sendfile(file);
http.ServerResponse.prototype.download = function(path, filename, fn){
this.attachment(filename || path).sendfile(path, fn);
};
/**
* Set or get response header `name` with optional `val`.
*
* Headers that may be set multiple times (as indicated by the `multiple` array)
* can be called with a value several times, for example:
*
* res.header('Set-Cookie', '...');
* res.header('Set-Cookie', '...');
*
* @param {String} name
* @param {String} val
* @return {String}
@@ -166,9 +253,45 @@ http.ServerResponse.prototype.download = function(file, filename){
*/
http.ServerResponse.prototype.header = function(name, val){
return val === undefined
? this.headers[name]
: this.headers[name] = val;
if (val === undefined) {
return this.headers[name];
} else {
if (this.headers[name] && ~multiple.indexOf(name)) {
return this.headers[name] += '\r\n' + name + ': ' + val;
} else {
return this.headers[name] = val;
}
}
};
/**
* Clear cookie `name`.
*
* @param {String} name
* @api public
*/
http.ServerResponse.prototype.clearCookie = function(name){
this.cookie(name, '', { expires: new Date(1) });
};
/**
* Set cookie `name` to `val`.
*
* Examples:
*
* // "Remember Me" for 15 minutes
* res.cookie('rememberme', '1', { expires: new Date(Date.now() + 900000), httpOnly: true });
*
* @param {String} name
* @param {String} val
* @param {Options} options
* @api public
*/
http.ServerResponse.prototype.cookie = function(name, val, options){
var cookie = utils.serializeCookie(name, val, options);
this.header('Set-Cookie', cookie);
};
/**
@@ -195,8 +318,8 @@ http.ServerResponse.prototype.header = function(name, val){
*
* We may also map dynamic redirects:
*
* app.redirect('comments', function(req, res, params){
* return '/post/' + params.id + '/comments';
* app.redirect('comments', function(req, res){
* return '/post/' + req.params.id + '/comments';
* });
*
* So now we may do the following, and the redirect will dynamically adjust to
@@ -226,10 +349,15 @@ http.ServerResponse.prototype.redirect = function(url, status){
// Attempt mapped redirect
var mapped = typeof map[url] === 'function'
? map[url](this.req, this, this.req.params.path)
? map[url](this.req, this)
: map[url];
// Perform redirect
this.writeHead(status || 302, { 'Location': mapped || url });
this.end();
url = mapped || url;
// Respond
this.send('Redirecting to ' + url, {
'Content-Type': 'text/plain',
'Location': url
}, status || 302);
};
+99 -13
Ver Arquivo
@@ -9,8 +9,7 @@
* Module dependencies.
*/
var sys = require('sys'),
url = require('url'),
var url = require('url'),
view = require('./view'),
connect = require('connect'),
utils = require('connect/utils'),
@@ -31,15 +30,20 @@ var Server = exports = module.exports = function Server(middleware){
this.redirects = {};
this.viewHelpers = {};
this.dynamicViewHelpers = {};
this.errorHandlers = [];
connect.Server.call(this, middleware || []);
// Default "home" to /
this.set('home', '/');
// Set "env" to EXPRESS_ENV or connectEnv.name
this.set('env',
process.env.EXPRESS_ENV ||
process.connectEnv.name);
// DEPRECATED: remove in 1.0
if (process.env.EXPRESS_ENV) {
process.env.NODE_ENV = process.env.EXPRESS_ENV;
console.warn('\x1b[33mWarning\x1b[0m: EXPRESS_ENV is deprecated, use NODE_ENV.');
}
// Set "env" to NODE_ENV, defaulting to "development"
this.set('env', process.env.NODE_ENV || 'development');
// Expose objects to each other
this.use(function(req, res, next){
@@ -52,7 +56,7 @@ var Server = exports = module.exports = function Server(middleware){
// Assign req.params.get
if (req.url.indexOf('?') > 0) {
var query = url.parse(req.url).query;
req.query = queryString.parse(query);
req.query = exports.parseQueryString(query);
}
next();
});
@@ -69,7 +73,48 @@ var Server = exports = module.exports = function Server(middleware){
* Inherit from `connect.Server`.
*/
sys.inherits(Server, connect.Server);
Server.prototype.__proto__ = connect.Server.prototype;
/**
* Support swappable querystring parsers.
*/
exports.parseQueryString = queryString.parse;
/**
* Proxy in order to register error handlers.
*/
Server.prototype.listen = function(){
this.registerErrorHandlers();
connect.Server.prototype.listen.apply(this, arguments);
};
/**
* Proxy in order to register error handlers.
*/
Server.prototype.listenFD = function(){
this.registerErrorHandlers();
connect.Server.prototype.listenFD.apply(this, arguments);
};
/**
* Register error handlers. This is automatically
* called from within `Server#listen()` and `Server#listenFD()`.
*
* @return {Server} for chaining
* @api public
*/
Server.prototype.registerErrorHandlers = function(){
this.errorHandlers.forEach(function(fn){
this.use(function(err, req, res, next){
fn.apply(this, arguments);
});
}, this);
return this;
};
/**
* Proxy `connect.Server#use()` to apply settings to
@@ -95,6 +140,7 @@ Server.prototype.use = function(route, middleware){
home = app.set('home');
if (home === '/') home = '';
app.set('home', (app.route || '') + home);
app.parent = this;
// Mounted hook
if (app.__mounted) app.__mounted.call(app, this);
}
@@ -128,6 +174,18 @@ Server.prototype.mounted = function(fn){
return this;
};
/**
* See: view.register.
*
* @return {Server} for chaining
* @api public
*/
Server.prototype.register = function(){
view.register.apply(this, arguments);
return this;
};
/**
* Register the given view helpers `obj`. This method
* can be called several times to apply additional helpers.
@@ -158,6 +216,7 @@ Server.prototype.dynamicHelpers = function(obj){
/**
* Assign a custom exception handler callback `fn`.
* These handlers are always _last_ in the middleware stack.
*
* @param {Function} fn
* @return {Server} for chaining
@@ -165,14 +224,13 @@ Server.prototype.dynamicHelpers = function(obj){
*/
Server.prototype.error = function(fn){
this.use(function(err, req, res, next){
fn.apply(this, arguments);
});
this.errorHandlers.push(fn);
return this;
};
/**
* Assign `setting` to `val`, or return `setting`'s value.
* Mounted servers inherit their parent server's settings.
*
* @param {String} setting
* @param {String} val
@@ -182,7 +240,11 @@ Server.prototype.error = function(fn){
Server.prototype.set = function(setting, val){
if (val === undefined) {
return this.settings[setting];
if (this.settings.hasOwnProperty(setting)) {
return this.settings[setting];
} else if (this.parent) {
return this.parent.set(setting);
}
} else {
this.settings[setting] = val;
return this;
@@ -250,11 +312,35 @@ Server.prototype.configure = function(env, fn){
(function(method){
Server.prototype[method] = function(path, fn){
var self = this;
// Ensure router is mounted
if (!this.__usedRouter) {
this.use(this.router);
}
// Route specific middleware support
if (arguments.length > 2) {
var args = Array.prototype.slice.call(arguments, 1);
fn = args.pop();
(function stack(middleware){
middleware.forEach(function(fn){
if (Array.isArray(fn)) {
stack(fn);
} else {
self[method](path, fn);
}
});
})(args);
}
// Generate the route
this.routes[method](path, fn);
return this;
};
return arguments.callee;
})('get')('post')('put')('del');
})('get')('post')('put')('delete');
// Alias delete as "del"
Server.prototype.del = Server.prototype.delete;
+35 -2
Ver Arquivo
@@ -16,7 +16,7 @@
*
* @param {String} str
* @return {String}
* @api public
* @api private
*/
exports.miniMarkdown = function(str){
@@ -31,7 +31,7 @@ exports.miniMarkdown = function(str){
*
* @param {String} html
* @return {String}
* @api public
* @api private
*/
exports.htmlEscape = function(html) {
@@ -40,4 +40,37 @@ exports.htmlEscape = function(html) {
.replace(/"/g, '&quot;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;');
};
/**
* Parse "Range" header `str` relative to the given file `size`.
*
* @param {Number} size
* @param {String} str
* @return {Array}
* @api private
*/
exports.parseRange = function(size, str){
var valid = true;
var arr = str.substr(6).split(',').map(function(range){
var range = range.split('-'),
start = parseInt(range[0], 10),
end = parseInt(range[1], 10);
// -500
if (isNaN(start)) {
start = size - end;
end = size - 1;
// 500-
} else if (isNaN(end)) {
end = size - 1;
}
// Invalid
if (isNaN(start) || isNaN(end) || start > end) valid = false;
return { start: start, end: end };
});
return valid ? arr : undefined;
};
+83 -36
Ver Arquivo
@@ -13,7 +13,6 @@ var extname = require('path').extname,
mime = require('connect/utils').mime,
utils = require('connect/utils'),
http = require('http'),
sys = require('sys'),
fs = require('fs');
/**
@@ -34,16 +33,6 @@ var cache = {};
var viewCache = {};
/**
* Clear the view cache.
*
* @api public
*/
exports.clearCache = function(){
viewCache = {};
};
/**
* Synchronously cache view at the given `path`.
*
@@ -68,6 +57,39 @@ function viewRoot(app) {
return app.set('views') || process.cwd() + '/views';
}
/**
* Register the given template engine `exports`
* as `ext`. For example we may wish to map ".html"
* files to jade:
*
* app.register('.html', require('jade'));
*
* This is also useful for libraries that may not
* match extensions correctly. For example my haml.js
* library is installed from npm as "hamljs" so instead
* of layout.hamljs, we can register the engine as ".haml":
*
* app.register('.haml', require('haml-js'));
*
* For engines that do not comply with the Express
* specification, we can also wrap their api this way.
*
* app.register('.foo', {
* render: function(str, options) {
* // perhaps their api is
* // foo.toHTML(str, options);
* }
* });
*
* @param {String} ext
* @param {Object} obj
* @api public
*/
exports.register = function(ext, exports) {
cache[ext] = exports;
};
/**
* Render `view` partial with the given `options`.
*
@@ -95,7 +117,7 @@ http.ServerResponse.prototype.partial = function(view, options, ext){
}
// Allow collection to be passed as second param
if (Array.isArray(options)) {
if (options && options.hasOwnProperty('length')) {
options = { collection: options };
}
@@ -109,16 +131,19 @@ http.ServerResponse.prototype.partial = function(view, options, ext){
var collection = options.collection;
if (collection) {
var name = options.as || view.split('.')[0],
len = collection.length;
len = collection.length,
buf = '';
delete options.collection;
options.locals.collectionLength = len;
return collection.map(function(val, i){
for (var i = 0, len = collection.length; i < len; ++i) {
var val = collection[i];
options.locals.firstInCollection = i === 0;
options.locals.indexInCollection = i;
options.locals.lastInCollection = i === len - 1;
options.object = val;
return this.partial(view, options);
}, this).join('');
buf += this.partial(view, options);
}
return buf;
} else {
if (options.object) {
var name = options.as || view.split('.')[0];
@@ -127,7 +152,7 @@ http.ServerResponse.prototype.partial = function(view, options, ext){
} else if (name === global) {
utils.merge(options.locals, options.object);
} else {
options.context = options.object;
options.scope = options.object;
}
}
return this.render(view, options);
@@ -144,19 +169,30 @@ http.ServerResponse.prototype.partial = function(view, options, ext){
* Most engines accept one or more of the following options,
* both _haml_ and _jade_ accept all:
*
* - `context|scope` Template evaluation context (`this`)
* - `scope` Template evaluation context (the value of `this`)
* - `locals` Object containing local variables
* - `debug` Output debugging information
* - `status` Response status code, defaults to 200
* - `headers` Response headers object
*
* @param {String} view
* @param {Object} options
* @param {Object|Function} options or callback function
* @param {Function} fn
* @api public
*/
http.ServerResponse.prototype.render = function(view, options, fn){
options = options || {};
var defaultEngine = this.app.set('view engine');
// Support callback function as second arg
if (typeof options === 'function') {
fn = options, options = {};
}
var options = options || {},
viewOptions = this.app.set('view options'),
defaultEngine = this.app.set('view engine');
// Mixin "view options"
if (viewOptions) options.__proto__ = viewOptions;
// Support "view engine" setting
if (view.indexOf('.') < 0 && defaultEngine) {
@@ -180,8 +216,8 @@ http.ServerResponse.prototype.render = function(view, options, fn){
layout += ext;
}
// Normalize "context" to "scope", defaulting to this response
options.scope = options.scope || options.context || this;
// Default execution scope to the response
options.scope = options.scope || this.req;
// Auto-cache in production
if (this.app.set('env') === 'production') {
@@ -190,7 +226,9 @@ http.ServerResponse.prototype.render = function(view, options, fn){
// Partials support
if (options.partial) {
root += '/partials';
root = this.app.set('partials')
? this.app.set('partials')
: root + '/partials';
}
// View path
@@ -203,17 +241,25 @@ http.ServerResponse.prototype.render = function(view, options, fn){
options.locals.__filename = options.filename = path;
// Dynamic helper support
var keys = Object.keys(dynamicHelpers);
for (var i = 0, len = keys.length; i < len; ++i) {
var key = keys[i],
val = dynamicHelpers[key];
if (typeof val === 'function') {
helpers[key] = val.call(
this.app,
this.req,
this,
this.req.params.path);
if (options.dynamicHelpers !== false) {
// cache
if (!this.__dynamicHelpers) {
this.__dynamicHelpers = {};
var keys = Object.keys(dynamicHelpers);
for (var i = 0, len = keys.length; i < len; ++i) {
var key = keys[i],
val = dynamicHelpers[key];
if (typeof val === 'function') {
this.__dynamicHelpers[key] = val.call(
this.app,
this.req,
this);
}
}
}
// apply
utils.merge(helpers, this.__dynamicHelpers);
}
// Always expose partial() as a local
@@ -253,12 +299,13 @@ http.ServerResponse.prototype.render = function(view, options, fn){
if (layout) {
options.layout = false;
options.locals.body = str;
self.render(layout, options);
options.isLayout = true;
self.render(layout, options, fn);
} else if (partial) {
return str;
} else if (fn) {
fn(null, str);
} else {
self.send(str);
this.send(str, options.headers, options.status);
}
};
+5 -4
Ver Arquivo
@@ -1,17 +1,18 @@
{
"name": "express",
"description": "Sinatra inspired web development framework",
"version": "1.0.0rc",
"version": "1.0.0rc4",
"author": "TJ Holowaychuk <tj@vision-media.ca>",
"contributors": [
{ "name": "TJ Holowaychuk", "email": "tj@vision-media.ca" },
{ "name": "Aaron Heckmann", "email": "aaron.heckmann+github@gmail.com" },
{ "name": "Ciaran Jessup", "email": "ciaranj@gmail.com" }
{ "name": "Ciaran Jessup", "email": "ciaranj@gmail.com" },
{ "name": "Guillermo Rauch", "email": "rauchg@gmail.com" }
],
"dependencies": { "connect": ">= 0.2.2" },
"dependencies": { "connect": ">= 0.2.6" },
"keywords": ["framework", "sinatra", "web", "rest", "restful"],
"directories": { "lib": "./lib/express" },
"scripts": { "test": "make test" },
"bin": { "express": "./bin/express" },
"engines": { "node": ">= 0.1.98" }
"engines": { "node": ">= 0.2.0" }
}
+28 -3
Ver Arquivo
@@ -6,16 +6,41 @@ var fs = require('fs'),
if (file) {
var js = fs.readFileSync(file, 'utf8'),
headers = js.match(/<h3 id="(.*?)">(.*?)<\/h3>/g),
toc = ['<ul id="toc">'];
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(/\(.*?\)/, '()');
toc.push('<li><a href="#' + id + '">' + title + '</a></li>');
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);
}
}
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');
}
+87 -12
Ver Arquivo
@@ -8,6 +8,10 @@ module.exports = {
assert.ok(server instanceof connect.Server, 'Test serverlication inheritance');
},
'test connect middleware autoloaders': function(assert){
assert.equal(express.errorHandler, connect.errorHandler);
},
'test basic server': function(assert){
var server = express.createServer();
@@ -22,6 +26,14 @@ module.exports = {
res.end('updated user ' + req.params.id)
});
server.del('/something', function(req, res){
res.send('Destroyed');
});
server.delete('/something/else', function(req, res){
res.send('Destroyed');
});
assert.response(server,
{ url: '/' },
{ body: 'wahoo' });
@@ -29,6 +41,14 @@ module.exports = {
assert.response(server,
{ url: '/user/12', method: 'PUT' },
{ body: 'updated user 12' });
assert.response(server,
{ url: '/something', method: 'DELETE' },
{ body: 'Destroyed' });
assert.response(server,
{ url: '/something/else', method: 'DELETE' },
{ body: 'Destroyed' });
},
'test constructor middleware': function(assert, beforeExit){
@@ -73,13 +93,13 @@ module.exports = {
// Custom handler
var app = express.createServer();
app.get('/', function(req, res, next){
next(new Error('broken'));
});
app.error(function(err, req, res){
res.send('Shit: ' + err.message, 500);
});
app.get('/', function(req, res, next){
next(new Error('broken'));
});
assert.response(app,
{ url: '/' },
@@ -88,13 +108,6 @@ module.exports = {
// Multiple error()s
var app = express.createServer();
app.get('/', function(req, res, next){
throw new Error('broken');
});
app.get('/foo', function(req, res, next){
throw new Error('oh noes');
});
app.error(function(err, req, res, next){
if (err.message === 'broken') {
next(err);
@@ -106,6 +119,13 @@ module.exports = {
app.error(function(err, req, res, next){
res.send(err.message, 500);
});
app.get('/', function(req, res, next){
throw new Error('broken');
});
app.get('/foo', function(req, res, next){
throw new Error('oh noes');
});
assert.response(app,
{ url: '/' },
@@ -158,7 +178,7 @@ module.exports = {
'test #configure()': function(assert, beforeExit){
var calls = [];
process.env.EXPRESS_ENV = 'development';
process.env.NODE_ENV = 'development';
var server = express.createServer();
// Config blocks
@@ -284,6 +304,11 @@ module.exports = {
assert.equal('/blog', blog.route);
assert.equal('/contact', map.route);
assert.ok(called, 'mounted() hook failed');
app.set("test", "parent setting");
assert.equal(blog.set("test"), "parent setting", "settings did not inherit from parent app")
blog.set("test", "overridden");
assert.equal(blog.set("test"), "overridden", "mounted app setting did not override parent app")
app.get('/', function(req, res){
assert.equal('/', app.set('home'), "home did not default to /");
@@ -312,5 +337,55 @@ module.exports = {
assert.response(blog,
{ url: '/' },
{ body: 'blog index' });
},
'test route middleware': function(assert){
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);
}
}
}
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 });
});
}
};
+1
Ver Arquivo
@@ -0,0 +1 @@
<p><%= greetings(session, lastName) %></p>
+1 -1
Ver Arquivo
@@ -1 +1 @@
p #{session.name} #{lastName} #{foo()}
p #{greetings(session, lastName)}
+1
Ver Arquivo
@@ -0,0 +1 @@
p This is actually jade :)
+1
Ver Arquivo
@@ -0,0 +1 @@
p :(
+151
Ver Arquivo
@@ -0,0 +1,151 @@
{
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar",
"foo":"bar"
}
+1
Ver Arquivo
@@ -0,0 +1 @@
li.item= item
+2
Ver Arquivo
@@ -0,0 +1,2 @@
<h1>{{= name }}</h1>
<p>{{= email }}</p>
+1
Ver Arquivo
@@ -0,0 +1 @@
<h1><?= title ?></h1>
+13
Ver Arquivo
@@ -117,6 +117,12 @@ module.exports = {
connect.session({ store: memoryStore })
);
app.flashFormatters = {
u: function(val){
return String(val).toUpperCase();
}
};
app.get('/', function(req, res){
assert.eql([], req.flash('info'));
assert.eql([], req.flash('error'));
@@ -134,6 +140,13 @@ module.exports = {
req.flash('info', 'Email _sent_.');
req.flash('info', '<script>');
assert.eql(['Email <em>sent</em>.', '&lt;script&gt;'], req.flash('info'));
req.flash('info', 'Welcome _%s_ to %s', 'TJ', 'something');
assert.eql(['Welcome <em>TJ</em> to something'], req.flash('info'));
req.flash('info', 'Foo %u', 'bar');
assert.eql(['Foo BAR'], req.flash('info'));
res.send('ok');
});
+200 -21
Ver Arquivo
@@ -19,6 +19,13 @@ module.exports = {
res.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
});
app.get('/jsonp', function(req, res){
app.enable('jsonp callback');
res.header('X-Foo', 'bar');
res.send({ foo: 'bar' }, { 'X-Foo': 'baz' }, 201);
app.disable('jsonp callback');
});
app.get('/text', function(req, res){
res.header('X-Foo', 'bar');
res.contentType('.txt');
@@ -36,6 +43,10 @@ module.exports = {
app.get('/buffer', function(req, res){
res.send(new Buffer('wahoo!'));
});
app.get('/noargs', function(req, res, next){
res.send();
});
assert.response(app,
{ url: '/html' },
@@ -43,6 +54,19 @@ module.exports = {
assert.response(app,
{ url: '/json' },
{ body: '{"foo":"bar"}', status: 201, headers: { 'Content-Type': 'application/json', 'X-Foo': 'baz' }});
assert.response(app,
{ url: '/jsonp?callback=test' },
{ body: 'test({"foo":"bar"});', status: 201, headers: { 'Content-Type': 'application/json', 'X-Foo': 'baz' }});
assert.response(app,
{ url: '/jsonp?callback=baz' },
{ body: 'baz({"foo":"bar"});', status: 201, headers: { 'Content-Type': 'application/json', 'X-Foo': 'baz' }});
assert.response(app,
{ url: '/json?callback=test' },
{ body: '{"foo":"bar"}', status: 201, headers: { 'Content-Type': 'application/json', 'X-Foo': 'baz' }});
assert.response(app,
{ url: '/text' },
{ body: 'wahoo', headers: { 'Content-Type': 'text/plain; charset=utf-8', 'X-Foo': 'bar' }});
@@ -55,9 +79,15 @@ module.exports = {
assert.response(app,
{ url: '/buffer' },
{ body: 'wahoo!', headers: { 'Content-Type': 'application/octet-stream' }});
assert.response(app,
{ url: '/noargs' },
{ status: 204 }, function(res){
assert.equal(undefined, res.headers['content-type']);
assert.equal(undefined, res.headers['content-length']);
});
},
'test #contentType': function(assert){
'test #contentType()': function(assert){
var app = express.createServer();
app.get('/html', function(req, res){
@@ -71,7 +101,7 @@ module.exports = {
{ body: '<p>yay</p>', headers: { 'Content-Type': 'text/html; charset=utf-8' }});
},
'test #attachment': function(assert){
'test #attachment()': function(assert){
var app = express.createServer();
app.get('/style.css', function(req, res){
@@ -135,50 +165,62 @@ module.exports = {
});
app2.get('/user/:id', function(req, res){
res.header('X-Foo', 'bar');
res.redirect('blog');
});
assert.response(app,
{ url: '/' },
{ body: '', status: 301, headers: { Location: 'http://google.com' }});
{ body: 'Redirecting to http://google.com', status: 301, headers: { Location: 'http://google.com' }});
assert.response(app,
{ url: '/back' },
{ body: '', status: 302, headers: { Location: '/' }});
{ body: 'Redirecting to /', status: 302, headers: { Location: '/', 'Content-Type': 'text/plain' }});
assert.response(app,
{ url: '/back', headers: { Referer: '/foo' }},
{ body: '', status: 302, headers: { Location: '/foo' }});
{ body: 'Redirecting to /foo', status: 302, headers: { Location: '/foo' }});
assert.response(app,
{ url: '/back', headers: { Referrer: '/foo' }},
{ body: '', status: 302, headers: { Location: '/foo' }});
{ body: 'Redirecting to /foo', status: 302, headers: { Location: '/foo' }});
assert.response(app,
{ url: '/home' },
{ body: '', status: 302, headers: { Location: '/' }});
{ body: 'Redirecting to /', status: 302, headers: { Location: '/' }});
assert.response(app2,
{ url: '/' },
{ body: '', status: 301, headers: { Location: 'http://google.com' }});
{ body: 'Redirecting to http://google.com', status: 301, headers: { Location: 'http://google.com' }});
assert.response(app2,
{ url: '/back' },
{ body: '', status: 302, headers: { Location: '/blog' }});
{ body: 'Redirecting to /blog', status: 302, headers: { Location: '/blog' }});
assert.response(app2,
{ url: '/home' },
{ body: '', status: 302, headers: { Location: '/blog' }});
{ body: 'Redirecting to /blog', status: 302, headers: { Location: '/blog' }});
assert.response(app2,
{ url: '/google' },
{ body: '', headers: { Location: 'http://google.com' }});
{ body: 'Redirecting to http://google.com', headers: { Location: 'http://google.com' }});
assert.response(app2,
{ url: '/user/12' },
{ body: '', headers: { Location: '/user/12/blog' }});
{ body: 'Redirecting to /user/12/blog', headers: { Location: '/user/12/blog', 'X-Foo': 'bar' }});
},
'test #sendfile()': function(assert){
var app = express.createServer();
app.get('/*', function(req, res){
var file = req.params[0];
res.sendfile(__dirname + '/fixtures/' + file);
app.set('stream threshold', 2 * 1024)
app.get('/*', function(req, res, next){
var file = req.params[0],
filePath = __dirname + '/fixtures/' + file;
res.sendfile(filePath, function(err, path){
assert.equal(path, filePath);
if (err) next(err);
});
});
app.use(express.errorHandler());
assert.response(app,
{ url: '/../express.test.js' },
{ body: 'Forbidden', status: 403 });
assert.response(app,
{ url: '/user.json' },
{ body: '{"name":"tj"}', status: 200, headers: { 'Content-Type': 'application/json' }});
@@ -187,23 +229,107 @@ module.exports = {
{ body: '%p Hello World', status: 200, headers: { 'Content-Type': 'application/octet-stream' }});
assert.response(app,
{ url: '/doesNotExist' },
{ body: 'Not Found', status: 404 });
{ body: 'Internal Server Error', status: 500 });
assert.response(app,
{ url: '/partials' },
{ body: 'Internal Server Error', status: 500 });
assert.response(app,
{ url: '/large.json' },
{ status: 200, headers: { 'Content-Length': 2535, 'Content-Type': 'application/json' }});
},
'test #sendfile() Accept-Ranges': function(assert){
var app = express.createServer();
app.set('stream threshold', 1024);
app.get('/*', function(req, res, next){
var file = req.params[0],
path = __dirname + '/fixtures/' + file;
res.sendfile(path);
});
assert.response(app,
{ url: '/large.json' },
{ headers: { 'Accept-Ranges': 'bytes' }});
},
'test #sendfile() Range': function(assert){
var app = express.createServer();
app.set('stream threshold', 1024);
app.get('/*', function(req, res, next){
var file = req.params[0],
path = __dirname + '/fixtures/' + file;
res.sendfile(path);
});
assert.response(app,
{ url: '/large.json', headers: { 'Range': 'bytes=0-499' }},
{ headers: {
'Content-Type': 'application/json',
'Content-Length': 500,
'Content-Range': 'bytes 0-499/2535'
}, status: 206 });
},
'test #sendfile() Range invalid syntax': function(assert){
var app = express.createServer();
app.set('stream threshold', 1024);
app.get('/*', function(req, res, next){
var file = req.params[0],
path = __dirname + '/fixtures/' + file;
res.sendfile(path);
});
assert.response(app,
{ url: '/large.json', headers: { 'Range': 'basdytes=asdf' }},
{ headers: {
'Content-Type': 'application/json',
'Content-Length': 2535
}, status: 200 });
},
'test #sendfile() Range invalid range': function(assert){
var app = express.createServer();
app.set('stream threshold', 1024);
app.get('/*', function(req, res, next){
var file = req.params[0],
path = __dirname + '/fixtures/' + file;
res.sendfile(path);
});
assert.response(app,
{ url: '/large.json', headers: { 'Range': 'bytes=500-10' }},
{ headers: {
'Content-Type': 'application/json',
'Content-Length': 2535
}, status: 200 });
},
'test #download()': function(assert){
var app = express.createServer();
app.get('/json', function(req, res, next){
res.download(__dirname + '/fixtures/user.json', 'account.json');
var filePath = __dirname + '/fixtures/user.json';
res.download(filePath, 'account.json', function(err, path){
assert.equal(filePath, path);
});
});
app.get('/:file', function(req, res, next){
res.download(__dirname + '/fixtures/' + req.params.file);
app.get('/*', function(req, res, next){
res.download(__dirname + '/fixtures/' + req.params[0]);
});
app.error(function(err, req, res){
res.send(404);
})
assert.response(app,
{ url: '/user.json' },
{ body: '{"name":"tj"}', status: 200, headers: {
@@ -215,7 +341,60 @@ module.exports = {
{ url: '/json' },
{ body: '{"name":"tj"}', status: 200, headers: {
'Content-Type': 'application/json',
'Content-Length': '13',
'Content-Disposition': 'attachment; filename="account.json"'
}});
assert.response(app,
{ url: '/missing' },
function(res){
assert.equal(null, res.headers['content-disposition']);
});
},
'test #cookie()': function(assert){
var app = express.createServer();
app.get('/', function(req, res){
res.cookie('rememberme', 'yes', { expires: new Date(1), httpOnly: true });
res.redirect('/');
});
assert.response(app,
{ url: '/' },
function(res){
assert.equal(
'rememberme=yes; expires=Thu, 01 Jan 1970 00:00:00 GMT; httpOnly',
res.headers['set-cookie']);
});
},
'test #clearCookie()': function(assert){
var app = express.createServer();
app.get('/', function(req, res){
res.clearCookie('rememberme');
res.redirect('/');
});
assert.response(app,
{ url: '/' },
function(res){
assert.equal(
'rememberme=; expires=Thu, 01 Jan 1970 00:00:00 GMT',
res.headers['set-cookie']);
});
},
'test HEAD': function(assert){
var app = express.createServer();
app.get('/', function(req, res, next){
res.send('Hello World');
});
assert.response(app,
{ url: '/', method: 'HEAD' },
{ body: '', headers: { 'Content-Length': 11 }});
}
};
};
+10 -1
Ver Arquivo
@@ -6,12 +6,21 @@
var utils = require('express/utils');
module.exports = {
'test miniMarkdown': function(assert){
'test .miniMarkdown()': function(assert){
assert.equal('<em>foo</em>', utils.miniMarkdown('_foo_'));
assert.equal('<em>foo</em>', utils.miniMarkdown('*foo*'));
assert.equal('<strong>foo</strong>', utils.miniMarkdown('**foo**'));
assert.equal('<strong>foo</strong>', utils.miniMarkdown('__foo__'));
assert.equal('<em>foo</em>_', utils.miniMarkdown('_foo__'));
assert.equal('<a href="/login">User Login</a>', utils.miniMarkdown('[User Login](/login)'));
},
'test .parseRange()': function(assert){
assert.eql([{start: 0, end: 499 }], utils.parseRange(1000, 'bytes=0-499'));
assert.eql([{start: 40, end: 80 }], utils.parseRange(1000, 'bytes=40-80'));
assert.eql([{start: 500, end: 999 }], utils.parseRange(1000, 'bytes=-500'));
assert.eql([{start: 500, end: 999 }], utils.parseRange(1000, 'bytes=500-'));
assert.eql([{start: 0, end: 0 }], utils.parseRange(1000, 'bytes=0-0'));
assert.eql([{start: 999, end: 999 }], utils.parseRange(1000, 'bytes=-1'));
}
};
+207 -10
Ver Arquivo
@@ -15,7 +15,7 @@ var create = function(){
module.exports = {
'test #render()': function(assert){
var app = create(connect.errorHandler({ showMessage: true }));
var app = create();
app.set('view engine', 'jade');
app.get('/', function(req, res){
@@ -27,6 +27,18 @@ module.exports = {
app.get('/haml', function(req, res){
res.render('hello.haml', { layout: false });
});
app.get('/callback/layout/no-options', function(req, res){
res.render('hello.jade', function(err, str){
assert.ok(!err);
res.send(str.replace(':(', ':)'));
});
});
app.get('/callback/layout', function(req, res){
res.render('hello.jade', {}, function(err, str){
assert.ok(!err);
res.send(str.replace(':(', ':)'));
});
});
app.get('/callback', function(req, res){
res.render('hello.haml', { layout: false }, function(err, str){
assert.ok(!err);
@@ -65,6 +77,12 @@ module.exports = {
assert.response(app,
{ url: '/callback' },
{ body: '\n<p>:)</p>' });
assert.response(app,
{ url: '/callback/layout' },
{ body: '<html><body><p>:)</p></body></html>' });
assert.response(app,
{ url: '/callback/layout/no-options' },
{ body: '<html><body><p>:)</p></body></html>' });
assert.response(app,
{ url: '/error' },
{ body: 'doesNotExist' });
@@ -99,11 +117,15 @@ module.exports = {
{ body: '<html><body><p>Welcome</p></body></html>' });
},
'test #render() specific layout': function(assert){
var app = create();
'test #render() specific layout': function(assert, beforeExit){
var app = create(),
called;
app.get('/', function(req, res){
res.render('index.jade', { layout: 'cool-layout.jade' });
res.render('index.jade', { layout: 'cool-layout.jade' }, function(err, html){
called = true;
res.send(html);
});
});
app.get('/no-ext', function(req, res){
res.render('index.jade', { layout: 'cool-layout' });
@@ -114,6 +136,9 @@ module.exports = {
app.get('/absolute', function(req, res){
res.render('index.jade', { layout: __dirname + '/fixtures/layouts/foo.jade' });
});
app.get('/nope', function(req, res){
res.render('index.jade', { layout: 'nope.jade' });
});
assert.response(app,
{ url: '/' },
@@ -127,6 +152,16 @@ module.exports = {
assert.response(app,
{ url: '/absolute' },
{ body: '<foo></foo>' });
assert.response(app,
{ url: '/nope' },
function(res){
assert.ok(res.body.indexOf('Error: ENOENT') >= 0);
assert.ok(res.body.indexOf('nope.jade') >= 0);
});
beforeExit(function(){
assert.ok(called, 'Layout callback never called');
});
},
'test #render() specific layout "view engine"': function(assert){
@@ -147,8 +182,8 @@ module.exports = {
app.set('view engine', 'jade');
app.get('/', function(req, res){
res.internal = '1';
res.method = function(){
req.internal = '1';
req.method = function(){
return this.internal;
};
res.render('scope.jade', { layout: false });
@@ -173,18 +208,56 @@ module.exports = {
{ body: '<p>2</p>'});
},
'test #render() view helpers': function(assert){
'test #render() status': function(assert){
var app = create();
app.get('/', function(req, res){
res.render('hello.jade', {
layout: false,
status: 404
});
});
assert.response(app,
{ url: '/' },
{ body: '<p>:(</p>', status: 404 });
},
'test #render() headers': function(assert){
var app = create();
app.get('/', function(req, res){
res.render('hello.jade', {
layout: false,
status: 500,
headers: {
'X-Foo': 'bar'
}
});
});
assert.response(app,
{ url: '/' },
{ body: '<p>:(</p>', status: 500, headers: {
'X-Foo': 'bar',
'Content-Type': 'text/html; charset=utf-8'
}});
},
'test #render() view helpers': function(assert, beforeExit){
var app = create(),
calls = 0;
app.helpers({
lastName: 'holowaychuk',
foo: function(){
return 'bar';
greetings: function(sess, lastName){
return 'Hello ' + sess.name + ' ' + lastName;
}
});
var ret = app.dynamicHelpers({
session: function(req, res){
++calls;
assert.equal('object', typeof req, 'Test dynamic helper req');
assert.equal('object', typeof res, 'Test dynamic helper res');
assert.ok(this instanceof express.Server, 'Test dynamic helper app scope');
@@ -198,10 +271,32 @@ module.exports = {
req.session = { name: 'tj' };
res.render('dynamic-helpers.jade', { layout: false });
});
app.get('/ejs', function(req, res){
req.session = { name: 'tj' };
res.render('dynamic-helpers.ejs', { layout: false });
});
app.get('/precedence', function(req, res){
req.session = { name: 'tj' };
res.render('dynamic-helpers.jade', {
locals: {
lastName: 'foobar'
}
});
});
assert.response(app,
{ url: '/' },
{ body: '<p>tj holowaychuk bar</p>' });
{ body: '<p>Hello tj holowaychuk</p>' });
assert.response(app,
{ url: '/ejs' },
{ body: '<p>Hello tj holowaychuk</p>' });
assert.response(app,
{ url: '/precedence' },
{ body: '<html><body><p>Hello tj foobar</p></body></html>' });
beforeExit(function(){
assert.equal(3, calls);
});
},
'test #partial()': function(assert){
@@ -339,5 +434,107 @@ module.exports = {
assert.response(app,
{ url: '/nothing' },
{ body: 'Hello' });
},
'test #partial() with array-like collection': function(assert){
var app = create();
var movies = {
0: { title: 'Nightmare Before Christmas', director: 'Tim Burton' },
1: { title: 'Avatar', director: 'James Cameron' },
length: 2
};
app.get('/movies', function(req, res){
res.render('movies.jade', { locals: { movies: movies }});
});
var html = [
'<html>',
'<body>',
'<ul>',
'<li>',
'<div class="title">Nightmare Before Christmas</div>',
'<div class="director">Tim Burton</div>',
'</li>',
'<li>',
'<div class="title">Avatar</div>',
'<div class="director">James Cameron</div>',
'</li>',
'</ul>',
'</body>',
'</html>'
].join('');
assert.response(app,
{ url: '/movies' },
{ body: html });
},
'test "partials" setting': function(assert){
var app = create();
app.set('partials', __dirname + '/fixtures/sub-templates');
app.get('/', function(req, res){
res.render('items.jade', {
layout: false,
locals: {
items: ['foo', 'bar']
}
});
});
assert.response(app,
{ url: '/' },
{ body: '<ul><li class="item">foo</li><li class="item">bar</li></ul>' });
},
'test "view options"': function(assert){
var app = create();
app.set('view options', {
layout: false,
open: '{{',
close: '}}'
});
app.get('/', function(req, res, next){
res.render('user.ejs', {
locals: {
name: 'tj',
email: 'tj@vision-media.ca'
}
});
});
app.get('/video', function(req, res, next){
res.render('video.ejs', {
open: '<?',
close: '?>',
locals: {
title: 'keyboard cat'
}
});
});
assert.response(app,
{ url: '/' },
{ body: '<h1>tj</h1>\n<p>tj@vision-media.ca</p>' });
assert.response(app,
{ url: '/video' },
{ body: '<h1>keyboard cat</h1>' });
},
'test .register()': function(assert){
var app = create();
app.register('.bar', require('jade'));
app.get('/', function(req, res, next){
res.render('foo.bar', { layout: false });
});
assert.response(app,
{ url: '/' },
{ body: '<p>This is actually jade :)</p>' });
}
};