Comparar commits
1055 Commits
| Autor | SHA1 | Data | |
|---|---|---|---|
| 2572a78648 | |||
| 470774cfba | |||
| 351f6abe4c | |||
| 53a16e1795 | |||
| ff77c8b205 | |||
| b33f38b109 | |||
| b9596d7ce8 | |||
| 251175c025 | |||
| fda1bc4630 | |||
| 83c2c176a9 | |||
| 9be5992f22 | |||
| d8d23c0bf8 | |||
| b2689fc40e | |||
| a4cfde350f | |||
| 7374027457 | |||
| 63328c2177 | |||
| c4e2ce23e5 | |||
| dacad53b2e | |||
| 4ffd5280a7 | |||
| 74310fb464 | |||
| 8b2268cf38 | |||
| fb655f4981 | |||
| 7208c33d72 | |||
| 4efb25d048 | |||
| a3678cd7f6 | |||
| 393d38f1ab | |||
| 805b9ac3a9 | |||
| 379b9812be | |||
| a9992b5647 | |||
| b6c0a9b1b5 | |||
| 1d2dd2a375 | |||
| 63db694aa2 | |||
| b6aca36ad9 | |||
| 8420ae93fd | |||
| 6722716fa7 | |||
| 658e064220 | |||
| 66b472e567 | |||
| 3d242a607e | |||
| bc68e5e7a3 | |||
| aa6858189c | |||
| 06fda62c9e | |||
| 5688ea650d | |||
| f5c4e9d612 | |||
| b9eda2a59d | |||
| 8c168b0971 | |||
| 9c20a50ee2 | |||
| eac666574e | |||
| e4c840f6b8 | |||
| 3afbcd0acf | |||
| 8f054dbcaf | |||
| ccc39e5aa2 | |||
| 53d4da2a9c | |||
| d9e7153fc9 | |||
| dc02b0d5ae | |||
| e0bc5711b8 | |||
| 957cf45fa1 | |||
| f4487343df | |||
| ca1bdb31e3 | |||
| 236a412459 | |||
| 759a57bdb6 | |||
| 1abb674a07 | |||
| 961146a287 | |||
| 573f940985 | |||
| 882916bb2e | |||
| 47d1c62732 | |||
| d39293c025 | |||
| 2942dafdfd | |||
| 4e1aefa5b5 | |||
| 89383ecc57 | |||
| 08046f7692 | |||
| 885fb1fa92 | |||
| 6a58c71528 | |||
| 371d66ba2a | |||
| 25bddf3fb5 | |||
| f6e9fb13f8 | |||
| f0df8434e7 | |||
| e4d3f239e5 | |||
| bcc22dfa6f | |||
| f614709a01 | |||
| 11250d22c9 | |||
| 4b4de29725 | |||
| 99b49d2718 | |||
| 6230ec55be | |||
| 0733d3c585 | |||
| 6f8370ff0e | |||
| bc244ed07e | |||
| 41266aa8e4 | |||
| 45faee3e5b | |||
| 74ff735e10 | |||
| 97879f2b16 | |||
| 1a338251ad | |||
| 354dc768c1 | |||
| 8a0796cd94 | |||
| cbc3b26584 | |||
| d628583db8 | |||
| 058777be1e | |||
| 260d03a0c4 | |||
| 6dcf6f41cc | |||
| 799f790886 | |||
| cb3c4b0ea9 | |||
| 798d255ba6 | |||
| 28ba9e8ac5 | |||
| 7888cb0506 | |||
| 5e284a20cc | |||
| 770357e727 | |||
| 673ba22555 | |||
| fb38d9cfb7 | |||
| dda89a57ec | |||
| 62df63d3a0 | |||
| e71696cf34 | |||
| b5d8d58670 | |||
| 14e6a667f5 | |||
| 0c324783ae | |||
| 5d6ce251ca | |||
| 92c04cee1d | |||
| 7fdf587a7b | |||
| 1e46218b09 | |||
| c56fcd8fb9 | |||
| 319fbf7f64 | |||
| bf06d9077c | |||
| 9d2bd29ee1 | |||
| d11fa1f74e | |||
| c824da0dab | |||
| 9362c83a33 | |||
| 0c38098f02 | |||
| be7068f569 | |||
| b122bf22e3 | |||
| b7232f38f3 | |||
| c1b72ac1b7 | |||
| b9e0d15878 | |||
| cf26cf7afc | |||
| a75e60ae47 | |||
| 187dc5dd03 | |||
| 9c9e2afade | |||
| fae1ba98c1 | |||
| c3e632620a | |||
| dd7158ac46 | |||
| eefe51c7a7 | |||
| bf596dc023 | |||
| 220d88d654 | |||
| a254e64bdb | |||
| 1555b92fb8 | |||
| d5b7a40b39 | |||
| bd1ab7ab96 | |||
| 2ff991bfcf | |||
| 0b1378a539 | |||
| 723c908bd7 | |||
| 4874404701 | |||
| 4c1374840a | |||
| 5da01633fd | |||
| cdbd8af527 | |||
| a6fdc1bfd2 | |||
| 20b8facb05 | |||
| 909914f7af | |||
| 3f31ebc676 | |||
| f3c068a90c | |||
| 90d7e193d1 | |||
| c9f5bb6f17 | |||
| 9865a4c4f2 | |||
| f12baf32d4 | |||
| 80f4d08e8b | |||
| 07c9cae923 | |||
| d867cc9271 | |||
| 3a1fe1e295 | |||
| 3cacf050df | |||
| 1536d73d1b | |||
| 55143a9d44 | |||
| c92e193916 | |||
| d12452fc49 | |||
| 3d8400a40c | |||
| 1fe0aea0b0 | |||
| 5df2544883 | |||
| acbf224277 | |||
| d0d17a0d35 | |||
| f7b53d33bc | |||
| 92be06874b | |||
| f327455d9d | |||
| 54415bf2af | |||
| bac62dfcd9 | |||
| 820b43c1f3 | |||
| a5b69290d5 | |||
| 5c7a9c86f6 | |||
| 7f83f916f6 | |||
| c15a949cc3 | |||
| ce47f96570 | |||
| 57b035cd94 | |||
| f1f126171c | |||
| 71e1bcd855 | |||
| b5579b6307 | |||
| 110b0fe14a | |||
| d7488bbb62 | |||
| 2007407e7b | |||
| d152e7e780 | |||
| d4f4b6682d | |||
| 7f4a12f4cd | |||
| 07d06adf86 | |||
| 47dc188d57 | |||
| 85d22541d2 | |||
| 90ec1031f9 | |||
| 9bc9ad13a4 | |||
| 1782c7d778 | |||
| e85752c527 | |||
| dfdbbb514f | |||
| 976f5ca93e | |||
| 2086f8d34c | |||
| 6873a68216 | |||
| 9eb2c61760 | |||
| 543e791206 | |||
| b6e8b3fa84 | |||
| 6f8e406b7b | |||
| 93bc54b65c | |||
| 3bf0ac4a36 | |||
| bc01bfff21 | |||
| 0d4eaf03fc | |||
| 2bb0412473 | |||
| 07ce5d3158 | |||
| 131970b3e5 | |||
| 00e1dbfbd9 | |||
| 4c274c524d | |||
| 5581ca2751 | |||
| 5cd9a9540f | |||
| 1db2efa75a | |||
| c1a82bf3ea | |||
| 7705042cac | |||
| b9e311e893 | |||
| 460998497d | |||
| 022c5e1411 | |||
| 60daf0d6c7 | |||
| 0b6a941624 | |||
| 1fc520b45a | |||
| cbbee38be8 | |||
| 2dae6d8285 | |||
| 65c8a956f1 | |||
| 2b907e9a8c | |||
| 25e520c243 | |||
| 2124c8465a | |||
| a49e1f4f3d | |||
| 32be4a7812 | |||
| 92967d3457 | |||
| d70fee7432 | |||
| 97b8e75932 | |||
| 8a62f8cef0 | |||
| 10f4f523fa | |||
| 1e392d2628 | |||
| eb6ff62113 | |||
| 64da2621da | |||
| 61aec6e961 | |||
| 42f3ad436d | |||
| 2f7b78c03a | |||
| 402d37d613 | |||
| 96327c979c | |||
| 9d1b3f59d5 | |||
| 14bd50efe4 | |||
| 631c1f95e7 | |||
| 50006f7e43 | |||
| 4c9503158b | |||
| 6c8e461db5 | |||
| aaa8415169 | |||
| 51e964498c | |||
| 0cddc70ff5 | |||
| 99dcf7508f | |||
| 7822d1f148 | |||
| a572a7126c | |||
| a1609faba0 | |||
| 9bf823d893 | |||
| ca184e7725 | |||
| 5054507487 | |||
| ab93be0178 | |||
| 8b0993bb10 | |||
| 0a034bb3ea | |||
| 4ce815a3ed | |||
| 4da860eae1 | |||
| 27e4bdb814 | |||
| dd9406cd52 | |||
| 509312773d | |||
| 42d2758c36 | |||
| ef1c858ffc | |||
| 7fd47c0ac6 | |||
| a63b232ef1 | |||
| acb502e5e3 | |||
| 3661922bb0 | |||
| 0704f149be | |||
| 334604de5d | |||
| 530328ac15 | |||
| 9914a1eb3f | |||
| 5db5710d3f | |||
| 9e9f55a8e3 | |||
| 0800141a9d | |||
| dc6d469f0c | |||
| 89299d77e4 | |||
| 27d8bf6311 | |||
| 8c229a8560 | |||
| 29eed65a67 | |||
| f0e0073a09 | |||
| b70bde58be | |||
| 635fac776d | |||
| 7753d4e91d | |||
| 66a6214216 | |||
| 6cae1f09f1 | |||
| c4432ee827 | |||
| 80a5bf2063 | |||
| 5b3d5fc431 | |||
| cd38fb4177 | |||
| 9b1cc75b36 | |||
| db8b2883e2 | |||
| 59a74ecce4 | |||
| 9099c61df9 | |||
| 50521281ce | |||
| 0cc6abf741 | |||
| fe266ae4fe | |||
| 7c15cdeb69 | |||
| 848086947c | |||
| 5ec4ea754c | |||
| 41467d54df | |||
| 8261437764 | |||
| 54eac368d0 | |||
| 72511ea1e9 | |||
| da7921f2c6 | |||
| cd519e97e7 | |||
| 2a7a5aeefe | |||
| bdbacb41da | |||
| 2dd736a201 | |||
| ec3c797105 | |||
| 770f05c060 | |||
| 6993a27d8e | |||
| 68fd658536 | |||
| 9fc349965d | |||
| 0a667ff741 | |||
| d37ad85f72 | |||
| 2b63568611 | |||
| fcf9f93825 | |||
| 07efc0b73b | |||
| 6813b48c7e | |||
| d58444c6d2 | |||
| dd8a0bd30f | |||
| b65b0636aa | |||
| 9ec6321261 | |||
| 07eb66c205 | |||
| 0e790e6fb5 | |||
| 08186924a4 | |||
| e9faf5cf94 | |||
| 8c0efe09e5 | |||
| 32d2c96d22 | |||
| e958988989 | |||
| 131f10bd80 | |||
| a61615a196 | |||
| 4fdb6910a5 | |||
| 67c4c90351 | |||
| 16f7288b19 | |||
| 36a7e87ae4 | |||
| 16badda118 | |||
| 99820e7edc | |||
| 6a03a92f5f | |||
| 80c3b98a84 | |||
| afd2b4b387 | |||
| df49769f3d | |||
| 05c2946322 | |||
| 2ed392edbf | |||
| 3d597a554d | |||
| 42af4e40ae | |||
| 242efc7219 | |||
| a6bacb857d | |||
| eab54724a1 | |||
| 49885cfeff | |||
| 4330f49011 | |||
| 7687299275 | |||
| 79ba787b03 | |||
| 5a184a205f | |||
| 4b5a6b597a | |||
| 9e9c3eeb79 | |||
| cd996f9dbf | |||
| ab4c91158a | |||
| 1130a23e5b | |||
| 9ac6943220 | |||
| 0f55284b77 | |||
| c75046de3f | |||
| e7df82f110 | |||
| ee4c7d3007 | |||
| 2895303421 | |||
| 3f16f05525 | |||
| fb33671923 | |||
| 482fb5b6e2 | |||
| b1004b61f9 | |||
| 27af9b2605 | |||
| e28ac866f8 | |||
| 5e0a80f227 | |||
| ec0982ca1f | |||
| e7c442d21c | |||
| a9db47fdd3 | |||
| b856a72886 | |||
| 03da0d31f9 | |||
| 1c40e4374b | |||
| d7df1426f2 | |||
| 5035e86f59 | |||
| 0af91265ab | |||
| 8e63d04e66 | |||
| 5ac0c6deb0 | |||
| 6b0d82fb0c | |||
| 2bfdfe366c | |||
| 898fb0f320 | |||
| f53686a2f3 | |||
| eae1670f54 | |||
| 544d68833c | |||
| c24a6b2359 | |||
| b720b57d1d | |||
| 365b7fe90a | |||
| c7ab833e66 | |||
| 9536f288e9 | |||
| e64d4039d0 | |||
| 5f077e56f0 | |||
| a9253a24e0 | |||
| 749136b526 | |||
| 36a8b7b7f3 | |||
| a29908c865 | |||
| 57d87c27cd | |||
| 1c2e9a476c | |||
| 5d87133df0 | |||
| fd2f67b73f | |||
| d3abcaf67b | |||
| 14dce5ac2b | |||
| abd002071a | |||
| 77add42eeb | |||
| bba049b4bf | |||
| 4e41c3520c | |||
| 4144af3351 | |||
| 30f749a2a2 | |||
| 6e9f282af7 | |||
| 4491be01dd | |||
| 8c163fd980 | |||
| 9f6a158fc2 | |||
| 19965fa3c8 | |||
| 6bb42ffafc | |||
| 4a1edc0720 | |||
| a5a016bf33 | |||
| 4be4f597a3 | |||
| dfbbd4f784 | |||
| 2662dae560 | |||
| 6f6f9f08f8 | |||
| a76fba57c4 | |||
| 680766f29b | |||
| adcfd4ca4d | |||
| e276a49a91 | |||
| 5995f763b1 | |||
| a123639b5c | |||
| 8a118d53c8 | |||
| d572391532 | |||
| 58ae7a0402 | |||
| f843093a8c | |||
| 3e06586f81 | |||
| 60f148326a | |||
| 02e1413ca5 | |||
| 7b4b85ee54 | |||
| 6c0a416f16 | |||
| 23cc88bf92 | |||
| 5528f0ddf2 | |||
| 8d3296fb1b | |||
| 136f9209a3 | |||
| a290134fd2 | |||
| a54e59666a | |||
| 42ea6af1eb | |||
| f9b741766b | |||
| 09359329a0 | |||
| 647bb41ddf | |||
| f25c14d48d | |||
| 81a3d559f5 | |||
| 4e0ee7a5da | |||
| 1c80a258d2 | |||
| 129ed6a07b | |||
| df09a0c73b | |||
| ff5c958354 | |||
| 4f90c89db6 | |||
| 524b515fdb | |||
| 163256b001 | |||
| 05bbc65dcc | |||
| 6624f6b7f9 | |||
| f32279dec5 | |||
| a90fd85907 | |||
| 9815b27e94 | |||
| ebbe481fe7 | |||
| 9e337faa33 | |||
| 37826e0261 | |||
| 20e4342796 | |||
| 51be16950d | |||
| be866a78ff | |||
| f9d2432c69 | |||
| 6543a3d75a | |||
| a77936981e | |||
| 6f2120bb59 | |||
| 98e18239fd | |||
| 3d159015be | |||
| f5af66a1ab | |||
| 540192ff96 | |||
| c1cfd4a8c6 | |||
| 322300c329 | |||
| 1ac18f7903 | |||
| 4f4987e9ea | |||
| 65bf562ff4 | |||
| f47032e7e9 | |||
| 379f3ec8dd | |||
| 8d7e35b723 | |||
| 1349eea0d0 | |||
| bed5157f32 | |||
| 12c7f51a1e | |||
| 7e32c8de4d | |||
| 3a42350c21 | |||
| 1a54597202 | |||
| 39f4eb9940 | |||
| 4b996651af | |||
| 884d40f321 | |||
| 3d2b41da08 | |||
| 1bf2dbb7df | |||
| e0dfb77dd1 | |||
| 578fddc1b7 | |||
| 089355d706 | |||
| 6b2c3caf03 | |||
| 065c37cb28 | |||
| 3a40025fd3 | |||
| 8dc341d868 | |||
| 0ed121ddbd | |||
| 969f4229dc | |||
| 27a940184e | |||
| 77fa09a506 | |||
| 8836104ab2 | |||
| 396837517d | |||
| 38b3f1bde2 | |||
| 4780168ba8 | |||
| d1a790d103 | |||
| 3ad6ab8c6a | |||
| 1e189da85e | |||
| a094c50873 | |||
| ad324410c6 | |||
| 9f4b7368a0 | |||
| c8ddedb8d0 | |||
| 7deab2826b | |||
| 643879f779 | |||
| ce2a18f483 | |||
| 487d067f75 | |||
| c4d5e1b4f3 | |||
| 0e0cc30039 | |||
| a766cc791b | |||
| d017c3e248 | |||
| cc27ee04a8 | |||
| 87459bf24b | |||
| 5091c9e10f | |||
| 14a22d9033 | |||
| 7ded5a2de5 | |||
| 6b436a0192 | |||
| 52343686f5 | |||
| eec9d05b19 | |||
| 89703fd430 | |||
| dc246946a7 | |||
| 944e1120cb | |||
| cccda60df8 | |||
| e338e748eb | |||
| 0526a24cf3 | |||
| c4ba1b025a | |||
| 252f330658 | |||
| 45590415a6 | |||
| e562adc752 | |||
| 1e2fd44a6b | |||
| 1c903320be | |||
| 74eaf3a268 | |||
| 6fcd74c713 | |||
| 33136c6553 | |||
| cb225db203 | |||
| 0dbd5006ea | |||
| 08b3677ce8 | |||
| b08549a25b | |||
| 221bc4cb5d | |||
| b655b8e97b | |||
| 03e7b5dcc6 | |||
| 5f09e829df | |||
| e6759919dc | |||
| 6c65a3fc74 | |||
| e0ffd31123 | |||
| 4c1597113a | |||
| 4006506b64 | |||
| 14facdbcd2 | |||
| 726ace49d8 | |||
| f45d383367 | |||
| 3a32e2b53a | |||
| dc45e93cea | |||
| c0f9642719 | |||
| da8d036997 | |||
| 3c911cc785 | |||
| 4071f15f27 | |||
| 90de7923db | |||
| c28f1535a3 | |||
| 704e9c132e | |||
| 713d6f5f76 | |||
| 1a6d245eae | |||
| d909dd2a32 | |||
| 7bb466dd86 | |||
| e09bf5cc40 | |||
| 129e21e7c3 | |||
| 87dcd2909d | |||
| e47ec4f551 | |||
| d6cfcbe149 | |||
| 7a713807e9 | |||
| 6e49b5bc30 | |||
| adbc5b86f5 | |||
| 071ea955af | |||
| 806aefa966 | |||
| 5b7d23186d | |||
| 0cb4677e80 | |||
| bd65ecf17b | |||
| 9da2adff4c | |||
| a2a1aad109 | |||
| dcc530a389 | |||
| 97037a87f3 | |||
| 4e67efc0d6 | |||
| 7cbb73719e | |||
| d3f139c31e | |||
| 8940b6e5a3 | |||
| 1c9831a9f4 | |||
| 7da27435cc | |||
| bd18f86517 | |||
| 41f66b435b | |||
| 48481a7230 | |||
| dde6bc1f80 | |||
| e39fd1de0e | |||
| 2fc9a81e9e | |||
| f65874d751 | |||
| 3914895784 | |||
| 0bc7e24d0a | |||
| 2a40ceced7 | |||
| d4868f2cc2 | |||
| e87d63779d | |||
| 6672d65018 | |||
| 1f78c520f5 | |||
| 687e435a38 | |||
| b3222dc107 | |||
| ebba6caa3d | |||
| 88bbef9003 | |||
| 712771a139 | |||
| 9033b0ded7 | |||
| 877cfe9393 | |||
| 347aa56acb | |||
| 0fc410139c | |||
| 51528235db | |||
| 003599cbda | |||
| bacfad37a9 | |||
| 6ef6ba7b8a | |||
| 93c0da0367 | |||
| 268d19e8a3 | |||
| a383b302c2 | |||
| 95ebbf2608 | |||
| f880574c78 | |||
| 350c310906 | |||
| 5026f7686e | |||
| ab3337ddf5 | |||
| c9268f2e3d | |||
| fa01388f97 | |||
| d8dc89ac7b | |||
| a659fd1fa9 | |||
| 27fa3ba34c | |||
| 663f42948e | |||
| baca8f0cdb | |||
| c5c3a82b23 | |||
| c0617864e1 | |||
| d9ef9ab497 | |||
| 65d9f783e1 | |||
| f70232d1b0 | |||
| b083aed245 | |||
| 0faac20d53 | |||
| cef14ca7cd | |||
| 090debe0df | |||
| 296f1270ee | |||
| 334a18a114 | |||
| dd4ddae082 | |||
| 4983e967bc | |||
| 0b5e2f14ed | |||
| 70102aecb5 | |||
| ae75153561 | |||
| dd23d60513 | |||
| db95481b76 | |||
| 3e0da535b9 | |||
| c74602551e | |||
| 1722e2c7a5 | |||
| 8a845e2951 | |||
| 72765bada0 | |||
| b380f180cc | |||
| f1799799a1 | |||
| 4bc8a8673d | |||
| cbdd907393 | |||
| aa7ff3ef47 | |||
| ebdf6434eb | |||
| 9b7470d204 | |||
| 9f3ab8683c | |||
| fa464aca90 | |||
| 2724ce368a | |||
| 89f7cbe877 | |||
| fab9af2206 | |||
| 4d01b4fa59 | |||
| c3365aa37e | |||
| 426e80d4c3 | |||
| 30a282d126 | |||
| 0a96621e3e | |||
| d9d30abe7a | |||
| 03b56d8140 | |||
| 1f79ce4e96 | |||
| 8d80f23a0f | |||
| d89b951451 | |||
| 1906a38ec8 | |||
| 8d91a3e3b8 | |||
| 3324259977 | |||
| ec2db2d541 | |||
| 5cf248c7f3 | |||
| 3876a566ff | |||
| 334e6cc618 | |||
| 64033fcfc2 | |||
| fbe3af4719 | |||
| 699ed4ee85 | |||
| 903f67a154 | |||
| 9a89dab9e0 | |||
| 435a2565b8 | |||
| 8267b8da57 | |||
| 8b5773c246 | |||
| fd4c88d202 | |||
| 9b26623aa5 | |||
| b115ec7c14 | |||
| a2e6e2a06a | |||
| f0eab06a8b | |||
| b6c839d693 | |||
| 177a724d58 | |||
| 949803dcf6 | |||
| b763a88e60 | |||
| 18c7f164a8 | |||
| e037ff6bba | |||
| 95bfda1ff0 | |||
| b9992e0b45 | |||
| f6ba793885 | |||
| bbe65a8ea3 | |||
| 20af7371d0 | |||
| a33fb0c439 | |||
| 87003c8b5b | |||
| ddd48ee79f | |||
| a9819df3ee | |||
| 15dffb22aa | |||
| 6ef5f1ff62 | |||
| acc28c68a4 | |||
| 6c2da60eb0 | |||
| 490081077d | |||
| f4a08092da | |||
| 0116efd4a3 | |||
| 9719a24f8a | |||
| 3a7d5a099b | |||
| 27dba655fc | |||
| a25dd387bf | |||
| a89767cc16 | |||
| a13df5b68d | |||
| 6a689504ff | |||
| 6521d2bcfb | |||
| 26d55b15de | |||
| 041b7e8a29 | |||
| 6eeb425e7a | |||
| d2d0eb829e | |||
| 058505b664 | |||
| 8adecedb40 | |||
| 838a51db21 | |||
| 4d4dfc27fc | |||
| 1444439cf3 | |||
| e64fc8d5ab | |||
| 9a31f772d9 | |||
| 12876ee4ce | |||
| a4a349da45 | |||
| ecbc3cae71 | |||
| 57609c87fb | |||
| bb2e24e028 | |||
| 5cafb26e93 | |||
| f74ef7b293 | |||
| 7ef8f531e7 | |||
| afb65c7b19 | |||
| 74a0673663 | |||
| 20fae6e512 | |||
| 509cd242f7 | |||
| f01ec516eb | |||
| 394b1000d2 | |||
| e0b9fba788 | |||
| 378ee29cc2 | |||
| 02bcef71dd | |||
| 248e7c8003 | |||
| a2e93ebd56 | |||
| 21a9454de1 | |||
| ec286eed08 | |||
| f16978d1fe | |||
| fa3adc7606 | |||
| 192144fc5d | |||
| a4d155c5e4 | |||
| 6c56e9d199 | |||
| bb18f87a98 | |||
| dda6a31381 | |||
| 6e25e18e98 | |||
| ffd978c24f | |||
| 4c4af03a5a | |||
| c0739bb8b7 | |||
| 5add7cb0b1 | |||
| 99f49f1061 | |||
| 401eaca9ab | |||
| da784fe02b | |||
| d507b29559 | |||
| 35ca13b38d | |||
| 04b92a31a8 | |||
| dd311d759d | |||
| b504ca9f0a | |||
| bd61c5c721 | |||
| 24694b0047 | |||
| 5258154285 | |||
| 221f993b46 | |||
| ae5ea70b93 | |||
| f59ffbeb63 | |||
| ce55275634 | |||
| 20c5892f9a | |||
| 9231245a3f | |||
| cbfed20e6f | |||
| 640274ed52 | |||
| 1507a39a2b | |||
| e6405653d8 | |||
| be1541a646 | |||
| f37f9bf5e3 | |||
| a5c39ca8c8 | |||
| cb7e7b2a74 | |||
| 2e8e705bab | |||
| ab3812432b | |||
| d2acb36e3b | |||
| 601743ff7a | |||
| 7eca74ac05 | |||
| 02b4ec04bb | |||
| 8ead9014d0 | |||
| 4920e82dd6 | |||
| 3150253661 | |||
| baa423b45a | |||
| 77e5d8ea96 | |||
| 9a8166d649 | |||
| da8c0cd2db | |||
| fc2711b543 | |||
| b4327bd36c | |||
| f7f75923aa | |||
| 0b4388bc5b | |||
| f08883071f | |||
| bb8a2ee3a6 | |||
| 6bfa7595d8 | |||
| 8308e501cd | |||
| 25909c0d9d | |||
| 2844b1ac38 | |||
| 6fd62b1cee | |||
| 1579f53a61 | |||
| 7af7851952 | |||
| 5dbd1a6dcc | |||
| 24881537a1 | |||
| 631c29b26b | |||
| ab249d4032 | |||
| 5bfa3f6274 | |||
| 82ee25dfaa | |||
| fd9a24553f | |||
| ae1c20fad9 | |||
| 734f777ff9 | |||
| 498115d5d9 | |||
| 033ff06b68 | |||
| c145a8e1a1 | |||
| ae5b0dbc29 | |||
| 95aa957c80 | |||
| 7dff9ddeeb | |||
| b4e755747f | |||
| 348fa402be | |||
| 344218ff3d | |||
| 90a1a2febf | |||
| e234a24894 | |||
| d9beb75bd6 | |||
| dadea8c83d | |||
| 31d40e5509 | |||
| a917e84b51 | |||
| f30b497206 | |||
| 706e32c20b | |||
| 4d98552c37 | |||
| 5311bd3f4e | |||
| 8d716fb7ba | |||
| 01b3caaf49 | |||
| cd73c0b9ca | |||
| 7ec5230402 | |||
| 9dbe026f83 | |||
| 3fae10da3e | |||
| d775154b92 | |||
| 6c306b3542 | |||
| 58b46ffeac | |||
| 0bb7b7c60e | |||
| 58adf9a385 | |||
| d6ba968ace | |||
| 1706721a62 | |||
| 7f88bd2eaf | |||
| b819366265 | |||
| 47ff7d9dba | |||
| e5f5254877 | |||
| 4778f728f9 | |||
| e80e4573be | |||
| fdf0f3f282 | |||
| a81fce47d0 | |||
| 2cd6989b11 | |||
| 21abb0d205 | |||
| 5e1a271ca2 | |||
| 868e4c41e1 | |||
| a6e23bb713 | |||
| 446406dd62 | |||
| 091f9f7eae | |||
| 10b1e06263 | |||
| a8865a51fd | |||
| 32343297fc | |||
| aaa5deb0c0 | |||
| 808c519a1f | |||
| 262b110f9f | |||
| d0fe19f996 | |||
| 8e6675677f | |||
| 82dbae6185 | |||
| 943f1df325 | |||
| 018abcfe5f | |||
| 6b4f716508 | |||
| 82b6ff0c28 | |||
| d0c3132a63 | |||
| 02f0252dea | |||
| 74b3921eee | |||
| 1695c79021 | |||
| a3b0534b86 | |||
| a13f94d40f | |||
| 73288420a8 | |||
| e224387eb4 | |||
| 626d322639 | |||
| 7dce7a84c7 | |||
| 6234268f10 | |||
| 7a1525c568 | |||
| fe99eb7147 | |||
| d691ecbea3 | |||
| f60bcca893 | |||
| 75661a2266 | |||
| 1026f361ef | |||
| 69fe203006 | |||
| f8ccaa3a81 | |||
| c10a0a0cdf | |||
| aceb6ab666 | |||
| c3a2aff9d3 | |||
| c9e41db826 | |||
| 1e7a12fff5 | |||
| ba88e48f6a | |||
| 52979662c5 | |||
| 50e6d80065 | |||
| 16a24cfeac | |||
| bab095e5b5 | |||
| b1a3b078e9 | |||
| baae44610a | |||
| 8d2dffe5b4 | |||
| 7b3eb23af8 | |||
| fb526846fe | |||
| 6bd1371283 | |||
| 3c8c7af196 | |||
| 929b43ed48 | |||
| a947064b7d | |||
| 5bc9866f43 | |||
| 61d82e210a | |||
| a876ac9386 | |||
| da1502c463 | |||
| 76bb782f64 | |||
| e6f4b043cb | |||
| d651ffaf2e | |||
| e554378b5e | |||
| b463bd797e | |||
| 699e6e16c9 | |||
| f8477c41e1 | |||
| 9a63333aec | |||
| 6e40914258 | |||
| 56a1dd931e | |||
| 57fd2e6a52 | |||
| 113db06cdd | |||
| 4ee5c59cfd | |||
| 15812678a3 | |||
| ee0379b321 | |||
| dcf279177d | |||
| 01115824f7 | |||
| b68473ae11 | |||
| a65c203c9b | |||
| 0a848240d4 | |||
| d03821b88a | |||
| 4d32762899 | |||
| 2cb4b6d16d | |||
| b770e566dd | |||
| 51ebc88f0c | |||
| a04a75724d | |||
| 6e5af3d799 | |||
| 0283a73a32 | |||
| 5f593031f0 | |||
| 0eeef2d4bd | |||
| bb6164e30b | |||
| d3beae4871 | |||
| 8df0df32f8 | |||
| b0228dd0fd | |||
| ffca2dee1d | |||
| cba985bfea | |||
| 6a10a67205 | |||
| 880fb5bd89 | |||
| 6a0ade074d | |||
| 6cf4629d0f | |||
| ce23e5fa9e | |||
| d16fb838ca | |||
| 6b9eb8aae3 | |||
| 78008d1fea | |||
| 69fb9bb39b | |||
| 92f1557235 | |||
| 8e487ba36d | |||
| 7eb088b5ed | |||
| 9ef8ea95b0 | |||
| eedf54b485 | |||
| a461815957 | |||
| 687c2f08f6 | |||
| 2294f23e5f | |||
| bdfd81322b | |||
| 3ceb0de8d8 | |||
| c4b5208867 | |||
| 9c3f6b0c10 | |||
| 0550755c7d | |||
| 47602f267a | |||
| 656e67ef90 | |||
| c53883b635 | |||
| c4278c81f1 | |||
| c94dcf5940 | |||
| 08ca3eff12 | |||
| b40a241b2e | |||
| f86f1e83ad | |||
| be85701abe | |||
| 190eb474a6 | |||
| 8db1138081 | |||
| afbfd4d3fa | |||
| 9b8efbfdd5 | |||
| cacada87f3 | |||
| 8a36c54318 | |||
| 879d411669 | |||
| b38be2ce7b | |||
| 1810c9b61c | |||
| 98737f14f1 | |||
| b311118a04 | |||
| 8a337f1faf | |||
| dbc5bd4104 | |||
| a9c3f566da | |||
| 6d9c9cd964 | |||
| 25c8883e49 | |||
| 793943fc2f | |||
| e98f86c540 | |||
| 1c8ff48f92 | |||
| 1fb73e2211 | |||
| 5279ade0e7 | |||
| 984134fb9d | |||
| eb8f1f24b7 | |||
| e49dc8bcfa | |||
| e742da24a7 | |||
| 203b65475c | |||
| 576c5f4c9e | |||
| d2434b5591 | |||
| 634c008fcd |
@@ -6,4 +6,7 @@ lib-cov
|
||||
*.dat
|
||||
*.out
|
||||
*.pid
|
||||
benchmarks/graphs
|
||||
*.swp
|
||||
*.swo
|
||||
benchmarks/graphs
|
||||
testing.js
|
||||
|
||||
@@ -1,18 +1,30 @@
|
||||
[submodule "support/expresso"]
|
||||
path = support/expresso
|
||||
url = git://github.com/visionmedia/expresso.git
|
||||
[submodule "support/jade"]
|
||||
path = support/jade
|
||||
url = git://github.com/visionmedia/jade.git
|
||||
[submodule "support/haml"]
|
||||
path = support/haml
|
||||
url = git://github.com/visionmedia/haml.js.git
|
||||
[submodule "support/ejs"]
|
||||
path = support/ejs
|
||||
url = http://github.com/visionmedia/ejs.git
|
||||
url = git://github.com/visionmedia/ejs.git
|
||||
[submodule "support/connect-form"]
|
||||
path = support/connect-form
|
||||
url = http://github.com/visionmedia/connect-form.git
|
||||
url = git://github.com/visionmedia/connect-form.git
|
||||
[submodule "support/connect"]
|
||||
path = support/connect
|
||||
url = http://github.com/senchalabs/connect.git
|
||||
url = git://github.com/senchalabs/connect.git
|
||||
[submodule "support/should"]
|
||||
path = support/should
|
||||
url = git://github.com/visionmedia/should.js.git
|
||||
[submodule "support/formidable"]
|
||||
path = support/formidable
|
||||
url = git://github.com/felixge/node-formidable.git
|
||||
[submodule "support/jade"]
|
||||
path = support/jade
|
||||
url = git://github.com/visionmedia/jade.git
|
||||
[submodule "support/qs"]
|
||||
path = support/qs
|
||||
url = git://github.com/visionmedia/node-querystring.git
|
||||
[submodule "support/mime"]
|
||||
path = support/mime
|
||||
url = https://github.com/bentomas/node-mime.git
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
.git*
|
||||
docs/
|
||||
examples/
|
||||
support/
|
||||
test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
@@ -1,21 +0,0 @@
|
||||
---
|
||||
- !ruby/object:Pomo::Task
|
||||
complete: false
|
||||
description:
|
||||
length: 25
|
||||
name: TM bundle
|
||||
- !ruby/object:Pomo::Task
|
||||
complete: false
|
||||
description:
|
||||
length: 25
|
||||
name: blog
|
||||
- !ruby/object:Pomo::Task
|
||||
complete: false
|
||||
description:
|
||||
length: 25
|
||||
name: release on nodejs group
|
||||
- !ruby/object:Pomo::Task
|
||||
complete: false
|
||||
description:
|
||||
length: 25
|
||||
name: Beta
|
||||
@@ -1,4 +1,326 @@
|
||||
|
||||
2.3.1 / 2011-04-26
|
||||
==================
|
||||
|
||||
* Added `app.match()` as `app.match.all()`
|
||||
* Added `app.lookup()` as `app.lookup.all()`
|
||||
* Added `app.remove()` for `app.remove.all()`
|
||||
* Added `app.remove.VERB()`
|
||||
* Fixed template caching collision issue. Closes #644
|
||||
* Moved router over from connect and started refactor
|
||||
|
||||
2.3.0 / 2011-04-25
|
||||
==================
|
||||
|
||||
* Added options support to `res.clearCookie()`
|
||||
* Added `res.helpers()` as alias of `res.locals()`
|
||||
* Added; json defaults to UTF-8 with `res.send()`. Closes #632. [Daniel * Dependency `connect >= 1.4.0`
|
||||
* Changed; auto set Content-Type in res.attachement [Aaron Heckmann]
|
||||
* Renamed "cache views" to "view cache". Closes #628
|
||||
* Fixed caching of views when using several apps. Closes #637
|
||||
* Fixed gotcha invoking `app.param()` callbacks once per route middleware.
|
||||
Closes #638
|
||||
* Fixed partial lookup precedence. Closes #631
|
||||
Shaw]
|
||||
|
||||
2.2.2 / 2011-04-12
|
||||
==================
|
||||
|
||||
* Added second callback support for `res.download()` connection errors
|
||||
* Fixed `filename` option passing to template engine
|
||||
|
||||
2.2.1 / 2011-04-04
|
||||
==================
|
||||
|
||||
* Added `layout(path)` helper to change the layout within a view. Closes #610
|
||||
* Fixed `partial()` collection object support.
|
||||
Previously only anything with `.length` would work.
|
||||
When `.length` is present one must still be aware of holes,
|
||||
however now `{ collection: {foo: 'bar'}}` is valid, exposes
|
||||
`keyInCollection` and `keysInCollection`.
|
||||
|
||||
* Performance improved with better view caching
|
||||
* Removed `request` and `response` locals
|
||||
* Changed; errorHandler page title is now `Express` instead of `Connect`
|
||||
|
||||
2.2.0 / 2011-03-30
|
||||
==================
|
||||
|
||||
* Added `app.lookup.VERB()`, ex `app.lookup.put('/user/:id')`. Closes #606
|
||||
* Added `app.match.VERB()`, ex `app.match.put('/user/12')`. Closes #606
|
||||
* Added `app.VERB(path)` as alias of `app.lookup.VERB()`.
|
||||
* Dependency `connect >= 1.2.0`
|
||||
|
||||
2.1.1 / 2011-03-29
|
||||
==================
|
||||
|
||||
* Added; expose `err.view` object when failing to locate a view
|
||||
* Fixed `res.partial()` call `next(err)` when no callback is given [reported by aheckmann]
|
||||
* Fixed; `res.send(undefined)` responds with 204 [aheckmann]
|
||||
|
||||
2.1.0 / 2011-03-24
|
||||
==================
|
||||
|
||||
* Added `<root>/_?<name>` partial lookup support. Closes #447
|
||||
* Added `request`, `response`, and `app` local variables
|
||||
* Added `settings` local variable, containing the app's settings
|
||||
* Added `req.flash()` exception if `req.session` is not available
|
||||
* Added `res.send(bool)` support (json response)
|
||||
* Fixed stylus example for latest version
|
||||
* Fixed; wrap try/catch around `res.render()`
|
||||
|
||||
2.0.0 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Fixed up index view path alternative.
|
||||
* Changed; `res.locals()` without object returns the locals
|
||||
|
||||
2.0.0rc3 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Added `res.locals(obj)` to compliment `res.local(key, val)`
|
||||
* Added `res.partial()` callback support
|
||||
* Fixed recursive error reporting issue in `res.render()`
|
||||
|
||||
2.0.0rc2 / 2011-03-17
|
||||
==================
|
||||
|
||||
* Changed; `partial()` "locals" are now optional
|
||||
* Fixed `SlowBuffer` support. Closes #584 [reported by tyrda01]
|
||||
* Fixed .filename view engine option [reported by drudge]
|
||||
* Fixed blog example
|
||||
* Fixed `{req,res}.app` reference when mounting [Ben Weaver]
|
||||
|
||||
2.0.0rc / 2011-03-14
|
||||
==================
|
||||
|
||||
* Fixed; expose `HTTPSServer` constructor
|
||||
* Fixed express(1) default test charset. Closes #579 [reported by secoif]
|
||||
* Fixed; default charset to utf-8 instead of utf8 for lame IE [reported by NickP]
|
||||
|
||||
2.0.0beta3 / 2011-03-09
|
||||
==================
|
||||
|
||||
* Added support for `res.contentType()` literal
|
||||
The original `res.contentType('.json')`,
|
||||
`res.contentType('application/json')`, and `res.contentType('json')`
|
||||
will work now.
|
||||
* Added `res.render()` status option support back
|
||||
* Added charset option for `res.render()`
|
||||
* Added `.charset` support (via connect 1.0.4)
|
||||
* Added view resolution hints when in development and a lookup fails
|
||||
* Added layout lookup support relative to the page view.
|
||||
For example while rendering `./views/user/index.jade` if you create
|
||||
`./views/user/layout.jade` it will be used in favour of the root layout.
|
||||
* Fixed `res.redirect()`. RFC states absolute url [reported by unlink]
|
||||
* Fixed; default `res.send()` string charset to utf8
|
||||
* Removed `Partial` constructor (not currently used)
|
||||
|
||||
2.0.0beta2 / 2011-03-07
|
||||
==================
|
||||
|
||||
* Added res.render() `.locals` support back to aid in migration process
|
||||
* Fixed flash example
|
||||
|
||||
2.0.0beta / 2011-03-03
|
||||
==================
|
||||
|
||||
* Added HTTPS support
|
||||
* Added `res.cookie()` maxAge support
|
||||
* Added `req.header()` _Referrer_ / _Referer_ special-case, either works
|
||||
* Added mount support for `res.redirect()`, now respects the mount-point
|
||||
* Added `union()` util, taking place of `merge(clone())` combo
|
||||
* Added stylus support to express(1) generated app
|
||||
* Added secret to session middleware used in examples and generated app
|
||||
* Added `res.local(name, val)` for progressive view locals
|
||||
* Added default param support to `req.param(name, default)`
|
||||
* Added `app.disabled()` and `app.enabled()`
|
||||
* Added `app.register()` support for omitting leading ".", either works
|
||||
* Added `res.partial()`, using the same interface as `partial()` within a view. Closes #539
|
||||
* Added `app.param()` to map route params to async/sync logic
|
||||
* Added; aliased `app.helpers()` as `app.locals()`. Closes #481
|
||||
* Added extname with no leading "." support to `res.contentType()`
|
||||
* Added `cache views` setting, defaulting to enabled in "production" env
|
||||
* Added index file partial resolution, eg: partial('user') may try _views/user/index.jade_.
|
||||
* Added `req.accepts()` support for extensions
|
||||
* Changed; `res.download()` and `res.sendfile()` now utilize Connect's
|
||||
static file server `connect.static.send()`.
|
||||
* Changed; replaced `connect.utils.mime()` with npm _mime_ module
|
||||
* Changed; allow `req.query` to be pre-defined (via middleware or other parent
|
||||
* Changed view partial resolution, now relative to parent view
|
||||
* Changed view engine signature. no longer `engine.render(str, options, callback)`, now `engine.compile(str, options) -> Function`, the returned function accepts `fn(locals)`.
|
||||
* Fixed `req.param()` bug returning Array.prototype methods. Closes #552
|
||||
* Fixed; using `Stream#pipe()` instead of `sys.pump()` in `res.sendfile()`
|
||||
* Fixed; using _qs_ module instead of _querystring_
|
||||
* Fixed; strip unsafe chars from jsonp callbacks
|
||||
* Removed "stream threshold" setting
|
||||
|
||||
1.0.8 / 2011-03-01
|
||||
==================
|
||||
|
||||
* Allow `req.query` to be pre-defined (via middleware or other parent app)
|
||||
* "connect": ">= 0.5.0 < 1.0.0". Closes #547
|
||||
* Removed the long deprecated __EXPRESS_ENV__ support
|
||||
|
||||
1.0.7 / 2011-02-07
|
||||
==================
|
||||
|
||||
* Fixed `render()` setting inheritance.
|
||||
Mounted apps would not inherit "view engine"
|
||||
|
||||
1.0.6 / 2011-02-07
|
||||
==================
|
||||
|
||||
* Fixed `view engine` setting bug when period is in dirname
|
||||
|
||||
1.0.5 / 2011-02-05
|
||||
==================
|
||||
|
||||
* Added secret to generated app `session()` call
|
||||
|
||||
1.0.4 / 2011-02-05
|
||||
==================
|
||||
|
||||
* Added `qs` dependency to _package.json_
|
||||
* Fixed namespaced `require()`s for latest connect support
|
||||
|
||||
1.0.3 / 2011-01-13
|
||||
==================
|
||||
|
||||
* Remove unsafe characters from JSONP callback names [Ryan Grove]
|
||||
|
||||
1.0.2 / 2011-01-10
|
||||
==================
|
||||
|
||||
* Removed nested require, using `connect.router`
|
||||
|
||||
1.0.1 / 2010-12-29
|
||||
==================
|
||||
|
||||
* Fixed for middleware stacked via `createServer()`
|
||||
previously the `foo` middleware passed to `createServer(foo)`
|
||||
would not have access to Express methods such as `res.send()`
|
||||
or props like `req.query` etc.
|
||||
|
||||
1.0.0 / 2010-11-16
|
||||
==================
|
||||
|
||||
* Added; deduce partial object names from the last segment.
|
||||
For example by default `partial('forum/post', postObject)` will
|
||||
give you the _post_ object, providing a meaningful default.
|
||||
* Added http status code string representation to `res.redirect()` body
|
||||
* Added; `res.redirect()` supporting _text/plain_ and _text/html_ via __Accept__.
|
||||
* Added `req.is()` to aid in content negotiation
|
||||
* Added partial local inheritance [suggested by masylum]. Closes #102
|
||||
providing access to parent template locals.
|
||||
* Added _-s, --session[s]_ flag to express(1) to add session related middleware
|
||||
* Added _--template_ flag to express(1) to specify the
|
||||
template engine to use.
|
||||
* Added _--css_ flag to express(1) to specify the
|
||||
stylesheet engine to use (or just plain css by default).
|
||||
* Added `app.all()` support [thanks aheckmann]
|
||||
* Added partial direct object support.
|
||||
You may now `partial('user', user)` providing the "user" local,
|
||||
vs previously `partial('user', { object: user })`.
|
||||
* Added _route-separation_ example since many people question ways
|
||||
to do this with CommonJS modules. Also view the _blog_ example for
|
||||
an alternative.
|
||||
* Performance; caching view path derived partial object names
|
||||
* Fixed partial local inheritance precedence. [reported by Nick Poulden] Closes #454
|
||||
* Fixed jsonp support; _text/javascript_ as per mailinglist discussion
|
||||
|
||||
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
|
||||
==================
|
||||
|
||||
* Added mounted hook. Closes #369
|
||||
* Added connect dependency to _package.json_
|
||||
|
||||
* Removed "reload views" setting and support code
|
||||
development env never caches, production always caches.
|
||||
|
||||
* Removed _param_ in route callbacks, signature is now
|
||||
simply (req, res, next), previously (req, res, params, next).
|
||||
Use _req.params_ for path captures, _req.query_ for GET params.
|
||||
|
||||
* Fixed "home" setting
|
||||
* Fixed middleware/router precedence issue. Closes #366
|
||||
* Fixed; _configure()_ callbacks called immediately. Closes #368
|
||||
|
||||
1.0.0beta2 / 2010-07-23
|
||||
==================
|
||||
|
||||
* Added more examples
|
||||
* Added; exporting `Server` constructor
|
||||
* Added `Server#helpers()` for view locals
|
||||
* Added `Server#dynamicHelpers()` for dynamic view locals. Closes #349
|
||||
* Added support for absolute view paths
|
||||
* Added; _home_ setting defaults to `Server#route` for mounted apps. Closes #363
|
||||
* Added Guillermo Rauch to the contributor list
|
||||
* Added support for "as" for non-collection partials. Closes #341
|
||||
* Fixed _install.sh_, ensuring _~/.node_libraries_ exists. Closes #362 [thanks jf]
|
||||
* Fixed `res.render()` exceptions, now passed to `next()` when no callback is given [thanks guillermo]
|
||||
* Fixed instanceof `Array` checks, now `Array.isArray()`
|
||||
* Fixed express(1) expansion of public dirs. Closes #348
|
||||
* Fixed middleware precedence. Closes #345
|
||||
* Fixed view watcher, now async [thanks aheckmann]
|
||||
|
||||
1.0.0beta / 2010-07-15
|
||||
==================
|
||||
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2011 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.
|
||||
@@ -1,72 +1,39 @@
|
||||
|
||||
PREFIX = /usr/local
|
||||
LIB_PREFIX = ~/.node_libraries
|
||||
|
||||
DOCS = docs/index.md \
|
||||
docs/executable.md \
|
||||
docs/contrib.md \
|
||||
docs/guide.md \
|
||||
docs/migrate.md
|
||||
|
||||
MANPAGES =$(DOCS:.md=.1)
|
||||
DOCS = $(shell find docs/*.md)
|
||||
HTMLDOCS =$(DOCS:.md=.html)
|
||||
|
||||
install: install-docs
|
||||
@mkdir -p $(PREFIX)/bin
|
||||
cp -f bin/express $(PREFIX)/bin/express
|
||||
cp -fr lib/express $(LIB_PREFIX)/express
|
||||
|
||||
uninstall: uninstall-docs
|
||||
rm -f $(PREFIX)/bin/express
|
||||
rm -fr $(LIB_PREFIX)/express
|
||||
|
||||
install-support:
|
||||
cd support/connect && $(MAKE) install
|
||||
cd support/jade && $(MAKE) install
|
||||
|
||||
uninstall-support:
|
||||
cd support/connect && $(MAKE) uninstall
|
||||
cd support/jade && $(MAKE) uninstall
|
||||
|
||||
install-docs:
|
||||
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 \
|
||||
@NODE_ENV=test ./support/expresso/bin/expresso \
|
||||
-I lib \
|
||||
-I support \
|
||||
-I support/connect/lib \
|
||||
-I support/haml/lib \
|
||||
-I support/jade/lib \
|
||||
-I support/ejs/lib \
|
||||
$(TESTFLAGS) \
|
||||
test/*.test.js
|
||||
|
||||
test-cov:
|
||||
@TESTFLAGS=--cov $(MAKE) test
|
||||
|
||||
docs: docs/api.html $(MANPAGES) $(HTMLDOCS)
|
||||
docs: $(HTMLDOCS)
|
||||
@ echo "... generating TOC"
|
||||
@./support/toc.js docs/guide.html
|
||||
|
||||
docs/api.html: lib/express/*.js
|
||||
dox --title Express \
|
||||
--desc "High performance web framework for [node](http://nodejs.org)." \
|
||||
$(shell find lib/express/* -type f) > $@
|
||||
|
||||
%.1: %.md
|
||||
@echo "... $< -> $@"
|
||||
@ronn -r --pipe $< > $@
|
||||
|
||||
%.html: %.md
|
||||
@echo "... $< -> $@"
|
||||
@ronn -5 --pipe --fragment $< \
|
||||
@markdown $< \
|
||||
| cat docs/layout/head.html - docs/layout/foot.html \
|
||||
| sed 's/NAME/Express/g' \
|
||||
> $@
|
||||
|
||||
site:
|
||||
rm -fr /tmp/docs \
|
||||
&& cp -fr docs /tmp/docs \
|
||||
&& git checkout gh-pages \
|
||||
&& cp -fr /tmp/docs/* . \
|
||||
&& echo "done"
|
||||
|
||||
docclean:
|
||||
rm -f docs/*.{1,html}
|
||||
|
||||
.PHONY: install uninstall install-docs install-support uninstall-support install-docs uninstall-docs test test-cov docs docclean
|
||||
.PHONY: site test test-cov docs docclean
|
||||
@@ -2,43 +2,34 @@
|
||||
# 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();
|
||||
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
## Installation
|
||||
|
||||
npm:
|
||||
|
||||
$ npm install connect
|
||||
$ npm install express
|
||||
|
||||
curl (or similar):
|
||||
|
||||
$ curl -# http://github.com/visionmedia/express/raw/master/install.sh | sh
|
||||
|
||||
git clone, first update the submodules:
|
||||
|
||||
$ git submodule update --init
|
||||
$ make install
|
||||
$ make install-support
|
||||
|
||||
## Features
|
||||
|
||||
* Robust routing
|
||||
* Redirection helpers
|
||||
* Dynamic view helpers
|
||||
* Content negotiation
|
||||
* Focus on high performance
|
||||
* View rendering and partials support
|
||||
* Environment based configuration
|
||||
* Session based flash notifications
|
||||
* Built on [Connect](http://extjs.github.com/Connect)
|
||||
* Built on [Connect](http://github.com/senchalabs/connect)
|
||||
* High test coverage
|
||||
* Executable for generating applications quickly
|
||||
* Application level view options
|
||||
|
||||
Via Connect:
|
||||
|
||||
@@ -59,32 +50,33 @@ The following are the major contributors of Express (in no specific order).
|
||||
* TJ Holowaychuk ([visionmedia](http://github.com/visionmedia))
|
||||
* Ciaran Jessup ([ciaranj](http://github.com/ciaranj))
|
||||
* Aaron Heckmann ([aheckmann](http://github.com/aheckmann))
|
||||
* Guillermo Rauch ([guille](http://github.com/guille))
|
||||
|
||||
## More Information
|
||||
|
||||
* [express-expose](http://github.com/visionmedia/express-expose) expose objects, functions, modules and more to client-side js with ease
|
||||
* [express-configure](http://github.com/visionmedia/express-configuration) async configuration support
|
||||
* [express-messages](http://github.com/visionmedia/express-messages) flash notification rendering helper
|
||||
* [express-namespace](http://github.com/visionmedia/express-namespace) namespaced route support
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* [JavaScript Extensions & 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) spiritual successor of Haml
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* Screencast - [Introduction](http://bit.ly/eRYu0O)
|
||||
* Screencast - [View Partials](http://bit.ly/dU13Fx)
|
||||
* Screencast - [Route Specific Middleware](http://bit.ly/hX4IaH)
|
||||
* Screencast - [Route Path Placeholder Preconditions](http://bit.ly/eNqmVs)
|
||||
|
||||
## Running Tests
|
||||
## Node Compatibility
|
||||
|
||||
Express uses the [Expresso](http://github.com/visionmedia/expresso) TDD
|
||||
framework to write and run elegant test suites extremely fast. First `$ git submodule update --init`, then run:
|
||||
Express 1.x is compatible with node 0.2.x and connect < 1.0.
|
||||
|
||||
$ make test
|
||||
|
||||
The latest release of Express is compatible with node --version:
|
||||
Express 2.x is compatible with node 0.4.x and connect 1.x
|
||||
|
||||
v0.1.100
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2009-2011 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
|
||||
|
||||
@@ -4,48 +4,60 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var sys = require('sys'),
|
||||
fs = require('fs'),
|
||||
childProcess = require('child_process');
|
||||
var fs = require('fs')
|
||||
, exec = require('child_process').exec;
|
||||
|
||||
/**
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
var version = '1.0.0beta';
|
||||
var version = '2.3.0';
|
||||
|
||||
/**
|
||||
* Add session support.
|
||||
*/
|
||||
|
||||
var sessions = false;
|
||||
|
||||
/**
|
||||
* CSS engine to utilize.
|
||||
*/
|
||||
|
||||
var cssEngine;
|
||||
|
||||
/**
|
||||
* Template engine to utilize.
|
||||
*/
|
||||
|
||||
var templateEngine = 'jade';
|
||||
|
||||
/**
|
||||
* Usage documentation.
|
||||
*/
|
||||
|
||||
var usage = ''
|
||||
+ '\x1b[1mUsage\x1b[0m: express [options] [PATH]\n'
|
||||
+ '\n'
|
||||
+ '\x1b[1mOptions\x1b[0m:\n'
|
||||
+ ' -v, --version Output framework version\n'
|
||||
+ ' -h, --help Output help information\n';
|
||||
+ '\n'
|
||||
+ ' Usage: express [options] [path]\n'
|
||||
+ '\n'
|
||||
+ ' Options:\n'
|
||||
+ ' -s, --sessions add session support\n'
|
||||
+ ' -t, --template <engine> add template <engine> support (jade|ejs). default=jade\n'
|
||||
+ ' -c, --css <engine> add stylesheet <engine> support (less|sass|stylus). default=plain css\n'
|
||||
+ ' -v, --version output framework version\n'
|
||||
+ ' -h, --help output help information\n'
|
||||
;
|
||||
|
||||
/**
|
||||
* Jade layout template.
|
||||
*/
|
||||
|
||||
var jadeLayout = [
|
||||
'!!!',
|
||||
'html',
|
||||
' head',
|
||||
' title= title',
|
||||
' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')',
|
||||
' body!= body'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Default sass template.
|
||||
*/
|
||||
|
||||
var sass = [
|
||||
'body',
|
||||
' :padding 50px',
|
||||
' :font 14px "Lucida Grande", "Helvetica Nueue", Arial, sans-serif'
|
||||
'!!!'
|
||||
, 'html'
|
||||
, ' head'
|
||||
, ' title= title'
|
||||
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
|
||||
, ' body!= body'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
@@ -53,8 +65,115 @@ var sass = [
|
||||
*/
|
||||
|
||||
var jadeIndex = [
|
||||
'h1= title',
|
||||
'p Welcome to #{title}'
|
||||
'h1= title'
|
||||
, 'p Welcome to #{title}'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* EJS layout template.
|
||||
*/
|
||||
|
||||
var ejsLayout = [
|
||||
'<!DOCTYPE html>'
|
||||
, '<html>'
|
||||
, ' <head>'
|
||||
, ' <title><%= title %></title>'
|
||||
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
|
||||
, ' </head>'
|
||||
, ' <body>'
|
||||
, ' <%- body %>'
|
||||
, ' </body>'
|
||||
, '</html>'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* EJS index template.
|
||||
*/
|
||||
|
||||
var ejsIndex = [
|
||||
'<h1><%= title %></h1>'
|
||||
, '<p>Welcome to <%= title %></p>'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Default css template.
|
||||
*/
|
||||
|
||||
var css = [
|
||||
'body {'
|
||||
, ' padding: 50px;'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
|
||||
, '}'
|
||||
, ''
|
||||
, 'a {'
|
||||
, ' color: #00B7FF;'
|
||||
, '}'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Default less template.
|
||||
*/
|
||||
|
||||
var less = [
|
||||
'body {'
|
||||
, ' padding: 50px;'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif;'
|
||||
, '}'
|
||||
, ''
|
||||
, 'a {'
|
||||
, ' color: #00B7FF;'
|
||||
, '}'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Default sass template.
|
||||
*/
|
||||
|
||||
var sass = [
|
||||
'body'
|
||||
, ' :padding 50px'
|
||||
, ' :font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, 'a'
|
||||
, ' :color #00B7FF'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* Default stylus template.
|
||||
*/
|
||||
|
||||
var stylus = [
|
||||
'body'
|
||||
, ' padding 50px'
|
||||
, ' font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, 'a'
|
||||
, ' color #00B7FF'
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
* App test template.
|
||||
*/
|
||||
|
||||
var appTest = [
|
||||
""
|
||||
, "// Run $ expresso"
|
||||
, ""
|
||||
, "/**"
|
||||
, " * Module dependencies."
|
||||
, " */"
|
||||
, ""
|
||||
, "var app = require('../app')"
|
||||
, " , assert = require('assert');"
|
||||
, "",
|
||||
, "module.exports = {"
|
||||
, " 'GET /': function(){"
|
||||
, " assert.response(app,"
|
||||
, " { url: '/' },"
|
||||
, " { status: 200, headers: { 'Content-Type': 'text/html; charset=utf-8' }},"
|
||||
, " function(res){"
|
||||
, " assert.includes(res.body, '<title>Express</title>');"
|
||||
, " });"
|
||||
, " }"
|
||||
, "};"
|
||||
].join('\n');
|
||||
|
||||
/**
|
||||
@@ -62,88 +181,202 @@ var jadeIndex = [
|
||||
*/
|
||||
|
||||
var app = [
|
||||
'',
|
||||
'/**',
|
||||
' * Module dependencies.',
|
||||
' */',
|
||||
'',
|
||||
'var express = require(\'express\'),',
|
||||
' connect = require(\'connect\');',
|
||||
'',
|
||||
'// Create and export Express app',
|
||||
'',
|
||||
'var app = 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: [\'sass\'] }));',
|
||||
' app.use(\'/\', connect.staticProvider(__dirname + \'/public\'));',
|
||||
'});',
|
||||
'',
|
||||
'app.configure(\'development\', function(){',
|
||||
' app.set(\'reload views\', 1000);',
|
||||
' app.use(\'/\', connect.errorHandler({ dumpExceptions: true, showStack: true })); ',
|
||||
'});',
|
||||
'',
|
||||
'app.configure(\'production\', function(){',
|
||||
' app.use(\'/\', connect.errorHandler()); ',
|
||||
'});',
|
||||
'',
|
||||
'// Routes',
|
||||
'',
|
||||
'app.get(\'/\', function(req, res){',
|
||||
' res.render(\'index.jade\', {',
|
||||
' locals: {',
|
||||
' title: \'Express\'',
|
||||
' }',
|
||||
' });',
|
||||
'});',
|
||||
'',
|
||||
'app.listen(3000);',
|
||||
''
|
||||
, '/**'
|
||||
, ' * Module dependencies.'
|
||||
, ' */'
|
||||
, ''
|
||||
, 'var express = require(\'express\');'
|
||||
, ''
|
||||
, 'var app = module.exports = express.createServer();'
|
||||
, ''
|
||||
, '// Configuration'
|
||||
, ''
|
||||
, 'app.configure(function(){'
|
||||
, ' app.set(\'views\', __dirname + \'/views\');'
|
||||
, ' app.set(\'view engine\', \':TEMPLATE\');'
|
||||
, ' app.use(express.bodyParser());'
|
||||
, ' app.use(express.methodOverride());{sess}{css}'
|
||||
, ' app.use(app.router);'
|
||||
, ' app.use(express.static(__dirname + \'/public\'));'
|
||||
, '});'
|
||||
, ''
|
||||
, 'app.configure(\'development\', function(){'
|
||||
, ' app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); '
|
||||
, '});'
|
||||
, ''
|
||||
, 'app.configure(\'production\', function(){'
|
||||
, ' app.use(express.errorHandler()); '
|
||||
, '});'
|
||||
, ''
|
||||
, '// Routes'
|
||||
, ''
|
||||
, 'app.get(\'/\', function(req, res){'
|
||||
, ' res.render(\'index\', {'
|
||||
, ' title: \'Express\''
|
||||
, ' });'
|
||||
, '});'
|
||||
, ''
|
||||
, '// 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');
|
||||
|
||||
// Parse arguments
|
||||
|
||||
var args = process.argv.slice(2),
|
||||
path = '.';
|
||||
var args = process.argv.slice(2)
|
||||
, path = '.';
|
||||
|
||||
while (args.length) {
|
||||
var arg = args.shift();
|
||||
switch (arg) {
|
||||
case '-h':
|
||||
case '--help':
|
||||
abort(usage);
|
||||
break;
|
||||
case '-v':
|
||||
case '--version':
|
||||
abort(version);
|
||||
break;
|
||||
default:
|
||||
path = arg;
|
||||
}
|
||||
var arg = args.shift();
|
||||
switch (arg) {
|
||||
case '-h':
|
||||
case '--help':
|
||||
abort(usage);
|
||||
break;
|
||||
case '-v':
|
||||
case '--version':
|
||||
abort(version);
|
||||
break;
|
||||
case '-s':
|
||||
case '--session':
|
||||
case '--sessions':
|
||||
sessions = true;
|
||||
break;
|
||||
case '-c':
|
||||
case '--css':
|
||||
args.length
|
||||
? (cssEngine = args.shift())
|
||||
: abort('--css requires an argument');
|
||||
break;
|
||||
case '-t':
|
||||
case '--template':
|
||||
args.length
|
||||
? (templateEngine = args.shift())
|
||||
: abort('--template requires an argument');
|
||||
break;
|
||||
default:
|
||||
path = arg;
|
||||
}
|
||||
}
|
||||
|
||||
// Generate application
|
||||
|
||||
(function createApplication(path) {
|
||||
mkdir(path, function(){
|
||||
mkdir(path + '/pids');
|
||||
mkdir(path + '/logs');
|
||||
mkdir(path + '/public/{javascripts,stylesheets,images}', function(){
|
||||
write(path + '/public/stylesheets/style.sass', sass);
|
||||
});
|
||||
mkdir(path + '/views/partials', function(){
|
||||
write(path + '/views/layout.jade', jadeLayout);
|
||||
write(path + '/views/index.jade', jadeIndex);
|
||||
});
|
||||
write(path + '/app.js', app);
|
||||
});
|
||||
emptyDirectory(path, function(empty){
|
||||
if (empty) {
|
||||
createApplicationAt(path);
|
||||
} else {
|
||||
confirm('destination is not empty, continue? ', function(ok){
|
||||
if (ok) {
|
||||
process.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');
|
||||
mkdir(path + '/public/javascripts');
|
||||
mkdir(path + '/public/images');
|
||||
mkdir(path + '/public/stylesheets', function(){
|
||||
switch (cssEngine) {
|
||||
case 'stylus':
|
||||
write(path + '/public/stylesheets/style.styl', stylus);
|
||||
break;
|
||||
case 'less':
|
||||
write(path + '/public/stylesheets/style.less', less);
|
||||
break;
|
||||
case 'sass':
|
||||
write(path + '/public/stylesheets/style.sass', sass);
|
||||
break;
|
||||
default:
|
||||
write(path + '/public/stylesheets/style.css', css);
|
||||
}
|
||||
});
|
||||
mkdir(path + '/views', function(){
|
||||
switch (templateEngine) {
|
||||
case 'ejs':
|
||||
write(path + '/views/layout.ejs', ejsLayout);
|
||||
write(path + '/views/index.ejs', ejsIndex);
|
||||
break;
|
||||
case 'jade':
|
||||
write(path + '/views/layout.jade', jadeLayout);
|
||||
write(path + '/views/index.jade', jadeIndex);
|
||||
break;
|
||||
}
|
||||
});
|
||||
mkdir(path + '/test', function(){
|
||||
write(path + '/test/app.test.js', appTest);
|
||||
});
|
||||
|
||||
// CSS Engine support
|
||||
switch (cssEngine) {
|
||||
case 'sass':
|
||||
case 'less':
|
||||
app = app.replace('{css}', '\n app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'' + cssEngine + '\'] }));');
|
||||
break;
|
||||
case 'stylus':
|
||||
app = app.replace('{css}', '\n app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
|
||||
break;
|
||||
default:
|
||||
app = app.replace('{css}', '');
|
||||
}
|
||||
|
||||
// Session support
|
||||
app = app.replace('{sess}', sessions
|
||||
? '\n app.use(express.cookieParser());\n app.use(express.session({ secret: \'your secret here\' }));'
|
||||
: '');
|
||||
|
||||
// Template support
|
||||
app = app.replace(':TEMPLATE', templateEngine);
|
||||
|
||||
write(path + '/app.js', app);
|
||||
|
||||
// Suggestions
|
||||
process.on('exit', function(){
|
||||
if (cssEngine) {
|
||||
console.log(' - make sure you have installed %s: \x1b[33m$ npm install %s\x1b[0m'
|
||||
, cssEngine
|
||||
, cssEngine);
|
||||
}
|
||||
console.log(' - make sure you have installed %s: \x1b[33m$ npm install %s\x1b[0m'
|
||||
, templateEngine
|
||||
, templateEngine);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 && 'ENOENT' != err.code) throw err;
|
||||
fn(!files || !files.length);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* echo str > path.
|
||||
*
|
||||
@@ -152,8 +385,43 @@ while (args.length) {
|
||||
*/
|
||||
|
||||
function write(path, str) {
|
||||
fs.writeFile(path, str);
|
||||
sys.puts(' create : ' + path);
|
||||
fs.writeFile(path, str);
|
||||
console.log(' \x1b[36mcreate\x1b[0m : ' + 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) {
|
||||
// prompt
|
||||
if (' ' == msg[msg.length - 1]) {
|
||||
process.stdout.write(msg);
|
||||
} else {
|
||||
console.log(msg);
|
||||
}
|
||||
|
||||
// stdin
|
||||
process.stdin.setEncoding('ascii');
|
||||
process.stdin.once('data', function(data){
|
||||
fn(data);
|
||||
}).resume();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -164,11 +432,11 @@ function write(path, str) {
|
||||
*/
|
||||
|
||||
function mkdir(path, fn) {
|
||||
childProcess.exec('mkdir -p ' + path, function(err){
|
||||
if (err) throw err;
|
||||
sys.puts(' create : ' + path);
|
||||
fn && fn();
|
||||
});
|
||||
exec('mkdir -p ' + path, function(err){
|
||||
if (err) throw err;
|
||||
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
|
||||
fn && fn();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -178,6 +446,6 @@ function mkdir(path, fn) {
|
||||
*/
|
||||
|
||||
function abort(str) {
|
||||
sys.error(str);
|
||||
process.exit(1);
|
||||
}
|
||||
console.error(str);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,245 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
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;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
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; }
|
||||
img {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 80px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
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 {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-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="wrapper">
|
||||
<div id="container">
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<br />
|
||||
|
||||
|
||||
<p><a href="http://learnboost.com">Learnboost</a> is a free online gradebook application, aimed to crush the competition with innovative, realtime, enjoyable features.</p>
|
||||
|
||||
<p><a href="http://learnboost.com"><img src="images/apps/learnboost.png" alt="LearnBoost" /></a></p>
|
||||
|
||||
<p><a href="http://storify.com">Storify</a> lets you turn what people post on social media websites into compelling stories.</p>
|
||||
|
||||
<p><a href="http://storify.com"><img src="images/apps/storify.png" alt="Storify" /></a></p>
|
||||
|
||||
<p><a href="http://pakistansurvey.org/">Pakistan Survey</a> by <a href="http://developmentseed.org">Development Seed</a>, provides in-depth agency-specific analysis from regional experts with data from 1,000 interviews across 120 villages in all seven tribal agencies and mapping of 142 reported drone strikes in FATA through July 2010.</p>
|
||||
|
||||
<p><a href="http://pakistansurvey.org"><img src="images/apps/developmentseed.png" alt="Pakistan Survey" /></a></p>
|
||||
|
||||
<p><a href="http://markup.io">Markup.IO</a> allows you to draw directly on <em>any</em> website, then share with others to share your thoughts.</p>
|
||||
|
||||
<p><a href="http://markup.io"><img src="images/apps/markupio.png" alt="Markup.IO" /></a></p>
|
||||
|
||||
<p><a href="http://scrabb.ly">Scrabb.ly</a> is a massively multiplayer scrabble game initially created for the <a href="http://nodeknockout.com/">Node Knockout</a> competition.</p>
|
||||
|
||||
<p><a href="http://scrabb.ly"><img src="images/apps/scrabbly.png" alt="Online Realtime Scrabble" /></a></p>
|
||||
|
||||
<p><a href="http://clickdummy.net/">ClickDummy</a> is a rapid mockup prototyping application for designers and dummies.</p>
|
||||
|
||||
<p><a href="http://clickdummy.net"><img src="images/apps/clickdummy.png" alt="Mockup Prototying" /></a></p>
|
||||
|
||||
<p><a href="http://nodeknockout.com">Node Knockout</a> organized the first ever node-specific competition with hundreds of contestants.</p>
|
||||
|
||||
<p><a href="http://nodeknockout.com"><img src="images/apps/nodeko.png" alt="Node Knockout Competition Express" /></a></p>
|
||||
|
||||
<p><a href="http://widescript.com">Widescript</a> is an innovative app that helps you focus and interact with your texts – on your desktop, your couch or on the go.</p>
|
||||
|
||||
<p><a href="http://widescript.com"><img src="images/apps/widescript.png" alt="Widescript" /></a></p>
|
||||
|
||||
<p><a href="http://www.e-resistible.co.uk/">e-resistable</a> is an online order takeaway system providing an intuitive way to fill your belly from your computer!</p>
|
||||
|
||||
<p><a href="http://www.e-resistible.co.uk"><img src="images/apps/e-resistable.png" alt="Online Takeaway" /></a></p>
|
||||
|
||||
<p><a href="http://toptwittertrends.com">Top Twitter Trends</a> utilizes MongoDB, Socket.IO, jQuery and many other exciting libraries to bring you trending tweets in realtime.</p>
|
||||
|
||||
<p><a href="http://toptwittertrends.com"><img src="images/apps/toptwittertrends.png" alt="Twitter Trends" /></a></p>
|
||||
|
||||
<br />
|
||||
|
||||
|
||||
<p>The applications shown above are not listed in any specific order. To have an application added or removed please contact <a href="http://github.com/visionmedia">TJ Holowaychuk</a>.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
<br />
|
||||
|
||||
[Learnboost](http://learnboost.com) is a free online gradebook application, aimed to crush the competition with innovative, realtime, enjoyable features.
|
||||
|
||||
[](http://learnboost.com)
|
||||
|
||||
[Storify](http://storify.com) lets you turn what people post on social media websites into compelling stories.
|
||||
|
||||
[](http://storify.com)
|
||||
|
||||
|
||||
[Pakistan Survey](http://pakistansurvey.org/) by [Development Seed](http://developmentseed.org), provides in-depth agency-specific analysis from regional experts with data from 1,000 interviews across 120 villages in all seven tribal agencies and mapping of 142 reported drone strikes in FATA through July 2010.
|
||||
|
||||
[](http://pakistansurvey.org)
|
||||
|
||||
[Markup.IO](http://markup.io) allows you to draw directly on _any_ website, then share with others to share your thoughts.
|
||||
|
||||
[](http://markup.io)
|
||||
|
||||
[Scrabb.ly](http://scrabb.ly) is a massively multiplayer scrabble game initially created for the [Node Knockout](http://nodeknockout.com/) competition.
|
||||
|
||||
[](http://scrabb.ly)
|
||||
|
||||
[ClickDummy](http://clickdummy.net/) is a rapid mockup prototyping application for designers and dummies.
|
||||
|
||||
[](http://clickdummy.net)
|
||||
|
||||
[Node Knockout](http://nodeknockout.com) organized the first ever node-specific competition with hundreds of contestants.
|
||||
|
||||
[](http://nodeknockout.com)
|
||||
|
||||
[Widescript](http://widescript.com) is an innovative app that helps you focus and interact with your texts - on your desktop, your couch or on the go.
|
||||
|
||||
[](http://widescript.com)
|
||||
|
||||
[e-resistable](http://www.e-resistible.co.uk/) is an online order takeaway system providing an intuitive way to fill your belly from your computer!
|
||||
|
||||
[](http://www.e-resistible.co.uk)
|
||||
|
||||
[Top Twitter Trends](http://toptwittertrends.com) utilizes MongoDB, Socket.IO, jQuery and many other exciting libraries to bring you trending tweets in realtime.
|
||||
|
||||
[](http://toptwittertrends.com)
|
||||
|
||||
<br />
|
||||
|
||||
The applications shown above are not listed in any specific order. To have an application added or removed please contact [TJ Holowaychuk](http://github.com/visionmedia).
|
||||
@@ -1,64 +0,0 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "CONTRIB" "" "July 2010" "" ""
|
||||
.
|
||||
.SS "Development Dependencies"
|
||||
Express development dependencies are stored within the \fI\./support\fR directory\. To update them execute:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ git submodule update \-\-init
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Running Tests"
|
||||
Express uses the Expresso \fIhttp://github\.com/visionmedia/expresso\fR TDD framework to write and run elegant test suites extremely fast\. To run all test suites simply execute:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To target specific suites we may specify the files via:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ make test TESTS=test/view\.test\.js
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Contributions"
|
||||
To accept a contribution, you should follow these guidelines:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
All tests \fImust\fR pass
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Your alterations or additions \fImust\fR include tests
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Your commit(s) should be \fIfocused\fR, do not commit once for several changes
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Do \fInot\fR alter release information such as the \fIversion\fR, or \fIHistory\.md\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Indents are \fI4\fR spaces\.
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></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;
|
||||
@@ -28,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; }
|
||||
|
||||
@@ -52,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;
|
||||
@@ -80,9 +71,19 @@
|
||||
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; }
|
||||
img {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
@@ -110,28 +111,29 @@
|
||||
|
||||
#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 {
|
||||
padding: 1px 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
margin-left: 75px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -158,12 +160,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>
|
||||
@@ -174,11 +187,10 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h3 id="Development-Dependencies">Development Dependencies</h3>
|
||||
<h3>Development Dependencies</h3>
|
||||
|
||||
<p>Express development dependencies are stored within the <em>./support</em> directory. To
|
||||
update them execute:</p>
|
||||
@@ -186,7 +198,7 @@ update them execute:</p>
|
||||
<pre><code>$ git submodule update --init
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Running-Tests">Running Tests</h3>
|
||||
<h3>Running Tests</h3>
|
||||
|
||||
<p>Express uses the <a href="http://github.com/visionmedia/expresso">Expresso</a> TDD
|
||||
framework to write and run elegant test suites extremely fast. To run all test suites
|
||||
@@ -200,7 +212,12 @@ simply execute:</p>
|
||||
<pre><code>$ make test TESTS=test/view.test.js
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Contributions">Contributions</h3>
|
||||
<p>To check test coverage run:</p>
|
||||
|
||||
<pre><code>$ make test-cov
|
||||
</code></pre>
|
||||
|
||||
<h3>Contributions</h3>
|
||||
|
||||
<p>To accept a contribution, you should follow these guidelines:</p>
|
||||
|
||||
@@ -209,12 +226,15 @@ simply execute:</p>
|
||||
<li>Your alterations or additions <em>must</em> include tests</li>
|
||||
<li>Your commit(s) should be <em>focused</em>, do not commit once for several changes</li>
|
||||
<li>Do <em>not</em> alter release information such as the <em>version</em>, or <em>History.md</em></li>
|
||||
<li>Indents are <em>4</em> spaces.</li>
|
||||
<li>Indents are <em>2</em> spaces.</li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
<h3>Documentation</h3>
|
||||
|
||||
<p>To contribute documentation edit the markdown files in <em>./docs</em>, however
|
||||
do <em>not</em> run <em>make docs</em>, as they will be re-built and published with each release.</p>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -18,6 +18,10 @@ To target specific suites we may specify the files via:
|
||||
|
||||
$ make test TESTS=test/view.test.js
|
||||
|
||||
To check test coverage run:
|
||||
|
||||
$ make test-cov
|
||||
|
||||
### Contributions
|
||||
|
||||
To accept a contribution, you should follow these guidelines:
|
||||
@@ -26,4 +30,9 @@ To accept a contribution, you should follow these guidelines:
|
||||
* Your alterations or additions _must_ include tests
|
||||
* Your commit(s) should be _focused_, do not commit once for several changes
|
||||
* Do _not_ alter release information such as the _version_, or _History.md_
|
||||
* Indents are _4_ spaces.
|
||||
* Indents are _2_ spaces.
|
||||
|
||||
### Documentation
|
||||
|
||||
To contribute documentation edit the markdown files in _./docs_, however
|
||||
do _not_ run _make docs_, as they will be re-built and published with each release.
|
||||
@@ -1,25 +0,0 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "EXECUTABLE" "" "July 2010" "" ""
|
||||
.
|
||||
.SH "Synopsis"
|
||||
.
|
||||
.nf
|
||||
|
||||
express [\-h|\-\-help] [\-v|\-\-version] [PATH]
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.SH "Description"
|
||||
The \fBexpress\fR executable generates apps at the given \fBPATH\fR or the current working directory\. Although Express is not bound to a specific application structure, this executable creates a maintainable base app\.
|
||||
.
|
||||
.SH "Options"
|
||||
.
|
||||
.nf
|
||||
|
||||
\-v, \-\-version Output framework version
|
||||
\-h, \-\-help Display help information
|
||||
.
|
||||
.fi
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></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;
|
||||
@@ -28,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; }
|
||||
|
||||
@@ -52,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;
|
||||
@@ -80,9 +71,19 @@
|
||||
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; }
|
||||
img {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
@@ -110,28 +111,29 @@
|
||||
|
||||
#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 {
|
||||
padding: 1px 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
margin-left: 75px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -158,12 +160,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>
|
||||
@@ -174,29 +187,29 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h2 id="Synopsis">Synopsis</h2>
|
||||
<h2>Synopsis</h2>
|
||||
|
||||
<pre><code>express [-h|--help] [-v|--version] [PATH]
|
||||
<pre><code>express [options] [PATH]
|
||||
</code></pre>
|
||||
|
||||
<h2 id="Description">Description</h2>
|
||||
<h2>Description</h2>
|
||||
|
||||
<p>The <code>express</code> executable generates apps at the given <strong>PATH</strong> or the
|
||||
<p>The <em>express</em> executable generates apps at the given <strong>PATH</strong> or the
|
||||
current working directory. Although Express is not bound to a specific
|
||||
application structure, this executable creates a maintainable base app.</p>
|
||||
|
||||
<h2 id="Options">Options</h2>
|
||||
<h2>Options</h2>
|
||||
|
||||
<pre><code>-v, --version Output framework version
|
||||
-h, --help Display help information
|
||||
<pre><code> -s, --sessions Add session support
|
||||
-t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade
|
||||
-c, --css ENGINE Add stylesheet ENGINE support (less|sass|stylus). Defaults to plain css
|
||||
-v, --version Output framework version
|
||||
-h, --help Output help information
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,16 +1,21 @@
|
||||
|
||||
## Synopsis
|
||||
|
||||
express [-h|--help] [-v|--version] [PATH]
|
||||
express [options] [PATH]
|
||||
|
||||
## Description
|
||||
|
||||
The `express` executable generates apps at the given **PATH** or the
|
||||
The _express_ executable generates apps at the given **PATH** or the
|
||||
current working directory. Although Express is not bound to a specific
|
||||
application structure, this executable creates a maintainable base app.
|
||||
|
||||
## Options
|
||||
|
||||
-v, --version Output framework version
|
||||
-h, --help Display help information
|
||||
-s, --sessions Add session support
|
||||
-t, --template ENGINE Add template ENGINE support (jade|ejs). Defaults to jade
|
||||
-c, --css ENGINE Add stylesheet ENGINE support (less|sass|stylus). Defaults to plain css
|
||||
-v, --version Output framework version
|
||||
-h, --help Output help information
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,860 +0,0 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "GUIDE" "" "July 2010" "" ""
|
||||
.
|
||||
.SS "Installation"
|
||||
npm:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ npm install connect
|
||||
$ npm install express
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
curl (or similar):
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ curl \-# http://github\.com/visionmedia/express/raw/master/install\.sh | sh
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
git clone, first update the submodules:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ git submodule update \-\-init
|
||||
$ make install
|
||||
$ make install\-support
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Creating An Application"
|
||||
The \fIexpress\.Server\fR now inherits from \fIhttp\.Server\fR, however follows the same idiom by providing \fIexpress\.createServer()\fR as shown below\. This means that you can utilize Express server\'s transparently with other libraries\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = require(\'express\')\.createServer();
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'hello world\');
|
||||
});
|
||||
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Configuration"
|
||||
Express supports arbitrary environments, such as \fIproduction\fR and \fIdevelopment\fR\. Developers can use the \fIconfigure()\fR method to setup needs required by the current environment\. When \fIconfigure()\fR is called without an environment name it will be run in \fIevery\fR environment prior to the environment specific callback\.
|
||||
.
|
||||
.P
|
||||
In the example below we only \fIdumpExceptions\fR, and respond with exception stack traces in \fIdevelopment\fR mode, however for both environments we utilize \fImethodOverride\fR and \fIbodyDecoder\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.configure(function(){
|
||||
app\.use(\'/\', connect\.methodOverride());
|
||||
app\.use(\'/\', connect\.bodyDecoder());
|
||||
});
|
||||
|
||||
app\.configure(\'development\', function(){
|
||||
app\.use(\'/\', connect\.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
app\.configure(\'production\', function(){
|
||||
app\.use(\'/\', connect\.errorHandler());
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
For internal and arbitrary settings Express provides the \fIset(key[, val])\fR, \fIenable(key)\fR, \fIdisable(key)\fR methods:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.configure(function(){
|
||||
app\.set(\'views\', __dirname + \'/views\');
|
||||
app\.set(\'views\');
|
||||
// => \"\.\.\. views directory \.\.\.\"
|
||||
|
||||
app\.enable(\'some feature\');
|
||||
// same as app\.set(\'some feature\', true);
|
||||
|
||||
app\.disable(\'some feature\');
|
||||
// same as app\.set(\'some feature\', false);
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To alter the environment we can set the \fICONNECT_ENV\fR environment variable, or more specifically \fIEXPRESS_ENV\fR, for example:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ EXPRESS_ENV=production node app\.js
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Settings"
|
||||
Express supports the following settings out of the box:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fIenv\fR Application environment set internally, use \fIapp\.set(\'env\')\fR to utilize
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fIhome\fR Application base path used with \fIres\.redirect()\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\fIviews\fR Root views directory defaulting to \fBCWD/views\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
\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 5 minute interval\. Example: \fIapp\.set(\'reload views\', 60000);\fR
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Routing"
|
||||
Express utilizes the HTTP verbs to provide a meaningful, expressive routing API\. For example we may want to render a user\'s account for the path \fI/user/12\fR, this can be done by defining the route below\. The values associated to the named placeholders, are passed as the \fIthird\fR argument, which here we name \fIparams\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id\', function(req, res, params){
|
||||
res\.send(\'user \' + params\.id);
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
A route is simple a string which is compiled to a \fIRegExp\fR internally\. For example when \fI/user/:id\fR is compiled, a simplified version of the regexp may look similar to:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
\\/user\\/([^\\/]+)\\/?
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Literal regular expressions may also be passed for complex uses:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(/^\\/foo(bar)?$/, function(){});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Below are some route examples, and the associated paths that they may consume:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
\"/user/:id\"
|
||||
/user/12
|
||||
|
||||
\"/users/:id?\"
|
||||
/users/5
|
||||
/users
|
||||
|
||||
\"/files/*\"
|
||||
/files/jquery\.js
|
||||
/files/javascripts/jquery\.js
|
||||
|
||||
\"/file/*\.*\"
|
||||
/files/jquery\.js
|
||||
/files/javascripts/jquery\.js
|
||||
|
||||
\"/user/:id/:operation?\"
|
||||
/user/1
|
||||
/user/1/edit
|
||||
|
||||
\"/products\.:format\"
|
||||
/products\.json
|
||||
/products\.xml
|
||||
|
||||
\"/products\.:format?\"
|
||||
/products\.json
|
||||
/products\.xml
|
||||
/products
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Passing Route Control"
|
||||
We may pass control to the next \fImatching\fR route, by calling the \fIfourth\fR parameter, the \fInext()\fR function\. When a match cannot be made, control is passed back to Connect\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/users/:id?\', function(req, res, params){
|
||||
if (params\.id) {
|
||||
// do something
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
app\.get(\'/users\', function(req, res, params){
|
||||
// do something else
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.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:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var connect = require(\'connect\'),
|
||||
express = require(\'express\');
|
||||
|
||||
var app = express\.createServer(
|
||||
connect\.logger(),
|
||||
connect\.bodyDecoder()
|
||||
);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Alternatively we can \fIuse()\fR them which is useful when adding middleware within \fIconfigure()\fR blocks:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.use(\'/\', connect\.logger({ format: \':method :uri\' }));
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Error Handling"
|
||||
Express provides the \fIapp\.error()\fR method which receives exceptions thrown within a route, or passed to \fInext(err)\fR\. Below is an example which serves different pages based on our ad\-hoc \fINotFound\fR exception:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
function NotFound(msg){
|
||||
this\.name = \'NotFound\';
|
||||
Error\.call(this, msg);
|
||||
Error\.captureStackTrace(this, arguments\.callee);
|
||||
}
|
||||
|
||||
sys\.inherits(NotFound, Error);
|
||||
|
||||
app\.get(\'/404\', function(req, res){
|
||||
throw new NotFound;
|
||||
});
|
||||
|
||||
app\.get(\'/500\', function(req, res){
|
||||
throw new Error(\'keyboard cat!\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.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\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res\.render(\'404\.jade\');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Here we assume all errors as 500 for the simplicity of this demo, however you can choose whatever you like
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.error(function(err, req, res){
|
||||
res\.render(\'500\.jade\', {
|
||||
locals: {
|
||||
error: err
|
||||
}
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.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:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.use(\'/\', connect\.errorHandler({ dumpExceptions: true }));
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Also during development we may want fancy html pages to show exceptions that are passed or thrown, so we can set \fIshowStack\fR to true:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.use(\'/\', connect\.errorHandler({ showStack: true, dumpExceptions: true }));
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
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\.
|
||||
.
|
||||
.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\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.render(\'index\.haml\', {
|
||||
locals: { title: \'My Site\' }
|
||||
});
|
||||
});
|
||||
.
|
||||
.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:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\.haml\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To make things even less verbose we can assume the extension as \fI\.haml\fR when omitted, however if we wished we could use an ejs partial, within a haml view for example\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
And once again even further, when rendering a collection we can simply pass an array, if no other options are desired:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comments\', comments);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Template Engines"
|
||||
Below are a few template engines commonly used with Express:
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Jade \fIhttp://github\.com/visionmedia/jade\fR haml\.js successor
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Haml \fIhttp://github\.com/visionmedia/haml\.js\fR indented templates
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
EJS \fIhttp://github\.com/visionmedia/ejs\fR Embedded JavaScript
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.header(key[, defaultValue])"
|
||||
Get the case\-insensitive request header \fIkey\fR, with optional \fIdefaultValue\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
req\.header(\'Host\');
|
||||
req\.header(\'host\');
|
||||
req\.header(\'Accept\', \'*/*\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.accepts(type)"
|
||||
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\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
// Accept: text/html
|
||||
req\.accepts(\'html\');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req\.accepts(\'html\');
|
||||
req\.accepts(\'text/html\');
|
||||
req\.accepts(\'text/plain\');
|
||||
req\.accepts(\'application/json\');
|
||||
// => true
|
||||
|
||||
req\.accepts(\'image/png\');
|
||||
req\.accepts(\'png\');
|
||||
// => false
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "req\.param(name)"
|
||||
Return the value of param \fIname\fR when present\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Checks route placeholders, ex: /user/:id
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Checks query string params, ex: ?id=12
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Checks urlencoded body params, 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\.
|
||||
.
|
||||
.SS "req\.flash(type[, msg])"
|
||||
Queue flash \fImsg\fR of the given \fItype\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
req\.flash(\'info\', \'email sent\');
|
||||
req\.flash(\'error\', \'email delivery failed\');
|
||||
req\.flash(\'info\', \'email re\-sent\');
|
||||
// => 2
|
||||
|
||||
req\.flash(\'info\');
|
||||
// => [\'email sent\', \'email re\-sent\']
|
||||
|
||||
req\.flash(\'info\');
|
||||
// => []
|
||||
|
||||
req\.flash();
|
||||
// => { error: [\'email delivery failed\'], info: [] }
|
||||
.
|
||||
.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:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
req\.xhr
|
||||
req\.isXMLHttpRequest
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.header(key[, val])"
|
||||
Get or set the response header \fIkey\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.header(\'Content\-Length\');
|
||||
// => undefined
|
||||
|
||||
res\.header(\'Content\-Length\', 123);
|
||||
// => 123
|
||||
|
||||
res\.header(\'Content\-Length\');
|
||||
// => 123
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.contentType(type)"
|
||||
Sets the \fIContent\-Type\fR response header to the given \fItype\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var filename = \'path/to/image\.png\';
|
||||
res\.contentType(filename);
|
||||
// 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\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.attachment(\'path/to/my/image\.png\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.sendfile(path)"
|
||||
Used by \fBres\.download()\fR to transfer an arbitrary file\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.sendfile(\'path/to/my\.file\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
\fBNOTE\fR: this is \fInot\fR a replacement for Connect\'s \fIstaticProvider\fR middleware, nor does it perform any security checks, use with caution when using in a dynamic manor\.
|
||||
.
|
||||
.SS "res\.download(file[, filename])"
|
||||
Transfer the given \fIfile\fR as an attachment with optional alternative \fIfilename\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.download(\'path/to/image\.png\');
|
||||
res\.download(\'path/to/image\.png\', \'foo\.png\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
This is equivalent to:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.attachment(file);
|
||||
res\.sendfile(file);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "res\.send(body|status[, headers|status[, status]])"
|
||||
The \fBres\.send()\fR 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:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.send(new Buffer(\'wahoo\'));
|
||||
res\.send({ some: \'json\' });
|
||||
res\.send(\'<p>some html</p>\');
|
||||
res\.send(\'Sorry, cant find that\', 404);
|
||||
res\.send(\'text\', { \'Content\-Type\': \'text/plain\' }, 201);
|
||||
res\.send(404);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
By default the \fIContent\-Type\fR response header is set, however if explicitly assigned through \fBres\.send()\fR or previously with \fBres\.header()\fR or \fBres\.contentType()\fR it will not be set again\.
|
||||
.
|
||||
.SS "res\.redirect(url[, status])"
|
||||
Redirect to the given \fIurl\fR with a default response \fIstatus\fR of 302\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.redirect(\'/\', 301);
|
||||
res\.redirect(\'/account\');
|
||||
res\.redirect(\'http://google\.com\');
|
||||
res\.redirect(\'home\');
|
||||
res\.redirect(\'back\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.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 \"/\"\.
|
||||
.
|
||||
.SS "app\.set(name[, val])"
|
||||
Apply an application level setting \fIname\fR to \fIval\fR, or get the value of \fIname\fR when \fIval\fR is not present:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.set(\'reload views\', 200);
|
||||
app\.set(\'reload views\');
|
||||
// => 200
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.enable(name)"
|
||||
Enable the given setting \fIname\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.enable(\'some arbitrary setting\');
|
||||
app\.set(\'some arbitrary setting\');
|
||||
// => true
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.disable(name)"
|
||||
Disable the given setting \fIname\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.disable(\'some setting\');
|
||||
app\.set(\'some setting\');
|
||||
// => false
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.configure(env|function[, function])"
|
||||
Define a callback function for the given \fIenv\fR (or all environments) with callback \fIfunction\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.configure(function(){
|
||||
// executed for each env
|
||||
});
|
||||
|
||||
app\.configure(\'development\', function(){
|
||||
// executed for \'development\' only
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.redirect(name, val)"
|
||||
For use with \fBres\.redirect()\fR we can map redirects at the application level as shown below:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.redirect(\'google\', \'http://google\.com\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now in a route we may call:
|
||||
.
|
||||
.P
|
||||
res\.redirect(\'google\');
|
||||
.
|
||||
.P
|
||||
We may also map dynamic redirects:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.redirect(\'comments\', function(req, res, params){
|
||||
return \'/post/\' + params\.id + \'/comments\';
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
So now we may do the following, and the redirect will dynamically adjust to the context of the request\. If we called this route with \fIGET /post/12\fR our redirect \fILocation\fR would be \fI/post/12/comments\fR\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/post/:id\', function(req, res){
|
||||
res\.redirect(\'comments\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "app\.error(function)"
|
||||
Adds an error handler \fIfunction\fR which will receive the exception as the first parameter as shown below\. Note that we may set several error handlers by making several calls to this method, however the handler should call \fInext(err)\fR if it does not wish to deal with the exception:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.error(function(err, req, res, next){
|
||||
res\.send(err\.message, 500);
|
||||
});
|
||||
.
|
||||
.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\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.listen();
|
||||
app\.listen(3000);
|
||||
app\.listen(3000, \'n\.n\.n\.n\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
The \fIport\fR argument may also be a string representing the path to a unix domain socket:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.listen(\'/tmp/express\.sock\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Then try it out:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ telnet /tmp/express\.sock
|
||||
GET / HTTP/1\.1
|
||||
|
||||
HTTP/1\.1 200 OK
|
||||
Content\-Type: text/plain
|
||||
Content\-Length: 11
|
||||
|
||||
Hello World
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
|
Depois Largura: | Altura: | Tamanho: 108 KiB |
|
Depois Largura: | Altura: | Tamanho: 159 KiB |
|
Depois Largura: | Altura: | Tamanho: 145 KiB |
|
Depois Largura: | Altura: | Tamanho: 145 KiB |
|
Depois Largura: | Altura: | Tamanho: 154 KiB |
|
Depois Largura: | Altura: | Tamanho: 181 KiB |
|
Depois Largura: | Altura: | Tamanho: 200 KiB |
|
Depois Largura: | Altura: | Tamanho: 236 KiB |
|
Depois Largura: | Altura: | Tamanho: 112 KiB |
|
Depois Largura: | Altura: | Tamanho: 88 KiB |
|
Depois Largura: | Altura: | Tamanho: 127 KiB |
@@ -1,85 +0,0 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "INDEX" "" "July 2010" "" ""
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var app = express\.createServer();
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'Hello World\');
|
||||
});
|
||||
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Features"
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Robust routing
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Redirection helpers
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Focus on high performance
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View rendering and partials support
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Environment based configuration
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Session based flash notifications
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Built on Connect \fIhttp://extjs\.github\.com/Connect\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Executable \fIexecutable\.html\fR for generating applications quickly
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "Contributors"
|
||||
The following are the major contributors of Express (in no specific order)\.
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
TJ Holowaychuk (visionmedia \fIhttp://github\.com/visionmedia\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Ciaran Jessup (ciaranj \fIhttp://github\.com/ciaranj\fR)
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Aaron Heckmann (aheckmann \fIhttp://github\.com/aheckmann\fR)
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SH "More Information"
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Google Group \fIhttp://groups\.google\.com/group/express\-js\fR for discussion
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Follow tjholowaychuk \fIhttp://twitter\.com/tjholowaychuk\fR on twitter for updates
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
Annotated source documentation \fIapi\.html\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the source \fIhttp://github\.com/visionmedia/express\fR
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the Connect \fIhttp://github\.com/extjs/Connect\fR repo for middleware usage
|
||||
.
|
||||
.IP "\(bu" 4
|
||||
View the Connect Wiki \fIhttp://wiki\.github\.com/extjs/Connect/\fR for contrib middleware
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></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;
|
||||
@@ -28,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; }
|
||||
|
||||
@@ -52,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;
|
||||
@@ -80,9 +71,19 @@
|
||||
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; }
|
||||
img {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
@@ -110,28 +111,29 @@
|
||||
|
||||
#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 {
|
||||
padding: 1px 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
margin-left: 75px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -158,12 +160,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>
|
||||
@@ -174,10 +187,9 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<pre><code>var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
@@ -187,21 +199,26 @@ app.get('/', function(req, res){
|
||||
app.listen(3000);
|
||||
</code></pre>
|
||||
|
||||
<h2 id="Features">Features</h2>
|
||||
<h2>Features</h2>
|
||||
|
||||
<ul>
|
||||
<li>Robust routing</li>
|
||||
<li>Redirection helpers</li>
|
||||
<li>Dynamic view helpers</li>
|
||||
<li>Application level view options</li>
|
||||
<li>Content negotiation</li>
|
||||
<li>Application mounting</li>
|
||||
<li>Focus on high performance</li>
|
||||
<li>View rendering and partials support</li>
|
||||
<li>Environment based configuration</li>
|
||||
<li>Session based flash notifications</li>
|
||||
<li>Built on <a href="http://extjs.github.com/Connect">Connect</a></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>
|
||||
|
||||
|
||||
<h2 id="Contributors">Contributors</h2>
|
||||
<h2>Contributors</h2>
|
||||
|
||||
<p>The following are the major contributors of Express (in no specific order).</p>
|
||||
|
||||
@@ -209,23 +226,35 @@ app.listen(3000);
|
||||
<li>TJ Holowaychuk (<a href="http://github.com/visionmedia">visionmedia</a>)</li>
|
||||
<li>Ciaran Jessup (<a href="http://github.com/ciaranj">ciaranj</a>)</li>
|
||||
<li>Aaron Heckmann (<a href="http://github.com/aheckmann">aheckmann</a>)</li>
|
||||
<li>Guillermo Rauch (<a href="http://github.com/guille">guille</a>)</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2 id="More-Information">More Information</h2>
|
||||
<h2>Third-Party Modules</h2>
|
||||
|
||||
<p>The following modules compliment or extend Express directly:</p>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://github.com/visionmedia/express-resource">express-resource</a> provides resourceful routing</li>
|
||||
<li><a href="http://github.com/visionmedia/express-messages">express-messages</a> flash message notification rendering</li>
|
||||
<li><a href="http://github.com/visionmedia/express-configuration">express-configure</a> async configuration support (load settings from redis etc)</li>
|
||||
<li><a href="http://github.com/visionmedia/express-namespace">express-namespace</a> namespaced routing support</li>
|
||||
</ul>
|
||||
|
||||
|
||||
<h2>More Information</h2>
|
||||
|
||||
<ul>
|
||||
<li><a href="http://groups.google.com/group/express-js">Google Group</a> for discussion</li>
|
||||
<li>Follow <a href="http://twitter.com/tjholowaychuk">tjholowaychuk</a> on twitter for updates</li>
|
||||
<li>Annotated source <a href="api.html">documentation</a></li>
|
||||
<li>View the <a href="http://senchalabs.github.com/connect">Connect</a> documentation</li>
|
||||
<li>View the <a href="http://wiki.github.com/senchalabs/connect/">Connect Wiki</a> for contrib middleware</li>
|
||||
<li>View the <a href="http://github.com/visionmedia/express/tree/master/examples/">examples</a></li>
|
||||
<li>View the <a href="http://github.com/visionmedia/express">source</a></li>
|
||||
<li>View the <a href="http://github.com/extjs/Connect">Connect</a> repo for middleware usage</li>
|
||||
<li>View the <a href="http://wiki.github.com/extjs/Connect/">Connect Wiki</a> for contrib middleware</li>
|
||||
<li>View the <a href="contrib.html">contrib guide</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -11,12 +11,17 @@
|
||||
|
||||
* Robust routing
|
||||
* Redirection helpers
|
||||
* Dynamic view helpers
|
||||
* Application level view options
|
||||
* Content negotiation
|
||||
* Application mounting
|
||||
* Focus on high performance
|
||||
* View rendering and partials support
|
||||
* Environment based configuration
|
||||
* Session based flash notifications
|
||||
* Built on [Connect](http://extjs.github.com/Connect)
|
||||
* Built on [Connect](http://github.com/senchalabs/connect)
|
||||
* [Executable](executable.html) for generating applications quickly
|
||||
* High test coverage
|
||||
|
||||
## Contributors
|
||||
|
||||
@@ -25,12 +30,23 @@ The following are the major contributors of Express (in no specific order).
|
||||
* TJ Holowaychuk ([visionmedia](http://github.com/visionmedia))
|
||||
* Ciaran Jessup ([ciaranj](http://github.com/ciaranj))
|
||||
* Aaron Heckmann ([aheckmann](http://github.com/aheckmann))
|
||||
* Guillermo Rauch ([guille](http://github.com/guille))
|
||||
|
||||
## Third-Party Modules
|
||||
|
||||
The following modules compliment or extend Express directly:
|
||||
|
||||
* [express-resource](http://github.com/visionmedia/express-resource) provides resourceful routing
|
||||
* [express-messages](http://github.com/visionmedia/express-messages) flash message notification rendering
|
||||
* [express-configure](http://github.com/visionmedia/express-configuration) async configuration support (load settings from redis etc)
|
||||
* [express-namespace](http://github.com/visionmedia/express-namespace) namespaced routing support
|
||||
|
||||
## More Information
|
||||
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* Annotated source [documentation](api.html)
|
||||
* View the [Connect](http://senchalabs.github.com/connect) documentation
|
||||
* View the [Connect Wiki](http://wiki.github.com/senchalabs/connect/) for contrib middleware
|
||||
* View the [examples](http://github.com/visionmedia/express/tree/master/examples/)
|
||||
* View the [source](http://github.com/visionmedia/express)
|
||||
* View the [Connect](http://github.com/extjs/Connect) repo for middleware usage
|
||||
* View the [Connect Wiki](http://wiki.github.com/extjs/Connect/) for contrib middleware
|
||||
* View the [contrib guide](contrib.html)
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></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;
|
||||
@@ -28,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; }
|
||||
|
||||
@@ -52,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;
|
||||
@@ -80,9 +71,19 @@
|
||||
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; }
|
||||
img {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
@@ -110,28 +111,29 @@
|
||||
|
||||
#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 {
|
||||
padding: 1px 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
margin-left: 75px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -158,12 +160,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>
|
||||
@@ -174,6 +187,6 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
|
||||
@@ -1,347 +0,0 @@
|
||||
.\" generated with Ronn/v0.6.6
|
||||
.\" http://github.com/rtomayko/ronn/
|
||||
.
|
||||
.TH "MIGRATE" "" "July 2010" "" ""
|
||||
.
|
||||
.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\.
|
||||
.
|
||||
.SS "Creating Applications"
|
||||
Previously due to legacy code implemented in the early days of node, Express unfortunately had some globals\. The DSL would previously be accessed as shown below:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
require(\'express\');
|
||||
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
get(\'/\', function(){
|
||||
return \'hello world\';
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now we utilize the CommonJS module system appropriately, and introduce \fIexpress\.createServer()\fR which accepts the same arguments as \fIhttp\.createServer()\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var express = require(\'express\'),
|
||||
app = express\.createServer();
|
||||
|
||||
app\.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.send(\'hello world\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Express 1\.x does \fInot\fR currently allow returning of a string\.
|
||||
.
|
||||
.SS "Plugins vs Middleware"
|
||||
Previously Express was bundled with plugins, which were essentially what are now Connect middleware\. Previously plugins would be utilized in a manor similar to below:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Which we can now \fIuse()\fR within our app, or pass to the \fIexpress\.createServer()\fR method:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var connect = require(\'connect\');
|
||||
|
||||
var app = express\.createServer(
|
||||
connect\.logger(),
|
||||
connect\.methodOverride(),
|
||||
connect\.cookie()
|
||||
);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
or:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
var connect = require(\'connect\');
|
||||
var app = express\.createServer();
|
||||
|
||||
app\.use(\'/\', connect\.logger());
|
||||
app\.use(\'/\', connect\.methodOverride());
|
||||
app\.use(\'/\', connect\.cookieDecoder());
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
For documentation on creating Connect middleware visit Middleware Authoring \fIhttp://extjs\.github\.com/Connect/#Middleware\-Authoring\fR\.
|
||||
.
|
||||
.SS "Running Applications"
|
||||
Previously a global function \fIrun()\fR, was available:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
run();
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
The new \fIexpress\.Server\fR has the same API as \fIhttp\.Server\fR, so we can do things like:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.listen();
|
||||
app\.listen(3000);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Route Parameters"
|
||||
Previously we could use \fIthis\.param()\fR to attempt fetching a route, query string, or request body parameter:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/user/:id\', function(){
|
||||
this\.param(\'id\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Polymorphic parameter access can be done using \fBreq\.param()\fR:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id\', function(){
|
||||
req\.param(\'id\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Route parameters are also passed as the third argument:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id\', function(req, res, params){
|
||||
params\.id;
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Passing Route Control"
|
||||
Old express had a weak notion of route passing, which did not support async, and was never properly implemented for practical use:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/\', function(){
|
||||
this\.pass(\'/foobar\');
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Now Express has access to Connect\'s \fInext()\fR function, which is passed as the fourth and final argument\. Calling \fInext()\fR will pass control to the next \fImatching route\fR, or continue down the stack of Connect middleware\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/user/:id?\', function(req, res, params, next){
|
||||
next();
|
||||
});
|
||||
|
||||
app\.get(\'/user\', function(){
|
||||
// \.\.\. respond
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "View Rendering"
|
||||
View filenames no longer take the form \fINAME\fR\.\fITYPE\fR\.\fIENGINE\fR, the \fIContent\-Type\fR can be set via \fIres\.contentType()\fR or \fIres\.header()\fR\. For example what was previously \fIlayout\.html\.haml\fR, should now be \fIlayout\.haml\fR\.
|
||||
.
|
||||
.P
|
||||
Previously a view render looked something like this:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
get(\'/\', function(){
|
||||
this\.render(\'index\.html\.haml\', {
|
||||
locals: { title: \'My Site\' }
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
We now have \fIres\.render()\fR, however the options passed to haml \fIhttp://github\.com/visionmedia/haml\.js\fR, jade \fIhttp://github\.com/visionmedia/jade\fR, and others remain the same\.
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
app\.get(\'/\', function(req, res){
|
||||
res\.render(\'index\.haml\', {
|
||||
locals: { title: \'My Site\' }
|
||||
});
|
||||
});
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Previously rendering of a collection via \fIpartial()\fR would look something like this:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
this\.partial(\'comment\.html\.haml\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
Although this worked just fine, it was generally to verbose, the similar but new API looks like this, as \fIpartial()\fR is \fIalways\fR passed as a local variable:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\.haml\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
To make things even less verbose we can assume the extension when omitted:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comment\', { collection: comments });
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
And once again even further, when rendering a collection we can simply pass an array, if no other options are desired:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
partial(\'comments\', comments);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "Redirecting"
|
||||
Previously you would
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
this\.redirect(\'/somewhere\');
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.P
|
||||
However you would now:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
res\.redirect(\'/somewhere\');
|
||||
res\.redirect(\'/somewhere\', 301);
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
.
|
||||
.SS "HTTP Client"
|
||||
Previously Express provided a high level http client, this library is no more as it does not belong in Express, however it may be resurrected as a separate module\.
|
||||
.
|
||||
.SS "Core Extensions"
|
||||
Express is no longer dependent on the JavaScript Extensions \fIhttp://github\.com/visionmedia/ext\.js\fR library, so those of you using the methods provided by it such as \fBObject\.merge(a, b)\fR will need to roll your own, or install the module via:
|
||||
.
|
||||
.IP "" 4
|
||||
.
|
||||
.nf
|
||||
|
||||
$ npm install ext
|
||||
.
|
||||
.fi
|
||||
.
|
||||
.IP "" 0
|
||||
|
||||
@@ -1,20 +1,8 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></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;
|
||||
@@ -28,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; }
|
||||
|
||||
@@ -52,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;
|
||||
@@ -80,9 +71,19 @@
|
||||
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; }
|
||||
img {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
@@ -110,28 +111,29 @@
|
||||
|
||||
#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 {
|
||||
padding: 1px 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 65px;
|
||||
margin-left: 75px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
@@ -158,12 +160,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>
|
||||
@@ -174,225 +187,207 @@
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="contrib.html">Contributing</a></li>
|
||||
<li><a href="migrate.html">1.x Migration</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<div class='mp'>
|
||||
<h3 id="Built-On-Connect">Built On Connect</h3>
|
||||
<h3>Express 1.x to 2.x Migration</h3>
|
||||
|
||||
<p>Express 1.x is written to run on-top of the <a href="http://extjs.github.com/Connect">Connect</a> middlware
|
||||
framework, thus the <em>Plugin</em> 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.</p>
|
||||
<h3>HTTPS</h3>
|
||||
|
||||
<h3 id="Creating-Applications">Creating Applications</h3>
|
||||
<p> Creating an HTTPS server is simply, simply pass the TLS options to <em>express.createServer()</em>:</p>
|
||||
|
||||
<p>Previously due to legacy code implemented in the early days of node,
|
||||
Express unfortunately had some globals. The DSL would previously be
|
||||
accessed as shown below:</p>
|
||||
<pre><code> var app = express.createServer({
|
||||
key: ...
|
||||
, cert: ...
|
||||
});
|
||||
|
||||
<pre><code>require('express');
|
||||
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
get('/', function(){
|
||||
return 'hello world';
|
||||
});
|
||||
app.listen(443);
|
||||
</code></pre>
|
||||
|
||||
<p>Now we utilize the CommonJS module system appropriately, and
|
||||
introduce <em>express.createServer()</em> which accepts the same arguments
|
||||
as <em>http.createServer()</em>:</p>
|
||||
<h3>req.header() Referrer</h3>
|
||||
|
||||
<pre><code>var express = require('express'),
|
||||
app = express.createServer();
|
||||
<p> Previously if anyone was doing something similar to:</p>
|
||||
|
||||
app.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
<pre><code> req.headers.referrer || req.headers.referer
|
||||
req.header('Referrer') || req.header('Referer')
|
||||
</code></pre>
|
||||
|
||||
<p>Express 1.x does <em>not</em> currently allow returning of a string.</p>
|
||||
<p> With the new special-case we may now simply use <em>Referrer</em> which will return either if defined:</p>
|
||||
|
||||
<h3 id="Plugins-vs-Middleware">Plugins vs Middleware</h3>
|
||||
|
||||
<p>Previously Express was bundled with plugins, which were essentially what
|
||||
are now Connect middleware. Previously plugins would be utilized in a manor
|
||||
similar to below:</p>
|
||||
|
||||
<pre><code>use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
<pre><code> req.header('Referrer')
|
||||
</code></pre>
|
||||
|
||||
<p>Which we can now <em>use()</em> within our app, or pass to the <em>express.createServer()</em> method:</p>
|
||||
<h3>res.local(name, val)</h3>
|
||||
|
||||
<pre><code>var connect = require('connect');
|
||||
<p> Previously all local variables had to be passed to <em>res.render()</em>, or either <em>app.helpers()</em> or <em>app.dynamicHelpers()</em>, now we may do this at the request-level progressively. The <em>res.local()</em> method accepts a <em>name</em> and <em>val</em>, however the locals passed to <em>res.render()</em> will take precedence.</p>
|
||||
|
||||
var app = express.createServer(
|
||||
connect.logger(),
|
||||
connect.methodOverride(),
|
||||
connect.cookie()
|
||||
);
|
||||
<p> For example we may utilize this feature to create locals in middleware:</p>
|
||||
|
||||
<pre><code> function loadUser(req, res, next) {
|
||||
User.get(req.params.id, function(err, user){
|
||||
res.local('user', user);
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
app.get('/user/:id', loadUser, function(req, res){
|
||||
res.render('user');
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<p>or:</p>
|
||||
<h3>req.param(name[, defaultValue])</h3>
|
||||
|
||||
<pre><code>var connect = require('connect');
|
||||
var app = express.createServer();
|
||||
<p> Previously only <em>name</em> was accepted, so some of you may have been doing the following:</p>
|
||||
|
||||
app.use('/', connect.logger());
|
||||
app.use('/', connect.methodOverride());
|
||||
app.use('/', connect.cookieDecoder());
|
||||
<pre><code> var id = req.param('id') || req.user.id;
|
||||
</code></pre>
|
||||
|
||||
<p>For documentation on creating Connect middleware visit <a href="http://extjs.github.com/Connect/#Middleware-Authoring">Middleware Authoring</a>.</p>
|
||||
<p> The new <em>defaultValue</em> argument can handle this nicely:</p>
|
||||
|
||||
<h3 id="Running-Applications">Running Applications</h3>
|
||||
|
||||
<p>Previously a global function <em>run()</em>, was available:</p>
|
||||
|
||||
<pre><code>run();
|
||||
<pre><code> var id = req.param('id', req.user.id);
|
||||
</code></pre>
|
||||
|
||||
<p>The new <em>express.Server</em> has the same API as <em>http.Server</em>,
|
||||
so we can do things like:</p>
|
||||
<h3>app.helpers() / app.locals()</h3>
|
||||
|
||||
<pre><code>app.listen();
|
||||
app.listen(3000);
|
||||
<p> <em>app.locals()</em> is now an alias of <em>app.helpers()</em>, as helpers makes more sense for functions.</p>
|
||||
|
||||
<h3>req.accepts(type)</h3>
|
||||
|
||||
<p> <em>req.accepts()</em> now accepts extensions:</p>
|
||||
|
||||
<pre><code> // Accept: text/html
|
||||
req.accepts('html');
|
||||
req.accepts('.html');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req.accepts('html');
|
||||
req.accepts('text/*');
|
||||
req.accepts('text/plain');
|
||||
req.accepts('application/json');
|
||||
// => true
|
||||
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => false
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Route-Parameters">Route Parameters</h3>
|
||||
<h3>res.cookie()</h3>
|
||||
|
||||
<p>Previously we could use <em>this.param()</em> to attempt
|
||||
fetching a route, query string, or request body parameter:</p>
|
||||
<p> Previously only directly values could be passed, so for example:</p>
|
||||
|
||||
<pre><code>get('/user/:id', function(){
|
||||
this.param('id');
|
||||
});
|
||||
<pre><code>res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000) });
|
||||
</code></pre>
|
||||
|
||||
<p>Polymorphic parameter access can be done using <code>req.param()</code>:</p>
|
||||
<p>However now we have the alternative <em>maxAge</em> property which may be used to set <em>expires</em> relative to <em>Date.now()</em> in milliseconds, so our example above can now become:</p>
|
||||
|
||||
<pre><code>app.get('/user/:id', function(){
|
||||
req.param('id');
|
||||
});
|
||||
<pre><code>res.cookie('rememberme', 'yes', { maxAge: 900000 });
|
||||
</code></pre>
|
||||
|
||||
<p>Route parameters are also passed as the third argument:</p>
|
||||
<h3>res.download() / res.sendfile()</h3>
|
||||
|
||||
<pre><code>app.get('/user/:id', function(req, res, params){
|
||||
params.id;
|
||||
});
|
||||
<p> Both of these methods now utilize Connect’s static file server behind the scenes (actually the previous Express code was ported to Connect 1.0). With this change comes a change to the callback as well. Previously the <em>path</em> and <em>stream</em> were passed, however now only an <em>error</em> is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete. The callback remains optional:</p>
|
||||
|
||||
<pre><code> res.download('/path/to/file');
|
||||
|
||||
res.download('/path/to/file', function(err){
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('transferred');
|
||||
}
|
||||
});
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Passing-Route-Control">Passing Route Control</h3>
|
||||
<p> The <em>stream threshold</em> setting was removed.</p>
|
||||
|
||||
<p>Old express had a weak notion of route passing,
|
||||
which did not support async, and was never properly
|
||||
implemented for practical use:</p>
|
||||
<h3>res.render()</h3>
|
||||
|
||||
<pre><code>get('/', function(){
|
||||
this.pass('/foobar');
|
||||
});
|
||||
<p> Previously locals were passed as a separate key:</p>
|
||||
|
||||
<pre><code> res.render('user', { layout: false, locals: { user: user }});
|
||||
</code></pre>
|
||||
|
||||
<p>Now Express has access to Connect's <em>next()</em> function,
|
||||
which is passed as the fourth and final argument. Calling <em>next()</em> will
|
||||
pass control to the next <em>matching route</em>, or continue down the stack
|
||||
of Connect middleware.</p>
|
||||
<p> In Express 2.0 both the locals and the options are one in the same, meaning you cannot have a local variable named <em>layout</em> as it is reserved for express, however this cleans up the API:</p>
|
||||
|
||||
<pre><code>app.get('/user/:id?', function(req, res, params, next){
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/user', function(){
|
||||
// ... respond
|
||||
});
|
||||
<pre><code> res.render('user', { layout: false, user: user });
|
||||
</code></pre>
|
||||
|
||||
<h3 id="View-Rendering">View Rendering</h3>
|
||||
<h3>res.partial()</h3>
|
||||
|
||||
<p>View filenames no longer take the form <em>Express</em>.<em>TYPE</em>.<em>ENGINE</em>,
|
||||
the <em>Content-Type</em> can be set via <em>res.contentType()</em> or
|
||||
<em>res.header()</em>. For example what was previously <em>layout.html.haml</em>,
|
||||
should now be <em>layout.haml</em>.</p>
|
||||
<p> Express 2.0 adds the <em>res.partial()</em> method, helpful for rendering partial fragments over WebSockets or Ajax requests etc. The API is identical to the <em>partial()</em> calls within views.</p>
|
||||
|
||||
<p>Previously a view render looked something like this:</p>
|
||||
<pre><code> // render a collection of comments
|
||||
res.partial('comment', [comment1, comment2]);
|
||||
|
||||
<pre><code>get('/', function(){
|
||||
this.render('index.html.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
// render a single comment
|
||||
res.partial('comment', comment);
|
||||
</code></pre>
|
||||
|
||||
<p>We now have <em>res.render()</em>, however the options passed to <a href="http://github.com/visionmedia/haml.js">haml</a>, <a href="http://github.com/visionmedia/jade">jade</a>, and others
|
||||
remain the same.</p>
|
||||
<h3>partial() locals</h3>
|
||||
|
||||
<pre><code>app.get('/', function(req, res){
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
<p> Both <em>res.partial()</em> and the <em>partial()</em> functions accept an single object consisting of both the options and the locals. Previously with Express 1.x you may pass <em>user</em> to a partial, along with <em>date</em> like so:</p>
|
||||
|
||||
<pre><code> partial('user', { object: user, locals: { date: new Date }})
|
||||
</code></pre>
|
||||
|
||||
<p>Previously rendering of a collection via <em>partial()</em> would look something like this:</p>
|
||||
<p>or perhaps if you preferred not to use the inferred name <em>user</em> you may used a local for this as well:</p>
|
||||
|
||||
<pre><code>this.partial('comment.html.haml', { collection: comments });
|
||||
<pre><code> partial('user', { locals: { user: user, date: new Date }})
|
||||
</code></pre>
|
||||
|
||||
<p>Although this worked just fine, it was generally to verbose, the similar but new API
|
||||
looks like this, as <em>partial()</em> is <em>always</em> passed as a local variable:</p>
|
||||
<p> With recent changes to Express 2.x the object passed is now both, so the following is valid for the <em>object</em> option and locals:</p>
|
||||
|
||||
<pre><code>partial('comment.haml', { collection: comments });
|
||||
<pre><code> partial('user', { object: user, date: new Date })
|
||||
</code></pre>
|
||||
|
||||
<p>To make things even less verbose we can assume the extension when omitted:</p>
|
||||
<p> Or the following which is equivalent, however the local var name is explicitly set to <em>user</em> instead of deduced from the filename.</p>
|
||||
|
||||
<pre><code>partial('comment', { collection: comments });
|
||||
<pre><code> partial('user', { user: user, date: new Date })
|
||||
</code></pre>
|
||||
|
||||
<p>And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:</p>
|
||||
<p> When a “basic” object aka <em>{}</em> or <em>new Object</em> is passed, it is considered options, otherwise it is considered the <em>object</em>. The following are equivalent:</p>
|
||||
|
||||
<pre><code>partial('comments', comments);
|
||||
<pre><code> partial('user', user);
|
||||
partial('user', { object: user });
|
||||
</code></pre>
|
||||
|
||||
<h3 id="Redirecting">Redirecting</h3>
|
||||
<h3>Template Engine Compliance</h3>
|
||||
|
||||
<p>Previously you would</p>
|
||||
<p> To comply with Express previously engines needed the following signature:</p>
|
||||
|
||||
<pre><code>this.redirect('/somewhere');
|
||||
<pre><code> engine.render(str, options, function(err){});
|
||||
</code></pre>
|
||||
|
||||
<p>However you would now:</p>
|
||||
<p> Now they must export a <em>compile()</em> function, returning a function which when called with local variables will render the template. This allows Express to cache the compiled function in memory during production.</p>
|
||||
|
||||
<pre><code>res.redirect('/somewhere');
|
||||
res.redirect('/somewhere', 301);
|
||||
<pre><code> var fn = engine.compile(str, options);
|
||||
fn(locals);
|
||||
</code></pre>
|
||||
|
||||
<h3 id="HTTP-Client">HTTP Client</h3>
|
||||
<h3>View Partial Lookup</h3>
|
||||
|
||||
<p>Previously Express provided a high level http client, this library is no more
|
||||
as it does not belong in Express, however it may be resurrected as a separate module.</p>
|
||||
<p> Previously partials were loaded relative to the now removed <em>view partials</em> directory setting, or by default <em>views/partials</em>, now they are relative to the view calling them, read more on <a href="guide.html#View-Lookup">view lookup</a>.</p>
|
||||
|
||||
<h3 id="Core-Extensions">Core Extensions</h3>
|
||||
<h3>Mime Types</h3>
|
||||
|
||||
<p>Express is no longer dependent on the <a href="http://github.com/visionmedia/ext.js">JavaScript Extensions</a> library, so those of you using the methods provided by it such as <code>Object.merge(a, b)</code> will need to
|
||||
roll your own, or install the module via:</p>
|
||||
<p> Express and Connect now utilize the <em>mime</em> module in npm, so to add more use:</p>
|
||||
|
||||
<pre><code>$ npm install ext
|
||||
<pre><code> require('mime').define({ 'foo/bar': ['foo', 'bar'] });
|
||||
</code></pre>
|
||||
|
||||
</div>
|
||||
<h3>static() middleware</h3>
|
||||
|
||||
<p> Previously named <code>staticProvider()</code>, the now <code>static()</code> middleware takes a single directory path, followed by options.</p>
|
||||
|
||||
<pre><code> app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||
</code></pre>
|
||||
|
||||
<p>Previously when using options the <code>root</code> option would be used for this:</p>
|
||||
|
||||
<pre><code> app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
@@ -1,192 +1,177 @@
|
||||
|
||||
### Built On Connect
|
||||
### Express 1.x to 2.x Migration
|
||||
|
||||
Express 1.x is written to run on-top of the [Connect](http://extjs.github.com/Connect) middlware
|
||||
framework, thus the _Plugin_ 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.
|
||||
### HTTPS
|
||||
|
||||
### Creating Applications
|
||||
Creating an HTTPS server is simply, simply pass the TLS options to _express.createServer()_:
|
||||
|
||||
var app = express.createServer({
|
||||
key: ...
|
||||
, cert: ...
|
||||
});
|
||||
|
||||
Previously due to legacy code implemented in the early days of node,
|
||||
Express unfortunately had some globals. The DSL would previously be
|
||||
accessed as shown below:
|
||||
app.listen(443);
|
||||
|
||||
require('express');
|
||||
### req.header() Referrer
|
||||
|
||||
configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
Previously if anyone was doing something similar to:
|
||||
|
||||
req.headers.referrer || req.headers.referer
|
||||
req.header('Referrer') || req.header('Referer')
|
||||
|
||||
get('/', function(){
|
||||
return 'hello world';
|
||||
});
|
||||
With the new special-case we may now simply use _Referrer_ which will return either if defined:
|
||||
|
||||
req.header('Referrer')
|
||||
|
||||
Now we utilize the CommonJS module system appropriately, and
|
||||
introduce _express.createServer()_ which accepts the same arguments
|
||||
as _http.createServer()_:
|
||||
### res.local(name, val)
|
||||
|
||||
var express = require('express'),
|
||||
app = express.createServer();
|
||||
|
||||
app.configure(function(){
|
||||
// app configuration
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('hello world');
|
||||
});
|
||||
Previously all local variables had to be passed to _res.render()_, or either _app.helpers()_ or _app.dynamicHelpers()_, now we may do this at the request-level progressively. The _res.local()_ method accepts a _name_ and _val_, however the locals passed to _res.render()_ will take precedence.
|
||||
|
||||
Express 1.x does _not_ currently allow returning of a string.
|
||||
For example we may utilize this feature to create locals in middleware:
|
||||
|
||||
### Plugins vs Middleware
|
||||
function loadUser(req, res, next) {
|
||||
User.get(req.params.id, function(err, user){
|
||||
res.local('user', user);
|
||||
next();
|
||||
});
|
||||
}
|
||||
|
||||
Previously Express was bundled with plugins, which were essentially what
|
||||
are now Connect middleware. Previously plugins would be utilized in a manor
|
||||
similar to below:
|
||||
app.get('/user/:id', loadUser, function(req, res){
|
||||
res.render('user');
|
||||
});
|
||||
|
||||
use(Logger);
|
||||
use(MethodOverride);
|
||||
use(Cookie);
|
||||
### req.param(name[, defaultValue])
|
||||
|
||||
Which we can now _use()_ within our app, or pass to the _express.createServer()_ method:
|
||||
Previously only _name_ was accepted, so some of you may have been doing the following:
|
||||
|
||||
var id = req.param('id') || req.user.id;
|
||||
|
||||
var connect = require('connect');
|
||||
The new _defaultValue_ argument can handle this nicely:
|
||||
|
||||
var id = req.param('id', req.user.id);
|
||||
|
||||
var app = express.createServer(
|
||||
connect.logger(),
|
||||
connect.methodOverride(),
|
||||
connect.cookie()
|
||||
);
|
||||
### app.helpers() / app.locals()
|
||||
|
||||
or:
|
||||
_app.locals()_ is now an alias of _app.helpers()_, as helpers makes more sense for functions.
|
||||
|
||||
var connect = require('connect');
|
||||
var app = express.createServer();
|
||||
### req.accepts(type)
|
||||
|
||||
app.use('/', connect.logger());
|
||||
app.use('/', connect.methodOverride());
|
||||
app.use('/', connect.cookieDecoder());
|
||||
_req.accepts()_ now accepts extensions:
|
||||
|
||||
|
||||
// Accept: text/html
|
||||
req.accepts('html');
|
||||
req.accepts('.html');
|
||||
// => true
|
||||
|
||||
// Accept: text/*; application/json
|
||||
req.accepts('html');
|
||||
req.accepts('text/*');
|
||||
req.accepts('text/plain');
|
||||
req.accepts('application/json');
|
||||
// => true
|
||||
|
||||
req.accepts('image/png');
|
||||
req.accepts('png');
|
||||
// => false
|
||||
|
||||
For documentation on creating Connect middleware visit [Middleware Authoring](http://extjs.github.com/Connect/#Middleware-Authoring).
|
||||
### res.cookie()
|
||||
|
||||
### Running Applications
|
||||
Previously only directly values could be passed, so for example:
|
||||
|
||||
Previously a global function _run()_, was available:
|
||||
res.cookie('rememberme', 'yes', { expires: new Date(Date.now() + 900000) });
|
||||
|
||||
run();
|
||||
However now we have the alternative _maxAge_ property which may be used to set _expires_ relative to _Date.now()_ in milliseconds, so our example above can now become:
|
||||
|
||||
The new _express.Server_ has the same API as _http.Server_,
|
||||
so we can do things like:
|
||||
res.cookie('rememberme', 'yes', { maxAge: 900000 });
|
||||
|
||||
app.listen();
|
||||
app.listen(3000);
|
||||
### res.download() / res.sendfile()
|
||||
|
||||
### Route Parameters
|
||||
Both of these methods now utilize Connect's static file server behind the scenes (actually the previous Express code was ported to Connect 1.0). With this change comes a change to the callback as well. Previously the _path_ and _stream_ were passed, however now only an _error_ is passed, when no error has occurred the callback will be invoked indicating that the file transfer is complete. The callback remains optional:
|
||||
|
||||
res.download('/path/to/file');
|
||||
|
||||
Previously we could use _this.param()_ to attempt
|
||||
fetching a route, query string, or request body parameter:
|
||||
res.download('/path/to/file', function(err){
|
||||
if (err) {
|
||||
console.error(err);
|
||||
} else {
|
||||
console.log('transferred');
|
||||
}
|
||||
});
|
||||
|
||||
get('/user/:id', function(){
|
||||
this.param('id');
|
||||
});
|
||||
The _stream threshold_ setting was removed.
|
||||
|
||||
Polymorphic parameter access can be done using `req.param()`:
|
||||
### res.render()
|
||||
|
||||
app.get('/user/:id', function(){
|
||||
req.param('id');
|
||||
});
|
||||
Previously locals were passed as a separate key:
|
||||
|
||||
res.render('user', { layout: false, locals: { user: user }});
|
||||
|
||||
Route parameters are also passed as the third argument:
|
||||
In Express 2.0 both the locals and the options are one in the same, meaning you cannot have a local variable named _layout_ as it is reserved for express, however this cleans up the API:
|
||||
|
||||
res.render('user', { layout: false, user: user });
|
||||
|
||||
app.get('/user/:id', function(req, res, params){
|
||||
params.id;
|
||||
});
|
||||
### res.partial()
|
||||
|
||||
### Passing Route Control
|
||||
Express 2.0 adds the _res.partial()_ method, helpful for rendering partial fragments over WebSockets or Ajax requests etc. The API is identical to the _partial()_ calls within views.
|
||||
|
||||
// render a collection of comments
|
||||
res.partial('comment', [comment1, comment2]);
|
||||
|
||||
Old express had a weak notion of route passing,
|
||||
which did not support async, and was never properly
|
||||
implemented for practical use:
|
||||
// render a single comment
|
||||
res.partial('comment', comment);
|
||||
|
||||
get('/', function(){
|
||||
this.pass('/foobar');
|
||||
});
|
||||
### partial() locals
|
||||
|
||||
Now Express has access to Connect's _next()_ function,
|
||||
which is passed as the fourth and final argument. Calling _next()_ will
|
||||
pass control to the next _matching route_, or continue down the stack
|
||||
of Connect middleware.
|
||||
Both _res.partial()_ and the _partial()_ functions accept an single object consisting of both the options and the locals. Previously with Express 1.x you may pass _user_ to a partial, along with _date_ like so:
|
||||
|
||||
partial('user', { object: user, locals: { date: new Date }})
|
||||
|
||||
app.get('/user/:id?', function(req, res, params, next){
|
||||
next();
|
||||
});
|
||||
or perhaps if you preferred not to use the inferred name _user_ you may used a local for this as well:
|
||||
|
||||
app.get('/user', function(){
|
||||
// ... respond
|
||||
});
|
||||
partial('user', { locals: { user: user, date: new Date }})
|
||||
|
||||
### View Rendering
|
||||
With recent changes to Express 2.x the object passed is now both, so the following is valid for the _object_ option and locals:
|
||||
|
||||
partial('user', { object: user, date: new Date })
|
||||
|
||||
View filenames no longer take the form _NAME_._TYPE_._ENGINE_,
|
||||
the _Content-Type_ can be set via _res.contentType()_ or
|
||||
_res.header()_. For example what was previously _layout.html.haml_,
|
||||
should now be _layout.haml_.
|
||||
Or the following which is equivalent, however the local var name is explicitly set to _user_ instead of deduced from the filename.
|
||||
|
||||
partial('user', { user: user, date: new Date })
|
||||
|
||||
Previously a view render looked something like this:
|
||||
When a "basic" object aka _{}_ or _new Object_ is passed, it is considered options, otherwise it is considered the _object_. The following are equivalent:
|
||||
|
||||
partial('user', user);
|
||||
partial('user', { object: user });
|
||||
|
||||
get('/', function(){
|
||||
this.render('index.html.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
### Template Engine Compliance
|
||||
|
||||
We now have _res.render()_, however the options passed to [haml](http://github.com/visionmedia/haml.js), [jade](http://github.com/visionmedia/jade), and others
|
||||
remain the same.
|
||||
To comply with Express previously engines needed the following signature:
|
||||
|
||||
engine.render(str, options, function(err){});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.haml', {
|
||||
locals: { title: 'My Site' }
|
||||
});
|
||||
});
|
||||
Now they must export a _compile()_ function, returning a function which when called with local variables will render the template. This allows Express to cache the compiled function in memory during production.
|
||||
|
||||
var fn = engine.compile(str, options);
|
||||
fn(locals);
|
||||
|
||||
Previously rendering of a collection via _partial()_ would look something like this:
|
||||
### View Partial Lookup
|
||||
|
||||
this.partial('comment.html.haml', { collection: comments });
|
||||
Previously partials were loaded relative to the now removed _view partials_ directory setting, or by default _views/partials_, now they are relative to the view calling them, read more on [view lookup](guide.html#View-Lookup).
|
||||
|
||||
Although this worked just fine, it was generally to verbose, the similar but new API
|
||||
looks like this, as _partial()_ is _always_ passed as a local variable:
|
||||
### Mime Types
|
||||
|
||||
partial('comment.haml', { collection: comments });
|
||||
Express and Connect now utilize the _mime_ module in npm, so to add more use:
|
||||
|
||||
require('mime').define({ 'foo/bar': ['foo', 'bar'] });
|
||||
|
||||
To make things even less verbose we can assume the extension when omitted:
|
||||
### static() middleware
|
||||
|
||||
partial('comment', { collection: comments });
|
||||
Previously named `staticProvider()`, the now `static()` middleware takes a single directory path, followed by options.
|
||||
|
||||
app.use(express.static(__dirname + '/public', { maxAge: oneYear }));
|
||||
|
||||
And once again even further, when rendering a collection we can simply pass
|
||||
an array, if no other options are desired:
|
||||
Previously when using options the `root` option would be used for this:
|
||||
|
||||
partial('comments', comments);
|
||||
|
||||
### Redirecting
|
||||
|
||||
Previously you would
|
||||
|
||||
this.redirect('/somewhere');
|
||||
|
||||
However you would now:
|
||||
|
||||
res.redirect('/somewhere');
|
||||
res.redirect('/somewhere', 301);
|
||||
|
||||
### HTTP Client
|
||||
|
||||
Previously Express provided a high level http client, this library is no more
|
||||
as it does not belong in Express, however it may be resurrected as a separate module.
|
||||
|
||||
### Core Extensions
|
||||
|
||||
Express is no longer dependent on the [JavaScript Extensions](http://github.com/visionmedia/ext.js) library, so those of you using the methods provided by it such as `Object.merge(a, b)` will need to
|
||||
roll your own, or install the module via:
|
||||
|
||||
$ npm install ext
|
||||
app.use(express.staticProvider({ root: __dirname + '/public', maxAge: oneYear }));
|
||||
|
||||
@@ -0,0 +1,223 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - node web framework</title>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js"></script>
|
||||
<style>
|
||||
#tagline {
|
||||
margin-left: 75px;
|
||||
margin-bottom: 30px;
|
||||
color: rgba(255,255,255,0.7); }
|
||||
html {
|
||||
background: #1c1c1c url(images/bg.tile.jpg); }
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding-bottom: 30px;
|
||||
font: 14px/1.4 "Helvetica Neue", "Lucida Grande", "Arial";
|
||||
font-size: 14px;
|
||||
line-height: 1.5;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
background: url(images/bg.jpg) 50% 0 no-repeat;
|
||||
color: #8b8b8b; }
|
||||
|
||||
* {
|
||||
outline: none; }
|
||||
|
||||
em {
|
||||
color: white; }
|
||||
|
||||
a img {
|
||||
border: none !important; }
|
||||
|
||||
a {
|
||||
font-weight: bold;
|
||||
text-decoration: none;
|
||||
color: white;
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, padding, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
a:hover {
|
||||
opacity: 0.8; }
|
||||
|
||||
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;
|
||||
padding: 25px 20px;
|
||||
background: rgba(0,0,0,0.5);
|
||||
border: 1px solid #323232;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px; }
|
||||
|
||||
code {
|
||||
font-family: "Helvetica Neue", "Lucida Grande", "Arial"; }
|
||||
|
||||
ul {
|
||||
margin: 15px 0;
|
||||
padding: 0 0 0 35px; }
|
||||
ul li {
|
||||
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; }
|
||||
img {
|
||||
margin-left: 20px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
#logo {
|
||||
display: block;
|
||||
margin-left: 30%;
|
||||
margin-bottom: 30px;
|
||||
width: 194px;
|
||||
height: 51px;
|
||||
background: url(images/logo.png) 0 0 no-repeat;
|
||||
text-indent: -99999px; }
|
||||
#logo:hover {
|
||||
opacity: 0.7; }
|
||||
#logo:active {
|
||||
opacity: 0.3; }
|
||||
|
||||
#ribbon {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 2; }
|
||||
|
||||
#wrapper {
|
||||
width: 100%;
|
||||
min-height: 800px;
|
||||
background: url(images/top.png) 0 0 repeat-x; }
|
||||
|
||||
#container {
|
||||
margin: 0 auto;
|
||||
padding-top: 80px;
|
||||
width: 550px; }
|
||||
|
||||
#toc {
|
||||
position: fixed;
|
||||
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 {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
}
|
||||
#toc li a {
|
||||
font-size: 11px;
|
||||
}
|
||||
#menu {
|
||||
margin-left: 75px;
|
||||
padding: 0;
|
||||
padding-bottom: 30px; }
|
||||
#menu li {
|
||||
display: inline;
|
||||
list-style: none; }
|
||||
#menu li a {
|
||||
display: block;
|
||||
float: left;
|
||||
margin: 0 2px;
|
||||
padding: 3px 15px;
|
||||
background: rgba(0,0,0,0.2);
|
||||
-webkit-border-radius: 8px;
|
||||
-moz-border-radius: 8px;
|
||||
-webkit-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-moz-box-shadow: 1px 2px 2px rgba(0,0,0,0.6);
|
||||
-webkit-transition-property: opacity, -webkit-transform, color, background-color, -webkit-box-shadow;
|
||||
-webkit-transition-duration: 0.15s;
|
||||
-webkit-transition-timing-function: ease-out; }
|
||||
#menu li a:hover,
|
||||
#menu li a.active {
|
||||
background: rgba(0,0,0,0.5); }
|
||||
#menu li a:active {
|
||||
background: rgba(0,0,0,0.1);
|
||||
-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="wrapper">
|
||||
<div id="container">
|
||||
<a href='http://github.com/visionmedia/express' id='logo'>Express</a>
|
||||
<p id="tagline">
|
||||
High performance, high class web development for
|
||||
<a href="http://nodejs.org">Node.js</a>
|
||||
</p>
|
||||
<ul id="menu">
|
||||
<li><a href="index.html">Home</a></li>
|
||||
<li><a href="guide.html">Guide</a></li>
|
||||
<li><a href="screencasts.html">Screencasts</a></li>
|
||||
<li><a href="applications.html">Applications</a></li>
|
||||
</ul>
|
||||
<h3>Introduction</h3>
|
||||
|
||||
<p>This introduction screencast covers the basics of Express, and how to get started with your first application.</p>
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139583' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139583' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
|
||||
<h3>View Partials</h3>
|
||||
|
||||
<p>In this screencast we work with partials to display a collection of users using the <a href="http://jade-lang.com">Jade</a> template engine, and learn about view path resolution.</p>
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139591' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139591' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
|
||||
<h3>Route Specific Middleware</h3>
|
||||
|
||||
<p>In the screencast below we learn about the benefits of route-specific middleware.</p>
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140296' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140296' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
|
||||
<h3>Route Param Preconditions</h3>
|
||||
|
||||
<p>Learn about route parameter (<em>/user/:id</em>) pre-conditions, providing automated validation, and loading of data via the named route param segments.</p>
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140300' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140300' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,24 @@
|
||||
|
||||
### Introduction
|
||||
|
||||
This introduction screencast covers the basics of Express, and how to get started with your first application.
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139583' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139583' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
### View Partials
|
||||
|
||||
In this screencast we work with partials to display a collection of users using the [Jade](http://jade-lang.com) template engine, and learn about view path resolution.
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=139591' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=139591' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
### Route Specific Middleware
|
||||
|
||||
In the screencast below we learn about the benefits of route-specific middleware.
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140296' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140296' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
|
||||
### Route Param Preconditions
|
||||
|
||||
Learn about route parameter (_/user/:id_) pre-conditions, providing automated validation, and loading of data via the named route param segments.
|
||||
|
||||
<object classid='clsid:d27cdb6e-ae6d-11cf-96b8-444553540000' codebase='http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=9,0,115,0' width='560' height='345'><param name='movie' value='http://screenr.com/Content/assets/screenr_1116090935.swf' /><param name='flashvars' value='i=140300' /><param name='allowFullScreen' value='true' /><embed src='http://screenr.com/Content/assets/screenr_1116090935.swf' flashvars='i=140300' allowFullScreen='true' width='560' height='345' pluginspage='http://www.macromedia.com/go/getflashplayer'></embed></object>
|
||||
@@ -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(
|
||||
express.bodyParser()
|
||||
, express.cookieParser()
|
||||
, express.session({ secret: 'keyboard cat' })
|
||||
);
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
// 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');
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, messages = require('express-messages');
|
||||
|
||||
var app = module.exports = express.createServer();
|
||||
|
||||
// Config
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// mount hook
|
||||
|
||||
app.mounted(function(other){
|
||||
console.log('ive been mounted!');
|
||||
});
|
||||
|
||||
// Flash message helper provided by express-messages
|
||||
// $ npm install express-messages
|
||||
|
||||
app.dynamicHelpers({
|
||||
messages: messages
|
||||
, base: function(){
|
||||
// return the app's mount-point
|
||||
// so that urls can adjust. For example
|
||||
// if you run this example /post/add works
|
||||
// however if you run the mounting example
|
||||
// it adjusts to /blog/post/add
|
||||
return '/' == app.route ? '' : app.route;
|
||||
}
|
||||
});
|
||||
|
||||
// Middleware
|
||||
|
||||
app.configure(function(){
|
||||
app.use(express.logger('\x1b[33m:method\x1b[0m \x1b[32m:url\x1b[0m :response-time'));
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
app.use(app.router);
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
require('./routes/site')(app);
|
||||
require('./routes/post')(app);
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
|
||||
// Fake data store
|
||||
|
||||
var ids = 0
|
||||
, db = {};
|
||||
|
||||
var Post = exports = module.exports = function Post(title, body) {
|
||||
this.id = ++ids;
|
||||
this.title = title;
|
||||
this.body = body;
|
||||
this.createdAt = new Date;
|
||||
};
|
||||
|
||||
Post.prototype.save = function(fn){
|
||||
db[this.id] = this;
|
||||
fn();
|
||||
};
|
||||
|
||||
Post.prototype.validate = function(fn){
|
||||
if (!this.title) return fn(new Error('_title_ required'));
|
||||
if (!this.body) return fn(new Error('_body_ required'));
|
||||
if (this.body.length < 10) {
|
||||
return fn(new Error(
|
||||
'_body_ should be at least **10** characters long, was only _' + this.title.length + '_'));
|
||||
}
|
||||
fn();
|
||||
};
|
||||
|
||||
|
||||
Post.prototype.update = function(data, fn){
|
||||
this.updatedAt = new Date;
|
||||
for (var key in data) {
|
||||
if (undefined != data[key]) {
|
||||
this[key] = data[key];
|
||||
}
|
||||
}
|
||||
this.save(fn);
|
||||
};
|
||||
|
||||
Post.prototype.destroy = function(fn){
|
||||
exports.destroy(this.id, fn);
|
||||
};
|
||||
|
||||
exports.count = function(fn){
|
||||
fn(null, Object.keys(db).length);
|
||||
};
|
||||
|
||||
exports.all = function(fn){
|
||||
var arr = Object.keys(db).reduce(function(arr, id){
|
||||
arr.push(db[id]);
|
||||
return arr;
|
||||
}, []);
|
||||
fn(null, arr);
|
||||
};
|
||||
|
||||
exports.get = function(id, fn){
|
||||
fn(null, db[id]);
|
||||
};
|
||||
|
||||
exports.destroy = function(id, fn) {
|
||||
if (db[id]) {
|
||||
delete db[id];
|
||||
fn();
|
||||
} else {
|
||||
fn(new Error('post ' + id + ' does not exist'));
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,58 @@
|
||||
body {
|
||||
font: 13px "Helvetica Neue", Arial, sans-serif;
|
||||
color: #111;
|
||||
padding: 60px 80px;
|
||||
}
|
||||
h1, h2 {
|
||||
color: #c00;
|
||||
}
|
||||
a {
|
||||
color: #c00;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
label {
|
||||
padding: 6px 0;
|
||||
display: block;
|
||||
text-transform: lowercase;
|
||||
}
|
||||
textarea,
|
||||
input {
|
||||
outline: none;
|
||||
padding: 5px;
|
||||
background: #f1f1f1;
|
||||
border: 1px solid #aaa;
|
||||
}
|
||||
textarea:focus,
|
||||
input:focus {
|
||||
background: #fff;
|
||||
}
|
||||
textarea {
|
||||
width: 500px;
|
||||
height: 200px;
|
||||
}
|
||||
a.edit {
|
||||
margin-left: 10px;
|
||||
font-size: 11px;
|
||||
font-weight: normal;
|
||||
}
|
||||
.date {
|
||||
font-size: 11px;
|
||||
}
|
||||
#messages ul {
|
||||
padding: 10px;
|
||||
border: 1px solid;
|
||||
}
|
||||
#messages ul li {
|
||||
list-style: none;
|
||||
}
|
||||
#messages ul.info {
|
||||
color: #2EBBE6;
|
||||
background: #F7FBFD;
|
||||
}
|
||||
#messages ul.error {
|
||||
color: #E4250C;
|
||||
background: #FDEAE7;
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var basicAuth = require('../../../lib/express').basicAuth
|
||||
, Post = require('../models/post');
|
||||
|
||||
module.exports = function(app){
|
||||
/**
|
||||
* Apply basic auth to all post related routes
|
||||
*/
|
||||
|
||||
app.all('/post(/*)?', basicAuth(function(user, pass){
|
||||
return 'admin' == user && 'express' == pass;
|
||||
}));
|
||||
|
||||
/**
|
||||
* Map :post to the database, loading
|
||||
* every time :post is present.
|
||||
*/
|
||||
|
||||
app.param('post', function(req, res, next, id){
|
||||
Post.get(id, function(err, post){
|
||||
if (err) return next(err);
|
||||
if (!post) return next(new Error('failed to load post ' + id));
|
||||
req.post = post;
|
||||
next();
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Add a post.
|
||||
*/
|
||||
|
||||
app.get('/post/add', function(req, res){
|
||||
res.render('post/form', { post: {}});
|
||||
});
|
||||
|
||||
/**
|
||||
* Save a post.
|
||||
*/
|
||||
|
||||
app.post('/post', function(req, res){
|
||||
var data = req.body.post
|
||||
, post = new Post(data.title, data.body);
|
||||
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.flash('error', err.message);
|
||||
return res.redirect('back');
|
||||
}
|
||||
|
||||
post.save(function(err){
|
||||
req.flash('info', 'Successfully created post _%s_', post.title);
|
||||
res.redirect('/post/' + post.id);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
/**
|
||||
* Display the post.
|
||||
*/
|
||||
|
||||
app.get('/post/:post', function(req, res){
|
||||
res.render('post', { post: req.post });
|
||||
});
|
||||
|
||||
/**
|
||||
* Display the post edit form.
|
||||
*/
|
||||
|
||||
app.get('/post/:post/edit', function(req, res){
|
||||
res.render('post/form', { post: req.post });
|
||||
});
|
||||
|
||||
/**
|
||||
* Update post. Typically a data layer would handle this stuff.
|
||||
*/
|
||||
|
||||
app.put('/post/:post', function(req, res, next){
|
||||
var post = req.post;
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.flash('error', err.message);
|
||||
return res.redirect('back');
|
||||
}
|
||||
post.update(req.body.post, function(err){
|
||||
if (err) return next(err);
|
||||
req.flash('info', 'Successfully updated post');
|
||||
res.redirect('back');
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var Post = require('../models/post');
|
||||
|
||||
module.exports = function(app){
|
||||
app.get('/', function(req, res){
|
||||
Post.count(function(err, count){
|
||||
Post.all(function(err, posts){
|
||||
res.render('index', {
|
||||
count: count
|
||||
, posts: posts
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,19 @@
|
||||
h1 Blog
|
||||
|
||||
!= messages()
|
||||
|
||||
- if (count)
|
||||
p Display all #{count} post(s)
|
||||
#posts!= partial('post', posts)
|
||||
- else
|
||||
p
|
||||
| It looks like you have no posts!
|
||||
p
|
||||
| Click
|
||||
a(href=base + '/post/add') here
|
||||
| to create a post. Login
|
||||
| as
|
||||
em "admin"
|
||||
| and
|
||||
em "express"
|
||||
| .
|
||||
@@ -0,0 +1,7 @@
|
||||
!!! 5
|
||||
html
|
||||
head
|
||||
title Blog
|
||||
link(rel='stylesheet', href=base + '/style.css')
|
||||
body
|
||||
#container!= body
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
- if (post.title)
|
||||
h1 Editing #{post.title}
|
||||
- else
|
||||
h1 New Post
|
||||
|
||||
!= messages()
|
||||
|
||||
form#post(action=base + '/post' + (post.title ? '/' + post.id : ''), method='post')
|
||||
- if (post.title)
|
||||
input(type='hidden', name='_method', value='put')
|
||||
p
|
||||
label(for='post[title]') Title:
|
||||
input(type='text', name='post[title]', value=post.title)
|
||||
p
|
||||
label(for='post[body]') Body:
|
||||
textarea(name='post[body]')= post.body || ''
|
||||
p
|
||||
input(type='submit', value=post.title ? 'Update' : 'Create')
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
.post
|
||||
// title
|
||||
h2
|
||||
= post.title
|
||||
a.edit(href=base + '/post/' + post.id + '/edit') Edit
|
||||
|
||||
// flash messages
|
||||
!= messages()
|
||||
|
||||
// dates
|
||||
p.date.created Created at #{post.createdAt}
|
||||
- if (post.updatedAt)
|
||||
p.date.updated Updated at #{post.updatedAt}
|
||||
|
||||
// body
|
||||
pre.body= post.body
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* 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.cookieParser(),
|
||||
|
||||
// Parses x-www-form-urlencoded request bodies (and json)
|
||||
express.bodyParser()
|
||||
);
|
||||
|
||||
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');
|
||||
@@ -0,0 +1,47 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* 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>');
|
||||
});
|
||||
|
||||
// /files/* is accessed via req.params[0]
|
||||
// but here we name it :file
|
||||
app.get('/files/:file(*)', function(req, res, next){
|
||||
var file = req.params.file
|
||||
, path = __dirname + '/files/' + file;
|
||||
// either res.download(path) and let
|
||||
// express handle failures, or provide
|
||||
// a callback
|
||||
res.download(path, function(err){
|
||||
if (err) return next(err);
|
||||
// the response has invoked .end()
|
||||
// so you cannnot respond here (of course)
|
||||
// but the callback is handy for statistics etc.
|
||||
console.log('transferred %s', path);
|
||||
});
|
||||
});
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if ('ENOENT' == err.code) {
|
||||
res.send('Cant find that file, sorry!');
|
||||
} else {
|
||||
// Not a 404
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
@@ -0,0 +1 @@
|
||||
what an amazing download
|
||||
@@ -1,29 +1,34 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express');
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Register ejs as .html
|
||||
|
||||
app.register('.html', require('ejs'));
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@sencha.com' },
|
||||
{ name: 'ciaran', email: 'ciaranj@gmail.com' },
|
||||
{ name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
{ name: 'tj', email: 'tj@sencha.com' }
|
||||
, { name: 'ciaran', email: 'ciaranj@gmail.com' }
|
||||
, { name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users.ejs', {
|
||||
locals: {
|
||||
users: users
|
||||
}
|
||||
});
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Users</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Users</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,3 +0,0 @@
|
||||
<ul id="users">
|
||||
<%- partial('user', users) %>
|
||||
</ul>
|
||||
@@ -0,0 +1,3 @@
|
||||
<ul id="users">
|
||||
<%- partial('user', users) %>
|
||||
</ul>
|
||||
@@ -0,0 +1,96 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// 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(path){
|
||||
this.name = 'NotFound';
|
||||
if (path) {
|
||||
Error.call(this, 'Cannot find ' + path);
|
||||
this.path = path;
|
||||
} else {
|
||||
Error.call(this, 'Not Found');
|
||||
}
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Error.prototype`.
|
||||
*/
|
||||
|
||||
NotFound.prototype.__proto__ = Error.prototype;
|
||||
|
||||
// We can call app.error() several times as shown below.
|
||||
// Here we check for an instanceof NotFound and show the
|
||||
// 404 page, or we pass on to the next error handler.
|
||||
|
||||
// These handlers could potentially be defined within
|
||||
// configure() blocks to provide introspection when
|
||||
// in the development environment.
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res.render('404.jade', { status: 404, error: err });
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
// Here we assume all errors as 500 for the simplicity of
|
||||
// this demo, however you can choose whatever you like
|
||||
|
||||
app.error(function(err, req, res){
|
||||
res.render('500.jade', { status: 500, error: err });
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index.jade');
|
||||
});
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound(req.url);
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res, next){
|
||||
next(new Error('keyboard cat!'));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -0,0 +1,4 @@
|
||||
- if (error.path)
|
||||
h2 Cannot find #{error.path}
|
||||
- else
|
||||
h2 Page Not Found
|
||||
@@ -1,26 +1,28 @@
|
||||
|
||||
// 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');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// Caught and passed down to the errorHandler middleware
|
||||
throw new Error('something broke!');
|
||||
// Caught and passed down to the errorHandler middleware
|
||||
throw new Error('something broke!');
|
||||
});
|
||||
|
||||
app.get('/next', function(req, res, params, next){
|
||||
// We can also pass exceptions to next()
|
||||
next(new Error('oh no!'))
|
||||
app.get('/next', function(req, res, next){
|
||||
// We can also pass exceptions to next()
|
||||
next(new Error('oh no!'))
|
||||
});
|
||||
|
||||
// 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({ dump: true, stack: true }));
|
||||
|
||||
app.listen(3000);
|
||||
@@ -0,0 +1,49 @@
|
||||
|
||||
// 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.cookieParser()
|
||||
, express.session({ secret: 'keyboard cat' })
|
||||
);
|
||||
|
||||
// View settings
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.dynamicHelpers({
|
||||
// express-messages is a dynamicHelper that
|
||||
// renders the flash messages to HTML for you
|
||||
// $ npm install express-messages
|
||||
messages: require('express-messages')
|
||||
});
|
||||
|
||||
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');
|
||||
@@ -0,0 +1,3 @@
|
||||
<h1>Flash Message Example</h1>
|
||||
<p>on page <%- page %></p>
|
||||
<%- messages() %>
|
||||
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,50 +1,81 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express'),
|
||||
connect = require('connect'),
|
||||
sys = require('sys');
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer(
|
||||
// Here we use the bodyDecoder middleware
|
||||
// to parse urlencoded request bodies
|
||||
// which populates req.body
|
||||
connect.bodyDecoder(),
|
||||
var app = express.createServer();
|
||||
|
||||
// Here we use the bodyDecoder middleware
|
||||
// to parse urlencoded request bodies
|
||||
// which populates req.body
|
||||
app.use(express.bodyParser());
|
||||
|
||||
// The methodOverride middleware allows us
|
||||
// to set a hidden input of _method to an arbitrary
|
||||
// HTTP method to support app.put(), app.del() etc
|
||||
connect.methodOverride()
|
||||
);
|
||||
// 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(express.methodOverride());
|
||||
|
||||
// Required by session
|
||||
app.use(express.cookieParser());
|
||||
|
||||
// Required by req.flash() for persistent
|
||||
// notifications
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// get ?name=foo
|
||||
var name = req.param('name') || '';
|
||||
|
||||
// Switch the button label based if we have a name
|
||||
var label = name ? 'Update' : 'Save';
|
||||
// get ?name=foo
|
||||
var name = req.param('name') || '';
|
||||
|
||||
// Switch the button label based if we have a name
|
||||
var label = name ? 'Update' : 'Save';
|
||||
|
||||
// If we have a name, we are updating,
|
||||
// so add the hidden _method input
|
||||
res.send('<form method="post">'
|
||||
+ (name ? '<input type="hidden" value="put" name="_method" />' : '')
|
||||
+ 'Name: <input type="text" name="name" value="' + name + '" />'
|
||||
+ '<input type="submit" value="' + label + '" />'
|
||||
+ '</form>');
|
||||
// Buffer all flash messages.
|
||||
// Typically this would all be done in a template
|
||||
// however for illustration purposes we iterate
|
||||
// here.
|
||||
|
||||
// The messages in req.flash() persist until called,
|
||||
// at which time they are flushed from the session
|
||||
var msgs = '<ul>',
|
||||
flash = req.flash();
|
||||
Object.keys(flash).forEach(function(type){
|
||||
flash[type].forEach(function(msg){
|
||||
msgs += '<li class="' + type + '">' + msg + '</li>';
|
||||
});
|
||||
});
|
||||
msgs += '</ul>';
|
||||
|
||||
// If we have a name, we are updating,
|
||||
// so add the hidden _method input
|
||||
res.send(msgs
|
||||
+ '<form method="post">'
|
||||
+ (name ? '<input type="hidden" value="put" name="_method" />' : '')
|
||||
+ 'Name: <input type="text" name="name" value="' + name + '" />'
|
||||
+ '<input type="submit" value="' + label + '" />'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
app.post('/', function(req, res){
|
||||
if (req.body.name) {
|
||||
// Typically here we would create a resource
|
||||
sys.puts('saved ' + req.body.name);
|
||||
req.flash('info', 'Saved ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
} else {
|
||||
req.flash('error', 'Error: name required');
|
||||
res.redirect('/');
|
||||
}
|
||||
});
|
||||
|
||||
app.put('/', function(req, res){
|
||||
// Typically here we would update a resource
|
||||
sys.puts('updated ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
// Typically here we would update a resource
|
||||
req.flash('info', 'Updated ' + req.body.name);
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -0,0 +1,71 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* 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.statusCode = 200;
|
||||
res.setHeader('Content-Type', 'text/html');
|
||||
res.write('<p>Visit /item/2</p>');
|
||||
res.write('<p>Visit /item/2.json</p>');
|
||||
res.write('<p>Visit /item/2.xml</p>');
|
||||
res.end();
|
||||
});
|
||||
|
||||
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');
|
||||
@@ -1,17 +1,20 @@
|
||||
|
||||
// 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 express = require('../../lib/express')
|
||||
, http = require('http');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Expose our views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
/**
|
||||
* Request github json api `path`.
|
||||
@@ -22,20 +25,20 @@ app.set('views', __dirname + '/views');
|
||||
*/
|
||||
|
||||
function request(path, fn){
|
||||
var client = http.createClient(80, 'github.com'),
|
||||
req = client.request('GET', '/api/v2/json' + path, { Host: 'github.com' });
|
||||
req.addListener('response', function(res){
|
||||
res.body = '';
|
||||
res.addListener('data', function(chunk){ res.body += chunk; });
|
||||
res.addListener('end', function(){
|
||||
try {
|
||||
fn(null, JSON.parse(res.body));
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
var client = http.createClient(80, 'github.com')
|
||||
, req = client.request('GET', '/api/v2/json' + path, { Host: 'github.com' });
|
||||
req.on('response', function(res){
|
||||
res.body = '';
|
||||
res.on('data', function(chunk){ res.body += chunk; });
|
||||
res.on('end', function(){
|
||||
try {
|
||||
fn(null, JSON.parse(res.body));
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
req.end();
|
||||
});
|
||||
req.end();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -46,11 +49,11 @@ function request(path, fn){
|
||||
*/
|
||||
|
||||
function sort(repos){
|
||||
return repos.sort(function(a, b){
|
||||
if (a.watchers == b.watchers) return 0;
|
||||
if (a.watchers > b.watchers) return -1;
|
||||
if (a.watchers < b.watchers) return 1;
|
||||
});
|
||||
return repos.sort(function(a, b){
|
||||
if (a.watchers == b.watchers) return 0;
|
||||
if (a.watchers > b.watchers) return -1;
|
||||
if (a.watchers < b.watchers) return 1;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -62,9 +65,9 @@ function sort(repos){
|
||||
*/
|
||||
|
||||
function totalWatchers(repos) {
|
||||
return repos.reduce(function(sum, repo){
|
||||
return sum + repo.watchers;
|
||||
}, 0);
|
||||
return repos.reduce(function(sum, repo){
|
||||
return sum + repo.watchers;
|
||||
}, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -72,32 +75,42 @@ function totalWatchers(repos) {
|
||||
*/
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/repos/visionmedia');
|
||||
res.redirect('/repos/visionmedia');
|
||||
});
|
||||
|
||||
/**
|
||||
* Display repos.
|
||||
*/
|
||||
|
||||
app.get('/repos/:user', function(req, res, params, next){
|
||||
var name = params.user;
|
||||
request('/repos/show/' + name, function(err, user){
|
||||
app.get('/repos/*', function(req, res, next){
|
||||
var names = req.params[0].split('/')
|
||||
, users = [];
|
||||
(function fetchData(name){
|
||||
// We have a user name
|
||||
if (name) {
|
||||
console.log('... fetching \x1b[33m%s\x1b[0m', name);
|
||||
request('/repos/show/' + name, function(err, user){
|
||||
if (err) {
|
||||
next(err)
|
||||
next(err)
|
||||
} else {
|
||||
res.render('index.jade', {
|
||||
locals: {
|
||||
totalWatchers: totalWatchers(user.repositories),
|
||||
repos: sort(user.repositories),
|
||||
name: name
|
||||
}
|
||||
});
|
||||
user.totalWatchers = totalWatchers(user.repositories);
|
||||
user.repos = sort(user.repositories);
|
||||
user.name = name;
|
||||
users.push(user);
|
||||
fetchData(names.shift());
|
||||
}
|
||||
});
|
||||
});
|
||||
// No more users
|
||||
} else {
|
||||
console.log('... done');
|
||||
res.render('index', { users: users });
|
||||
}
|
||||
})(names.shift());
|
||||
});
|
||||
|
||||
// Serve statics from ./public
|
||||
app.use('/', connect.staticProvider(__dirname + '/public'));
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// Listen on port 3000
|
||||
app.listen(3000);
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,11 +1,19 @@
|
||||
body {
|
||||
padding: 30px 50px;
|
||||
font: 12px/1.4 "Lucida Grande", "Helvetica Nueue", Arial, sans-serif;
|
||||
padding: 30px 50px;
|
||||
font: 12px/1.4 "Helvetica Neue", Arial, sans-serif;
|
||||
}
|
||||
a {
|
||||
color: #00AAFF;
|
||||
text-decoration: none;
|
||||
color: #00AAFF;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration: underline;
|
||||
}
|
||||
.user {
|
||||
margin: 0 10px;
|
||||
float: left;
|
||||
width: 300px;
|
||||
}
|
||||
table td:nth-child(2) {
|
||||
padding: 0 5px;
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
h1= name
|
||||
p.summary
|
||||
| <a href="http://github.com/#{name}">#{name}</a> has <strong>#{repos.length}</strong> repositories
|
||||
| with a total of <strong>#{totalWatchers}</strong> watchers.
|
||||
table#repos!= partial('repo', repos)
|
||||
- each user in users
|
||||
.user
|
||||
h2= user.name
|
||||
p.summary
|
||||
| <a href="http://github.com/#{user.name}">#{user.name}</a> has
|
||||
| <strong>#{user.repos.length}</strong> repositories
|
||||
| with a total of <strong>#{user.totalWatchers}</strong> watchers.
|
||||
table#repos!= partial('repo', user.repos)
|
||||
@@ -3,12 +3,12 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('./../../lib/express');
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -13,38 +15,63 @@ var pub = __dirname + '/public';
|
||||
// Auto-compile sass to css with "compiler"
|
||||
// and then serve with connect's staticProvider
|
||||
|
||||
var app = express.createServer(
|
||||
connect.compiler({ src: pub, enable: ['sass'] }),
|
||||
connect.staticProvider(pub)
|
||||
);
|
||||
var app = express.createServer();
|
||||
app.use(express.compiler({ src: pub, enable: ['sass'] }));
|
||||
app.use(app.router);
|
||||
app.use(express.static(pub));
|
||||
app.use(express.errorHandler({ dump: true, stack: true }));
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Enable auto-reloading of view contents when changed
|
||||
// with an interval of 1000 milliseconds. Start the app
|
||||
// with $ node examples/jade/app.js
|
||||
// then alter some views :)
|
||||
// Set our default template engine to "jade"
|
||||
// which prevents the need for extensions
|
||||
// (although you can still mix and match)
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
app.set('reload views', 1000);
|
||||
// or app.enable('reload views'); for defaults
|
||||
|
||||
// Re-compile
|
||||
function User(name, email) {
|
||||
this.name = name;
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@sencha.com' },
|
||||
{ name: 'ciaran', email: 'ciaranj@gmail.com' },
|
||||
{ name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
new User('tj', 'tj@vision-media.ca')
|
||||
, new User('ciaran', 'ciaranj@gmail.com')
|
||||
, new User('aaron', 'aaron.heckmann+github@gmail.com')
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users.jade', {
|
||||
locals: {
|
||||
users: users
|
||||
}
|
||||
});
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
app.get('/users/callback', function(req, res){
|
||||
// a callback is also accepted
|
||||
res.partial('users/user', users, function(err, html){
|
||||
if (err) throw err;
|
||||
res.send(html);
|
||||
});
|
||||
});
|
||||
|
||||
app.get('/users', function(req, res){
|
||||
// we can use res.partial() as if
|
||||
// we were in a view, utilizing the same api
|
||||
// to render a fragment
|
||||
res.partial('users/user', users);
|
||||
});
|
||||
|
||||
app.get('/users/list', function(req, res){
|
||||
// use "object" to utilize the name deduced from
|
||||
// the view filename. The examples below are equivalent
|
||||
|
||||
//res.partial('users/list', { object: users });
|
||||
res.partial('users/list', { list: users });
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
res.partial('users/user', users[req.params.id]);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
ul#users
|
||||
- each user in list
|
||||
li!= partial('user', user)
|
||||
@@ -0,0 +1,35 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// set default layout, usually "layout"
|
||||
app.set('view options', { layout: 'layouts/default' });
|
||||
|
||||
// Set our default template engine to "jade"
|
||||
// which prevents the need for extensions
|
||||
// (although you can still mix and match)
|
||||
app.set('view engine', 'ejs');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('pages/default');
|
||||
});
|
||||
|
||||
app.get('/alternate', function(req, res){
|
||||
// note that we do not explicitly
|
||||
// state the layout here, the view does,
|
||||
// although we could do it here as well.
|
||||
res.render('pages/alternate');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Alternate Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,6 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Default Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,2 @@
|
||||
<% layout('layouts/alternate') %>
|
||||
<h1>Page</h1>
|
||||
@@ -0,0 +1 @@
|
||||
<h1>Page</h1>
|
||||
@@ -0,0 +1,41 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
// $ npm install markdown
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, md = require('markdown').markdown;
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// register .md so that markdown may comply
|
||||
// with the express view system by implementing
|
||||
// a .compile() method
|
||||
|
||||
app.register('.md', {
|
||||
compile: function(str, options){
|
||||
var html = md.toHTML(str);
|
||||
return function(locals){
|
||||
return html.replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return locals[name];
|
||||
});
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'md');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index', { layout: false, title: 'Markdown Example' });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
# {title}
|
||||
|
||||
Just an example view rendered with _markdown_.
|
||||
@@ -0,0 +1,27 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, blog = require('../blog/app');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
|
||||
// mount the blog. the blog app is written using the "base"
|
||||
// local variable, allowing it's urls to adjust to wherever
|
||||
// we wish to mount it.
|
||||
app.use('/blog', blog);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Visit <a href="/blog">/blog</a>');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Server listening on port 3000');
|
||||
@@ -1,47 +1,51 @@
|
||||
|
||||
// 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'),
|
||||
sys = require('sys');
|
||||
var express = require('../../lib/express')
|
||||
, form = require('connect-form');
|
||||
|
||||
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()
|
||||
// connect-form (http://github.com/visionmedia/connect-form)
|
||||
// middleware uses the formidable middleware to parse urlencoded
|
||||
// and multipart form data
|
||||
form({ keepExtensions: true })
|
||||
);
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<form method="post" enctype="form-data/multipart">'
|
||||
+ '<p>Image: <input type="file" name="image" /></p>'
|
||||
+ '<p><input type="submit" value="Upload" /></p>'
|
||||
+ '</form>');
|
||||
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
|
||||
// the exception (if any) fields parsed, and files parsed
|
||||
req.form.complete(function(err, fields, files){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else {
|
||||
sys.puts('\nuploaded ' + files.image.filename);
|
||||
res.redirect('back');
|
||||
}
|
||||
});
|
||||
// connect-form adds the req.form object
|
||||
// we can (optionally) define onComplete, passing
|
||||
// the exception (if any) fields parsed, and files parsed
|
||||
req.form.complete(function(err, fields, files){
|
||||
if (err) {
|
||||
next(err);
|
||||
} else {
|
||||
console.log('\nuploaded %s to %s'
|
||||
, files.image.filename
|
||||
, files.image.path);
|
||||
res.redirect('back');
|
||||
}
|
||||
});
|
||||
|
||||
// We can add listeners for several form
|
||||
// events such as "progress"
|
||||
req.form.addListener('progress', function(bytesReceived, bytesExpected){
|
||||
var percent = (bytesReceived / bytesExpected * 100) | 0;
|
||||
sys.print('Uploading: %' + percent + '\r');
|
||||
});
|
||||
// We can add listeners for several form
|
||||
// events such as "progress"
|
||||
req.form.on('progress', function(bytesReceived, bytesExpected){
|
||||
var percent = (bytesReceived / bytesExpected * 100) | 0;
|
||||
process.stdout.write('Uploading: %' + percent + '\r');
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -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');
|
||||
@@ -0,0 +1,9 @@
|
||||
|
||||
module.exports = {
|
||||
|
||||
// /
|
||||
|
||||
index: function(req, res){
|
||||
res.render();
|
||||
}
|
||||
};
|
||||
@@ -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');
|
||||
});
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,149 @@
|
||||
|
||||
/**
|
||||
* 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(':method :url :status'));
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieParser());
|
||||
app.use(express.session({ secret: 'keyboard cat' }));
|
||||
app.use(app.router);
|
||||
app.use(express.static(__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){
|
||||
if (!req.session) return false;
|
||||
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 || {};
|
||||
// Expose obj as the "users" or "user" local
|
||||
if (action == 'index') {
|
||||
options[plural] = obj;
|
||||
} else {
|
||||
options[name] = obj;
|
||||
}
|
||||
return res.render(path, options, fn);
|
||||
};
|
||||
fn.apply(this, arguments);
|
||||
};
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
<h1>Cannot find <%= request.url %></h1>
|
||||
<p>Sorry we failed to locate this page or resource.</p>
|
||||
@@ -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>
|
||||
@@ -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>
|
||||
@@ -0,0 +1,9 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Express - MVC Example</title>
|
||||
<link rel="stylesheet" href="/style.css" />
|
||||
</head>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||