Comparar commits
666 Commits
2.5.10
...
3.0.0alpha1
| Autor | SHA1 | Data | |
|---|---|---|---|
| d1d3f310e9 | |||
| 089a83363f | |||
| c28e6db428 | |||
| 05253272cb | |||
| 12e087820c | |||
| 8dc001567b | |||
| 9af3256f72 | |||
| cdd333a2e0 | |||
| aa4ec8f3e2 | |||
| 5ea78cf259 | |||
| 600eaea2d1 | |||
| 81b15833f7 | |||
| 9abc221559 | |||
| 7048be2830 | |||
| 91eb27513a | |||
| 04f0682439 | |||
| 36bab5408d | |||
| c483dab4b8 | |||
| f1d759f279 | |||
| 4c1afb2984 | |||
| db9b2bfbe1 | |||
| 377677cf30 | |||
| 24a4e95ffe | |||
| 8fdd9e967a | |||
| acfa6a692e | |||
| 67ca22b6e4 | |||
| ba413ee98d | |||
| acc0e934cb | |||
| 1d485840fd | |||
| dcb147608a | |||
| ddf24d1fd6 | |||
| 886d7e6903 | |||
| 2079ccbf42 | |||
| 5ec88b0f6c | |||
| b0bcd27124 | |||
| 4dfee21e91 | |||
| 18fa2c9c7d | |||
| 04d43d60b7 | |||
| 419731ec41 | |||
| 082ba88084 | |||
| 09a8474521 | |||
| 6d323d3ff7 | |||
| c4e003518a | |||
| 9e251d14af | |||
| 9abba7d69a | |||
| 0377540e2d | |||
| e6a15f66fc | |||
| 1787fd1b52 | |||
| 54c3d5c113 | |||
| 6d0f9a37a2 | |||
| 60dac4d2ff | |||
| dd468fbe9a | |||
| 04ecf04832 | |||
| 6d45616a7a | |||
| 06d2016ee7 | |||
| 984bfadcdd | |||
| f79f3f8b22 | |||
| ba570a9842 | |||
| 365a98d00f | |||
| 4e2677fe2c | |||
| d6d16b7899 | |||
| 86a9e0803a | |||
| fdf96922b5 | |||
| d396761f76 | |||
| 711519361c | |||
| e4827b8d89 | |||
| 7ec7cb94f9 | |||
| 298899d02c | |||
| 43b35a4ae0 | |||
| a7a8dcd617 | |||
| bdf0b26a12 | |||
| 73e87b6a2e | |||
| ac3d002cb2 | |||
| ada2d0c627 | |||
| 286d3a1ff8 | |||
| 937f01a225 | |||
| c9559e03da | |||
| d115798d5b | |||
| b6ee5fafd0 | |||
| 533b31237c | |||
| 812a470122 | |||
| eb3105ef25 | |||
| ffcaa04d2c | |||
| a47660ba67 | |||
| e2291184dc | |||
| 4e332452b7 | |||
| e2f43df5e9 | |||
| 141929cd6e | |||
| ec03968bd4 | |||
| a21435cb05 | |||
| bdfa6d1fe7 | |||
| b565e258cb | |||
| 892b605ab5 | |||
| 4d99352526 | |||
| cbf0eaa429 | |||
| 80e16c8ca3 | |||
| 0ae024afa8 | |||
| 73caacbd42 | |||
| b667bda8f2 | |||
| 463e38cf67 | |||
| 83c8d65e3d | |||
| 9548e7d1e2 | |||
| ee290d82d1 | |||
| b75bb003bd | |||
| 6c2194fdb4 | |||
| da61d8b639 | |||
| 863ba199f6 | |||
| 161ebb354f | |||
| 1516ebf7f9 | |||
| 2820f2227d | |||
| a078f5f5c1 | |||
| d0a83053d3 | |||
| b6bc01abde | |||
| 196a1eb6ad | |||
| 593271f536 | |||
| 0f24f715ba | |||
| bb9bfa5618 | |||
| 8120a06cd6 | |||
| a153082120 | |||
| 970031e267 | |||
| a819856f3f | |||
| 4dbaaa2855 | |||
| 0593cf4379 | |||
| cd0e5dbb4c | |||
| 7cb1c3cabb | |||
| 550b9101b8 | |||
| 9d9564568b | |||
| 7ea7a53e7c | |||
| ac387caf21 | |||
| ad3f1e84aa | |||
| 2432a0dda5 | |||
| 03e5919910 | |||
| 277e35f8c8 | |||
| 0dd5836f3f | |||
| 9290f0d407 | |||
| 9bd3ad846e | |||
| a8c73649ce | |||
| 6368ab49b4 | |||
| 16b6a64ef9 | |||
| e46431ee0e | |||
| 2e4cf0aa5c | |||
| 3f8a7f05e8 | |||
| 3360613bcd | |||
| f8a9cbe074 | |||
| 15f2e0c899 | |||
| 6518e746c1 | |||
| 38b046d6ad | |||
| 28d7750eda | |||
| e5fc85bddc | |||
| 2a40571118 | |||
| f1aa57c57e | |||
| 0b876ece41 | |||
| ca0bd1a0b5 | |||
| 2d31b5df34 | |||
| 10f7ca0ebf | |||
| 85d6964874 | |||
| 76fd462bcc | |||
| c44f01879f | |||
| fe27989a69 | |||
| b04f3eeede | |||
| 6ab61d023f | |||
| 4d87efc771 | |||
| e47d368239 | |||
| 5b1235addc | |||
| 8c2c1240e1 | |||
| 27e696b7b1 | |||
| bfba98f532 | |||
| 8de3ad50b2 | |||
| 2ba343d2b5 | |||
| ff92afa557 | |||
| c3c0fb95a8 | |||
| bb56a094bb | |||
| 2211943170 | |||
| 4eb8bc0857 | |||
| fa51cb8d63 | |||
| 706dec93a4 | |||
| 7a32920448 | |||
| 57a3899c1d | |||
| 374d159e0e | |||
| 4f5a41b84f | |||
| a2f380fdd6 | |||
| 301085e510 | |||
| af5b64a434 | |||
| fa088e37bc | |||
| 613bdc91e5 | |||
| edfe50e713 | |||
| 26fb403ced | |||
| 014fb46449 | |||
| 144a88b109 | |||
| 34c0d2509a | |||
| a12a9e3fd9 | |||
| 8df73f3d83 | |||
| 6dac874ff4 | |||
| 81d25f6861 | |||
| c0c1975da6 | |||
| dd7af29103 | |||
| 85ea5f67f4 | |||
| 8e3cb6174d | |||
| 6c01e9a43a | |||
| 6061670161 | |||
| 7c66db2c45 | |||
| 05e95ae55a | |||
| 416ad564d0 | |||
| 55f8f9bc42 | |||
| 386a9e88b4 | |||
| f6ca25edbe | |||
| 1ea221aa4e | |||
| d72a6666a1 | |||
| b69199f884 | |||
| 65042c86a6 | |||
| d7dccfd56d | |||
| e60f4a5e1e | |||
| fa3d4cbb23 | |||
| 56d303ec09 | |||
| 4600548b4d | |||
| 7d24c2ba40 | |||
| b4ce57caec | |||
| 43c09b2eaa | |||
| b32fd0bee4 | |||
| 3b6e96efa9 | |||
| 29ef828b9c | |||
| 39ae443433 | |||
| c30c2f8d38 | |||
| 5757f875f2 | |||
| e49c482a3f | |||
| a592d6c1aa | |||
| 88f154fecd | |||
| d7dfe3e812 | |||
| 489265e3ca | |||
| 3212a70a0c | |||
| 1cda0a96ca | |||
| 83fb9f7548 | |||
| 43295289bf | |||
| 0f72ca823b | |||
| 41786bc776 | |||
| cca5d7ebbf | |||
| 43c8764465 | |||
| a1e325a2d9 | |||
| 17a831e32f | |||
| 2a13db3bb8 | |||
| eb5a73538a | |||
| 006a6c787b | |||
| c3d96df0f0 | |||
| 15e7218bc4 | |||
| 28f32b9b8e | |||
| 1d0a56b673 | |||
| ad1424681b | |||
| 87b991c076 | |||
| 5d593f26af | |||
| 853c270fce | |||
| fcf34e0587 | |||
| b6611c8a3b | |||
| aab08d774b | |||
| 321aa52384 | |||
| 1334a74ef1 | |||
| 19e3384bb1 | |||
| 6ae32fd596 | |||
| 4c316ba4ce | |||
| f2719411b7 | |||
| 11faf6684e | |||
| 547d18ce46 | |||
| f183a81289 | |||
| e44c874cab | |||
| 9c7380efe4 | |||
| 5e071a2e4b | |||
| ff9b82c4f6 | |||
| a0d0ac6cff | |||
| b605a5de87 | |||
| 36be2b0688 | |||
| 376a5da705 | |||
| c25258b75c | |||
| c685f3a294 | |||
| f63767af0e | |||
| 4fa9a2e54b | |||
| 68bc592a05 | |||
| 543fe2f2a4 | |||
| be52f38d8f | |||
| 722a92f374 | |||
| d771d06e19 | |||
| a5227191b5 | |||
| 499d3d6d78 | |||
| 2adf020753 | |||
| e8c373694c | |||
| a8fd8cb645 | |||
| fd06084106 | |||
| 285500488d | |||
| 29d4ffd089 | |||
| fe5efa597b | |||
| b93629a903 | |||
| 0c72940933 | |||
| 96f5279e3e | |||
| 821fa10203 | |||
| 9082e74971 | |||
| 7a7d77e7bd | |||
| ce2ec94e96 | |||
| d56e5d6923 | |||
| 5221ecc055 | |||
| 6ac2c6f578 | |||
| 772b752fa5 | |||
| 3c002d08c2 | |||
| 2876424240 | |||
| 74a0177ec9 | |||
| da96844e9a | |||
| 81652c8244 | |||
| f17434c95b | |||
| 01e854b5d2 | |||
| 42953c7c44 | |||
| 5e56446d26 | |||
| 61652f4ad9 | |||
| fc1e84941d | |||
| 953eba0376 | |||
| 5c032269e3 | |||
| 1322d5a2c1 | |||
| 114f272906 | |||
| 1286bbb982 | |||
| 9141c649e7 | |||
| ff2242da31 | |||
| 730a419984 | |||
| 25bd7d7997 | |||
| 77c9d9207d | |||
| 1e77629f1f | |||
| 76f65456ab | |||
| 26e20e7ae5 | |||
| 96217cf3fe | |||
| e9c96d8ccd | |||
| 7ae2b968bb | |||
| 83ea42afe5 | |||
| 16587fcce1 | |||
| ffded44f32 | |||
| 92c5284b80 | |||
| f96102224c | |||
| bf87d626eb | |||
| e21a79dc61 | |||
| caec590fe1 | |||
| 61ce6c59c2 | |||
| 205a1a14c7 | |||
| 6709ab5f27 | |||
| 2d77279c94 | |||
| e9d7b6fbad | |||
| 3e6171c7ff | |||
| 04389fd6ff | |||
| a47b599048 | |||
| 58eddd5ab4 | |||
| 7ac857acef | |||
| e27b224933 | |||
| 4f087df286 | |||
| 87f6f6a3e3 | |||
| 94d1e94cf2 | |||
| d249868e07 | |||
| 429e48d873 | |||
| abb414c702 | |||
| 8f45dd4190 | |||
| d9098c5fcd | |||
| b9dee730a8 | |||
| 26c62a667f | |||
| 7d42ad00ff | |||
| 4a5776b513 | |||
| f68ba2ef35 | |||
| a95a0ab5f9 | |||
| 30911f4a75 | |||
| c9f184c8d5 | |||
| 545f1fd07d | |||
| 2f762265a8 | |||
| 7e6d8f39b4 | |||
| bea74b7711 | |||
| 10e8dc57eb | |||
| f7b6dde086 | |||
| 2fa36fa721 | |||
| 40f8202402 | |||
| 4b0b55c1b7 | |||
| 0e395973c9 | |||
| 1f53cdb62d | |||
| c32023d8ed | |||
| d2f98940d8 | |||
| 536b16c7a9 | |||
| 9bfeae2df1 | |||
| 4d450ea25e | |||
| 84f01d1e30 | |||
| 1a5636b199 | |||
| af46df7eae | |||
| 801a1791d6 | |||
| 333cf0b280 | |||
| 52e26697a9 | |||
| fdf620b688 | |||
| 66f8ca52d6 | |||
| f98c1127a6 | |||
| 7e352fe28d | |||
| d3b48e2fd5 | |||
| 23159c094d | |||
| 2e22887f71 | |||
| c9b04b8be7 | |||
| 2c0e13b513 | |||
| 2937309f00 | |||
| 377056a33c | |||
| f42ce5be2b | |||
| ddeee9b6db | |||
| 6bee32c174 | |||
| 11f55a06dd | |||
| 6a2d6b872b | |||
| d8787867c4 | |||
| 43d9f6ea17 | |||
| 1faba1302f | |||
| 55ee0a0934 | |||
| 0653f73779 | |||
| 7a24021dc9 | |||
| 9f2fe94484 | |||
| a3950b5638 | |||
| 8c03428279 | |||
| f4d7ea386d | |||
| bee0496cb1 | |||
| 5960b90efe | |||
| 0f2f3d4e29 | |||
| bc673bef79 | |||
| 3fb7391b97 | |||
| 442cd7515d | |||
| a013ffe547 | |||
| 835558229f | |||
| dce23c79a1 | |||
| 4106bae79e | |||
| b756474d9d | |||
| b0c9209726 | |||
| c96b778771 | |||
| d0d721d80b | |||
| 7b50a78662 | |||
| aa869c637a | |||
| f467fe1cbf | |||
| 15fd639cd9 | |||
| f96f2cf56e | |||
| b0f6278cea | |||
| ae7bac9a6b | |||
| c8997204d4 | |||
| a2c51984a8 | |||
| ed93e33d59 | |||
| 6e15b1cd20 | |||
| cc2513facf | |||
| e303dcc915 | |||
| 8ad17abb7b | |||
| 5b296b5257 | |||
| 1a5c32f3ef | |||
| a15e745d42 | |||
| d17dd3e3d8 | |||
| 51a5c829bb | |||
| 8a91749e12 | |||
| 42fc0f9ee8 | |||
| 17ccd55d11 | |||
| b8a589d76f | |||
| becf6d9413 | |||
| 9efc4b23dc | |||
| 6b1dc31759 | |||
| 9205d632cc | |||
| a751346b20 | |||
| 8111a19307 | |||
| 860b4f1a3b | |||
| a5b1aa9ca7 | |||
| 62257bce18 | |||
| 0ed74aa68b | |||
| e0bd0d6470 | |||
| b433550556 | |||
| 04ebd8875b | |||
| 1a1ed0ae45 | |||
| d6fabb6f8e | |||
| 08e8194db1 | |||
| 0e4e050b21 | |||
| de17b285b1 | |||
| f640d0973d | |||
| 47ff0dc0cc | |||
| 965e1d63dc | |||
| 73f1da2ff7 | |||
| 0f6ad43e91 | |||
| 8d804a6c4a | |||
| 02f48aabbf | |||
| d4b08e9566 | |||
| 9f1f58fd4b | |||
| 4ed92d50e0 | |||
| 8848004cb7 | |||
| 371b381074 | |||
| 6d47011dcf | |||
| 705cdb6368 | |||
| 90fc80fd87 | |||
| 041974ee01 | |||
| 7369d861ca | |||
| e1da383aed | |||
| 6bcf29152f | |||
| 8a38211bd9 | |||
| cb2b9feb5f | |||
| c2d5d5619b | |||
| 76b7d6975f | |||
| 7e9186d1ae | |||
| d1ce4c9923 | |||
| fed7fa4cd2 | |||
| 0d0125fc29 | |||
| 98e2c0522f | |||
| 294bc8c1d7 | |||
| ae11d83684 | |||
| aab9b08ec2 | |||
| 31ff7db019 | |||
| c40c54fbee | |||
| cecbaf2efe | |||
| e90413243e | |||
| a9396efeaf | |||
| 60353c6d05 | |||
| 7b5ed50786 | |||
| 02baa2b17c | |||
| d0585bd910 | |||
| adf7124446 | |||
| 7710db4591 | |||
| ce03cf49d8 | |||
| 65c0d14a4e | |||
| 667ed6fcf5 | |||
| d7da40642d | |||
| d6b5047511 | |||
| 3b5a751b25 | |||
| 8a7491c97a | |||
| b3cab31cf7 | |||
| 021655a10a | |||
| e301d43704 | |||
| 54c1a208c3 | |||
| ad9bbda708 | |||
| 341e167eab | |||
| fb031d1ad5 | |||
| d3937b9a61 | |||
| 2c734e2e4c | |||
| 77d8823261 | |||
| 6f85c14738 | |||
| edb1dc067a | |||
| 6b161d5389 | |||
| c21e3feaa6 | |||
| a25ef01da3 | |||
| 0534266490 | |||
| 71aca3c199 | |||
| 72fab80a74 | |||
| c8ab058842 | |||
| b166fd7d6e | |||
| 9545face97 | |||
| 6dacd51b81 | |||
| 571a182712 | |||
| e3aabdc2bc | |||
| 4d8d5b12f2 | |||
| 5785e7e2e5 | |||
| 81f311105c | |||
| ad3679fcb8 | |||
| 29508f1cc7 | |||
| 426ba62348 | |||
| d2df055abe | |||
| d3ccdbcf72 | |||
| 7a476fc964 | |||
| 5da28be2b2 | |||
| 0932bdea30 | |||
| 75054e17cf | |||
| 664907f02e | |||
| f977272732 | |||
| d10b7b43ea | |||
| e9abe1b846 | |||
| cc21f38664 | |||
| 34606c26f0 | |||
| be9d66a574 | |||
| ed9fcde499 | |||
| 3c2139b0c3 | |||
| 7eeec91ab5 | |||
| fdd43ee448 | |||
| ca75492512 | |||
| a2a7644794 | |||
| 91aebc758c | |||
| ca0d29d811 | |||
| c8c6aa2a1f | |||
| 19a1a22e63 | |||
| c729d62f89 | |||
| 1793b50e6b | |||
| 23dbe6a5f0 | |||
| 509601e617 | |||
| d58beed752 | |||
| fd5d1076dc | |||
| 05b76a1f93 | |||
| f84ec803a4 | |||
| c35cd7fcb7 | |||
| bb9d50579b | |||
| 54d37512f5 | |||
| c516be8c4b | |||
| 82c78ec8f9 | |||
| b1d4b71609 | |||
| 7440c7ddc7 | |||
| 624dad5a78 | |||
| f4080210bd | |||
| c631eb43e3 | |||
| 376bedf624 | |||
| 36e9c02f6f | |||
| 8f798569a8 | |||
| 8c0e47baf9 | |||
| 461473e48e | |||
| 8b1f109c1d | |||
| e005cb31d8 | |||
| 579836e221 | |||
| b977ee6160 | |||
| ddce65b9dd | |||
| e0e1105965 | |||
| 1f520f9f99 | |||
| 05a929deb6 | |||
| 5ac5352e33 | |||
| 787a555253 | |||
| 388b84e858 | |||
| 7ffd17254a | |||
| 00affe187c | |||
| 05844efbd6 | |||
| 3ab37028fc | |||
| 3a04888c5f | |||
| 6bd518241a | |||
| a3cddb856f | |||
| 4b8e08d202 | |||
| 9413d30396 | |||
| 5982fa63c6 | |||
| a4621a6418 | |||
| 7eec21a5e6 | |||
| 96b72fe2f7 | |||
| c7c9606e87 | |||
| 772c135d98 | |||
| 365b3a76ba | |||
| 41568b7f2a | |||
| d853f6cf0e | |||
| bcfcce32d3 | |||
| 986916cf3e | |||
| d689656775 | |||
| 8c56df7322 | |||
| 1dc87761a0 | |||
| cd88b92587 | |||
| eab03fdfc4 | |||
| 9a8b86872d | |||
| 04ce039ba4 | |||
| 9605cbda45 | |||
| 3ec2739925 | |||
| d250bf9b56 | |||
| 6ebccb0518 | |||
| b30efe4e2f | |||
| c275d279fc | |||
| 7237a3c3b6 | |||
| f98896f6d7 | |||
| bcb2cdae4c | |||
| 08159211a3 | |||
| 232b6ab7ef | |||
| 3d2676d013 | |||
| 477d06f990 | |||
| ed19885ba1 | |||
| 297ae6fdb4 | |||
| 456a985666 | |||
| 261203d6c1 | |||
| 31262d2ea9 | |||
| a8930b161e | |||
| fa31d45285 | |||
| 914e4a4cf7 | |||
| f36d079f28 | |||
| 17ddeef3ba | |||
| f0c90ce668 | |||
| f19b312c48 | |||
| e899a54ba7 | |||
| a4db994c2f | |||
| f98f46c836 | |||
| 336aaaec9a | |||
| b13ec8526e | |||
| 541020ff1b | |||
| 9c153737b4 | |||
| 7d53bb4064 | |||
| 4670751479 | |||
| 8e058f16e4 | |||
| f519f0a5b3 | |||
| bb61304a49 | |||
| 781d991eac | |||
| d04c1f93a3 |
+6
-1
@@ -1,3 +1,4 @@
|
||||
coverage.html
|
||||
.DS_Store
|
||||
lib-cov
|
||||
*.seed
|
||||
@@ -9,5 +10,9 @@ lib-cov
|
||||
*.swp
|
||||
*.swo
|
||||
benchmarks/graphs
|
||||
testing.js
|
||||
testing
|
||||
node_modules/
|
||||
testing
|
||||
.coverage_data
|
||||
cover_html
|
||||
test.js
|
||||
|
||||
@@ -5,3 +5,4 @@ support/
|
||||
test/
|
||||
testing.js
|
||||
.DS_Store
|
||||
coverage.html
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
language: node_js
|
||||
node_js:
|
||||
- 0.6
|
||||
@@ -1,4 +1,23 @@
|
||||
|
||||
2.4.5 / 2011-08-19
|
||||
==================
|
||||
|
||||
* Added support for routes to handle errors. Closes #809
|
||||
* Added `app.routes.all()`. Closes #803
|
||||
* Added "basepath" setting to work in conjunction with reverse proxies etc. * Refactored `Route` to use a single array of callbacks
|
||||
* Added support for multiple callbacks for `app.param()`. Closes #801
|
||||
Closes #805
|
||||
* Changed: removed .call(self) for route callbacks
|
||||
* Dependency: `qs >= 0.3.1`
|
||||
* Fixed `res.redirect()` on windows due to `join()` usage. Closes #808
|
||||
|
||||
2.4.4 / 2011-08-05
|
||||
==================
|
||||
|
||||
* Fixed `res.header()` intention of a set, even when `undefined`
|
||||
* Fixed `*`, value no longer required
|
||||
* Fixed `res.send(204)` support. Closes #771
|
||||
|
||||
2.4.3 / 2011-07-14
|
||||
==================
|
||||
|
||||
|
||||
+12
-28
@@ -1,35 +1,19 @@
|
||||
|
||||
DOCS = $(shell find docs/*.md)
|
||||
HTMLDOCS =$(DOCS:.md=.html)
|
||||
TESTS = $(shell find test/*.test.js)
|
||||
REPORTER = dot
|
||||
|
||||
test:
|
||||
@NODE_ENV=test ./node_modules/.bin/expresso \
|
||||
-I lib \
|
||||
$(TESTFLAGS) \
|
||||
$(TESTS)
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter $(REPORTER)
|
||||
|
||||
test-cov:
|
||||
@TESTFLAGS=--cov $(MAKE) test
|
||||
test-acceptance:
|
||||
@NODE_ENV=test ./node_modules/.bin/mocha \
|
||||
--reporter spec \
|
||||
test/acceptance/*.js
|
||||
|
||||
docs: $(HTMLDOCS)
|
||||
@ echo "... generating TOC"
|
||||
@./support/toc.js docs/guide.html
|
||||
test-cov: lib-cov
|
||||
@EXPRESS_COV=1 $(MAKE) test REPORTER=html-cov > coverage.html
|
||||
|
||||
%.html: %.md
|
||||
@echo "... $< -> $@"
|
||||
@markdown $< \
|
||||
| cat docs/layout/head.html - docs/layout/foot.html \
|
||||
> $@
|
||||
lib-cov:
|
||||
@jscoverage lib lib-cov
|
||||
|
||||
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: site test test-cov docs docclean
|
||||
.PHONY: site test test-acceptance
|
||||
|
||||
+100
-65
@@ -1,23 +1,20 @@
|
||||
|
||||
# Express
|
||||
|
||||
Insanely fast (and small) server-side JavaScript web development framework
|
||||
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');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||

|
||||
|
||||
Fast, unopinionated, minimalist web framework for [node](http://nodejs.org).
|
||||
|
||||
```js
|
||||
var app = express.createServer();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
$ npm install express
|
||||
|
||||
or to access the `express(1)` executable install globally:
|
||||
|
||||
$ npm install -g express
|
||||
|
||||
## Quick Start
|
||||
@@ -31,81 +28,55 @@ or to access the `express(1)` executable install globally:
|
||||
|
||||
Install dependencies:
|
||||
|
||||
$ npm install -d
|
||||
$ npm install
|
||||
|
||||
Start the server:
|
||||
|
||||
$ node app.js
|
||||
$ node app
|
||||
|
||||
## Features
|
||||
|
||||
* Built on [Connect](http://github.com/senchalabs/connect)
|
||||
* Robust routing
|
||||
* Redirection helpers
|
||||
* Dynamic view helpers
|
||||
* HTTP helpers (redirection, caching, etc)
|
||||
* View system supporting 14+ template engines
|
||||
* Content negotiation
|
||||
* Focus on high performance
|
||||
* View rendering and partials support
|
||||
* Environment based configuration
|
||||
* Session based flash notifications
|
||||
* Built on [Connect](http://github.com/senchalabs/connect)
|
||||
* High test coverage
|
||||
* Executable for generating applications quickly
|
||||
* Application level view options
|
||||
* High test coverage
|
||||
|
||||
Via Connect:
|
||||
## Philosophy
|
||||
|
||||
* Session support
|
||||
* Cache API
|
||||
* Mime helpers
|
||||
* ETag support
|
||||
* Persistent flash notifications
|
||||
* Cookie support
|
||||
* JSON-RPC
|
||||
* Logging
|
||||
* and _much_ more!
|
||||
|
||||
## Contributors
|
||||
|
||||
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))
|
||||
The Express philosophy is to provide small, robust tooling for HTTP servers. Making
|
||||
it a great solution for single page applications, web sites, hybrids, or public
|
||||
HTTP APIs.
|
||||
|
||||
Built on Connect you can use _only_ what you need, and nothing more, applications
|
||||
can be as big or as small as you like, even a single file. Express does
|
||||
not force you to use any specific ORM or template engine. With support for over
|
||||
14 template engines via [Consolidate.js](github.com/visionmedia/consolidate.js) you
|
||||
can quickly craft your perfect framework.
|
||||
|
||||
## More Information
|
||||
|
||||
* #express on freenode
|
||||
* [express-expose](http://github.com/visionmedia/express-expose) expose objects, functions, modules and more to client-side js with ease
|
||||
* [express-configure](http://github.com/visionmedia/express-configuration) async configuration support
|
||||
* [express-messages](http://github.com/visionmedia/express-messages) flash notification rendering helper
|
||||
* [express-namespace](http://github.com/visionmedia/express-namespace) namespaced route support
|
||||
* [express-params](https://github.com/visionmedia/express-params) param pre-condition functions
|
||||
* [express-mongoose](https://github.com/LearnBoost/express-mongoose) plugin for easy rendering of Mongoose async Query results
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* Join #express on freenode
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Follow [tjholowaychuk](http://twitter.com/tjholowaychuk) on twitter for updates
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito)
|
||||
* Screencast - [Introduction](http://bit.ly/eRYu0O)
|
||||
* Screencast - [View Partials](http://bit.ly/dU13Fx)
|
||||
* Screencast - [Route Specific Middleware](http://bit.ly/hX4IaH)
|
||||
* Screencast - [Route Path Placeholder Preconditions](http://bit.ly/eNqmVs)
|
||||
|
||||
## Node Compatibility
|
||||
|
||||
Express 1.x is compatible with node 0.2.x and connect < 1.0.
|
||||
|
||||
Express 2.x is compatible with node 0.4.x and connect 1.x
|
||||
* [Русскоязычная документация](http://express-js.ru/)
|
||||
|
||||
## Viewing Examples
|
||||
|
||||
First install the dev dependencies to install all the example / test suite deps:
|
||||
|
||||
$ npm install
|
||||
$ cd express
|
||||
$ npm install -d
|
||||
|
||||
then run whichever tests you want:
|
||||
|
||||
$ node examples/jade/app.js
|
||||
$ node examples/content-negotiation
|
||||
|
||||
## Running Tests
|
||||
|
||||
@@ -117,11 +88,75 @@ then run the tests:
|
||||
|
||||
$ make test
|
||||
|
||||
## Contributors
|
||||
|
||||
```
|
||||
project: express
|
||||
commits: 3559
|
||||
active : 468 days
|
||||
files : 237
|
||||
authors:
|
||||
1891 Tj Holowaychuk 53.1%
|
||||
1285 visionmedia 36.1%
|
||||
182 TJ Holowaychuk 5.1%
|
||||
54 Aaron Heckmann 1.5%
|
||||
34 csausdev 1.0%
|
||||
26 ciaranj 0.7%
|
||||
21 Robert Sköld 0.6%
|
||||
6 Guillermo Rauch 0.2%
|
||||
3 Dav Glass 0.1%
|
||||
3 Nick Poulden 0.1%
|
||||
2 Randy Merrill 0.1%
|
||||
2 Benny Wong 0.1%
|
||||
2 Hunter Loftis 0.1%
|
||||
2 Jake Gordon 0.1%
|
||||
2 Brian McKinney 0.1%
|
||||
2 Roman Shtylman 0.1%
|
||||
2 Ben Weaver 0.1%
|
||||
2 Dave Hoover 0.1%
|
||||
2 Eivind Fjeldstad 0.1%
|
||||
2 Daniel Shaw 0.1%
|
||||
1 Matt Colyer 0.0%
|
||||
1 Pau Ramon 0.0%
|
||||
1 Pero Pejovic 0.0%
|
||||
1 Peter Rekdal Sunde 0.0%
|
||||
1 Raynos 0.0%
|
||||
1 Teng Siong Ong 0.0%
|
||||
1 Viktor Kelemen 0.0%
|
||||
1 ctide 0.0%
|
||||
1 8bitDesigner 0.0%
|
||||
1 isaacs 0.0%
|
||||
1 mgutz 0.0%
|
||||
1 pikeas 0.0%
|
||||
1 shuwatto 0.0%
|
||||
1 tstrimple 0.0%
|
||||
1 ewoudj 0.0%
|
||||
1 Adam Sanderson 0.0%
|
||||
1 Andrii Kostenko 0.0%
|
||||
1 Andy Hiew 0.0%
|
||||
1 Arpad Borsos 0.0%
|
||||
1 Ashwin Purohit 0.0%
|
||||
1 Benjen 0.0%
|
||||
1 Darren Torpey 0.0%
|
||||
1 Greg Ritter 0.0%
|
||||
1 Gregory Ritter 0.0%
|
||||
1 James Herdman 0.0%
|
||||
1 Jim Snodgrass 0.0%
|
||||
1 Joe McCann 0.0%
|
||||
1 Jonathan Dumaine 0.0%
|
||||
1 Jonathan Palardy 0.0%
|
||||
1 Jonathan Zacsh 0.0%
|
||||
1 Justin Lilly 0.0%
|
||||
1 Ken Sato 0.0%
|
||||
1 Maciej Małecki 0.0%
|
||||
1 Masahiro Hayashi 0.0%
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2009-2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
Copyright (c) 2009-2012 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
|
||||
|
||||
+156
-194
@@ -4,48 +4,52 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var fs = require('fs')
|
||||
, exec = require('child_process').exec;
|
||||
var express = require('../')
|
||||
, exec = require('child_process').exec
|
||||
, program = require('commander')
|
||||
, mkdirp = require('mkdirp')
|
||||
, os = require('os')
|
||||
, fs = require('fs');
|
||||
|
||||
// CLI
|
||||
|
||||
program
|
||||
.version(express.version)
|
||||
.option('-s, --sessions', 'add session support')
|
||||
.option('-e, --ejs', 'add ejs engine support (defaults to jade)')
|
||||
.option('-J, --jshtml', 'add jshtml engine support (defaults to jade)')
|
||||
.option('-c, --css <engine>', 'add stylesheet <engine> support (less|stylus) (defaults to plain css)')
|
||||
.option('-f, --force', 'force on non-empty directory')
|
||||
.parse(process.argv);
|
||||
|
||||
// Path
|
||||
|
||||
var path = program.args.shift() || '.';
|
||||
|
||||
// end-of-line code
|
||||
|
||||
var eol = 'win32' == os.platform() ? '\r\n' : '\n'
|
||||
|
||||
// Template engine
|
||||
|
||||
program.template = 'jade';
|
||||
if (program.ejs) program.template = 'ejs';
|
||||
if (program.jshtml) program.template = 'jshtml';
|
||||
|
||||
/**
|
||||
* Framework version.
|
||||
* Routes index template.
|
||||
*/
|
||||
|
||||
var version = '2.4.3';
|
||||
|
||||
/**
|
||||
* Add session support.
|
||||
*/
|
||||
|
||||
var sessions = false;
|
||||
|
||||
/**
|
||||
* CSS engine to utilize.
|
||||
*/
|
||||
|
||||
var cssEngine;
|
||||
|
||||
/**
|
||||
* Template engine to utilize.
|
||||
*/
|
||||
|
||||
var templateEngine = 'jade';
|
||||
|
||||
/**
|
||||
* Usage documentation.
|
||||
*/
|
||||
|
||||
var usage = ''
|
||||
+ '\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'
|
||||
;
|
||||
var index = [
|
||||
''
|
||||
, '/*'
|
||||
, ' * GET home page.'
|
||||
, ' */'
|
||||
, ''
|
||||
, 'exports.index = function(req, res){'
|
||||
, ' res.render(\'index\', { title: \'Express\' });'
|
||||
, '};'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Jade layout template.
|
||||
@@ -57,23 +61,27 @@ var jadeLayout = [
|
||||
, ' head'
|
||||
, ' title= title'
|
||||
, ' link(rel=\'stylesheet\', href=\'/stylesheets/style.css\')'
|
||||
, ' body!= body'
|
||||
].join('\n');
|
||||
, ' body'
|
||||
, ' block content'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Jade index template.
|
||||
*/
|
||||
|
||||
var jadeIndex = [
|
||||
'h1= title'
|
||||
, 'p Welcome to #{title}'
|
||||
].join('\n');
|
||||
'extends layout'
|
||||
, ''
|
||||
, 'block content'
|
||||
, ' h1= title'
|
||||
, ' p Welcome to #{title}'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* EJS layout template.
|
||||
* EJS index template.
|
||||
*/
|
||||
|
||||
var ejsLayout = [
|
||||
var ejsIndex = [
|
||||
'<!DOCTYPE html>'
|
||||
, '<html>'
|
||||
, ' <head>'
|
||||
@@ -81,19 +89,37 @@ var ejsLayout = [
|
||||
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
|
||||
, ' </head>'
|
||||
, ' <body>'
|
||||
, ' <%- body %>'
|
||||
, ' <h1><%= title %></h1>'
|
||||
, ' <p>Welcome to <%= title %></p>'
|
||||
, ' </body>'
|
||||
, '</html>'
|
||||
].join('\n');
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* EJS index template.
|
||||
* JSHTML layout template.
|
||||
*/
|
||||
|
||||
var ejsIndex = [
|
||||
'<h1><%= title %></h1>'
|
||||
, '<p>Welcome to <%= title %></p>'
|
||||
].join('\n');
|
||||
var jshtmlLayout = [
|
||||
'<!DOCTYPE html>'
|
||||
, '<html>'
|
||||
, ' <head>'
|
||||
, ' <title> @write(title) </title>'
|
||||
, ' <link rel=\'stylesheet\' href=\'/stylesheets/style.css\' />'
|
||||
, ' </head>'
|
||||
, ' <body>'
|
||||
, ' @write(body)'
|
||||
, ' </body>'
|
||||
, '</html>'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* JSHTML index template.
|
||||
*/
|
||||
|
||||
var jshtmlIndex = [
|
||||
'<h1>@write(title)</h1>'
|
||||
, '<p>Welcome to @write(title)</p>'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Default css template.
|
||||
@@ -108,7 +134,7 @@ var css = [
|
||||
, 'a {'
|
||||
, ' color: #00B7FF;'
|
||||
, '}'
|
||||
].join('\n');
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Default less template.
|
||||
@@ -123,19 +149,7 @@ var less = [
|
||||
, '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');
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* Default stylus template.
|
||||
@@ -143,11 +157,11 @@ var sass = [
|
||||
|
||||
var stylus = [
|
||||
'body'
|
||||
, ' padding 50px'
|
||||
, ' font 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, ' padding: 50px'
|
||||
, ' font: 14px "Lucida Grande", Helvetica, Arial, sans-serif'
|
||||
, 'a'
|
||||
, ' color #00B7FF'
|
||||
].join('\n');
|
||||
, ' color: #00B7FF'
|
||||
].join(eol);
|
||||
|
||||
/**
|
||||
* App template.
|
||||
@@ -159,88 +173,43 @@ var app = [
|
||||
, ' * Module dependencies.'
|
||||
, ' */'
|
||||
, ''
|
||||
, 'var express = require(\'express\');'
|
||||
, 'var express = require(\'express\')'
|
||||
, ' , routes = require(\'./routes\')'
|
||||
, ' , http = require(\'http\');'
|
||||
, ''
|
||||
, 'var app = module.exports = express.createServer();'
|
||||
, ''
|
||||
, '// Configuration'
|
||||
, 'var app = express();'
|
||||
, ''
|
||||
, '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.favicon());'
|
||||
, ' app.use(express.logger(\'dev\'));{css}'
|
||||
, ' app.use(express.static(__dirname + \'/public\'));'
|
||||
, ' app.use(express.bodyParser());'
|
||||
, ' app.use(express.methodOverride());{sess}'
|
||||
, ' app.use(app.router);'
|
||||
, '});'
|
||||
, ''
|
||||
, 'app.configure(\'development\', function(){'
|
||||
, ' app.use(express.errorHandler({ dumpExceptions: true, showStack: true })); '
|
||||
, ' app.use(express.errorHandler());'
|
||||
, '});'
|
||||
, ''
|
||||
, 'app.configure(\'production\', function(){'
|
||||
, ' app.use(express.errorHandler()); '
|
||||
, '});'
|
||||
, 'app.get(\'/\', routes.index);'
|
||||
, ''
|
||||
, '// Routes'
|
||||
, 'http.createServer(app).listen(3000);'
|
||||
, ''
|
||||
, 'app.get(\'/\', function(req, res){'
|
||||
, ' res.render(\'index\', {'
|
||||
, ' title: \'Express\''
|
||||
, ' });'
|
||||
, '});'
|
||||
, 'console.log("Express server listening on port 3000");'
|
||||
, ''
|
||||
, 'app.listen(3000);'
|
||||
, 'console.log("Express server listening on port %d in %s mode", app.address().port, app.settings.env);'
|
||||
, ''
|
||||
].join('\n');
|
||||
|
||||
// Parse arguments
|
||||
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
].join(eol);
|
||||
|
||||
// Generate application
|
||||
|
||||
(function createApplication(path) {
|
||||
emptyDirectory(path, function(empty){
|
||||
if (empty) {
|
||||
if (empty || program.force) {
|
||||
createApplicationAt(path);
|
||||
} else {
|
||||
confirm('destination is not empty, continue? ', function(ok){
|
||||
program.confirm('destination is not empty, continue? ', function(ok){
|
||||
if (ok) {
|
||||
process.stdin.destroy();
|
||||
createApplicationAt(path);
|
||||
@@ -259,72 +228,100 @@ while (args.length) {
|
||||
*/
|
||||
|
||||
function createApplicationAt(path) {
|
||||
console.log();
|
||||
process.on('exit', function(){
|
||||
console.log();
|
||||
console.log(' install dependencies:');
|
||||
console.log(' $ cd %s && npm install', path);
|
||||
console.log();
|
||||
console.log(' run the app:');
|
||||
console.log(' $ node app');
|
||||
console.log();
|
||||
});
|
||||
|
||||
mkdir(path, function(){
|
||||
mkdir(path + '/public');
|
||||
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;
|
||||
switch (program.css) {
|
||||
case 'less':
|
||||
write(path + '/public/stylesheets/style.less', less);
|
||||
break;
|
||||
case 'sass':
|
||||
write(path + '/public/stylesheets/style.sass', sass);
|
||||
case 'stylus':
|
||||
write(path + '/public/stylesheets/style.styl', stylus);
|
||||
break;
|
||||
default:
|
||||
write(path + '/public/stylesheets/style.css', css);
|
||||
}
|
||||
});
|
||||
|
||||
mkdir(path + '/routes', function(){
|
||||
write(path + '/routes/index.js', index);
|
||||
});
|
||||
|
||||
mkdir(path + '/views', function(){
|
||||
switch (templateEngine) {
|
||||
switch (program.template) {
|
||||
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;
|
||||
case 'jshtml':
|
||||
write(path + '/views/layout.jshtml', jshtmlLayout);
|
||||
write(path + '/views/index.jshtml', jshtmlIndex);
|
||||
break;
|
||||
|
||||
}
|
||||
});
|
||||
|
||||
// CSS Engine support
|
||||
switch (cssEngine) {
|
||||
case 'sass':
|
||||
switch (program.css) {
|
||||
case 'less':
|
||||
app = app.replace('{css}', '\n app.use(express.compiler({ src: __dirname + \'/public\', enable: [\'' + cssEngine + '\'] }));');
|
||||
app = app.replace('{css}', eol + ' app.use(require(\'less-middleware\')({ src: __dirname + \'/public\' }));');
|
||||
break;
|
||||
case 'stylus':
|
||||
app = app.replace('{css}', '\n app.use(require(\'stylus\').middleware({ src: __dirname + \'/public\' }));');
|
||||
app = app.replace('{css}', eol + ' 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\' }));'
|
||||
app = app.replace('{sess}', program.sessions
|
||||
? eol + ' app.use(express.cookieParser(\'your secret here\'));' + eol + ' app.use(express.session());'
|
||||
: '');
|
||||
|
||||
// Template support
|
||||
app = app.replace(':TEMPLATE', templateEngine);
|
||||
app = app.replace(':TEMPLATE', program.template);
|
||||
|
||||
// package.json
|
||||
var json = '{\n';
|
||||
json += ' "name": "application-name"\n';
|
||||
json += ' , "version": "0.0.1"\n';
|
||||
json += ' , "private": true\n';
|
||||
json += ' , "dependencies": {\n';
|
||||
json += ' "express": "' + version + '"\n';
|
||||
if (cssEngine) json += ' , "' + cssEngine + '": ">= 0.0.1"\n';
|
||||
if (templateEngine) json += ' , "' + templateEngine + '": ">= 0.0.1"\n';
|
||||
json += ' }\n';
|
||||
json += '}';
|
||||
var pkg = {
|
||||
name: 'application-name'
|
||||
, version: '0.0.1'
|
||||
, private: true
|
||||
, scripts: { start: 'node app' }
|
||||
, dependencies: {
|
||||
express: express.version
|
||||
}
|
||||
}
|
||||
|
||||
if (program.template) pkg.dependencies[program.template] = '*';
|
||||
|
||||
write(path + '/package.json', json);
|
||||
// CSS Engine support
|
||||
switch (program.css) {
|
||||
case 'less':
|
||||
pkg.dependencies['less-middleware'] = '*';
|
||||
break;
|
||||
default:
|
||||
if (program.css) {
|
||||
pkg.dependencies[program.css] = '*';
|
||||
}
|
||||
}
|
||||
|
||||
write(path + '/package.json', JSON.stringify(pkg, null, 2));
|
||||
write(path + '/app.js', app);
|
||||
});
|
||||
}
|
||||
@@ -355,41 +352,6 @@ function write(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();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mkdir -p.
|
||||
*
|
||||
@@ -398,9 +360,9 @@ function prompt(msg, fn) {
|
||||
*/
|
||||
|
||||
function mkdir(path, fn) {
|
||||
exec('mkdir -p ' + path, function(err){
|
||||
mkdirp(path, 0755, function(err){
|
||||
if (err) throw err;
|
||||
console.log(' \x1b[36mcreate\x1b[0m : ' + path);
|
||||
console.log(' \033[36mcreate\033[0m : ' + path);
|
||||
fn && fn();
|
||||
});
|
||||
}
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
|
||||
var http = require('http');
|
||||
|
||||
var times = 50;
|
||||
|
||||
while (times--) {
|
||||
var req = http.request({
|
||||
port: 3000
|
||||
, method: 'POST'
|
||||
, headers: { 'Content-Type': 'application/x-www-form-urlencoded' }
|
||||
});
|
||||
|
||||
req.on('response', function(res){
|
||||
console.log(res.statusCode);
|
||||
});
|
||||
|
||||
var n = 500000;
|
||||
while (n--) {
|
||||
req.write('foo=bar&bar=baz&');
|
||||
}
|
||||
|
||||
req.write('foo=bar&bar=baz');
|
||||
|
||||
req.end();
|
||||
}
|
||||
+4
-4
@@ -532,7 +532,7 @@ var app = express.createServer(
|
||||
);
|
||||
</code></pre>
|
||||
|
||||
<p>Alternatively we can <em>use()</em> them which is useful when adding middleware within <em>configure()</em> blocks, in a progressive manor.</p>
|
||||
<p>Alternatively we can <em>use()</em> them which is useful when adding middleware within <em>configure()</em> blocks, in a progressive manner.</p>
|
||||
|
||||
<pre><code>app.use(express.logger({ format: ':method :url' }));
|
||||
</code></pre>
|
||||
@@ -658,7 +658,7 @@ app.get('/', all, function(){});
|
||||
|
||||
<h3 id="http-methods">HTTP Methods</h3>
|
||||
|
||||
<p>We have seen <em>app.get()</em> a few times, however Express also exposes other familiar HTTP verbs in the same manor, such as <em>app.post()</em>, <em>app.del()</em>, etc.</p>
|
||||
<p>We have seen <em>app.get()</em> a few times, however Express also exposes other familiar HTTP verbs in the same manner, such as <em>app.post()</em>, <em>app.del()</em>, etc.</p>
|
||||
|
||||
<p> A common example for <em>POST</em> usage, is when “submitting” a form. Below we simply set our form method to “post” in our html, and control will be given to the route we have defined below it.</p>
|
||||
|
||||
@@ -1451,7 +1451,7 @@ partial('movie', movie)
|
||||
<pre><code> res.local('foo', bar);
|
||||
res.local('bar', baz);
|
||||
|
||||
res.locals({ foo: bar, bar, baz });
|
||||
res.locals({ foo: bar, bar: baz });
|
||||
</code></pre>
|
||||
|
||||
<h3 id="app.set()">app.set(name[, val])</h3>
|
||||
@@ -1804,4 +1804,4 @@ Hello World
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
+60
-118
@@ -107,8 +107,8 @@ Express supports the following settings out of the box:
|
||||
* _home_ Application base path used for _res.redirect()_ and transparently handling mounted apps.
|
||||
* _views_ Root views directory defaulting to **CWD/views**
|
||||
* _view engine_ Default view engine name for views rendered without extensions
|
||||
* _view options_ An object specifying global view options
|
||||
* _view cache_ Enable view caching (enabled in production)
|
||||
* _charset_ Alter the default charset of "utf-8"
|
||||
* _case sensitive routes_ Enable case-sensitive routing
|
||||
* _strict routing_ When enabled trailing slashes are no longer ignored
|
||||
* _jsonp callback_ Enable _res.send()_ / _res.json()_ transparent jsonp support
|
||||
@@ -245,7 +245,7 @@ The _app.all()_ method is useful for applying the same logic for all HTTP verbs
|
||||
});
|
||||
|
||||
app.get('*', function(req, res){
|
||||
res.send('what???', 404);
|
||||
res.send(404, 'what???');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
@@ -262,7 +262,7 @@ passed to _express.createServer()_ as you would with a regular Connect server. F
|
||||
, express.bodyParser()
|
||||
);
|
||||
|
||||
Alternatively we can _use()_ them which is useful when adding middleware within _configure()_ blocks, in a progressive manor.
|
||||
Alternatively we can _use()_ them which is useful when adding middleware within _configure()_ blocks, in a progressive manner.
|
||||
|
||||
app.use(express.logger({ format: ':method :url' }));
|
||||
|
||||
@@ -379,7 +379,7 @@ There are times when we may want to "skip" passed remaining route middleware, bu
|
||||
|
||||
### HTTP Methods
|
||||
|
||||
We have seen _app.get()_ a few times, however Express also exposes other familiar HTTP verbs in the same manor, such as _app.post()_, _app.del()_, etc.
|
||||
We have seen _app.get()_ a few times, however Express also exposes other familiar HTTP verbs in the same manner, such as _app.post()_, _app.del()_, etc.
|
||||
|
||||
A common example for _POST_ usage, is when "submitting" a form. Below we simply set our form method to "post" in our html, and control will be given to the route we have defined below it.
|
||||
|
||||
@@ -421,65 +421,16 @@ The reason that these are not always defaults, is simply because these are not r
|
||||
|
||||
### Error Handling
|
||||
|
||||
Express provides the _app.error()_ method which receives exceptions thrown within a route,
|
||||
or passed to _next(err)_. Below is an example which serves different pages based on our
|
||||
ad-hoc _NotFound_ exception:
|
||||
|
||||
function NotFound(msg){
|
||||
this.name = 'NotFound';
|
||||
Error.call(this, msg);
|
||||
Error.captureStackTrace(this, arguments.callee);
|
||||
}
|
||||
|
||||
NotFound.prototype.__proto__ = Error.prototype;
|
||||
|
||||
app.get('/404', function(req, res){
|
||||
throw new NotFound;
|
||||
Error handling middleware are simply middleware with an arity of 4, aka
|
||||
the signature _(err, req, res, next)_. When you _next(err)_ an error,
|
||||
only these middleware are executed and have a chance to respond. For example:
|
||||
|
||||
app.use(app.bodyParser());
|
||||
app.use(app.methodOverride());
|
||||
app.use(app.router);
|
||||
app.use(function(err, req, res, next){
|
||||
res.send(500, 'Server error');
|
||||
});
|
||||
|
||||
app.get('/500', function(req, res){
|
||||
throw new Error('keyboard cat!');
|
||||
});
|
||||
|
||||
We can call _app.error()_ several times as shown below.
|
||||
Here we check for an instanceof _NotFound_ and show the
|
||||
404 page, or we pass on to the next error handler.
|
||||
|
||||
Note that these handlers can be defined anywhere, as they
|
||||
will be placed below the route handlers on _listen()_. This
|
||||
allows for definition within _configure()_ blocks so we can
|
||||
handle exceptions in different ways based on the environment.
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if (err instanceof NotFound) {
|
||||
res.render('404.jade');
|
||||
} else {
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
Here we assume all errors as 500 for the simplicity of
|
||||
this demo, however you can choose whatever you like. For example when node performs filesystem syscalls, you may receive an error object with the _error.code_ of _ENOENT_, meaning "no such file or directory", we can utilize this in our error handling and display a page specific to this if desired.
|
||||
|
||||
app.error(function(err, req, res){
|
||||
res.render('500.jade', {
|
||||
error: err
|
||||
});
|
||||
});
|
||||
|
||||
Our apps could also utilize the Connect _errorHandler_ middleware
|
||||
to report on exceptions. For example if we wish to output exceptions
|
||||
in "development" mode to _stderr_ we can use:
|
||||
|
||||
app.use(express.errorHandler({ dumpExceptions: true }));
|
||||
|
||||
Also during development we may want fancy html pages to show exceptions
|
||||
that are passed or thrown, so we can set _showStack_ to true:
|
||||
|
||||
app.use(express.errorHandler({ showStack: true, dumpExceptions: true }));
|
||||
|
||||
The _errorHandler_ middleware also responds with _json_ if _Accept: application/json_
|
||||
is present, which is useful for developing apps that rely heavily on client-side JavaScript.
|
||||
|
||||
### Route Param Pre-conditions
|
||||
|
||||
@@ -542,13 +493,11 @@ mix and match template engines:
|
||||
|
||||
res.render('another-page.ejs');
|
||||
|
||||
Express also provides the _view options_ setting, which is applied each time a view is rendered, so for example if you rarely use layouts you may set:
|
||||
To apply application-level locals, or view engine options may be set using _app.local()_ or _app.locals()_, for example if we don't want layouts for most of our templates we may do:
|
||||
|
||||
app.set('view options', {
|
||||
layout: false
|
||||
});
|
||||
app.local('layout', false);
|
||||
|
||||
Which can then be overridden within the _res.render()_ call if need be:
|
||||
Which can then be overridden within the _res.render()_ call if desired, and is otherwise functionally equivalent to passing directly to `res.render()`:
|
||||
|
||||
res.render('myview.ejs', { layout: true });
|
||||
|
||||
@@ -564,13 +513,6 @@ These paths may also be absolute:
|
||||
|
||||
res.render('page', { layout: __dirname + '/../../mylayout.jade' });
|
||||
|
||||
A good example of this is specifying custom _ejs_ opening and closing tags:
|
||||
|
||||
app.set('view options', {
|
||||
open: '{{',
|
||||
close: '}}'
|
||||
});
|
||||
|
||||
### View Partials
|
||||
|
||||
The Express view system has built-in support for partials and collections, which are "mini" views representing a document fragment. For example rather than iterating
|
||||
@@ -647,7 +589,7 @@ Now the _req.session_ and _req.sessionStore_ properties will be accessible to al
|
||||
// we could check req.session.items && req.session.items.length
|
||||
// to print out a message
|
||||
if (req.session.items && req.session.items.length) {
|
||||
req.flash('info', 'You have %s items in your cart', req.session.items.length);
|
||||
req.notify('info', 'You have %s items in your cart', req.session.items.length);
|
||||
}
|
||||
res.render('shopping-cart');
|
||||
});
|
||||
@@ -772,27 +714,29 @@ the _express.bodyParser middleware.
|
||||
req.get('Content-Type', 'boundary');
|
||||
// => "--foo-bar-baz"
|
||||
|
||||
### req.flash(type[, msg])
|
||||
### req.notify(type[, msg])
|
||||
|
||||
Queue flash _msg_ of the given _type_.
|
||||
|
||||
req.flash('info', 'email sent');
|
||||
req.flash('error', 'email delivery failed');
|
||||
req.flash('info', 'email re-sent');
|
||||
req.notify('info', 'email sent');
|
||||
req.notify('error', 'email delivery failed');
|
||||
req.notify('info', 'email re-sent');
|
||||
// => 2
|
||||
|
||||
req.flash('info');
|
||||
req.notify('info');
|
||||
// => ['email sent', 'email re-sent']
|
||||
|
||||
req.flash('info');
|
||||
req.notify('info');
|
||||
// => []
|
||||
|
||||
req.flash();
|
||||
req.notify();
|
||||
// => { error: ['email delivery failed'], info: [] }
|
||||
|
||||
Flash notification message may also utilize formatters, by default only the %s string formatter is available:
|
||||
Flash notification message may also utilize formatters, by default only the %s string and %d integer formatters is available:
|
||||
|
||||
req.flash('info', 'email delivery to _%s_ from _%s_ failed.', toUser, fromUser);
|
||||
req.notify('info', 'email delivery to <em>%s</em> from <em>%s</em> failed.', toUser, fromUser);
|
||||
|
||||
Argument HTML is escaped, to prevent XSS, however HTML notification format is valid.
|
||||
|
||||
### req.isXMLHttpRequest
|
||||
|
||||
@@ -855,6 +799,21 @@ Sets the _Content-Disposition_ response header to "attachment", with optional _f
|
||||
|
||||
res.attachment('path/to/my/image.png');
|
||||
|
||||
### res.status(code)
|
||||
|
||||
Sets the `res.statusCode` property to `code` and returns for chaining:
|
||||
|
||||
res.status(500).send('Something bad happened');
|
||||
|
||||
is equivalent to:
|
||||
|
||||
res.statusCode = 500;
|
||||
res.send('Something bad happened');
|
||||
|
||||
and:
|
||||
|
||||
res.send(500, 'Something bad happened');
|
||||
|
||||
### res.sendfile(path[, options[, callback]])
|
||||
|
||||
Used by `res.download()` to transfer an arbitrary file.
|
||||
@@ -904,36 +863,31 @@ An optional second callback, _callback2_ may be given to allow you to act on con
|
||||
// connection related error
|
||||
});
|
||||
|
||||
### res.send(body|status[, headers|status[, status]])
|
||||
### res.send(body|status[, body])
|
||||
|
||||
The _res.send()_ method is a high level response utility allowing you to pass
|
||||
objects to respond with json, strings for html, Buffer instances, or numbers representing the status code. The following are all valid uses:
|
||||
|
||||
res.send(); // 204
|
||||
res.send(new Buffer('wahoo'));
|
||||
res.send({ some: 'json' });
|
||||
res.send(201, { message: 'User created' });
|
||||
res.send('<p>some html</p>');
|
||||
res.send('Sorry, cant find that', 404);
|
||||
res.send('text', { 'Content-Type': 'text/plain' }, 201);
|
||||
res.send(404);
|
||||
res.send(404, 'Sorry, cant find that');
|
||||
res.send(404); // "Not Found"
|
||||
res.send(500); // "Internal Server Error"
|
||||
|
||||
By default the _Content-Type_ response header is set, however if explicitly
|
||||
assigned through `res.send()` or previously with `res.header()` or `res.contentType()`
|
||||
it will not be set again.
|
||||
The _Content-Type_ response header is defaulted appropriately unless previously defined via `res.header()` / `res.contentType()` etc.
|
||||
|
||||
Note that this method _end()_s the response, so you will want to use node's _res.write()_ for multiple writes or streaming.
|
||||
|
||||
### res.json(obj[, headers|status[, status]])
|
||||
### res.json(obj|status[, obj])
|
||||
|
||||
Send a JSON response with optional _headers_ and _status_. This method
|
||||
is ideal for JSON-only APIs, however _res.send(obj)_ will send JSON as
|
||||
well, though not ideal for cases when you want to send for example a string
|
||||
as JSON, since the default for _res.send(string)_ is text/html.
|
||||
Send an explicit JSON response. This method is ideal for JSON-only APIs, while it is much like _res.send(obj)_, send is not ideal for cases when you want to send for example a single string as JSON, since the default for _res.send(string)_ is text/html.
|
||||
|
||||
res.json(null);
|
||||
res.json({ user: 'tj' });
|
||||
res.json('oh noes!', 500);
|
||||
res.json('I dont have that', 404);
|
||||
res.json(500, 'oh noes!');
|
||||
res.json(404, 'I dont have that');
|
||||
|
||||
### res.redirect(url[, status])
|
||||
|
||||
@@ -1209,21 +1163,11 @@ When mounted, _res.redirect()_ will respect the mount-point. For example if a bl
|
||||
|
||||
res.redirect('/posts');
|
||||
|
||||
### app.error(function)
|
||||
### app.locals(obj)
|
||||
|
||||
Adds an error handler _function_ 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 _next(err)_ if it does not wish to deal with the exception:
|
||||
Registers static view locals.
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
res.send(err.message, 500);
|
||||
});
|
||||
|
||||
### app.helpers(obj)
|
||||
|
||||
Registers static view helpers.
|
||||
|
||||
app.helpers({
|
||||
app.locals({
|
||||
name: function(first, last){ return first + ', ' + last }
|
||||
, firstName: 'tj'
|
||||
, lastName: 'holowaychuk'
|
||||
@@ -1239,16 +1183,14 @@ Express also provides a few locals by default:
|
||||
- `settings` the app's settings object
|
||||
- `layout(path)` specify the layout from within a view
|
||||
|
||||
This method is aliased as _app.locals()_.
|
||||
### app.dynamicLocals(obj)
|
||||
|
||||
### app.dynamicHelpers(obj)
|
||||
|
||||
Registers dynamic view helpers. Dynamic view helpers
|
||||
Registers dynamic view locals. Dynamic locals
|
||||
are simply functions which accept _req_, _res_, and are
|
||||
evaluated against the _Server_ instance before a view is rendered. The _return value_ of this function
|
||||
evaluated against the _Server_ instance before a view is rendered, and are unique to that specific request. The _return value_ of this function
|
||||
becomes the local variable it is associated with.
|
||||
|
||||
app.dynamicHelpers({
|
||||
app.dynamicLocals({
|
||||
session: function(req, res){
|
||||
return req.session;
|
||||
}
|
||||
|
||||
@@ -51,3 +51,4 @@ The following modules compliment or extend Express directly:
|
||||
* [Google Group](http://groups.google.com/group/express-js) for discussion
|
||||
* Visit the [Wiki](http://github.com/visionmedia/express/wiki)
|
||||
* [日本語ドキュメンテーション](http://hideyukisaito.com/doc/expressjs/) by [hideyukisaito](https://github.com/hideyukisaito)
|
||||
* [Русскоязычная документация](http://express-js.ru/)
|
||||
|
||||
+33
-34
@@ -6,47 +6,49 @@
|
||||
var express = require('../../lib/express')
|
||||
, crypto = require('crypto');
|
||||
|
||||
var app = express.createServer(
|
||||
express.bodyParser()
|
||||
, express.cookieParser()
|
||||
, express.session({ secret: 'keyboard cat' })
|
||||
);
|
||||
var app = module.exports = express();
|
||||
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.cookieParser('shhhh, very secret'));
|
||||
app.use(express.session());
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'ejs');
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Message helper, ideally we would use req.flash()
|
||||
// however this is more light-weight for an example
|
||||
// Session-persisted message middleware
|
||||
|
||||
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>';
|
||||
}
|
||||
});
|
||||
app.locals.use(function(req,res){
|
||||
var err = req.session.error
|
||||
, msg = req.session.success;
|
||||
delete req.session.error;
|
||||
delete req.session.success;
|
||||
res.locals.message = '';
|
||||
if (err) res.locals.message = '<p class="msg error">' + err + '</p>';
|
||||
if (msg) res.locals.message = '<p class="msg success">' + msg + '</p>';
|
||||
})
|
||||
|
||||
// Generate a salt for the user to prevent rainbow table attacks
|
||||
|
||||
// for better security take a look at the bcrypt c++ addon:
|
||||
// https://github.com/ncb000gt/node.bcrypt.js
|
||||
var users = {
|
||||
tj: {
|
||||
name: 'tj'
|
||||
name: 'tj'
|
||||
, salt: 'randomly-generated-salt'
|
||||
, pass: hash('foobar', 'randomly-generated-salt')
|
||||
}
|
||||
};
|
||||
|
||||
// Used to generate a hash of the plain-text password + salt
|
||||
|
||||
function hash(msg, key) {
|
||||
return crypto.createHmac('sha256', key).update(msg).digest('hex');
|
||||
return crypto
|
||||
.createHmac('sha256', key)
|
||||
.update(msg)
|
||||
.digest('hex');
|
||||
}
|
||||
// Authenticate using our plain-object database of doom!
|
||||
|
||||
// Authenticate using our plain-object database of doom!
|
||||
function authenticate(name, pass, fn) {
|
||||
console.log('authenticating %s:%s', name, pass);
|
||||
var user = users[name];
|
||||
// query the db for the given username
|
||||
if (!user) return fn(new Error('cannot find user'));
|
||||
@@ -67,16 +69,11 @@ function restrict(req, res, next) {
|
||||
}
|
||||
}
|
||||
|
||||
function accessLogger(req, res, next) {
|
||||
console.log('/restricted accessed by %s', req.session.user.name);
|
||||
next();
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.redirect('/login');
|
||||
res.redirect('login');
|
||||
});
|
||||
|
||||
app.get('/restricted', restrict, accessLogger, function(req, res){
|
||||
app.get('/restricted', restrict, function(req, res){
|
||||
res.send('Wahoo! restricted area');
|
||||
});
|
||||
|
||||
@@ -84,7 +81,7 @@ 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');
|
||||
res.redirect('/');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -113,10 +110,12 @@ app.post('/login', function(req, res){
|
||||
req.session.error = 'Authentication failed, please check your '
|
||||
+ ' username and password.'
|
||||
+ ' (use "tj" and "foobar")';
|
||||
res.redirect('back');
|
||||
res.redirect('login');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
<h1>Login</h1>
|
||||
<%- message %>
|
||||
Try accessing <a href="/restricted">/restricted</a>.
|
||||
Try accessing <a href="/restricted">/restricted</a>, then authenticate with "tj" and "foobar".
|
||||
<form method="post" action="/login">
|
||||
<p>
|
||||
<label>Username:</label>
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
|
||||
/**
|
||||
* 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,50 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
// config
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// middleware
|
||||
|
||||
app.configure('development',function(){
|
||||
app.use(express.logger('dev'));
|
||||
})
|
||||
|
||||
app.configure(function(){
|
||||
app.use(express.bodyParser());
|
||||
app.use(express.methodOverride());
|
||||
app.use(express.cookieParser('keyboard cat'));
|
||||
app.use(express.session());
|
||||
app.use(app.router);
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }));
|
||||
});
|
||||
|
||||
// Locals
|
||||
|
||||
app.locals.use(function(req, res){
|
||||
// expose "error" and "message" to all
|
||||
// views that are rendered.
|
||||
res.locals.error = req.session.error || '';
|
||||
res.locals.message = req.session.message || '';
|
||||
// remove them so they're not displayed on subsequent renders
|
||||
delete req.session.error;
|
||||
delete req.session.message;
|
||||
});
|
||||
|
||||
// Routes
|
||||
|
||||
require('./routes/site')(app);
|
||||
require('./routes/post')(app);
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -17,11 +17,11 @@ Post.prototype.save = function(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.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 + '_'));
|
||||
'body should be at least 10 characters long, was only ' + this.title.length));
|
||||
}
|
||||
fn();
|
||||
};
|
||||
|
||||
@@ -41,18 +41,16 @@ a.edit {
|
||||
.date {
|
||||
font-size: 11px;
|
||||
}
|
||||
#messages ul {
|
||||
p.error,
|
||||
p.message {
|
||||
padding: 10px;
|
||||
border: 1px solid;
|
||||
}
|
||||
#messages ul li {
|
||||
list-style: none;
|
||||
p.error {
|
||||
background: #FDEAE7;
|
||||
color: #E4250C;
|
||||
}
|
||||
#messages ul.info {
|
||||
p.message {
|
||||
color: #2EBBE6;
|
||||
background: #F7FBFD;
|
||||
}
|
||||
#messages ul.error {
|
||||
color: #E4250C;
|
||||
background: #FDEAE7;
|
||||
}
|
||||
@@ -42,17 +42,17 @@ module.exports = function(app){
|
||||
*/
|
||||
|
||||
app.post('/post', function(req, res){
|
||||
var data = req.body.post
|
||||
var data = req.body.post || {}
|
||||
, post = new Post(data.title, data.body);
|
||||
|
||||
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.flash('error', err.message);
|
||||
req.session.error = err.message;
|
||||
return res.redirect('back');
|
||||
}
|
||||
|
||||
post.save(function(err){
|
||||
req.flash('info', 'Successfully created post _%s_', post.title);
|
||||
req.session.message = 'Successfully created the post.';
|
||||
res.redirect('/post/' + post.id);
|
||||
});
|
||||
});
|
||||
@@ -82,12 +82,13 @@ module.exports = function(app){
|
||||
var post = req.post;
|
||||
post.validate(function(err){
|
||||
if (err) {
|
||||
req.flash('error', err.message);
|
||||
req.session.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');
|
||||
req.session.message = 'Successfully updated post';
|
||||
res.redirect('back');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
h1 Blog
|
||||
extends layout
|
||||
|
||||
!= messages()
|
||||
block content
|
||||
h1 Blog
|
||||
|
||||
- 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"
|
||||
| .
|
||||
if count
|
||||
p Display all #{count} post(s)
|
||||
#posts
|
||||
each post in posts
|
||||
include post/index
|
||||
else
|
||||
p
|
||||
| It looks like you have no posts!
|
||||
p
|
||||
| Click
|
||||
a(href='/post/add') here
|
||||
| to create a post. Login
|
||||
| as
|
||||
em "admin"
|
||||
| and
|
||||
em "express"
|
||||
| .
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
html
|
||||
head
|
||||
title Blog
|
||||
link(rel='stylesheet', href=base + '/style.css')
|
||||
link(rel='stylesheet', href='/style.css')
|
||||
body
|
||||
#container!= body
|
||||
#container
|
||||
block content
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
if error
|
||||
p.error= error
|
||||
|
||||
if message
|
||||
p.message= message
|
||||
@@ -1,20 +1,22 @@
|
||||
extends ../layout
|
||||
|
||||
- if (post.title)
|
||||
h1 Editing #{post.title}
|
||||
- else
|
||||
h1 New Post
|
||||
block content
|
||||
if post.title
|
||||
h1 Editing #{post.title}
|
||||
else
|
||||
h1 New Post
|
||||
|
||||
!= messages()
|
||||
include ../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')
|
||||
|
||||
form#post(action='/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')
|
||||
|
||||
@@ -1,16 +1,18 @@
|
||||
.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
|
||||
extends ../layout
|
||||
|
||||
block content
|
||||
.post
|
||||
// title
|
||||
h2
|
||||
= post.title
|
||||
a.edit(href='/post/' + post.id + '/edit') Edit
|
||||
|
||||
include ../messages
|
||||
|
||||
// dates
|
||||
p.date.created Created at #{post.createdAt}
|
||||
if post.updatedAt
|
||||
p.date.updated Updated at #{post.updatedAt}
|
||||
|
||||
// body
|
||||
pre.body= post.body
|
||||
@@ -1,47 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
var users = [
|
||||
{ name: 'tobi' }
|
||||
, { name: 'loki' }
|
||||
, { name: 'jane' }
|
||||
];
|
||||
|
||||
function provides(type) {
|
||||
return function(req, res, next){
|
||||
if (req.accepts(type)) return next();
|
||||
next('route');
|
||||
}
|
||||
}
|
||||
|
||||
// curl http://localhost:3000/users -H "Accept: application/json"
|
||||
|
||||
app.get('/users', provides('json'), function(req, res){
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
// curl http://localhost:3000/users -H "Accept: text/html"
|
||||
|
||||
app.get('/users', provides('html'), function(req, res){
|
||||
res.send('<ul>' + users.map(function(user){
|
||||
return '<li>' + user.name + '</li>';
|
||||
}).join('\n') + '</ul>');
|
||||
});
|
||||
|
||||
// curl http://localhost:3000/users -H "Accept: text/plain"
|
||||
|
||||
app.get('/users', function(req, res, next){
|
||||
res.contentType('txt');
|
||||
res.send(users.map(function(user){
|
||||
return user.name;
|
||||
}).join(', '));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
@@ -0,0 +1,34 @@
|
||||
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
var users = [];
|
||||
|
||||
users.push({ name: 'Tobi' });
|
||||
users.push({ name: 'Loki' });
|
||||
users.push({ name: 'Jane' });
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.format({
|
||||
html: function(){
|
||||
res.send('<ul>' + users.map(function(user){
|
||||
return '<li>' + user.name + '</li>';
|
||||
}).join('') + '</ul>');
|
||||
},
|
||||
|
||||
text: function(){
|
||||
res.send(users.map(function(user){
|
||||
return ' - ' + user.name + '\n';
|
||||
}).join(''));
|
||||
},
|
||||
|
||||
json: function(){
|
||||
res.json(users);
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('listening on port 3000');
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// pass a secret to cookieParser() for signed cookies
|
||||
app.use(express.cookieParser('manny is cool'));
|
||||
|
||||
// add req.session cookie support
|
||||
app.use(express.cookieSessions());
|
||||
|
||||
// do something with the session
|
||||
app.use(count);
|
||||
|
||||
// custom middleware
|
||||
function count(req, res) {
|
||||
req.session.count = req.session.count || 0;
|
||||
var n = req.session.count++;
|
||||
res.send('viewed ' + n + ' times\n');
|
||||
}
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
}
|
||||
+23
-14
@@ -3,22 +3,29 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../')
|
||||
, app = module.exports = 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[36m:method\x1b[0m \x1b[90m:url\x1b[0m :response-time' }),
|
||||
// add favicon() before logger() so
|
||||
// GET /favicon.ico requests are not
|
||||
// logged, because this middleware
|
||||
// reponds to /favicon.ico and does not
|
||||
// call next()
|
||||
app.use(express.favicon());
|
||||
|
||||
// Provides req.cookies
|
||||
express.cookieParser(),
|
||||
// custom log format
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
app.use(express.logger(':method :url'));
|
||||
|
||||
// Parses x-www-form-urlencoded request bodies (and json)
|
||||
express.bodyParser()
|
||||
);
|
||||
// parses request cookies, populating
|
||||
// req.cookies and req.signedCookies
|
||||
// when the secret is passed, used
|
||||
// for signing the cookies.
|
||||
app.use(express.cookieParser('my secret here'));
|
||||
|
||||
// parses json, x-www-form-urlencoded, and multipart/form-data
|
||||
app.use(express.bodyParser());
|
||||
|
||||
app.get('/', function(req, res){
|
||||
if (req.cookies.remember) {
|
||||
@@ -41,5 +48,7 @@ app.post('/', function(req, res){
|
||||
res.redirect('back');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
if (!module.parent){
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
+17
-24
@@ -3,9 +3,8 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<ul>'
|
||||
@@ -19,33 +18,27 @@ app.get('/', function(req, res){
|
||||
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 as shown below
|
||||
res.download(path, function(err){
|
||||
// if an error occurs in this callback
|
||||
// the file most likely does not exist,
|
||||
// and it's safe to respond or next(err)
|
||||
if (err) return next(err);
|
||||
|
||||
// the file has been transferred, do not respond
|
||||
// from here, though you may use this callback
|
||||
// for stats etc.
|
||||
console.log('transferred %s', path);
|
||||
}, function(err){
|
||||
// this second optional callback is used when
|
||||
// an error occurs during transmission
|
||||
});
|
||||
res.download(path);
|
||||
});
|
||||
|
||||
app.error(function(err, req, res, next){
|
||||
if ('ENOENT' == err.code) {
|
||||
// error handling middleware. Because it's
|
||||
// below our routes, you will be able to
|
||||
// "intercept" errors, otherwise Connect
|
||||
// will respond with 500 "Internal Server Error".
|
||||
app.use(function(err, req, res, next){
|
||||
// special-case 404s,
|
||||
// remember you could
|
||||
// render a 404 template here
|
||||
if (404 == err.status) {
|
||||
res.statusCode = 404;
|
||||
res.send('Cant find that file, sorry!');
|
||||
} else {
|
||||
// Not a 404
|
||||
next(err);
|
||||
}
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Register ejs as .html
|
||||
|
||||
app.register('.html', require('ejs'));
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@sencha.com' }
|
||||
, { name: 'ciaran', email: 'ciaranj@gmail.com' }
|
||||
, { name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../');
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// Register ejs as .html. If we did
|
||||
// not call this, we would need to
|
||||
// name our views foo.ejs instead
|
||||
// of foo.html. The __express method
|
||||
// is simply a function that engines
|
||||
// use to hook into the Express view
|
||||
// system by default, so if we want
|
||||
// to change "foo.ejs" to "foo.html"
|
||||
// we simply pass _any_ function, in this
|
||||
// case `ejs.__express`.
|
||||
|
||||
app.engine('.html', require('ejs').__express);
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// Without this you would need to
|
||||
// supply the extension to res.render()
|
||||
// ex: res.render('users.html').
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tobi', email: 'tobi@learnboost.com' }
|
||||
, { name: 'loki', email: 'loki@learnboost.com' }
|
||||
, { name: 'jane', email: 'jane@learnboost.com' }
|
||||
];
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
if (!module.parent) {
|
||||
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 @@
|
||||
<h1>Users</h1>
|
||||
<ul id="users">
|
||||
<% users.forEach(function(user){ %>
|
||||
<li><%= user.name %> <%= user.email %></li>
|
||||
<% }) %>
|
||||
</ul>
|
||||
@@ -1,3 +0,0 @@
|
||||
<ul id="users">
|
||||
<%- partial('user', users) %>
|
||||
</ul>
|
||||
@@ -1 +0,0 @@
|
||||
<li><%= user.name %> <<%= user.email %>></li>
|
||||
@@ -1,27 +1,25 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../')
|
||||
, app = module.exports = express()
|
||||
, silent = 'test' == process.env.NODE_ENV;
|
||||
|
||||
var app = express.createServer();
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
|
||||
// Serve default connect favicon
|
||||
app.use(express.favicon());
|
||||
|
||||
// Logger is placed below favicon, so favicon.ico
|
||||
// requests will not be logged
|
||||
app.use(express.logger('":method :url" :status'));
|
||||
silent || app.use(express.logger('dev'));
|
||||
|
||||
// "app.router" positions our routes
|
||||
// specifically above the middleware
|
||||
// assigned below
|
||||
// above the middleware defined below,
|
||||
// this means that Express will attempt
|
||||
// to match & call routes _before_ continuing
|
||||
// on, at which point we assume it's a 404 because
|
||||
// no route has handled the request.
|
||||
|
||||
app.use(app.router);
|
||||
|
||||
@@ -29,11 +27,26 @@ app.use(app.router);
|
||||
// middleware use()d, we assume 404, as nothing else
|
||||
// responded.
|
||||
|
||||
// $ curl http://localhost:3000/notfound
|
||||
// $ curl http://localhost:3000/notfound -H "Accept: application/json"
|
||||
// $ curl http://localhost:3000/notfound -H "Accept: text/plain"
|
||||
|
||||
app.use(function(req, res, next){
|
||||
// the status option, or res.statusCode = 404
|
||||
// are equivalent, however with the option we
|
||||
// get the "status" local available as well
|
||||
res.render('404', { status: 404, url: req.url });
|
||||
// respond with html page
|
||||
if (req.accepts('html')) {
|
||||
res.status(404);
|
||||
res.render('404', { url: req.url });
|
||||
return;
|
||||
}
|
||||
|
||||
// respond with json
|
||||
if (req.accepts('json')) {
|
||||
res.send({ error: 'Not found' });
|
||||
return;
|
||||
}
|
||||
|
||||
// default to plain-text. send()
|
||||
res.type('txt').send('Not found');
|
||||
});
|
||||
|
||||
// error-handling middleware, take the same form
|
||||
@@ -48,15 +61,12 @@ app.use(function(req, res, next){
|
||||
// would remain being executed, however here
|
||||
// we simply respond with an error page.
|
||||
|
||||
|
||||
app.use(function(err, req, res, next){
|
||||
// we may use properties of the error object
|
||||
// here and next(err) appropriately, or if
|
||||
// we possibly recovered from the error, simply next().
|
||||
res.render('500', {
|
||||
status: err.status || 500
|
||||
, error: err
|
||||
});
|
||||
res.status(err.status || 500);
|
||||
res.render('500', { error: err });
|
||||
});
|
||||
|
||||
// Routes
|
||||
@@ -79,5 +89,7 @@ app.get('/500', function(req, res, next){
|
||||
next(new Error('keyboard cat!'));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
silent || console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1 +1,5 @@
|
||||
h2 Cannot find #{url}
|
||||
|
||||
extends error
|
||||
|
||||
block content
|
||||
h2 Cannot find #{url}
|
||||
@@ -1,2 +1,10 @@
|
||||
h1 Error: #{error.message}
|
||||
pre= error.stack
|
||||
|
||||
// note that we extend a different
|
||||
// layout with jade for 4xx & 5xx
|
||||
// responses
|
||||
|
||||
extends error
|
||||
|
||||
block content
|
||||
h1 Error: #{error.message}
|
||||
pre= error.stack
|
||||
@@ -0,0 +1,6 @@
|
||||
html
|
||||
head
|
||||
title Error
|
||||
body
|
||||
h1 An error occurred!
|
||||
block content
|
||||
@@ -1,11 +1,15 @@
|
||||
h2 Pages Example
|
||||
ul
|
||||
li
|
||||
| visit
|
||||
a(href="/500") 500
|
||||
li
|
||||
| visit
|
||||
a(href="/404") 404
|
||||
li
|
||||
| visit
|
||||
a(href='/403') 403
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
h2 Pages Example
|
||||
ul
|
||||
li
|
||||
| visit
|
||||
a(href="/500") 500
|
||||
li
|
||||
| visit
|
||||
a(href="/404") 404
|
||||
li
|
||||
| visit
|
||||
a(href='/403') 403
|
||||
@@ -3,4 +3,4 @@ html
|
||||
title Custom Pages Example
|
||||
body
|
||||
h1 My Site
|
||||
!= body
|
||||
block content
|
||||
+33
-10
@@ -3,9 +3,33 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
var app = express.createServer();
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
app.use(express.logger('dev'));
|
||||
app.use(app.router);
|
||||
|
||||
// the error handler is strategically
|
||||
// placed *below* the app.router; if it
|
||||
// were above it would not receive errors
|
||||
// from app.get() etc
|
||||
app.use(error);
|
||||
|
||||
// error handling middleware have an arity of 4
|
||||
// instead of the typical (req, res, next),
|
||||
// otherwise they behave exactly like regular
|
||||
// middleware, you may have several of them,
|
||||
// in different orders etc.
|
||||
|
||||
function error(err, req, res, next) {
|
||||
// log it
|
||||
if ('test' != process.env.NODE_ENV)
|
||||
console.error(err.stack);
|
||||
|
||||
// respond with 500 "Internal Server Error".
|
||||
res.send(500);
|
||||
}
|
||||
|
||||
app.get('/', function(req, res){
|
||||
// Caught and passed down to the errorHandler middleware
|
||||
@@ -14,13 +38,12 @@ app.get('/', function(req, res){
|
||||
|
||||
app.get('/next', function(req, res, next){
|
||||
// We can also pass exceptions to next()
|
||||
next(new Error('oh no!'))
|
||||
process.nextTick(function(){
|
||||
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('/', express.errorHandler({ dump: true, stack: true }));
|
||||
|
||||
app.listen(3000);
|
||||
console.log('app listening on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
// Register ejs as .html
|
||||
|
||||
app.register('.html', require('ejs'));
|
||||
|
||||
// Optional since express defaults to CWD/views
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Dummy users
|
||||
var users = [
|
||||
{ name: 'tj', email: 'tj@sencha.com' }
|
||||
, { name: 'ciaran', email: 'ciaranj@gmail.com' }
|
||||
, { name: 'aaron', email: 'aaron.heckmann+github@gmail.com' }
|
||||
];
|
||||
|
||||
// dynamic helpers are simply functions that are invoked
|
||||
// per request (once), passed both the request and response
|
||||
// objects. These can be used for request-specific
|
||||
// details within a view, such telling the layout which
|
||||
// scripts to include.
|
||||
app.dynamicHelpers({
|
||||
// by simply returning an object here
|
||||
// we can set it's properties such as "page.title"
|
||||
// within a view, and it remains specific to that request,
|
||||
// so it would be valid to do:
|
||||
// page.title = user.name + "'s account"
|
||||
page: function() {
|
||||
return {};
|
||||
},
|
||||
|
||||
// the scripts array here is assigned once,
|
||||
// so by returning a closure, we can use script(path)
|
||||
// in a template, instead of something like
|
||||
// scripts.push(path).
|
||||
script: function(req){
|
||||
req._scripts = [];
|
||||
return function(path){
|
||||
req._scripts.push(path);
|
||||
}
|
||||
},
|
||||
|
||||
// to expose our scripts array for iteration within
|
||||
// our views (typically the layout), we simply return it
|
||||
// here, and since composite types are mutable, it will
|
||||
// contain all of the paths pushed with the helper above.
|
||||
scripts: function(req){
|
||||
return req._scripts;
|
||||
}
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('users', { users: users });
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
@@ -1,11 +0,0 @@
|
||||
<html>
|
||||
<head>
|
||||
<title><%- page.title %></title>
|
||||
<% for (var i in scripts) { %>
|
||||
<script src="<%= scripts[i] %>"></script>
|
||||
<% } %>
|
||||
</head>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,8 +0,0 @@
|
||||
<% page.title = 'Users' %>
|
||||
<% script('/javascripts/jquery.js') %>
|
||||
<% script('/javascripts/users.js') %>
|
||||
|
||||
<h1>Users</h1>
|
||||
<ul id="users">
|
||||
<%- partial('user', users) %>
|
||||
</ul>
|
||||
@@ -1 +0,0 @@
|
||||
<li><%= user.name %> <<%= user.email %>></li>
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
/**
|
||||
* 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');
|
||||
@@ -1,3 +0,0 @@
|
||||
<h1>Flash Message Example</h1>
|
||||
<p>on page <%- page %></p>
|
||||
<%- messages() %>
|
||||
@@ -1,5 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,14 +1,10 @@
|
||||
|
||||
// Expose modules in ./support for demo purposes
|
||||
require.paths.unshift(__dirname + '/../../support');
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = module.exports = express();
|
||||
|
||||
// Here we use the bodyDecoder middleware
|
||||
// to parse urlencoded request bodies
|
||||
@@ -77,5 +73,7 @@ app.put('/', function(req, res){
|
||||
res.redirect('/?name=' + req.body.name);
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -3,9 +3,9 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = module.exports = express();
|
||||
|
||||
// Fake items
|
||||
|
||||
@@ -45,8 +45,7 @@ app.get('/item/:id.:format?', function(req, res, next){
|
||||
+ '<items>'
|
||||
+ '<item>' + item.name + '</item>'
|
||||
+ '</items>';
|
||||
res.contentType('.xml');
|
||||
res.send(xml);
|
||||
res.type('xml').send(xml);
|
||||
break;
|
||||
case 'html':
|
||||
default:
|
||||
@@ -62,7 +61,9 @@ app.get('/item/:id.:format?', function(req, res, next){
|
||||
|
||||
// Middleware
|
||||
|
||||
app.use(express.errorHandler({ showStack: true }));
|
||||
app.use(express.errorHandler());
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
silent || console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -6,7 +6,7 @@
|
||||
var express = require('../../lib/express')
|
||||
, http = require('http');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = express();
|
||||
|
||||
// Expose our views
|
||||
|
||||
|
||||
@@ -1,8 +1,14 @@
|
||||
- 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)
|
||||
|
||||
extends layout
|
||||
|
||||
block content
|
||||
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
|
||||
for repo in user.repos
|
||||
include repo
|
||||
@@ -4,4 +4,5 @@ html
|
||||
title Github Example
|
||||
link(rel="stylesheet", href="/style.css")
|
||||
body
|
||||
#container!= body
|
||||
#container
|
||||
block content
|
||||
@@ -3,12 +3,13 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = express();
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('Hello World');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
head
|
||||
title Jade Example
|
||||
link(rel="stylesheet", href="/stylesheets/style.css")
|
||||
@@ -1,6 +1,4 @@
|
||||
!!!
|
||||
html
|
||||
head
|
||||
title Jade Example
|
||||
link(rel="stylesheet", href="/stylesheets/style.css")
|
||||
include header
|
||||
body!= body
|
||||
@@ -3,29 +3,49 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../lib/express')
|
||||
, url = require('url');
|
||||
|
||||
var app = express.createServer();
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// set default layout, usually "layout"
|
||||
app.set('view options', { layout: 'layouts/default' });
|
||||
// map .html to ejs module
|
||||
app.register('html', require('ejs'));
|
||||
app.set('view engine', 'html');
|
||||
|
||||
// Set our default template engine to "ejs"
|
||||
// which prevents the need for extensions
|
||||
// (although you can still mix and match)
|
||||
app.set('view engine', 'ejs');
|
||||
// set default layout, usually "layout"
|
||||
app.locals.layout = 'layouts/default';
|
||||
|
||||
app.use(function(req, res, next){
|
||||
// expose the current path as a view local
|
||||
res.locals.path = url.parse(req.url).pathname;
|
||||
|
||||
// assign content str for section
|
||||
res.locals.contentFor = function(section, str){
|
||||
res.locals[section] = str;
|
||||
};
|
||||
|
||||
next();
|
||||
});
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('pages/default');
|
||||
res.render('page');
|
||||
});
|
||||
|
||||
app.get('/alternate', function(req, res){
|
||||
res.render('page', { layout: 'layouts/alternate' });
|
||||
});
|
||||
|
||||
app.get('/alternate2', function(req, res){
|
||||
res.render('page2');
|
||||
});
|
||||
|
||||
app.get('/defined-in-view', 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');
|
||||
res.render('pages');
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<p>Moar sidebar here</p>
|
||||
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Alternate Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,26 @@
|
||||
<html>
|
||||
<title>Alternate</title>
|
||||
<style>
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px "helvetica neue", helvetica, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
#sidebar {
|
||||
float: left;
|
||||
width: 150px;
|
||||
}
|
||||
#content {
|
||||
float: left;
|
||||
}
|
||||
</style>
|
||||
<body>
|
||||
<h1>Alternate Layout</h1>
|
||||
<div id="sidebar">
|
||||
<%- sidebar %>
|
||||
</div>
|
||||
<div id="content">
|
||||
<%- body %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,6 +0,0 @@
|
||||
<html>
|
||||
<body>
|
||||
<h1>Default Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,16 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Default</title>
|
||||
<style>
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px "helvetica neue", helvetica, sans-serif;
|
||||
color: #333;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Default Layout</h1>
|
||||
<%- body %>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,8 @@
|
||||
<h1>Page</h1>
|
||||
<% if (path == '/alternate') { %>
|
||||
<% contentFor('sidebar', partial('alternate-sidebar')) %>
|
||||
<p>Click <a href="/">here</a> to view the default layout.</p>
|
||||
<p>Click <a href="/alternate2">here</a> to view another alternate page.</p>
|
||||
<% } else { %>
|
||||
<p>Click <a href="/alternate">here</a> to view the alternate layout.</p>
|
||||
<% } %>
|
||||
@@ -0,0 +1,4 @@
|
||||
<% layout('layouts/alternate') %>
|
||||
<% contentFor('sidebar', '<p>Another sidebar here</p>') %>
|
||||
<h1>Page</h1>
|
||||
<p>Click <a href="/">here</a> to view the default layout.</p>
|
||||
@@ -1,2 +0,0 @@
|
||||
<% layout('layouts/alternate') %>
|
||||
<h1>Page</h1>
|
||||
@@ -1 +0,0 @@
|
||||
<h1>Page</h1>
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, md = require('node-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(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,45 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../')
|
||||
, fs = require('fs')
|
||||
, md = require('github-flavored-markdown').parse;
|
||||
|
||||
var app = module.exports = express();
|
||||
|
||||
// register .md as an engine in express view system
|
||||
|
||||
app.engine('md', function(path, options, fn){
|
||||
fs.readFile(path, 'utf8', function(err, str){
|
||||
if (err) return fn(err);
|
||||
try {
|
||||
var html = md(str);
|
||||
html = html.replace(/\{([^}]+)\}/g, function(_, name){
|
||||
return options[name] || '';
|
||||
})
|
||||
fn(null, html);
|
||||
} catch(err) {
|
||||
fn(err);
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
|
||||
// make it the default so we dont need .md
|
||||
app.set('view engine', 'md');
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index', { title: 'Markdown Example' });
|
||||
})
|
||||
|
||||
app.get('/fail', function(req, res){
|
||||
res.render('missing', { title: 'Markdown Example' });
|
||||
})
|
||||
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
|
||||
/**
|
||||
* 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');
|
||||
+19
-31
@@ -3,46 +3,34 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, form = require('connect-form');
|
||||
var express = require('../../')
|
||||
, format = require('util').format;
|
||||
|
||||
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({ keepExtensions: true })
|
||||
);
|
||||
var app = module.exports = express()
|
||||
|
||||
// bodyParser in connect 2.x uses node-formidable to parse
|
||||
// the multipart form data.
|
||||
app.use(express.bodyParser())
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.send('<form method="post" enctype="multipart/form-data">'
|
||||
+ '<p>Title: <input type="text" name="title" /></p>'
|
||||
+ '<p>Image: <input type="file" name="image" /></p>'
|
||||
+ '<p><input type="submit" value="Upload" /></p>'
|
||||
+ '</form>');
|
||||
});
|
||||
|
||||
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 {
|
||||
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.on('progress', function(bytesReceived, bytesExpected){
|
||||
var percent = (bytesReceived / bytesExpected * 100) | 0;
|
||||
process.stdout.write('Uploading: %' + percent + '\r');
|
||||
});
|
||||
// the uploaded file can be found as `req.files.image` and the
|
||||
// title field as `req.body.title`
|
||||
res.send(format('\nuploaded %s (%d Kb) to %s as %s'
|
||||
, req.files.image.name
|
||||
, req.files.image.size / 1024 | 0
|
||||
, req.files.image.path
|
||||
, req.body.title));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -23,8 +23,7 @@ function bootApplication(app) {
|
||||
app.use(express.static(__dirname + '/public'));
|
||||
|
||||
// Example 500 page
|
||||
app.error(function(err, req, res){
|
||||
console.dir(err)
|
||||
app.use(function(err, req, res, next){
|
||||
res.render('500');
|
||||
});
|
||||
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, app = express.createServer();
|
||||
var express = require('../../')
|
||||
, app = module.exports = express();
|
||||
|
||||
// Faux database
|
||||
|
||||
@@ -18,7 +18,14 @@ var users = [
|
||||
|
||||
// Convert :to and :from to integers
|
||||
|
||||
app.param(['to', 'from'], function(n){ return parseInt(n, 10); });
|
||||
app.param(['to', 'from'], function(req, res, next, num, name){
|
||||
req.params[name] = num = parseInt(num, 10);
|
||||
if( isNaN(num) ){
|
||||
next(new Error('failed to parseInt '+num));
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
});
|
||||
|
||||
// Load user by id
|
||||
|
||||
@@ -57,5 +64,7 @@ app.get('/users/:from-:to', function(req, res, next){
|
||||
res.send('users ' + names.slice(from, to).join(', '));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express application listening on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -3,9 +3,9 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = module.exports = express();
|
||||
|
||||
// Ad-hoc example resource method
|
||||
|
||||
@@ -85,5 +85,7 @@ app.get('/', function(req, res){
|
||||
].join('\n'));
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express started on port 3000');
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express')
|
||||
, redis = require('redis')
|
||||
, app = express.createServer()
|
||||
, db = { users: [] };
|
||||
|
||||
app.set('views', __dirname + '/views');
|
||||
app.set('view engine', 'jade');
|
||||
app.use(express.bodyParser());
|
||||
|
||||
// pretend db is a database, could be
|
||||
// whatever you like
|
||||
require('./boot')(app, db);
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express app started on port 3000');
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var vm = require('vm')
|
||||
, fs = require('fs');
|
||||
|
||||
module.exports = function(app, db){
|
||||
var dir = __dirname + '/routes';
|
||||
// grab a list of our route files
|
||||
fs.readdirSync(dir).forEach(function(file){
|
||||
var str = fs.readFileSync(dir + '/' + file, 'utf8');
|
||||
// inject some pseudo globals by evaluating
|
||||
// the file with vm.runInNewContext()
|
||||
// instead of loading it with require(). require's
|
||||
// internals use similar, so dont be afraid of "boot time".
|
||||
var context = { app: app, db: db };
|
||||
// we have to merge the globals for console, process etc
|
||||
for (var key in global) context[key] = global[key];
|
||||
// note that this is essentially no different than ... just using
|
||||
// global variables, though it's only YOUR code that could influence
|
||||
// them, which is a bonus.
|
||||
vm.runInNewContext(str, context, file);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
|
||||
app.get('/', function(req, res){
|
||||
res.render('index');
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
|
||||
app.redirect('user list', '/users');
|
||||
|
||||
app.get('/users', function(req, res){
|
||||
res.render('user/list', { users: db.users });
|
||||
});
|
||||
|
||||
app.get('/user/add', function(req, res){
|
||||
res.render('user/add');
|
||||
});
|
||||
|
||||
app.post('/user', function(req, res){
|
||||
var user = req.body.user;
|
||||
db.users.push(user);
|
||||
res.redirect('user list');
|
||||
});
|
||||
|
||||
app.get('/user/:id', function(req, res){
|
||||
res.render('user');
|
||||
});
|
||||
@@ -0,0 +1,4 @@
|
||||
h2 Route sharing example
|
||||
ul
|
||||
li: a(href='/user/add') Add user
|
||||
li: a(href='/users') User list
|
||||
@@ -0,0 +1,11 @@
|
||||
doctype html
|
||||
html
|
||||
head
|
||||
title Route loading example
|
||||
style
|
||||
body {
|
||||
padding: 50px;
|
||||
font: 14px/1.5 solid helvetica, arial, sans-serif;
|
||||
}
|
||||
body
|
||||
#content!= body
|
||||
@@ -0,0 +1,5 @@
|
||||
h2 Add a user
|
||||
form(action='/user', method='post')
|
||||
p: input(type='text', name='user[name]', placeholder='Username')
|
||||
p: input(type='text', name='user[email]', placeholder='Email')
|
||||
p: input(type='submit', value='Add')
|
||||
@@ -0,0 +1,6 @@
|
||||
h1= user.name
|
||||
table
|
||||
tbody
|
||||
tr
|
||||
td Email
|
||||
td= user.email
|
||||
@@ -0,0 +1,5 @@
|
||||
h1 Users
|
||||
- if (users.length)
|
||||
!= partial('user', users)
|
||||
- else
|
||||
p No users, head over to <a href='/user/add'>/user/add</a> to create one.
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
@@ -40,7 +39,7 @@ function andRestrictToSelf(req, res, next) {
|
||||
} else {
|
||||
// You may want to implement specific exceptions
|
||||
// such as UnauthorizedError or similar so that you
|
||||
// can handle these in app.error() specifically
|
||||
// can handle these can be special-cased in an error handler
|
||||
// (view ./examples/pages for this)
|
||||
next(new Error('Unauthorized'));
|
||||
}
|
||||
@@ -59,7 +58,7 @@ function andRestrictTo(role) {
|
||||
// Middleware for faux authentication
|
||||
// you would of course implement something real,
|
||||
// but this illustrates how an authenticated user
|
||||
// may interacte with middleware
|
||||
// may interact with middleware
|
||||
|
||||
app.use(function(req, res, next){
|
||||
req.authenticatedUser = users[0];
|
||||
|
||||
@@ -3,29 +3,37 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var express = require('../../lib/express');
|
||||
var express = require('../../');
|
||||
|
||||
var app = express.createServer();
|
||||
var app = module.exports = express();
|
||||
|
||||
// configuration
|
||||
// create an error with .status. we
|
||||
// can then use the property in our
|
||||
// custom error handler (Connect repects this prop as well)
|
||||
|
||||
function error(status, msg) {
|
||||
var err = new Error(msg);
|
||||
err.status = status;
|
||||
return err;
|
||||
}
|
||||
|
||||
// if we wanted to supply more than JSON, we could
|
||||
// use something similar to the content-negotiation
|
||||
// example.
|
||||
|
||||
// here we validate the API key,
|
||||
// by mounting this middleware to /api/v1
|
||||
// meaning only paths prefixed with "/api/v1"
|
||||
// by mounting this middleware to /api
|
||||
// meaning only paths prefixed with "/api"
|
||||
// will cause this middleware to be invoked
|
||||
|
||||
app.use('/api/v1', function(req, res, next){
|
||||
app.use('/api', function(req, res, next){
|
||||
var key = req.query['api-key'];
|
||||
|
||||
// key isnt present
|
||||
if (!key) return next(new Error('api key required'));
|
||||
if (!key) return next(error(400, 'api key required'));
|
||||
|
||||
// key is invalid
|
||||
if (!~apiKeys.indexOf(key)) return next(new Error('invalid api key'));
|
||||
if (!~apiKeys.indexOf(key)) return next(error(401, 'invalid api key'));
|
||||
|
||||
// all good, store req.key for route access
|
||||
req.key = key;
|
||||
@@ -44,39 +52,23 @@ app.use(app.router);
|
||||
// regular middleware.
|
||||
app.use(function(err, req, res, next){
|
||||
// whatever you want here, feel free to populate
|
||||
// properties on `err` to treat it differently in here,
|
||||
// or when you next(err) set res.statusCode= etc.
|
||||
res.send({ error: err.message }, 500);
|
||||
// properties on `err` to treat it differently in here.
|
||||
res.send(err.status || 500, { error: err.message });
|
||||
});
|
||||
|
||||
// our custom JSON 404 middleware. Since it's placed last
|
||||
// it will be the last middleware called, if all others
|
||||
// invoke next() and do not respond.
|
||||
app.use(function(req, res){
|
||||
res.send({ error: "Lame, can't find that" }, 404);
|
||||
res.send(404, { error: "Lame, can't find that" });
|
||||
});
|
||||
|
||||
/**
|
||||
* Generate our unique identifier.
|
||||
*/
|
||||
|
||||
function uid() {
|
||||
return [
|
||||
Math.random() * 0xffff | 0
|
||||
, Math.random() * 0xffff | 0
|
||||
, Math.random() * 0xffff | 0
|
||||
, Date.now()
|
||||
].join('-');
|
||||
}
|
||||
|
||||
// map of valid api keys, typically mapped to
|
||||
// account info with some sort of database like redis.
|
||||
// api keys do _not_ serve as authentication, merely to
|
||||
// track API usage or help prevent malicious behavior etc.
|
||||
|
||||
var apiKeys = [uid(), uid(), uid()];
|
||||
|
||||
console.log('valid keys:\n ', apiKeys.join('\n '));
|
||||
var apiKeys = ['foo', 'bar', 'baz'];
|
||||
|
||||
// these two objects will serve as our faux database
|
||||
|
||||
@@ -101,15 +93,15 @@ var userRepos = {
|
||||
// we now can assume the api key is valid,
|
||||
// and simply expose the data
|
||||
|
||||
app.get('/api/v1/users', function(req, res, next){
|
||||
app.get('/api/users', function(req, res, next){
|
||||
res.send(users);
|
||||
});
|
||||
|
||||
app.get('/api/v1/repos', function(req, res, next){
|
||||
app.get('/api/repos', function(req, res, next){
|
||||
res.send(repos);
|
||||
});
|
||||
|
||||
app.get('/api/v1/user/:name/repos', function(req, res, next){
|
||||
app.get('/api/user/:name/repos', function(req, res, next){
|
||||
var name = req.params.name
|
||||
, user = userRepos[name];
|
||||
|
||||
@@ -117,5 +109,7 @@ app.get('/api/v1/user/:name/repos', function(req, res, next){
|
||||
else next();
|
||||
});
|
||||
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
if (!module.parent) {
|
||||
app.listen(3000);
|
||||
console.log('Express server listening on port 3000');
|
||||
}
|
||||
+3
-1
@@ -1,2 +1,4 @@
|
||||
|
||||
module.exports = require('./lib/express');
|
||||
module.exports = process.env.EXPRESS_COV
|
||||
? require('./lib-cov/express')
|
||||
: require('./lib/express');
|
||||
@@ -0,0 +1,535 @@
|
||||
|
||||
/*!
|
||||
* Express - proto
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect')
|
||||
, Router = require('./router')
|
||||
, methods = Router.methods.concat('del', 'all')
|
||||
, middleware = require('./middleware')
|
||||
, debug = require('debug')('express:application')
|
||||
, View = require('./view')
|
||||
, url = require('url')
|
||||
, utils = connect.utils
|
||||
, path = require('path')
|
||||
, http = require('http')
|
||||
, join = path.join
|
||||
, fs = require('fs');
|
||||
|
||||
/**
|
||||
* Application prototype.
|
||||
*/
|
||||
|
||||
var app = exports = module.exports = {};
|
||||
|
||||
/**
|
||||
* Initialize the server.
|
||||
*
|
||||
* - setup default configuration
|
||||
* - setup default middleware
|
||||
* - setup route reflection methods
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
app.init = function(){
|
||||
var self = this;
|
||||
this.cache = {};
|
||||
this.settings = {};
|
||||
this.engines = {};
|
||||
this.viewCallbacks = [];
|
||||
this.defaultConfiguration();
|
||||
|
||||
// route reflection methods
|
||||
methods.forEach(function(method){
|
||||
self.lookup[method] = function(path){
|
||||
return self._router.lookup(method, path);
|
||||
};
|
||||
|
||||
self.remove[method] = function(path){
|
||||
return self._router.lookup(method, path).remove();
|
||||
};
|
||||
});
|
||||
|
||||
// del -> delete
|
||||
self.lookup.del = self.lookup.delete;
|
||||
self.remove.del = self.remove.delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize application configuration.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
app.defaultConfiguration = function(){
|
||||
var self = this;
|
||||
|
||||
// default settings
|
||||
this.set('env', process.env.NODE_ENV || 'development');
|
||||
debug('booting in %s mode', this.get('env'));
|
||||
|
||||
// implicit middleware
|
||||
this.use(connect.query());
|
||||
this.use(middleware.init(this));
|
||||
|
||||
// app locals
|
||||
this.locals = function(obj){
|
||||
for (var key in obj) self.locals[key] = obj[key];
|
||||
return self;
|
||||
};
|
||||
|
||||
// response locals
|
||||
this.locals.use = function(fn){
|
||||
if (3 == fn.length) {
|
||||
self.viewCallbacks.push(fn);
|
||||
} else {
|
||||
self.viewCallbacks.push(function(req, res, done){
|
||||
fn(req, res);
|
||||
done();
|
||||
});
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
// router
|
||||
this._router = new Router(this);
|
||||
this.routes = this._router.routes;
|
||||
this.__defineGetter__('router', function(){
|
||||
this._usedRouter = true;
|
||||
this._router.caseSensitive = this.enabled('case sensitive routing');
|
||||
this._router.strict = this.enabled('strict routing');
|
||||
return this._router.middleware;
|
||||
});
|
||||
|
||||
// default locals
|
||||
this.locals.settings = this.settings;
|
||||
|
||||
// default configuration
|
||||
this.enable('jsonp callback');
|
||||
|
||||
this.configure('development', function(){
|
||||
this.set('json spaces', 2);
|
||||
});
|
||||
|
||||
this.configure('production', function(){
|
||||
this.enable('view cache');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove routes matching the given `path`.
|
||||
*
|
||||
* @param {Route} path
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.remove = function(path){
|
||||
return this._router.lookup('all', path).remove();
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup routes defined with a path
|
||||
* equivalent to `path`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.lookup = function(path){
|
||||
return this._router.lookup('all', path);
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy `connect#use()` to apply settings to
|
||||
* mounted applications.
|
||||
*
|
||||
* @param {String|Function|Server} route
|
||||
* @param {Function|Server} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.use = function(route, fn){
|
||||
var app, home, handle;
|
||||
|
||||
// default route to '/'
|
||||
if ('string' != typeof route) fn = route, route = '/';
|
||||
|
||||
// express app
|
||||
if (fn.handle && fn.set) app = fn;
|
||||
|
||||
// restore .app property on req and res
|
||||
if (app) {
|
||||
app.route = route;
|
||||
fn = function(req, res, next) {
|
||||
var orig = req.app;
|
||||
app.handle(req, res, function(err){
|
||||
req.app = res.app = orig;
|
||||
next(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
connect.proto.use.call(this, route, fn);
|
||||
|
||||
// mounted an app
|
||||
if (app) {
|
||||
app.parent = this;
|
||||
app.emit('mount', this);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the given template engine callback `fn`
|
||||
* as `ext`. For example we may wish to map ".html"
|
||||
* files to ejs rather than using the ".ejs" extension.
|
||||
*
|
||||
* app.engine('.html', require('ejs').render);
|
||||
*
|
||||
* or
|
||||
*
|
||||
* app.engine('html', require('ejs').render);
|
||||
*
|
||||
* @param {String} ext
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.engine = function(ext, fn){
|
||||
if ('function' != typeof fn) throw new Error('callback function required');
|
||||
if ('.' != ext[0]) ext = '.' + ext;
|
||||
this.engines[ext] = fn;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map the given param placeholder `name`(s) to the given callback(s).
|
||||
*
|
||||
* Param mapping is used to provide pre-conditions to routes
|
||||
* which us normalized placeholders. This callback has the same
|
||||
* signature as regular middleware, for example below when ":userId"
|
||||
* is used this function will be invoked in an attempt to load the user.
|
||||
*
|
||||
* app.param('userId', function(req, res, next, id){
|
||||
* User.find(id, function(err, user){
|
||||
* if (err) {
|
||||
* next(err);
|
||||
* } else if (user) {
|
||||
* req.user = user;
|
||||
* next();
|
||||
* } else {
|
||||
* next(new Error('failed to load user'));
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* Passing a single function allows you to map logic
|
||||
* to the values passed to `app.param()`, for example
|
||||
* this is useful to provide coercion support in a concise manner.
|
||||
*
|
||||
* The following example maps regular expressions to param values
|
||||
* ensuring that they match, otherwise passing control to the next
|
||||
* route:
|
||||
*
|
||||
* app.param(function(name, regexp){
|
||||
* if (regexp instanceof RegExp) {
|
||||
* return function(req, res, next, val){
|
||||
* var captures;
|
||||
* if (captures = regexp.exec(String(val))) {
|
||||
* req.params[name] = captures;
|
||||
* next();
|
||||
* } else {
|
||||
* next('route');
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* We can now use it as shown below, where "/commit/:commit" expects
|
||||
* that the value for ":commit" is at 5 or more digits. The capture
|
||||
* groups are then available as `req.params.commit` as we defined
|
||||
* in the function above.
|
||||
*
|
||||
* app.param('commit', /^\d{5,}$/);
|
||||
*
|
||||
* For more of this useful functionality take a look
|
||||
* at [express-params](http://github.com/visionmedia/express-params).
|
||||
*
|
||||
* @param {String|Array|Function} name
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.param = function(name, fn){
|
||||
var self = this
|
||||
, fns = [].slice.call(arguments, 1);
|
||||
|
||||
// array
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function(name){
|
||||
fns.forEach(function(fn){
|
||||
self.param(name, fn);
|
||||
});
|
||||
});
|
||||
// param logic
|
||||
} else if ('function' == typeof name) {
|
||||
this._router.param(name);
|
||||
// single
|
||||
} else {
|
||||
if (':' == name[0]) name = name.substr(1);
|
||||
fns.forEach(function(fn){
|
||||
self._router.param(name, fn);
|
||||
});
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign `setting` to `val`, or return `setting`'s value.
|
||||
* Mounted servers inherit their parent server's settings.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @param {String} val
|
||||
* @return {Server|Mixed} for chaining, or the setting value
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.set = function(setting, val){
|
||||
if (1 == arguments.length) {
|
||||
if (this.settings.hasOwnProperty(setting)) {
|
||||
return this.settings[setting];
|
||||
} else if (this.parent) {
|
||||
return this.parent.set(setting);
|
||||
}
|
||||
} else {
|
||||
this.settings[setting] = val;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the app's absolute pathname
|
||||
* based on the parent(s) that have
|
||||
* mounted it.
|
||||
*
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
app.path = function(){
|
||||
return this.parent
|
||||
? this.parent.path() + this.route
|
||||
: '';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `setting` is enabled.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.enabled = function(setting){
|
||||
return !!this.set(setting);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `setting` is disabled.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.disabled = function(setting){
|
||||
return !this.set(setting);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable `setting`.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.enable = function(setting){
|
||||
return this.set(setting, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable `setting`.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.disable = function(setting){
|
||||
return this.set(setting, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Configure callback for zero or more envs,
|
||||
* when no env is specified that callback will
|
||||
* be invoked for all environments. Any combination
|
||||
* can be used multiple times, in any order desired.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* app.configure(function(){
|
||||
* // executed for all envs
|
||||
* });
|
||||
*
|
||||
* app.configure('stage', function(){
|
||||
* // executed staging env
|
||||
* });
|
||||
*
|
||||
* app.configure('stage', 'production', function(){
|
||||
* // executed for stage and production
|
||||
* });
|
||||
*
|
||||
* @param {String} env...
|
||||
* @param {Function} fn
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.configure = function(env, fn){
|
||||
var envs = 'all'
|
||||
, args = [].slice.call(arguments);
|
||||
fn = args.pop();
|
||||
if (args.length) envs = args;
|
||||
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Listen for connections.
|
||||
*
|
||||
* This method takes the same arguments
|
||||
* as node's `http.Server#listen()`.
|
||||
*
|
||||
* @return {http.Server}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.listen = function(){
|
||||
var server = http.createServer(this);
|
||||
return server.listen.apply(server, arguments);
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegate `.VERB(...)` calls to `.route(VERB, ...)`.
|
||||
*/
|
||||
|
||||
methods.forEach(function(method){
|
||||
app[method] = function(path){
|
||||
if ('get' == method && 1 == arguments.length) return this.set(path);
|
||||
var args = [method].concat([].slice.call(arguments));
|
||||
if (!this._usedRouter) this.use(this.router);
|
||||
return this._router.route.apply(this._router, args);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Special-cased "all" method, applying the given route `path`,
|
||||
* middleware, and callback to _every_ HTTP method.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} ...
|
||||
* @return {app} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.all = function(path){
|
||||
var args = arguments;
|
||||
methods.forEach(function(method){
|
||||
if ('all' == method || 'del' == method) return;
|
||||
app[method].apply(this, args);
|
||||
}, this);
|
||||
return this;
|
||||
};
|
||||
|
||||
// del -> delete alias
|
||||
|
||||
app.del = app.delete;
|
||||
|
||||
/**
|
||||
* Render the given view `name` name with `options`
|
||||
* and a callback accepting an error and the
|
||||
* rendered template string.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String|Function} options or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.render = function(name, options, fn){
|
||||
var self = this
|
||||
, opts = {}
|
||||
, cache = this.cache
|
||||
, engines = this.engines
|
||||
, view;
|
||||
|
||||
// support callback function as second arg
|
||||
if ('function' == typeof options) {
|
||||
fn = options, options = {};
|
||||
}
|
||||
|
||||
// merge app.locals
|
||||
utils.merge(opts, this.locals);
|
||||
|
||||
// merge options.locals
|
||||
if (options.locals) utils.merge(opts, options.locals);
|
||||
|
||||
// merge options
|
||||
utils.merge(opts, options);
|
||||
|
||||
// set .cache unless explicitly provided
|
||||
opts.cache = null == opts.cache
|
||||
? this.enabled('view cache')
|
||||
: opts.cache;
|
||||
|
||||
// primed cache
|
||||
if (opts.cache) view = cache[name];
|
||||
|
||||
// view
|
||||
if (!view) {
|
||||
view = new View(name, {
|
||||
defaultEngine: this.get('view engine')
|
||||
, root: this.get('views') || process.cwd() + '/views'
|
||||
, engines: engines
|
||||
});
|
||||
|
||||
if (!view.path) {
|
||||
return fn(new Error('Failed to lookup view "' + name + '"'));
|
||||
}
|
||||
|
||||
// prime the cache
|
||||
if (opts.cache) cache[name] = view;
|
||||
}
|
||||
|
||||
// render
|
||||
try {
|
||||
view.render(opts, fn);
|
||||
} catch (err) {
|
||||
fn(err);
|
||||
}
|
||||
};
|
||||
+43
-37
@@ -9,69 +9,75 @@
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect')
|
||||
, HTTPSServer = require('./https')
|
||||
, HTTPServer = require('./http')
|
||||
var http = require('http')
|
||||
, connect = require('connect')
|
||||
, proto = require('./application')
|
||||
, Route = require('./router/route')
|
||||
, Router = require('./router')
|
||||
, req = require('./request')
|
||||
, res = require('./response')
|
||||
, utils = connect.utils;
|
||||
|
||||
/**
|
||||
* Re-export connect auto-loaders.
|
||||
*
|
||||
* This prevents the need to `require('connect')` in order
|
||||
* to access core middleware, so for example `express.logger()` instead
|
||||
* of `require('connect').logger()`.
|
||||
* Expose `createApplication()`.
|
||||
*/
|
||||
|
||||
var exports = module.exports = connect.middleware;
|
||||
exports = module.exports = createApplication;
|
||||
|
||||
/**
|
||||
* Framework version.
|
||||
*/
|
||||
|
||||
exports.version = '2.4.3';
|
||||
exports.version = '3.0.0alpha1';
|
||||
|
||||
/**
|
||||
* Shortcut for `new Server(...)`.
|
||||
* Create an express application.
|
||||
*
|
||||
* @param {Function} ...
|
||||
* @return {Server}
|
||||
* @return {Function}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.createServer = function(options){
|
||||
if ('object' == typeof options) {
|
||||
return new HTTPSServer(options, Array.prototype.slice.call(arguments, 1));
|
||||
} else {
|
||||
return new HTTPServer(Array.prototype.slice.call(arguments));
|
||||
}
|
||||
};
|
||||
function createApplication() {
|
||||
var app = connect();
|
||||
utils.merge(app, proto);
|
||||
app.request = { __proto__: req };
|
||||
app.response = { __proto__: res };
|
||||
app.init();
|
||||
return app;
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose connect.middleware as express.*
|
||||
* for example `express.logger` etc.
|
||||
*/
|
||||
|
||||
for (var key in connect.middleware) {
|
||||
Object.defineProperty(
|
||||
exports
|
||||
, key
|
||||
, Object.getOwnPropertyDescriptor(connect.middleware, key));
|
||||
}
|
||||
|
||||
/**
|
||||
* Expose the prototypes.
|
||||
*/
|
||||
|
||||
exports.application = proto;
|
||||
exports.request = req;
|
||||
exports.response = res;
|
||||
|
||||
/**
|
||||
* Expose constructors.
|
||||
*/
|
||||
|
||||
exports.HTTPServer = HTTPServer;
|
||||
exports.HTTPSServer = HTTPSServer;
|
||||
exports.Route = Route;
|
||||
exports.Router = Router;
|
||||
|
||||
/**
|
||||
* View extensions.
|
||||
* Expose HTTP methods.
|
||||
*/
|
||||
|
||||
exports.View =
|
||||
exports.view = require('./view');
|
||||
|
||||
/**
|
||||
* Response extensions.
|
||||
*/
|
||||
|
||||
require('./response');
|
||||
|
||||
/**
|
||||
* Request extensions.
|
||||
*/
|
||||
|
||||
require('./request');
|
||||
exports.methods = require('./router/methods');
|
||||
|
||||
// Error handler title
|
||||
|
||||
|
||||
-544
@@ -1,544 +0,0 @@
|
||||
|
||||
/*!
|
||||
* Express - HTTPServer
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var qs = require('qs')
|
||||
, connect = require('connect')
|
||||
, router = require('./router')
|
||||
, Router = require('./router')
|
||||
, view = require('./view')
|
||||
, toArray = require('./utils').toArray
|
||||
, methods = router.methods.concat('del', 'all')
|
||||
, url = require('url')
|
||||
, utils = connect.utils;
|
||||
|
||||
/**
|
||||
* Expose `HTTPServer`.
|
||||
*/
|
||||
|
||||
exports = module.exports = HTTPServer;
|
||||
|
||||
/**
|
||||
* Server proto.
|
||||
*/
|
||||
|
||||
var app = HTTPServer.prototype;
|
||||
|
||||
/**
|
||||
* Initialize a new `HTTPServer` with optional `middleware`.
|
||||
*
|
||||
* @param {Array} middleware
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function HTTPServer(middleware){
|
||||
connect.HTTPServer.call(this, []);
|
||||
this.init(middleware);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `connect.HTTPServer`.
|
||||
*/
|
||||
|
||||
app.__proto__ = connect.HTTPServer.prototype;
|
||||
|
||||
/**
|
||||
* Initialize the server.
|
||||
*
|
||||
* @param {Array} middleware
|
||||
* @api private
|
||||
*/
|
||||
|
||||
app.init = function(middleware){
|
||||
var self = this;
|
||||
this.cache = {};
|
||||
this.settings = {};
|
||||
this.redirects = {};
|
||||
this.isCallbacks = {};
|
||||
this._locals = {};
|
||||
this.dynamicViewHelpers = {};
|
||||
this.errorHandlers = [];
|
||||
|
||||
this.set('home', '/');
|
||||
this.set('env', process.env.NODE_ENV || 'development');
|
||||
|
||||
// expose objects to each other
|
||||
this.use(function(req, res, next){
|
||||
req.query = req.query || {};
|
||||
res.setHeader('X-Powered-By', 'Express');
|
||||
req.app = res.app = self;
|
||||
req.res = res;
|
||||
res.req = req;
|
||||
req.next = next;
|
||||
// assign req.query
|
||||
if (req.url.indexOf('?') > 0) {
|
||||
var query = url.parse(req.url).query;
|
||||
req.query = qs.parse(query);
|
||||
}
|
||||
next();
|
||||
});
|
||||
|
||||
// apply middleware
|
||||
if (middleware) middleware.forEach(self.use.bind(self));
|
||||
|
||||
// router
|
||||
this.routes = new Router(this);
|
||||
this.__defineGetter__('router', function(){
|
||||
this.__usedRouter = true;
|
||||
return self.routes.middleware;
|
||||
});
|
||||
|
||||
// default locals
|
||||
this.locals({
|
||||
settings: this.settings
|
||||
, app: this
|
||||
});
|
||||
|
||||
// default development configuration
|
||||
this.configure('development', function(){
|
||||
this.enable('hints');
|
||||
});
|
||||
|
||||
// default production configuration
|
||||
this.configure('production', function(){
|
||||
this.enable('view cache');
|
||||
});
|
||||
|
||||
// register error handlers on "listening"
|
||||
// so that they disregard definition position.
|
||||
this.on('listening', this.registerErrorHandlers.bind(this));
|
||||
|
||||
// route manipulation methods
|
||||
methods.forEach(function(method){
|
||||
self.lookup[method] = function(path){
|
||||
return self.routes.lookup(method, path);
|
||||
};
|
||||
|
||||
self.match[method] = function(path){
|
||||
return self.routes.match(method, path);
|
||||
};
|
||||
|
||||
self.remove[method] = function(path){
|
||||
return self.routes.lookup(method, path).remove();
|
||||
};
|
||||
});
|
||||
|
||||
// del -> delete
|
||||
self.lookup.del = self.lookup.delete;
|
||||
self.match.del = self.match.delete;
|
||||
self.remove.del = self.remove.delete;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove routes matching the given `path`.
|
||||
*
|
||||
* @param {Route} path
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.remove = function(path){
|
||||
return this.routes.lookup('all', path).remove();
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup routes defined with a path
|
||||
* equivalent to `path`.
|
||||
*
|
||||
* @param {Stirng} path
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.lookup = function(path){
|
||||
return this.routes.lookup('all', path);
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup routes matching the given `url`.
|
||||
*
|
||||
* @param {Stirng} url
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.match = function(url){
|
||||
return this.routes.match('all', url);
|
||||
};
|
||||
|
||||
/**
|
||||
* When using the vhost() middleware register error handlers.
|
||||
*/
|
||||
|
||||
app.onvhost = function(){
|
||||
this.registerErrorHandlers();
|
||||
};
|
||||
|
||||
/**
|
||||
* Register error handlers.
|
||||
*
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.registerErrorHandlers = function(){
|
||||
this.errorHandlers.forEach(function(fn){
|
||||
this.use(function(err, req, res, next){
|
||||
fn.apply(this, arguments);
|
||||
});
|
||||
}, this);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Proxy `connect.HTTPServer#use()` to apply settings to
|
||||
* mounted applications.
|
||||
*
|
||||
* @param {String|Function|Server} route
|
||||
* @param {Function|Server} middleware
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.use = function(route, middleware){
|
||||
var app, home, handle;
|
||||
|
||||
if ('string' != typeof route) {
|
||||
middleware = route, route = '/';
|
||||
}
|
||||
|
||||
// express app
|
||||
if (middleware.handle && middleware.set) app = middleware;
|
||||
|
||||
// restore .app property on req and res
|
||||
if (app) {
|
||||
app.route = route;
|
||||
middleware = function(req, res, next) {
|
||||
var orig = req.app;
|
||||
app.handle(req, res, function(err){
|
||||
req.app = res.app = orig;
|
||||
next(err);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
connect.HTTPServer.prototype.use.call(this, route, middleware);
|
||||
|
||||
// mounted an app, invoke the hook
|
||||
// and adjust some settings
|
||||
if (app) {
|
||||
home = app.set('home');
|
||||
if ('/' == home) home = '';
|
||||
app.set('home', app.route + home);
|
||||
app.parent = this;
|
||||
if (app.__mounted) app.__mounted.call(app, this);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign a callback `fn` which is called
|
||||
* when this `Server` is passed to `Server#use()`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* var app = express.createServer()
|
||||
* , blog = express.createServer();
|
||||
*
|
||||
* blog.mounted(function(parent){
|
||||
* // parent is app
|
||||
* // "this" is blog
|
||||
* });
|
||||
*
|
||||
* app.use(blog);
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.mounted = function(fn){
|
||||
this.__mounted = fn;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* See: view.register.
|
||||
*
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.register = function(){
|
||||
view.register.apply(this, arguments);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the given view helpers `obj`. This method
|
||||
* can be called several times to apply additional helpers.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.helpers =
|
||||
app.locals = function(obj){
|
||||
utils.merge(this._locals, obj);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the given dynamic view helpers `obj`. This method
|
||||
* can be called several times to apply additional helpers.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.dynamicHelpers = function(obj){
|
||||
utils.merge(this.dynamicViewHelpers, obj);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Map the given param placeholder `name`(s) to the given callback `fn`.
|
||||
*
|
||||
* Param mapping is used to provide pre-conditions to routes
|
||||
* which us normalized placeholders. This callback has the same
|
||||
* signature as regular middleware, for example below when ":userId"
|
||||
* is used this function will be invoked in an attempt to load the user.
|
||||
*
|
||||
* app.param('userId', function(req, res, next, id){
|
||||
* User.find(id, function(err, user){
|
||||
* if (err) {
|
||||
* next(err);
|
||||
* } else if (user) {
|
||||
* req.user = user;
|
||||
* next();
|
||||
* } else {
|
||||
* next(new Error('failed to load user'));
|
||||
* }
|
||||
* });
|
||||
* });
|
||||
*
|
||||
* @param {String|Array|Function} name
|
||||
* @param {Function} fn
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.param = function(name, fn){
|
||||
// array
|
||||
if (Array.isArray(name)) {
|
||||
name.forEach(function(name){
|
||||
this.param(name, fn);
|
||||
}, this);
|
||||
// param logic
|
||||
} else if ('function' == typeof name) {
|
||||
this.routes.param(name);
|
||||
// single
|
||||
} else {
|
||||
if (':' == name[0]) name = name.substr(1);
|
||||
this.routes.param(name, fn);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign a custom exception handler callback `fn`.
|
||||
* These handlers are always _last_ in the middleware stack.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.error = function(fn){
|
||||
this.errorHandlers.push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the given callback `fn` for the given `type`.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {Function} fn
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.is = function(type, fn){
|
||||
if (!fn) return this.isCallbacks[type];
|
||||
this.isCallbacks[type] = fn;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign `setting` to `val`, or return `setting`'s value.
|
||||
* Mounted servers inherit their parent server's settings.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @param {String} val
|
||||
* @return {Server|Mixed} for chaining, or the setting value
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.set = function(setting, val){
|
||||
if (val === undefined) {
|
||||
if (this.settings.hasOwnProperty(setting)) {
|
||||
return this.settings[setting];
|
||||
} else if (this.parent) {
|
||||
return this.parent.set(setting);
|
||||
}
|
||||
} else {
|
||||
this.settings[setting] = val;
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `setting` is enabled.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.enabled = function(setting){
|
||||
return !!this.set(setting);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `setting` is disabled.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.disabled = function(setting){
|
||||
return !this.set(setting);
|
||||
};
|
||||
|
||||
/**
|
||||
* Enable `setting`.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.enable = function(setting){
|
||||
return this.set(setting, true);
|
||||
};
|
||||
|
||||
/**
|
||||
* Disable `setting`.
|
||||
*
|
||||
* @param {String} setting
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.disable = function(setting){
|
||||
return this.set(setting, false);
|
||||
};
|
||||
|
||||
/**
|
||||
* Redirect `key` to `url`.
|
||||
*
|
||||
* @param {String} key
|
||||
* @param {String} url
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.redirect = function(key, url){
|
||||
this.redirects[key] = url;
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Configure callback for zero or more envs,
|
||||
* when no env is specified that callback will
|
||||
* be invoked for all environments. Any combination
|
||||
* can be used multiple times, in any order desired.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* app.configure(function(){
|
||||
* // executed for all envs
|
||||
* });
|
||||
*
|
||||
* app.configure('stage', function(){
|
||||
* // executed staging env
|
||||
* });
|
||||
*
|
||||
* app.configure('stage', 'production', function(){
|
||||
* // executed for stage and production
|
||||
* });
|
||||
*
|
||||
* @param {String} env...
|
||||
* @param {Function} fn
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.configure = function(env, fn){
|
||||
var envs = 'all'
|
||||
, args = toArray(arguments);
|
||||
fn = args.pop();
|
||||
if (args.length) envs = args;
|
||||
if ('all' == envs || ~envs.indexOf(this.settings.env)) fn.call(this);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Delegate `.VERB(...)` calls to `.route(VERB, ...)`.
|
||||
*/
|
||||
|
||||
methods.forEach(function(method){
|
||||
app[method] = function(path){
|
||||
if (1 == arguments.length) return this.routes.lookup(method, path);
|
||||
var args = [method].concat(toArray(arguments));
|
||||
if (!this.__usedRouter) this.use(this.router);
|
||||
return this.routes._route.apply(this.routes, args);
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Special-cased "all" method, applying the given route `path`,
|
||||
* middleware, and callback to _every_ HTTP method.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {Function} ...
|
||||
* @return {Server} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
app.all = function(path){
|
||||
var args = arguments;
|
||||
if (1 == args.length) return this.routes.lookup('all', path);
|
||||
methods.forEach(function(method){
|
||||
if ('all' == method) return;
|
||||
app[method].apply(this, args);
|
||||
}, this);
|
||||
return this;
|
||||
};
|
||||
|
||||
// del -> delete alias
|
||||
|
||||
app.del = app.delete;
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
/*!
|
||||
* Express - HTTPSServer
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var connect = require('connect')
|
||||
, HTTPServer = require('./http')
|
||||
, https = require('https');
|
||||
|
||||
/**
|
||||
* Expose `HTTPSServer`.
|
||||
*/
|
||||
|
||||
exports = module.exports = HTTPSServer;
|
||||
|
||||
/**
|
||||
* Server proto.
|
||||
*/
|
||||
|
||||
var app = HTTPSServer.prototype;
|
||||
|
||||
/**
|
||||
* Initialize a new `HTTPSServer` with the
|
||||
* given `options`, and optional `middleware`.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Array} middleware
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function HTTPSServer(options, middleware){
|
||||
connect.HTTPSServer.call(this, options, []);
|
||||
this.init(middleware);
|
||||
};
|
||||
|
||||
/**
|
||||
* Inherit from `connect.HTTPSServer`.
|
||||
*/
|
||||
|
||||
app.__proto__ = connect.HTTPSServer.prototype;
|
||||
|
||||
// mixin HTTPServer methods
|
||||
|
||||
Object.keys(HTTPServer.prototype).forEach(function(method){
|
||||
app[method] = HTTPServer.prototype[method];
|
||||
});
|
||||
@@ -0,0 +1,37 @@
|
||||
|
||||
/*!
|
||||
* Express - middleware - init
|
||||
* Copyright(c) 2010-2011 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Initialization middleware, exposing the
|
||||
* request and response to eachother, as well
|
||||
* as defaulting the X-Powered-By header field.
|
||||
*
|
||||
* @param {Function} app
|
||||
* @return {Function}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.init = function(app){
|
||||
return function expressInit(req, res, next){
|
||||
var charset;
|
||||
res.setHeader('X-Powered-By', 'Express');
|
||||
req.app = res.app = app;
|
||||
req.res = res;
|
||||
res.req = req;
|
||||
req.next = next;
|
||||
|
||||
req.__proto__ = app.request;
|
||||
res.__proto__ = app.response;
|
||||
|
||||
res.locals = function(obj){
|
||||
for (var key in obj) res.locals[key] = obj[key];
|
||||
return res;
|
||||
};
|
||||
|
||||
next();
|
||||
}
|
||||
};
|
||||
+278
-172
@@ -10,24 +10,21 @@
|
||||
*/
|
||||
|
||||
var http = require('http')
|
||||
, req = http.IncomingMessage.prototype
|
||||
, utils = require('./utils')
|
||||
, connect = require('connect')
|
||||
, parse = require('url').parse
|
||||
, mime = require('mime');
|
||||
|
||||
/**
|
||||
* Default flash formatters.
|
||||
*
|
||||
* @type Object
|
||||
* Request prototype.
|
||||
*/
|
||||
|
||||
var flashFormatters = exports.flashFormatters = {
|
||||
s: function(val){
|
||||
return String(val);
|
||||
}
|
||||
var req = exports = module.exports = {
|
||||
__proto__: http.IncomingMessage.prototype
|
||||
};
|
||||
|
||||
/**
|
||||
* Return request header or optional default.
|
||||
* Return request header.
|
||||
*
|
||||
* The `Referrer` header field is special-cased,
|
||||
* both `Referrer` and `Referer` will yield are
|
||||
@@ -35,122 +32,193 @@ var flashFormatters = exports.flashFormatters = {
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* req.header('Content-Type');
|
||||
* req.get('Content-Type');
|
||||
* // => "text/plain"
|
||||
*
|
||||
* req.header('content-type');
|
||||
* req.get('content-type');
|
||||
* // => "text/plain"
|
||||
*
|
||||
* req.header('Accept');
|
||||
* req.get('Something');
|
||||
* // => undefined
|
||||
*
|
||||
* req.header('Accept', 'text/html');
|
||||
* // => "text/html"
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} defaultValue
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.header = function(name, defaultValue){
|
||||
req.get = function(name){
|
||||
switch (name = name.toLowerCase()) {
|
||||
case 'referer':
|
||||
case 'referrer':
|
||||
return this.headers.referrer
|
||||
|| this.headers.referer
|
||||
|| defaultValue;
|
||||
|| this.headers.referer;
|
||||
default:
|
||||
return this.headers[name] || defaultValue;
|
||||
return this.headers[name];
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get `field`'s `param` value, defaulting to ''.
|
||||
* Check if the given `type(s)` is acceptable, returning
|
||||
* the best match when true, otherwise `undefined`, in which
|
||||
* case you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* req.get('content-disposition', 'filename');
|
||||
* // => "something.png"
|
||||
*
|
||||
* @param {String} field
|
||||
* @param {String} param
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.get = function(field, param){
|
||||
var val = this.header(field);
|
||||
if (!val) return '';
|
||||
var regexp = new RegExp(param + ' *= *(?:"([^"]+)"|([^;]+))', 'i');
|
||||
if (!regexp.exec(val)) return '';
|
||||
return RegExp.$1 || RegExp.$2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the _Accept_ header is present, and includes the given `type`.
|
||||
*
|
||||
* When the _Accept_ header is not present `true` is returned. Otherwise
|
||||
* the given `type` 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.
|
||||
* The `type` value may be a single mime type string
|
||||
* such as "application/json", the extension name
|
||||
* such as "json", a comma-delimted list such as "json, html, text/plain",
|
||||
* or an array `["json", "html", "text/plain"]`. When a list
|
||||
* or array is given the _best_ match, if any is returned.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* // Accept: text/html
|
||||
* req.accepts('html');
|
||||
* // => true
|
||||
* // => "html"
|
||||
*
|
||||
* // Accept: text/*; application/json
|
||||
* // Accept: text/*, application/json
|
||||
* req.accepts('html');
|
||||
* // => "html"
|
||||
* req.accepts('text/html');
|
||||
* req.accepts('text/plain');
|
||||
* // => "text/html"
|
||||
* req.accepts('json, text');
|
||||
* // => "json"
|
||||
* req.accepts('application/json');
|
||||
* // => true
|
||||
* // => "application/json"
|
||||
*
|
||||
* // Accept: text/*, application/json
|
||||
* req.accepts('image/png');
|
||||
* req.accepts('png');
|
||||
* // => false
|
||||
* // => undefined
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {Boolean}
|
||||
* // Accept: text/*;q=.5, application/json
|
||||
* req.accepts(['html', 'json']);
|
||||
* req.accepts('html, json');
|
||||
* // => "json"
|
||||
*
|
||||
* @param {String|Array} type(s)
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.accepts = function(type){
|
||||
var accept = this.header('Accept');
|
||||
|
||||
// normalize extensions ".json" -> "json"
|
||||
if (type && '.' == type[0]) type = type.substr(1);
|
||||
|
||||
// when Accept does not exist, or is '*/*' return true
|
||||
if (!accept || '*/*' == accept) {
|
||||
return true;
|
||||
} else if (type) {
|
||||
// allow "html" vs "text/html" etc
|
||||
if (!~type.indexOf('/')) type = mime.lookup(type);
|
||||
|
||||
// check if we have a direct match
|
||||
if (~accept.indexOf(type)) return true;
|
||||
|
||||
// check if we have type/*
|
||||
type = type.split('/')[0] + '/*';
|
||||
return !!~accept.indexOf(type);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return utils.accepts(type, this.get('Accept'));
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given `charset` is acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* @param {String} charset
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsCharset = function(charset){
|
||||
var accepted = this.acceptedCharsets;
|
||||
return accepted.length
|
||||
? ~accepted.indexOf(charset)
|
||||
: true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the given `lang` is acceptable,
|
||||
* otherwise you should respond with 406 "Not Acceptable".
|
||||
*
|
||||
* @param {String} lang
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.acceptsLanguage = function(lang){
|
||||
var accepted = this.acceptedLanguages;
|
||||
return accepted.length
|
||||
? ~accepted.indexOf(lang)
|
||||
: true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return an array of Accepted media types
|
||||
* ordered from highest quality to lowest.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* [ { value: 'application/json',
|
||||
* quality: 1,
|
||||
* type: 'application',
|
||||
* subtype: 'json' },
|
||||
* { value: 'text/html',
|
||||
* quality: 0.5,
|
||||
* type: 'text',
|
||||
* subtype: 'html' } ]
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('accepted', function(){
|
||||
var accept = this.get('Accept');
|
||||
return accept
|
||||
? utils.parseAccept(accept)
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return an array of Accepted languages
|
||||
* ordered from highest quality to lowest.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Accept-Language: en;q=.5, en-us
|
||||
* ['en-us', 'en']
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('acceptedLanguages', function(){
|
||||
var accept = this.get('Accept-Language');
|
||||
return accept
|
||||
? utils
|
||||
.parseQuality(accept)
|
||||
.map(function(obj){
|
||||
return obj.value;
|
||||
})
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return an array of Accepted charsets
|
||||
* ordered from highest quality to lowest.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* Accept-Charset: iso-8859-5;q=.2, unicode-1-1;q=0.8
|
||||
* ['unicode-1-1', 'iso-8859-5']
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('acceptedCharsets', function(){
|
||||
var accept = this.get('Accept-Charset');
|
||||
return accept
|
||||
? utils
|
||||
.parseQuality(accept)
|
||||
.map(function(obj){
|
||||
return obj.value;
|
||||
})
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return the value of param `name` when present or `defaultValue`.
|
||||
*
|
||||
* - Checks body params, ex: id=12, {"id":12}
|
||||
* - Checks route placeholders, ex: _/user/:id_
|
||||
* - Checks query string params, ex: ?id=12
|
||||
* - Checks urlencoded body params, ex: id=12
|
||||
*
|
||||
* To utilize urlencoded request bodies, `req.body`
|
||||
* To utilize request bodies, `req.body`
|
||||
* should be an object. This can be done by using
|
||||
* the `connect.bodyParser` middleware.
|
||||
* the `connect.bodyParser()` middleware.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Mixed} defaultValue
|
||||
@@ -159,80 +227,22 @@ req.accepts = function(type){
|
||||
*/
|
||||
|
||||
req.param = function(name, defaultValue){
|
||||
// route params like /user/:id
|
||||
if (this.params && this.params.hasOwnProperty(name) && undefined !== this.params[name]) {
|
||||
// req.body
|
||||
if (this.body && undefined !== this.body[name]) return this.body[name];
|
||||
|
||||
// route params
|
||||
if (this.params
|
||||
&& this.params.hasOwnProperty(name)
|
||||
&& undefined !== this.params[name]) {
|
||||
return this.params[name];
|
||||
}
|
||||
// query string params
|
||||
if (undefined !== this.query[name]) {
|
||||
return this.query[name];
|
||||
}
|
||||
// request body params via connect.bodyParser
|
||||
if (this.body && undefined !== this.body[name]) {
|
||||
return this.body[name];
|
||||
}
|
||||
|
||||
// query-string
|
||||
if (undefined !== this.query[name]) return this.query[name];
|
||||
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Queue flash `msg` of the given `type`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* 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: [] }
|
||||
*
|
||||
* Formatting:
|
||||
*
|
||||
* Flash notifications also support arbitrary formatting support.
|
||||
* For example you may pass variable arguments to `req.flash()`
|
||||
* and use the %s specifier to be replaced by the associated argument:
|
||||
*
|
||||
* req.flash('info', 'email has been sent to %s.', userName);
|
||||
*
|
||||
* To add custom formatters use the `exports.flashFormatters` object.
|
||||
*
|
||||
* @param {String} type
|
||||
* @param {String} msg
|
||||
* @return {Array|Object|Number}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.flash = function(type, msg){
|
||||
if (this.session === undefined) throw Error('req.flash() requires sessions');
|
||||
var msgs = this.session.flash = this.session.flash || {};
|
||||
if (type && msg) {
|
||||
var i = 2
|
||||
, args = arguments
|
||||
, formatters = this.app.flashFormatters || {};
|
||||
formatters.__proto__ = flashFormatters;
|
||||
msg = utils.miniMarkdown(utils.escape(msg));
|
||||
msg = msg.replace(/%([a-zA-Z])/g, function(_, format){
|
||||
var formatter = formatters[format];
|
||||
if (formatter) return formatter(args[i++]);
|
||||
});
|
||||
return (msgs[type] = msgs[type] || []).push(msg);
|
||||
} else if (type) {
|
||||
var arr = msgs[type];
|
||||
delete msgs[type];
|
||||
return arr || [];
|
||||
} else {
|
||||
this.session.flash = {};
|
||||
return msgs;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the incoming request contains the "Content-Type"
|
||||
* header field, and it contains the give mime `type`.
|
||||
@@ -242,30 +252,23 @@ req.flash = function(type, msg){
|
||||
* // With Content-Type: text/html; charset=utf-8
|
||||
* req.is('html');
|
||||
* req.is('text/html');
|
||||
* req.is('text/*');
|
||||
* // => true
|
||||
*
|
||||
* // When Content-Type is application/json
|
||||
* req.is('json');
|
||||
* req.is('application/json');
|
||||
* req.is('application/*');
|
||||
* // => true
|
||||
*
|
||||
* req.is('html');
|
||||
* // => false
|
||||
*
|
||||
* Ad-hoc callbacks can also be registered with Express, to perform
|
||||
* assertions again the request, for example if we need an expressive
|
||||
* way to check if our incoming request is an image, we can register "an image"
|
||||
* callback:
|
||||
*
|
||||
* app.is('an image', function(req){
|
||||
* return 0 == req.headers['content-type'].indexOf('image');
|
||||
* });
|
||||
*
|
||||
* Now within our route callbacks, we can use to to assert content types
|
||||
* such as "image/jpeg", "image/png", etc.
|
||||
*
|
||||
* app.post('/image/upload', function(req, res, next){
|
||||
* if (req.is('an image')) {
|
||||
* if (req.is('image/*')) {
|
||||
* // do something
|
||||
* } else {
|
||||
* next();
|
||||
@@ -278,25 +281,126 @@ req.flash = function(type, msg){
|
||||
*/
|
||||
|
||||
req.is = function(type){
|
||||
var fn = this.app.is(type);
|
||||
if (fn) return fn(this);
|
||||
var contentType = this.headers['content-type'];
|
||||
if (!contentType) return;
|
||||
var ct = this.get('Content-Type');
|
||||
if (!ct) return false;
|
||||
ct = ct.split(';')[0];
|
||||
if (!~type.indexOf('/')) type = mime.lookup(type);
|
||||
if (~type.indexOf('*')) {
|
||||
type = type.split('/')
|
||||
contentType = contentType.split('/');
|
||||
if ('*' == type[0] && type[1] == contentType[1]) return true;
|
||||
if ('*' == type[1] && type[0] == contentType[0]) return true;
|
||||
type = type.split('/');
|
||||
ct = ct.split('/');
|
||||
if ('*' == type[0] && type[1] == ct[1]) return true;
|
||||
if ('*' == type[1] && type[0] == ct[0]) return true;
|
||||
return false;
|
||||
}
|
||||
return !! ~contentType.indexOf(type);
|
||||
return !! ~ct.indexOf(type);
|
||||
};
|
||||
|
||||
// Callback for isXMLHttpRequest / xhr
|
||||
/**
|
||||
* Return the protocol string "http" or "https"
|
||||
* when requested with TLS. When the "trust proxy"
|
||||
* setting is enabled the "X-Forwarded-Proto" header
|
||||
* field will be trusted. If you're running behind
|
||||
* a reverse proxy that supplies https for you this
|
||||
* may be enabled.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
function isxhr() {
|
||||
return this.header('X-Requested-With', '').toLowerCase() === 'xmlhttprequest';
|
||||
}
|
||||
req.__defineGetter__('protocol', function(trustProxy){
|
||||
var trustProxy = this.app.settings['trust proxy'];
|
||||
return this.connection.encrypted
|
||||
? 'https'
|
||||
: trustProxy
|
||||
? (this.get('X-Forwarded-Proto') || 'http')
|
||||
: 'http';
|
||||
});
|
||||
|
||||
/**
|
||||
* Short-hand for:
|
||||
*
|
||||
* req.protocol == 'https'
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('secure', function(){
|
||||
return 'https' == this.protocol;
|
||||
});
|
||||
|
||||
/**
|
||||
* When "trust proxy" is `true`, parse
|
||||
* the "X-Forwarded-For" ip address list.
|
||||
*
|
||||
* For example if the value were "client, proxy1, proxy2"
|
||||
* you would receive the array `["proxy2", "proxy1", "client"]`
|
||||
* where "proxy2" is the furthest down-stream.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('ips', function(){
|
||||
var val = this.get('X-Forwarded-For');
|
||||
return val
|
||||
? val.split(/ *, */).reverse()
|
||||
: [];
|
||||
});
|
||||
|
||||
/**
|
||||
* Return subdomains as an array.
|
||||
*
|
||||
* For example "tobi.ferrets.example.com"
|
||||
* would provide `["ferrets", "tobi"]`.
|
||||
*
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('subdomains', function(){
|
||||
return this.get('Host')
|
||||
.split('.')
|
||||
.slice(0, -2)
|
||||
.reverse();
|
||||
});
|
||||
|
||||
/**
|
||||
* Short-hand for `require('url').parse(req.url).pathname`.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('path', function(){
|
||||
return parse(this.url).pathname;
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the request is fresh, aka
|
||||
* Last-Modified and/or the ETag
|
||||
* still match.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('fresh', function(){
|
||||
return ! this.stale;
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the request is stale, aka
|
||||
* "Last-Modified" and / or the "ETag" for the
|
||||
* resource has changed.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('stale', function(){
|
||||
return connect.utils.modified(this, this.res);
|
||||
});
|
||||
|
||||
/**
|
||||
* Check if the request was an _XMLHttpRequest_.
|
||||
@@ -305,5 +409,7 @@ function isxhr() {
|
||||
* @api public
|
||||
*/
|
||||
|
||||
req.__defineGetter__('isXMLHttpRequest', isxhr);
|
||||
req.__defineGetter__('xhr', isxhr);
|
||||
req.__defineGetter__('xhr', function(){
|
||||
var val = this.get('X-Requested-With') || '';
|
||||
return 'xmlhttprequest' == val.toLowerCase();
|
||||
});
|
||||
|
||||
+432
-276
@@ -14,131 +14,21 @@ var fs = require('fs')
|
||||
, path = require('path')
|
||||
, connect = require('connect')
|
||||
, utils = connect.utils
|
||||
, parseRange = require('./utils').parseRange
|
||||
, res = http.ServerResponse.prototype
|
||||
, normalizeType = require('./utils').normalizeType
|
||||
, normalizeTypes = require('./utils').normalizeTypes
|
||||
, statusCodes = http.STATUS_CODES
|
||||
, send = connect.static.send
|
||||
, mime = require('mime')
|
||||
, basename = path.basename
|
||||
, extname = path.extname
|
||||
, join = path.join;
|
||||
|
||||
/**
|
||||
* Send a response with the given `body` and optional `headers` and `status` code.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.send();
|
||||
* res.send(new Buffer('wahoo'));
|
||||
* res.send({ some: 'json' });
|
||||
* res.send('<p>some html</p>');
|
||||
* res.send('Sorry, cant find that', 404);
|
||||
* res.send('text', { 'Content-Type': 'text/plain' }, 201);
|
||||
* res.send(404);
|
||||
*
|
||||
* @param {String|Object|Number|Buffer} body or status
|
||||
* @param {Object|Number} headers or status
|
||||
* @param {Number} status
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
* Response prototype.
|
||||
*/
|
||||
|
||||
res.send = function(body, headers, status){
|
||||
// allow status as second arg
|
||||
if ('number' == typeof headers) {
|
||||
status = headers,
|
||||
headers = null;
|
||||
}
|
||||
|
||||
// default status
|
||||
status = status || this.statusCode;
|
||||
|
||||
// allow 0 args as 204
|
||||
if (!arguments.length || undefined === body) body = status = 204;
|
||||
|
||||
// determine content type
|
||||
switch (typeof body) {
|
||||
case 'number':
|
||||
if (!this.header('Content-Type')) {
|
||||
this.contentType('.txt');
|
||||
}
|
||||
body = http.STATUS_CODES[status = body];
|
||||
break;
|
||||
case 'string':
|
||||
if (!this.header('Content-Type')) {
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.contentType('.html');
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
if (Buffer.isBuffer(body)) {
|
||||
if (!this.header('Content-Type')) {
|
||||
this.contentType('.bin');
|
||||
}
|
||||
} else {
|
||||
return this.json(body, headers, status);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// populate Content-Length
|
||||
if (!this.header('Content-Length')) {
|
||||
this.header('Content-Length', Buffer.isBuffer(body)
|
||||
? body.length
|
||||
: Buffer.byteLength(body));
|
||||
}
|
||||
|
||||
// merge headers passed
|
||||
if (headers) {
|
||||
var fields = Object.keys(headers);
|
||||
for (var i = 0, len = fields.length; i < len; ++i) {
|
||||
var field = fields[i];
|
||||
this.header(field, headers[field]);
|
||||
}
|
||||
}
|
||||
|
||||
// strip irrelevant headers
|
||||
if (204 == status || 304 == status) {
|
||||
this.removeHeader('Content-Type');
|
||||
this.removeHeader('Content-Length');
|
||||
}
|
||||
|
||||
// respond
|
||||
this.statusCode = status;
|
||||
this.end('HEAD' == this.req.method ? undefined : body);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send JSON response with `obj`, optional `headers`, and optional `status`.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.json(null);
|
||||
* res.json({ user: 'tj' });
|
||||
* res.json('oh noes!', 500);
|
||||
* res.json('I dont have that', 404);
|
||||
*
|
||||
* @param {Mixed} obj
|
||||
* @param {Object|Number} headers or status
|
||||
* @param {Number} status
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.json = function(obj, headers, status){
|
||||
var body = JSON.stringify(obj)
|
||||
, callback = this.req.query.callback
|
||||
, jsonp = this.app.enabled('jsonp callback');
|
||||
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.header('Content-Type', 'application/json');
|
||||
|
||||
if (callback && jsonp) {
|
||||
this.header('Content-Type', 'text/javascript');
|
||||
body = callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
|
||||
}
|
||||
|
||||
return this.send(body, headers, status);
|
||||
var res = module.exports = {
|
||||
__proto__: http.ServerResponse.prototype
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -155,9 +45,126 @@ res.status = function(code){
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`. Automatically sets
|
||||
* the _Content-Type_ response header field. `next()` is called
|
||||
* when `path` is a directory, or when an error occurs.
|
||||
* Send a response.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.send(new Buffer('wahoo'));
|
||||
* res.send({ some: 'json' });
|
||||
* res.send('<p>some html</p>');
|
||||
* res.send(404, 'Sorry, cant find that');
|
||||
* res.send(404);
|
||||
*
|
||||
* @param {Mixed} body or status
|
||||
* @param {Mixed} body
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.send = function(body){
|
||||
var req = this.req
|
||||
, head = 'HEAD' == req.method;
|
||||
|
||||
// allow status / body
|
||||
if (2 == arguments.length) {
|
||||
this.statusCode = body;
|
||||
body = arguments[1];
|
||||
}
|
||||
|
||||
switch (typeof body) {
|
||||
// response status
|
||||
case 'number':
|
||||
this.get('Content-Type') || this.contentType('.txt');
|
||||
this.statusCode = body;
|
||||
body = http.STATUS_CODES[body];
|
||||
break;
|
||||
// string defaulting to html
|
||||
case 'string':
|
||||
if (!this.get('Content-Type')) {
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.contentType('.html');
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
case 'object':
|
||||
if (null == body) {
|
||||
body = '';
|
||||
} else if (Buffer.isBuffer(body)) {
|
||||
this.get('Content-Type') || this.contentType('.bin');
|
||||
} else {
|
||||
return this.json(body);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// populate Content-Length
|
||||
if (undefined !== body && !this.get('Content-Length')) {
|
||||
this.set('Content-Length', Buffer.isBuffer(body)
|
||||
? body.length
|
||||
: Buffer.byteLength(body));
|
||||
}
|
||||
|
||||
// strip irrelevant headers
|
||||
if (204 == this.statusCode || 304 == this.statusCode) {
|
||||
this.removeHeader('Content-Type');
|
||||
this.removeHeader('Content-Length');
|
||||
body = '';
|
||||
}
|
||||
|
||||
// respond
|
||||
this.end(head ? null : body);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Send JSON response.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.json(null);
|
||||
* res.json({ user: 'tj' });
|
||||
* res.json(500, 'oh noes!');
|
||||
* res.json(404, 'I dont have that');
|
||||
*
|
||||
* @param {Mixed} obj or status
|
||||
* @param {Mixed} obj
|
||||
* @return {ServerResponse}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.json = function(obj){
|
||||
// allow status / body
|
||||
if (2 == arguments.length) {
|
||||
this.statusCode = obj;
|
||||
obj = arguments[1];
|
||||
}
|
||||
|
||||
var settings = this.app.settings
|
||||
, jsonp = settings['jsonp callback']
|
||||
, replacer = settings['json replacer']
|
||||
, spaces = settings['json spaces']
|
||||
, body = JSON.stringify(obj, replacer, spaces)
|
||||
, callback = this.req.query.callback;
|
||||
|
||||
this.charset = this.charset || 'utf-8';
|
||||
this.set('Content-Type', 'application/json');
|
||||
|
||||
if (callback && jsonp) {
|
||||
this.set('Content-Type', 'text/javascript');
|
||||
body = callback.replace(/[^\w$.]/g, '') + '(' + body + ');';
|
||||
}
|
||||
|
||||
return this.send(body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`.
|
||||
*
|
||||
* Automatically sets the _Content-Type_ response header field.
|
||||
* The callback `fn(err)` is invoked when the transfer is complete
|
||||
* or when an error occurs. Be sure to check `res.sentHeader`
|
||||
* if you wish to attempt responding, as the header and some data
|
||||
* may have already been transferred.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
@@ -171,8 +178,10 @@ res.status = function(code){
|
||||
*/
|
||||
|
||||
res.sendfile = function(path, options, fn){
|
||||
var next = this.req.next;
|
||||
options = options || {};
|
||||
var self = this
|
||||
, req = self.req
|
||||
, next = this.req.next
|
||||
, options = options || {};
|
||||
|
||||
// support function as second arg
|
||||
if ('function' == typeof options) {
|
||||
@@ -180,32 +189,155 @@ res.sendfile = function(path, options, fn){
|
||||
options = {};
|
||||
}
|
||||
|
||||
// callback
|
||||
options.callback = function(err){
|
||||
if (err) {
|
||||
// cast ENOENT
|
||||
if ('ENOENT' == err.code) err = 404;
|
||||
|
||||
// coerce numeric error to an Error
|
||||
// TODO: remove
|
||||
// TODO: remove docs for headerSent?
|
||||
if ('number' == typeof err) err = utils.error(err);
|
||||
|
||||
// ditch content-disposition to prevent funky responses
|
||||
if (!self.headerSent) self.removeHeader('Content-Disposition');
|
||||
|
||||
// woot! callback available
|
||||
if (fn) return fn(err);
|
||||
|
||||
// lost in limbo if there's no callback
|
||||
if (self.headerSent) return;
|
||||
|
||||
return req.next(err);
|
||||
}
|
||||
|
||||
fn && fn();
|
||||
};
|
||||
|
||||
// transfer
|
||||
options.path = encodeURIComponent(path);
|
||||
options.callback = fn;
|
||||
send(this.req, this, next, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set _Content-Type_ response header passed through `mime.lookup()`.
|
||||
* Transfer the file at the given `path` as an attachment.
|
||||
*
|
||||
* Examples:
|
||||
* Optionally providing an alternate attachment `filename`,
|
||||
* and optional callback `fn(err)`. The callback is invoked
|
||||
* when the data transfer is complete, or when an error has
|
||||
* ocurred. Be sure to check `res.headerSent` if you plan to respond.
|
||||
*
|
||||
* var filename = 'path/to/image.png';
|
||||
* res.contentType(filename);
|
||||
* // res.headers['Content-Type'] is now "image/png"
|
||||
*
|
||||
* res.contentType('.html');
|
||||
* res.contentType('html');
|
||||
* res.contentType('json');
|
||||
* res.contentType('png');
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {String} the resolved mime type
|
||||
* @param {String} path
|
||||
* @param {String|Function} filename or fn
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.contentType = function(type){
|
||||
return this.header('Content-Type', mime.lookup(type));
|
||||
res.download = function(path, filename, fn){
|
||||
// support function as second arg
|
||||
if ('function' == typeof filename) {
|
||||
fn = filename;
|
||||
filename = null;
|
||||
}
|
||||
|
||||
return this.attachment(filename || path).sendfile(path, fn);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set _Content-Type_ response header with `type` through `mime.lookup()`
|
||||
* when it does not contain "/", or set the Content-Type to `type` otherwise.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* res.type('.html');
|
||||
* res.type('html');
|
||||
* res.type('json');
|
||||
* res.type('application/json');
|
||||
* res.type('png');
|
||||
*
|
||||
* @param {String} type
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.contentType =
|
||||
res.type = function(type){
|
||||
return this.set('Content-Type', ~type.indexOf('/')
|
||||
? type
|
||||
: mime.lookup(type));
|
||||
};
|
||||
|
||||
/**
|
||||
* Respond to the Acceptable formats using an `obj`
|
||||
* of mime-type callbacks.
|
||||
*
|
||||
* This method uses `req.accepted`, an array of
|
||||
* acceptable types ordered by their quality values.
|
||||
* When "Accept" is not present the _first_ callback
|
||||
* is invoked, otherwise the first match is used. When
|
||||
* no match is performed the server responds with
|
||||
* 406 "Not Acceptable".
|
||||
*
|
||||
* Content-Type is set for you, however if you choose
|
||||
* you may alter this within the callback using `res.type()`
|
||||
* or `res.set('Content-Type', ...)`.
|
||||
*
|
||||
* res.format({
|
||||
* 'text/plain': function(){
|
||||
* res.send('hey');
|
||||
* },
|
||||
*
|
||||
* 'text/html': function(){
|
||||
* res.send('<p>hey</p>');
|
||||
* },
|
||||
*
|
||||
* 'appliation/json': function(){
|
||||
* res.send({ message: 'hey' });
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* In addition to canonicalized MIME types you may
|
||||
* also use extnames mapped to these types:
|
||||
*
|
||||
* res.format({
|
||||
* text: function(){
|
||||
* res.send('hey');
|
||||
* },
|
||||
*
|
||||
* html: function(){
|
||||
* res.send('<p>hey</p>');
|
||||
* },
|
||||
*
|
||||
* json: function(){
|
||||
* res.send({ message: 'hey' });
|
||||
* }
|
||||
* });
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.format = function(obj){
|
||||
var keys = Object.keys(obj)
|
||||
, req = this.req
|
||||
, next = req.next
|
||||
, key = req.accepts(keys);
|
||||
|
||||
this.set('Vary', 'Accept');
|
||||
|
||||
if (key) {
|
||||
this.set('Content-Type', normalizeType(key));
|
||||
obj[key](req, this, next);
|
||||
} else {
|
||||
var err = new Error('Not Acceptable');
|
||||
err.status = 406;
|
||||
err.types = normalizeTypes(keys);
|
||||
next(err);
|
||||
}
|
||||
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -217,70 +349,49 @@ res.contentType = function(type){
|
||||
*/
|
||||
|
||||
res.attachment = function(filename){
|
||||
if (filename) this.contentType(filename);
|
||||
this.header('Content-Disposition', filename
|
||||
if (filename) this.type(extname(filename));
|
||||
this.set('Content-Disposition', filename
|
||||
? 'attachment; filename="' + basename(filename) + '"'
|
||||
: 'attachment');
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transfer the file at the given `path`, with optional
|
||||
* `filename` as an attachment and optional callback `fn(err)`,
|
||||
* and optional `fn2(err)` which is invoked when an error has
|
||||
* occurred after header has been sent.
|
||||
* Set header `field` to `val`, or pass
|
||||
* an object of of header fields.
|
||||
*
|
||||
* @param {String} path
|
||||
* @param {String|Function} filename or fn
|
||||
* @param {Function} fn
|
||||
* @param {Function} fn2
|
||||
* Examples:
|
||||
*
|
||||
* res.set('Accept', 'application/json');
|
||||
* res.set({ Accept: 'text/plain', 'X-API-Key': 'tobi' });
|
||||
*
|
||||
* @param {String|Object} field
|
||||
* @param {String} val
|
||||
* @return {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.download = function(path, filename, fn, fn2){
|
||||
var self = this;
|
||||
|
||||
// support callback as second arg
|
||||
if ('function' == typeof filename) {
|
||||
fn2 = fn;
|
||||
fn = filename;
|
||||
filename = null;
|
||||
}
|
||||
|
||||
// transfer the file
|
||||
this.attachment(filename || path).sendfile(path, function(err){
|
||||
var sentHeader = self._header;
|
||||
if (err) {
|
||||
if (!sentHeader) self.removeHeader('Content-Disposition');
|
||||
if (sentHeader) {
|
||||
fn2 && fn2(err);
|
||||
} else if (fn) {
|
||||
fn(err);
|
||||
} else {
|
||||
self.req.next(err);
|
||||
}
|
||||
} else if (fn) {
|
||||
fn();
|
||||
res.set = function(field, val){
|
||||
if (2 == arguments.length) {
|
||||
this.setHeader(field, val);
|
||||
} else {
|
||||
for (var key in field) {
|
||||
this.setHeader(key, field[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set or get response header `name` with optional `val`.
|
||||
* Get value for header `field`.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} val
|
||||
* @param {String} field
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.header = function(name, val){
|
||||
if (val === undefined) {
|
||||
return this.getHeader(name);
|
||||
} else {
|
||||
this.setHeader(name, val);
|
||||
return val;
|
||||
}
|
||||
res.get = function(field){
|
||||
return this.getHeader(field);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -288,23 +399,42 @@ res.header = function(name, val){
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} options
|
||||
* @param {ServerResponse} for chaining
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.clearCookie = function(name, options){
|
||||
var opts = { expires: new Date(1) };
|
||||
this.cookie(name, '', options
|
||||
? utils.merge(options, opts)
|
||||
var opts = { expires: new Date(1), path: '/' };
|
||||
return this.cookie(name, '', options
|
||||
? utils.merge(opts, options)
|
||||
: opts);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a signed cookie with the given `name` and `val`.
|
||||
* See `res.cookie()` for details.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String|Object} val
|
||||
* @param {Object} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.signedCookie = function(name, val, options){
|
||||
var secret = this.req.secret;
|
||||
if (!secret) throw new Error('connect.cookieParser("secret") required for signed cookies');
|
||||
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
|
||||
val = utils.sign(val, secret);
|
||||
return this.cookie(name, val, options);
|
||||
};
|
||||
|
||||
/**
|
||||
* Set cookie `name` to `val`, with the given `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `maxAge` max-age in milliseconds, converted to `expires`
|
||||
* - `path` defaults to the "home" setting which is typically "/"
|
||||
* - `path` defaults to "/"
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
@@ -315,148 +445,174 @@ res.clearCookie = function(name, options){
|
||||
* res.cookie('rememberme', '1', { maxAge: 900000, httpOnly: true })
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} val
|
||||
* @param {String|Object} val
|
||||
* @param {Options} options
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.cookie = function(name, val, options){
|
||||
options = options || {};
|
||||
if ('object' == typeof val) val = 'j:' + JSON.stringify(val);
|
||||
if ('maxAge' in options) options.expires = new Date(Date.now() + options.maxAge);
|
||||
if (undefined === options.path) options.path = this.app.set('home');
|
||||
if (null == options.path) options.path = '/';
|
||||
var cookie = utils.serializeCookie(name, val, options);
|
||||
this.header('Set-Cookie', cookie);
|
||||
this.set('Set-Cookie', cookie);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Redirect to the given `url` with optional response `status`
|
||||
* defauling to 302.
|
||||
* defaulting to 302.
|
||||
*
|
||||
* The given `url` can also be the name of a mapped url, for
|
||||
* example by default express supports "back" which redirects
|
||||
* to the _Referrer_ or _Referer_ headers or the application's
|
||||
* "home" setting. Express also supports "home" out of the box,
|
||||
* which can be set via `app.set('home', '/blog');`, and defaults
|
||||
* to '/'.
|
||||
* to the _Referrer_ or _Referer_ headers or "/".
|
||||
*
|
||||
* Redirect Mapping:
|
||||
*
|
||||
* To extend the redirect mapping capabilities that Express provides,
|
||||
* we may use the `app.redirect()` method:
|
||||
*
|
||||
* app.redirect('google', 'http://google.com');
|
||||
*
|
||||
* Now in a route we may call:
|
||||
* Examples:
|
||||
*
|
||||
* res.redirect('google');
|
||||
* res.redirect('/foo/bar');
|
||||
* res.redirect('http://example.com');
|
||||
* res.redirect(301, 'http://example.com');
|
||||
* res.redirect('../login'); // /blog/post/1 -> /blog/login
|
||||
*
|
||||
* We may also map dynamic redirects:
|
||||
* Mounting:
|
||||
*
|
||||
* app.redirect('comments', function(req, res){
|
||||
* return '/post/' + req.params.id + '/comments';
|
||||
* });
|
||||
* When an application is mounted, and `res.redirect()`
|
||||
* is given a path that does _not_ lead with "/". For
|
||||
* example suppose a "blog" app is mounted at "/blog",
|
||||
* the following redirect would result in "/blog/login":
|
||||
*
|
||||
* 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 _GET /post/12_ our
|
||||
* redirect _Location_ would be _/post/12/comments_.
|
||||
* res.redirect('login');
|
||||
*
|
||||
* app.get('/post/:id', function(req, res){
|
||||
* res.redirect('comments');
|
||||
* });
|
||||
* While the leading slash would result in a redirect to "/login":
|
||||
*
|
||||
* Unless an absolute `url` is given, the app's mount-point
|
||||
* will be respected. For example if we redirect to `/posts`,
|
||||
* and our app is mounted at `/blog` we will redirect to `/blog/posts`.
|
||||
* res.redirect('/login');
|
||||
*
|
||||
* @param {String} url
|
||||
* @param {Number} code
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.redirect = function(url, status){
|
||||
res.redirect = function(url){
|
||||
var app = this.app
|
||||
, req = this.req
|
||||
, base = app.set('home') || '/'
|
||||
, status = status || 302
|
||||
, head = 'HEAD' == req.method
|
||||
, status = 302
|
||||
, body;
|
||||
|
||||
// Setup redirect map
|
||||
var map = {
|
||||
back: req.header('Referrer', base)
|
||||
, home: base
|
||||
};
|
||||
// allow status / url
|
||||
if (2 == arguments.length) {
|
||||
status = url;
|
||||
url = arguments[1];
|
||||
}
|
||||
|
||||
// Support custom redirect map
|
||||
map.__proto__ = app.redirects;
|
||||
// setup redirect map
|
||||
var map = { back: req.get('Referrer') || '/' };
|
||||
|
||||
// Attempt mapped redirect
|
||||
var mapped = 'function' == typeof map[url]
|
||||
? map[url](req, this)
|
||||
: map[url];
|
||||
// perform redirect
|
||||
url = map[url] || url;
|
||||
|
||||
// Perform redirect
|
||||
url = mapped || url;
|
||||
|
||||
// Relative
|
||||
// relative
|
||||
if (!~url.indexOf('://')) {
|
||||
// Respect mount-point
|
||||
if (app.route) url = join(app.route, url);
|
||||
var path = app.path();
|
||||
|
||||
// relative to path
|
||||
if (0 == url.indexOf('./') || 0 == url.indexOf('..')) {
|
||||
url = req.path + '/' + url;
|
||||
// relative to mount-point
|
||||
} else if ('/' != url[0]) {
|
||||
url = path + '/' + url;
|
||||
}
|
||||
|
||||
// Absolute
|
||||
var host = req.headers.host
|
||||
, tls = req.connection.encrypted;
|
||||
url = 'http' + (tls ? 's' : '') + '://' + host + url;
|
||||
var host = req.get('Host');
|
||||
url = req.protocol + '://' + host + url;
|
||||
}
|
||||
|
||||
|
||||
// Support text/{plain,html} by default
|
||||
if (req.accepts('html')) {
|
||||
body = '<p>' + http.STATUS_CODES[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
|
||||
this.header('Content-Type', 'text/html');
|
||||
} else {
|
||||
body = http.STATUS_CODES[status] + '. Redirecting to ' + url;
|
||||
this.header('Content-Type', 'text/plain');
|
||||
}
|
||||
this.format({
|
||||
'text/plain': function(){
|
||||
body = statusCodes[status] + '. Redirecting to ' + url;
|
||||
},
|
||||
|
||||
'text/html': function(){
|
||||
body = '<p>' + statusCodes[status] + '. Redirecting to <a href="' + url + '">' + url + '</a></p>';
|
||||
}
|
||||
})
|
||||
|
||||
// Respond
|
||||
this.statusCode = status;
|
||||
this.header('Location', url);
|
||||
this.end(body);
|
||||
this.set('Location', url);
|
||||
this.end(head ? null : body);
|
||||
};
|
||||
|
||||
/**
|
||||
* Assign the view local variable `name` to `val` or return the
|
||||
* local previously assigned to `name`.
|
||||
* Render `view` with the given `options` and optional callback `fn`.
|
||||
* When a callback function is given a response will _not_ be made
|
||||
* automatically, otherwise a response of _200_ and _text/html_ is given.
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Mixed} val
|
||||
* @return {Mixed} val
|
||||
* Options:
|
||||
*
|
||||
* - `status` Response status code (`res.statusCode`)
|
||||
* - `charset` Set the charset (`res.charset`)
|
||||
*
|
||||
* Reserved locals:
|
||||
*
|
||||
* - `cache` boolean hinting to the engine it should cache
|
||||
* - `filename` filename of the view being rendered
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object|Function} options or callback function
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.local = function(name, val){
|
||||
this._locals = this._locals || {};
|
||||
return undefined === val
|
||||
? this._locals[name]
|
||||
: this._locals[name] = val;
|
||||
};
|
||||
res.render = function(view, options, fn){
|
||||
var self = this
|
||||
, options = options || {}
|
||||
, req = this.req
|
||||
, app = req.app;
|
||||
|
||||
/**
|
||||
* Assign several locals with the given `obj`,
|
||||
* or return the locals.
|
||||
*
|
||||
* @param {Object} obj
|
||||
* @return {Object|Undefined}
|
||||
* @api public
|
||||
*/
|
||||
// support callback function as second arg
|
||||
if ('function' == typeof options) {
|
||||
fn = options, options = {};
|
||||
}
|
||||
|
||||
res.locals =
|
||||
res.helpers = function(obj){
|
||||
if (obj) {
|
||||
for (var key in obj) {
|
||||
this.local(key, obj[key]);
|
||||
function render() {
|
||||
// merge res.locals
|
||||
options.locals = self.locals;
|
||||
|
||||
// default callback to respond
|
||||
fn = fn || function(err, str){
|
||||
if (err) return req.next(err);
|
||||
self.send(str);
|
||||
};
|
||||
|
||||
// render
|
||||
app.render(view, options, fn);
|
||||
}
|
||||
|
||||
// invoke view callbacks
|
||||
var callbacks = app.viewCallbacks
|
||||
, pending = callbacks.length
|
||||
, len = pending
|
||||
, done;
|
||||
|
||||
if (len) {
|
||||
for (var i = 0; i < len; ++i) {
|
||||
callbacks[i](req, self, function(err){
|
||||
if (done) return;
|
||||
|
||||
if (err) {
|
||||
req.next(err);
|
||||
done = true;
|
||||
return;
|
||||
}
|
||||
|
||||
--pending || render();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
return this._locals;
|
||||
render();
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
/*!
|
||||
* Express - router - Collection
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Expose `Collection`.
|
||||
*/
|
||||
|
||||
module.exports = Collection;
|
||||
|
||||
/**
|
||||
* Initialize a new route `Collection`
|
||||
* with the given `router`.
|
||||
*
|
||||
* @param {Router} router
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Collection(router) {
|
||||
Array.apply(this, arguments);
|
||||
this.router = router;
|
||||
}
|
||||
|
||||
/**
|
||||
* Inherit from `Array.prototype`.
|
||||
*/
|
||||
|
||||
Collection.prototype.__proto__ = Array.prototype;
|
||||
|
||||
/**
|
||||
* Remove the routes in this collection.
|
||||
*
|
||||
* @return {Collection} of routes removed
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Collection.prototype.remove = function(){
|
||||
var router = this.router
|
||||
, len = this.length
|
||||
, ret = new Collection(this.router);
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (router.remove(this[i])) {
|
||||
ret.push(this[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
+94
-183
@@ -1,4 +1,3 @@
|
||||
|
||||
/*!
|
||||
* Express - Router
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
@@ -10,10 +9,9 @@
|
||||
*/
|
||||
|
||||
var Route = require('./route')
|
||||
, Collection = require('./collection')
|
||||
, utils = require('../utils')
|
||||
, parse = require('url').parse
|
||||
, toArray = utils.toArray;
|
||||
, debug = require('debug')('express:router')
|
||||
, parse = require('url').parse;
|
||||
|
||||
/**
|
||||
* Expose `Router` constructor.
|
||||
@@ -28,20 +26,22 @@ exports = module.exports = Router;
|
||||
var methods = exports.methods = require('./methods');
|
||||
|
||||
/**
|
||||
* Initialize a new `Router` with the given `app`.
|
||||
* Initialize a new `Router` with the given `options`.
|
||||
*
|
||||
* @param {express.HTTPServer} app
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Router(app) {
|
||||
function Router(options) {
|
||||
options = options || {};
|
||||
var self = this;
|
||||
this.app = app;
|
||||
this.routes = {};
|
||||
this.map = {};
|
||||
this.params = {};
|
||||
this._params = [];
|
||||
this.caseSensitive = options.caseSensitive;
|
||||
this.strict = options.strict;
|
||||
|
||||
this.middleware = function(req, res, next){
|
||||
this.middleware = function router(req, res, next){
|
||||
self._dispatch(req, res, next);
|
||||
};
|
||||
}
|
||||
@@ -79,95 +79,10 @@ Router.prototype.param = function(name, fn){
|
||||
throw new Error('invalid param() call for ' + name + ', got ' + fn);
|
||||
}
|
||||
|
||||
this.params[name] = fn;
|
||||
(this.params[name] = this.params[name] || []).push(fn);
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove the given `route`, returns
|
||||
* a bool indicating if the route was present
|
||||
* or not.
|
||||
*
|
||||
* @param {Route} route
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.remove = function(route){
|
||||
var routes = this.routes[route.method]
|
||||
, len = routes.length;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
if (route == routes[i]) {
|
||||
routes.splice(i, 1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return routes with route paths matching `path`.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.lookup = function(method, path){
|
||||
return this.find(function(route){
|
||||
return path == route.path
|
||||
&& (route.method == method
|
||||
|| method == 'all');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Return routes with regexps that match the given `url`.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} url
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.match = function(method, url){
|
||||
return this.find(function(route){
|
||||
return route.match(url)
|
||||
&& (route.method == method
|
||||
|| method == 'all');
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Find routes based on the return value of `fn`
|
||||
* which is invoked once per route.
|
||||
*
|
||||
* @param {Function} fn
|
||||
* @return {Collection}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
Router.prototype.find = function(fn){
|
||||
var len = methods.length
|
||||
, ret = new Collection(this)
|
||||
, method
|
||||
, routes
|
||||
, route;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
method = methods[i];
|
||||
routes = this.routes[method];
|
||||
if (!routes) continue;
|
||||
for (var j = 0, jlen = routes.length; j < jlen; ++j) {
|
||||
route = routes[j];
|
||||
if (fn(route)) ret.push(route);
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Route dispatcher aka the route "middleware".
|
||||
*
|
||||
@@ -181,25 +96,32 @@ Router.prototype._dispatch = function(req, res, next){
|
||||
var params = this.params
|
||||
, self = this;
|
||||
|
||||
debug('dispatching %s %s', req.method, req.url);
|
||||
|
||||
// route dispatch
|
||||
(function pass(i){
|
||||
var route
|
||||
(function pass(i, err){
|
||||
var paramCallbacks
|
||||
, paramIndex = 0
|
||||
, paramVal
|
||||
, route
|
||||
, keys
|
||||
, key
|
||||
, ret;
|
||||
|
||||
// match next route
|
||||
function nextRoute() {
|
||||
pass(req._route_index + 1);
|
||||
function nextRoute(err) {
|
||||
pass(req._route_index + 1, err);
|
||||
}
|
||||
|
||||
// match route
|
||||
req.route = route = self._match(req, i);
|
||||
req.route = route = self.match(req, i);
|
||||
|
||||
// implied OPTIONS
|
||||
if (!route && 'OPTIONS' == req.method) return self._options(req, res);
|
||||
|
||||
// no route
|
||||
if (!route) return next();
|
||||
if (!route) return next(err);
|
||||
debug('matched %s %s', route.method, route.path);
|
||||
|
||||
// we have a route
|
||||
// start at param 0
|
||||
@@ -207,50 +129,58 @@ Router.prototype._dispatch = function(req, res, next){
|
||||
keys = route.keys;
|
||||
i = 0;
|
||||
|
||||
(function param(err) {
|
||||
var key = keys[i++]
|
||||
, val = key && req.params[key.name]
|
||||
, fn = key && params[key.name]
|
||||
, ret;
|
||||
// param callbacks
|
||||
function param(err) {
|
||||
paramIndex = 0;
|
||||
key = keys[i++];
|
||||
paramVal = key && req.params[key.name];
|
||||
paramCallbacks = key && params[key.name];
|
||||
|
||||
try {
|
||||
if ('route' == err) {
|
||||
nextRoute();
|
||||
} else if (err) {
|
||||
next(err);
|
||||
} else if (fn && undefined !== val) {
|
||||
fn(req, res, param, val);
|
||||
i = 0;
|
||||
callbacks(err);
|
||||
} else if (paramCallbacks && undefined !== paramVal) {
|
||||
paramCallback();
|
||||
} else if (key) {
|
||||
param();
|
||||
} else {
|
||||
i = 0;
|
||||
middleware();
|
||||
callbacks();
|
||||
}
|
||||
} catch (err) {
|
||||
next(err);
|
||||
}
|
||||
})();
|
||||
|
||||
// invoke route middleware
|
||||
function middleware(err) {
|
||||
var fn = route.middleware[i++];
|
||||
if ('route' == err) {
|
||||
nextRoute();
|
||||
} else if (err) {
|
||||
next(err);
|
||||
} else if (fn) {
|
||||
fn(req, res, middleware);
|
||||
} else {
|
||||
done();
|
||||
param(err);
|
||||
}
|
||||
};
|
||||
|
||||
// invoke middleware callback
|
||||
function done() {
|
||||
route.callback.call(self, req, res, function(err){
|
||||
if (err) return next(err);
|
||||
pass(req._route_index + 1);
|
||||
});
|
||||
param(err);
|
||||
|
||||
// single param callbacks
|
||||
function paramCallback(err) {
|
||||
var fn = paramCallbacks[paramIndex++];
|
||||
if (err || !fn) return param(err);
|
||||
fn(req, res, paramCallback, paramVal, key.name);
|
||||
}
|
||||
|
||||
// invoke route callbacks
|
||||
function callbacks(err) {
|
||||
var fn = route.callbacks[i++];
|
||||
try {
|
||||
if ('route' == err) {
|
||||
nextRoute();
|
||||
} else if (err && fn) {
|
||||
if (fn.length < 4) return callbacks(err);
|
||||
fn(err, req, res, callbacks);
|
||||
} else if (fn) {
|
||||
fn(req, res, callbacks);
|
||||
} else {
|
||||
nextRoute(err);
|
||||
}
|
||||
} catch (err) {
|
||||
callbacks(err);
|
||||
}
|
||||
}
|
||||
})(0);
|
||||
};
|
||||
@@ -266,7 +196,7 @@ Router.prototype._dispatch = function(req, res, next){
|
||||
Router.prototype._options = function(req, res){
|
||||
var path = parse(req.url).pathname
|
||||
, body = this._optionsFor(path).join(',');
|
||||
res.send(body, { Allow: body });
|
||||
res.set('Allow', body).send(body);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -280,7 +210,7 @@ Router.prototype._options = function(req, res){
|
||||
Router.prototype._optionsFor = function(path){
|
||||
var self = this;
|
||||
return methods.filter(function(method){
|
||||
var routes = self.routes[method];
|
||||
var routes = self.map[method];
|
||||
if (!routes || 'options' == method) return;
|
||||
for (var i = 0, len = routes.length; i < len; ++i) {
|
||||
if (routes[i].match(path)) return true;
|
||||
@@ -292,7 +222,8 @@ Router.prototype._optionsFor = function(path){
|
||||
|
||||
/**
|
||||
* Attempt to match a route for `req`
|
||||
* starting from offset `i`.
|
||||
* with optional starting index of `i`
|
||||
* defaulting to 0.
|
||||
*
|
||||
* @param {IncomingMessage} req
|
||||
* @param {Number} i
|
||||
@@ -300,17 +231,25 @@ Router.prototype._optionsFor = function(path){
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._match = function(req, i){
|
||||
Router.prototype.match = function(req, i, head){
|
||||
var method = req.method.toLowerCase()
|
||||
, url = parse(req.url)
|
||||
, path = url.pathname
|
||||
, routes = this.routes
|
||||
, captures
|
||||
, route
|
||||
, keys;
|
||||
, routes = this.map
|
||||
, i = i || 0
|
||||
, route;
|
||||
|
||||
// pass HEAD to GET routes
|
||||
if ('head' == method) method = 'get';
|
||||
// HEAD support
|
||||
// TODO: clean this up
|
||||
if (!head && 'head' == method) {
|
||||
// attempt lookup
|
||||
route = this.match(req, i, true);
|
||||
if (route) return route;
|
||||
|
||||
// default to GET as res.render() / res.send()
|
||||
// etc support HEAD
|
||||
method = 'get';
|
||||
}
|
||||
|
||||
// routes for this method
|
||||
if (routes = routes[method]) {
|
||||
@@ -318,24 +257,7 @@ Router.prototype._match = function(req, i){
|
||||
// matching routes
|
||||
for (var len = routes.length; i < len; ++i) {
|
||||
route = routes[i];
|
||||
if (captures = route.match(path)) {
|
||||
keys = route.keys;
|
||||
route.params = [];
|
||||
|
||||
// params from capture groups
|
||||
for (var j = 1, jlen = captures.length; j < jlen; ++j) {
|
||||
var key = keys[j-1]
|
||||
, val = 'string' == typeof captures[j]
|
||||
? decodeURIComponent(captures[j])
|
||||
: captures[j];
|
||||
if (key) {
|
||||
route.params[key.name] = val;
|
||||
} else {
|
||||
route.params.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
// all done
|
||||
if (route.match(path)) {
|
||||
req._route_index = i;
|
||||
return route;
|
||||
}
|
||||
@@ -344,41 +266,30 @@ Router.prototype._match = function(req, i){
|
||||
};
|
||||
|
||||
/**
|
||||
* Route `method`, `path`, and optional middleware
|
||||
* to the callback `fn`.
|
||||
* Route `method`, `path`, and one or more callbacks.
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @param {Function} ...
|
||||
* @param {Function} fn
|
||||
* @param {Function} callback...
|
||||
* @return {Router} for chaining
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Router.prototype._route = function(method, path, fn){
|
||||
var app = this.app
|
||||
, middleware = [];
|
||||
Router.prototype.route = function(method, path, callbacks){
|
||||
var method = method.toLowerCase()
|
||||
, callbacks = utils.flatten([].slice.call(arguments, 2));
|
||||
|
||||
// slice middleware
|
||||
if (arguments.length > 3) {
|
||||
middleware = toArray(arguments, 2);
|
||||
fn = middleware.pop();
|
||||
middleware = utils.flatten(middleware);
|
||||
}
|
||||
|
||||
// ensure path and callback are given
|
||||
if (!path) throw new Error(method + 'route requires a path');
|
||||
if (!fn) throw new Error(method + ' route ' + path + ' requires a callback');
|
||||
// ensure path was given
|
||||
if (!path) throw new Error('Router#' + method + '() requires a path');
|
||||
|
||||
// create the route
|
||||
var route = new Route(method, path, fn, {
|
||||
sensitive: app.enabled('case sensitive routes')
|
||||
, strict: app.enabled('strict routing')
|
||||
, middleware: middleware
|
||||
debug('defined %s %s', method, path);
|
||||
var route = new Route(method, path, callbacks, {
|
||||
sensitive: this.caseSensitive
|
||||
, strict: this.strict
|
||||
});
|
||||
|
||||
// add it
|
||||
(this.routes[method] = this.routes[method] || [])
|
||||
.push(route);
|
||||
(this.map[method] = this.map[method] || []).push(route);
|
||||
return this;
|
||||
};
|
||||
|
||||
+26
-60
@@ -6,65 +6,31 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Hypertext Transfer Protocol -- HTTP/1.1
|
||||
* http://www.ietf.org/rfc/rfc2616.txt
|
||||
* HTTP methods supported by node.
|
||||
*/
|
||||
|
||||
var RFC2616 = ['OPTIONS', 'GET', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'];
|
||||
|
||||
/**
|
||||
* HTTP Extensions for Distributed Authoring -- WEBDAV
|
||||
* http://www.ietf.org/rfc/rfc2518.txt
|
||||
*/
|
||||
|
||||
var RFC2518 = ['PROPFIND', 'PROPPATCH', 'MKCOL', 'COPY', 'MOVE', 'LOCK', 'UNLOCK'];
|
||||
|
||||
/**
|
||||
* Versioning Extensions to WebDAV
|
||||
* http://www.ietf.org/rfc/rfc3253.txt
|
||||
*/
|
||||
|
||||
var RFC3253 = ['VERSION-CONTROL', 'REPORT', 'CHECKOUT', 'CHECKIN', 'UNCHECKOUT', 'MKWORKSPACE', 'UPDATE', 'LABEL', 'MERGE', 'BASELINE-CONTROL', 'MKACTIVITY'];
|
||||
|
||||
/**
|
||||
* Ordered Collections Protocol (WebDAV)
|
||||
* http://www.ietf.org/rfc/rfc3648.txt
|
||||
*/
|
||||
|
||||
var RFC3648 = ['ORDERPATCH'];
|
||||
|
||||
/**
|
||||
* Web Distributed Authoring and Versioning (WebDAV) Access Control Protocol
|
||||
* http://www.ietf.org/rfc/rfc3744.txt
|
||||
*/
|
||||
|
||||
var RFC3744 = ['ACL'];
|
||||
|
||||
/**
|
||||
* Web Distributed Authoring and Versioning (WebDAV) SEARCH
|
||||
* http://www.ietf.org/rfc/rfc5323.txt
|
||||
*/
|
||||
|
||||
var RFC5323 = ['SEARCH'];
|
||||
|
||||
/**
|
||||
* PATCH Method for HTTP
|
||||
* http://www.ietf.org/rfc/rfc5789.txt
|
||||
*/
|
||||
|
||||
var RFC5789 = ['PATCH'];
|
||||
|
||||
/**
|
||||
* Expose the methods.
|
||||
*/
|
||||
|
||||
module.exports = [].concat(
|
||||
RFC2616
|
||||
, RFC2518
|
||||
, RFC3253
|
||||
, RFC3648
|
||||
, RFC3744
|
||||
, RFC5323
|
||||
, RFC5789).map(function(method){
|
||||
return method.toLowerCase();
|
||||
});
|
||||
module.exports = [
|
||||
'get'
|
||||
, 'post'
|
||||
, 'put'
|
||||
, 'head'
|
||||
, 'delete'
|
||||
, 'options'
|
||||
, 'trace'
|
||||
, 'copy'
|
||||
, 'lock'
|
||||
, 'mkcol'
|
||||
, 'move'
|
||||
, 'propfind'
|
||||
, 'proppatch'
|
||||
, 'unlock'
|
||||
, 'report'
|
||||
, 'mkactivity'
|
||||
, 'checkout'
|
||||
, 'merge'
|
||||
, 'm-search'
|
||||
, 'notify'
|
||||
, 'subscribe'
|
||||
, 'unsubscribe'
|
||||
, 'patch'
|
||||
];
|
||||
+35
-47
@@ -5,6 +5,12 @@
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var utils = require('../utils');
|
||||
|
||||
/**
|
||||
* Expose `Route`.
|
||||
*/
|
||||
@@ -13,78 +19,60 @@ module.exports = Route;
|
||||
|
||||
/**
|
||||
* Initialize `Route` with the given HTTP `method`, `path`,
|
||||
* and callback `fn` and `options`.
|
||||
* and an array of `callbacks` and `options`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `sensitive` enable case-sensitive routes
|
||||
* - `strict` enable strict matching for trailing slashes
|
||||
* - `middleware` array of middleware
|
||||
*
|
||||
* @param {String} method
|
||||
* @param {String} path
|
||||
* @param {Function} fn
|
||||
* @param {Array} callbacks
|
||||
* @param {Object} options.
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function Route(method, path, fn, options) {
|
||||
function Route(method, path, callbacks, options) {
|
||||
options = options || {};
|
||||
this.callback = fn;
|
||||
this.path = path;
|
||||
this.method = method;
|
||||
this.middleware = options.middleware;
|
||||
this.regexp = normalize(path
|
||||
this.callbacks = callbacks;
|
||||
this.regexp = utils.pathRegexp(path
|
||||
, this.keys = []
|
||||
, options.sensitive
|
||||
, options.strict);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this route matches `path` and return captures made.
|
||||
* Check if this route matches `path`, if so
|
||||
* populate `.params`.
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {Array}
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
Route.prototype.match = function(path){
|
||||
return this.regexp.exec(path);
|
||||
var keys = this.keys
|
||||
, params = this.params = []
|
||||
, m = this.regexp.exec(path);
|
||||
|
||||
if (!m) return false;
|
||||
|
||||
for (var i = 1, len = m.length; i < len; ++i) {
|
||||
var key = keys[i - 1];
|
||||
|
||||
var val = 'string' == typeof m[i]
|
||||
? decodeURIComponent(m[i])
|
||||
: m[i];
|
||||
|
||||
if (key) {
|
||||
params[key.name] = val;
|
||||
} else {
|
||||
params.push(val);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize the given path string,
|
||||
* returning a regular expression.
|
||||
*
|
||||
* An empty array should be passed,
|
||||
* which will contain the placeholder
|
||||
* key names. For example "/user/:id" will
|
||||
* then contain ["id"].
|
||||
*
|
||||
* @param {String|RegExp} path
|
||||
* @param {Array} keys
|
||||
* @param {Boolean} sensitive
|
||||
* @param {Boolean} strict
|
||||
* @return {RegExp}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function normalize(path, keys, sensitive, strict) {
|
||||
if (path instanceof RegExp) return path;
|
||||
path = path
|
||||
.concat(strict ? '' : '/?')
|
||||
.replace(/\/\(/g, '(?:/')
|
||||
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
|
||||
keys.push({ name: key, optional: !! optional });
|
||||
slash = slash || '';
|
||||
return ''
|
||||
+ (optional ? '' : slash)
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (format || '') + (capture || '([^/]+?)') + ')'
|
||||
+ (optional || '');
|
||||
})
|
||||
.replace(/([\/.])/g, '\\$1')
|
||||
.replace(/\*/g, '(.+)');
|
||||
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
|
||||
}
|
||||
+189
-74
@@ -1,33 +1,27 @@
|
||||
|
||||
/*!
|
||||
* Express - Utils
|
||||
* Express - utils
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Merge object `b` with `a` giving precedence to
|
||||
* values in object `a`.
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var mime = require('mime');
|
||||
|
||||
/**
|
||||
* Check if `path` looks absolute.
|
||||
*
|
||||
* @param {Object} a
|
||||
* @param {Object} b
|
||||
* @return {Object} a
|
||||
* @param {String} path
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.union = function(a, b){
|
||||
if (a && b) {
|
||||
var keys = Object.keys(b)
|
||||
, len = keys.length
|
||||
, key;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
key = keys[i];
|
||||
if (!a.hasOwnProperty(key)) {
|
||||
a[key] = b[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
exports.isAbsolute = function(path){
|
||||
if ('/' == path[0]) return true;
|
||||
if (':' == path[1] && '\\' == path[2]) return true;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -52,26 +46,158 @@ exports.flatten = function(arr, ret){
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse mini markdown implementation.
|
||||
* The following conversions are supported,
|
||||
* primarily for the "flash" middleware:
|
||||
* Normalize the given `type`, for example "html" becomes "text/html".
|
||||
*
|
||||
* _foo_ or *foo* become <em>foo</em>
|
||||
* __foo__ or **foo** become <strong>foo</strong>
|
||||
* [A](B) becomes <a href="B">A</a>
|
||||
* @param {String} type
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.normalizeType = function(type){
|
||||
return ~type.indexOf('/') ? type : mime.lookup(type);
|
||||
};
|
||||
|
||||
/**
|
||||
* Normalize `types`, for example "html" becomes "text/html".
|
||||
*
|
||||
* @param {Array} types
|
||||
* @return {Array}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.normalizeTypes = function(types){
|
||||
var ret = [];
|
||||
|
||||
for (var i = 0; i < types.length; ++i) {
|
||||
ret.push(~types[i].indexOf('/')
|
||||
? types[i]
|
||||
: mime.lookup(types[i]));
|
||||
}
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the acceptable type in `types`, if any.
|
||||
*
|
||||
* @param {Array} types
|
||||
* @param {String} str
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.miniMarkdown = function(str){
|
||||
return String(str)
|
||||
.replace(/(__|\*\*)(.*?)\1/g, '<strong>$2</strong>')
|
||||
.replace(/(_|\*)(.*?)\1/g, '<em>$2</em>')
|
||||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>');
|
||||
exports.acceptsArray = function(types, str){
|
||||
// accept anything when Accept is not present
|
||||
if (!str) return types[0];
|
||||
|
||||
// parse
|
||||
var accepted = exports.parseAccept(str)
|
||||
, normalized = exports.normalizeTypes(types)
|
||||
, len = accepted.length;
|
||||
|
||||
for (var i = 0; i < len; ++i) {
|
||||
for (var j = 0, jlen = types.length; j < jlen; ++j) {
|
||||
if (exports.accept(normalized[j].split('/'), accepted[i])) {
|
||||
return types[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `type(s)` are acceptable based on
|
||||
* the given `str`.
|
||||
*
|
||||
* @param {String|Array} type(s)
|
||||
* @param {String} str
|
||||
* @return {Boolean|String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.accepts = function(type, str){
|
||||
if ('string' == typeof type) type = type.split(/ *, */);
|
||||
return exports.acceptsArray(type, str);
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if `type` array is acceptable for `other`.
|
||||
*
|
||||
* @param {Array} type
|
||||
* @param {Object} other
|
||||
* @return {Boolean}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.accept = function(type, other){
|
||||
return (type[0] == other.type || '*' == other.type)
|
||||
&& (type[1] == other.subtype || '*' == other.subtype);
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse accept `str`, returning
|
||||
* an array objects containing
|
||||
* `.type` and `.subtype` along
|
||||
* with the values provided by
|
||||
* `parseQuality()`.
|
||||
*
|
||||
* @param {Type} name
|
||||
* @return {Type}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.parseAccept = function(str){
|
||||
return exports
|
||||
.parseQuality(str)
|
||||
.map(function(obj){
|
||||
var parts = obj.value.split('/');
|
||||
obj.type = parts[0];
|
||||
obj.subtype = parts[1];
|
||||
return obj;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse quality `str`, returning an
|
||||
* array of objects with `.value` and
|
||||
* `.quality`.
|
||||
*
|
||||
* @param {Type} name
|
||||
* @return {Type}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.parseQuality = function(str){
|
||||
return str
|
||||
.split(/ *, */)
|
||||
.map(quality)
|
||||
.filter(function(obj){
|
||||
return obj.quality;
|
||||
})
|
||||
.sort(function(a, b){
|
||||
return b.quality - a.quality;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse quality `str` returning an
|
||||
* object with `.value` and `.quality`.
|
||||
*
|
||||
* @param {String} str
|
||||
* @return {Object}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function quality(str) {
|
||||
var parts = str.split(/ *; */)
|
||||
, val = parts[0];
|
||||
|
||||
var q = parts[1]
|
||||
? parseFloat(parts[1].split(/ *= */)[1])
|
||||
: 1;
|
||||
|
||||
return { value: val, quality: q };
|
||||
}
|
||||
|
||||
/**
|
||||
* Escape special characters in the given string of html.
|
||||
*
|
||||
@@ -89,51 +215,40 @@ exports.escape = function(html) {
|
||||
};
|
||||
|
||||
/**
|
||||
* Parse "Range" header `str` relative to the given file `size`.
|
||||
* Normalize the given path string,
|
||||
* returning a regular expression.
|
||||
*
|
||||
* @param {Number} size
|
||||
* @param {String} str
|
||||
* @return {Array}
|
||||
* An empty array should be passed,
|
||||
* which will contain the placeholder
|
||||
* key names. For example "/user/:id" will
|
||||
* then contain ["id"].
|
||||
*
|
||||
* @param {String|RegExp|Array} path
|
||||
* @param {Array} keys
|
||||
* @param {Boolean} sensitive
|
||||
* @param {Boolean} strict
|
||||
* @return {RegExp}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.parseRange = function(size, str){
|
||||
var valid = true;
|
||||
var arr = str.substr(6).split(',').map(function(range){
|
||||
var range = range.split('-')
|
||||
, start = parseInt(range[0], 10)
|
||||
, end = parseInt(range[1], 10);
|
||||
|
||||
// -500
|
||||
if (isNaN(start)) {
|
||||
start = size - end;
|
||||
end = size - 1;
|
||||
// 500-
|
||||
} else if (isNaN(end)) {
|
||||
end = size - 1;
|
||||
}
|
||||
|
||||
// Invalid
|
||||
if (isNaN(start) || isNaN(end) || start > end) valid = false;
|
||||
|
||||
return { start: start, end: end };
|
||||
});
|
||||
return valid ? arr : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Fast alternative to `Array.prototype.slice.call()`.
|
||||
*
|
||||
* @param {Arguments} args
|
||||
* @param {Number} n
|
||||
* @return {Array}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.toArray = function(args, i){
|
||||
var arr = []
|
||||
, len = args.length
|
||||
, i = i || 0;
|
||||
for (; i < len; ++i) arr.push(args[i]);
|
||||
return arr;
|
||||
};
|
||||
exports.pathRegexp = function(path, keys, sensitive, strict) {
|
||||
if (path instanceof RegExp) return path;
|
||||
if (path instanceof Array)
|
||||
path = '(' + path.join('|') + ')';
|
||||
path = path
|
||||
.concat(strict ? '' : '/?')
|
||||
.replace(/\/\(/g, '(?:/')
|
||||
.replace(/(\/)?(\.)?:(\w+)(?:(\(.*?\)))?(\?)?/g, function(_, slash, format, key, capture, optional){
|
||||
keys.push({ name: key, optional: !! optional });
|
||||
slash = slash || '';
|
||||
return ''
|
||||
+ (optional ? '' : slash)
|
||||
+ '(?:'
|
||||
+ (optional ? slash : '')
|
||||
+ (format || '') + (capture || (format && '([^/.]+?)' || '([^/]+?)')) + ')'
|
||||
+ (optional || '');
|
||||
})
|
||||
.replace(/([\/.])/g, '\\$1')
|
||||
.replace(/\*/g, '(.*)');
|
||||
return new RegExp('^' + path + '$', sensitive ? '' : 'i');
|
||||
}
|
||||
+51
-429
@@ -1,6 +1,5 @@
|
||||
|
||||
/*!
|
||||
* Express - view
|
||||
* Express - View
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
@@ -10,450 +9,73 @@
|
||||
*/
|
||||
|
||||
var path = require('path')
|
||||
, extname = path.extname
|
||||
, utils = require('./utils')
|
||||
, dirname = path.dirname
|
||||
, basename = path.basename
|
||||
, utils = require('connect').utils
|
||||
, View = require('./view/view')
|
||||
, partial = require('./view/partial')
|
||||
, union = require('./utils').union
|
||||
, merge = utils.merge
|
||||
, http = require('http')
|
||||
, res = http.ServerResponse.prototype;
|
||||
, extname = path.extname
|
||||
, exists = path.existsSync
|
||||
, join = path.join;
|
||||
|
||||
/**
|
||||
* Expose constructors.
|
||||
* Expose `View`.
|
||||
*/
|
||||
|
||||
exports = module.exports = View;
|
||||
module.exports = View;
|
||||
|
||||
/**
|
||||
* Export template engine registrar.
|
||||
*/
|
||||
|
||||
exports.register = View.register;
|
||||
|
||||
/**
|
||||
* Lookup and compile `view` with cache support by supplying
|
||||
* both the `cache` object and `cid` string,
|
||||
* followed by `options` passed to `exports.lookup()`.
|
||||
* Initialize a new `View` with the given `name`.
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object} cache
|
||||
* @param {Object} cid
|
||||
* Options:
|
||||
*
|
||||
* - `defaultEngine` the default template engine name
|
||||
* - `engines` template engine require() cache
|
||||
* - `root` root path for view lookup
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {Object} options
|
||||
* @return {View}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.compile = function(view, cache, cid, options){
|
||||
if (cache && cid && cache[cid]) return cache[cid];
|
||||
|
||||
// lookup
|
||||
view = exports.lookup(view, options);
|
||||
|
||||
// hints
|
||||
if (!view.exists) {
|
||||
if (options.hint) hintAtViewPaths(view.original, options);
|
||||
var err = new Error('failed to locate view "' + view.original.view + '"');
|
||||
err.view = view.original;
|
||||
throw err;
|
||||
}
|
||||
|
||||
// compile
|
||||
options.filename = view.path;
|
||||
view.fn = view.templateEngine.compile(view.contents, options);
|
||||
cache[cid] = view;
|
||||
|
||||
return view;
|
||||
};
|
||||
|
||||
/**
|
||||
* Lookup `view`, returning an instanceof `View`.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `root` root directory path
|
||||
* - `defaultEngine` default template engine
|
||||
* - `parentView` parent `View` object
|
||||
* - `cache` cache object
|
||||
* - `cacheid` optional cache id
|
||||
*
|
||||
* Lookup:
|
||||
*
|
||||
* - partial `_<name>`
|
||||
* - any `<name>/index`
|
||||
* - non-layout `../<name>/index`
|
||||
* - any `<root>/<name>`
|
||||
* - partial `<root>/_<name>`
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object} options
|
||||
* @return {View}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.lookup = function(view, options){
|
||||
var orig = view = new View(view, options)
|
||||
, partial = options.isPartial
|
||||
, layout = options.isLayout;
|
||||
|
||||
// Try _ prefix ex: ./views/_<name>.jade
|
||||
// taking precedence over the direct path
|
||||
if (partial) {
|
||||
view = new View(orig.prefixPath, options);
|
||||
if (!view.exists) view = orig;
|
||||
}
|
||||
|
||||
// Try index ex: ./views/user/index.jade
|
||||
if (!layout && !view.exists) view = new View(orig.indexPath, options);
|
||||
|
||||
// Try ../<name>/index ex: ../user/index.jade
|
||||
// when calling partial('user') within the same dir
|
||||
if (!layout && !view.exists) view = new View(orig.upIndexPath, options);
|
||||
|
||||
// Try root ex: <root>/user.jade
|
||||
if (!view.exists) view = new View(orig.rootPath, options);
|
||||
|
||||
// Try root _ prefix ex: <root>/_user.jade
|
||||
if (!view.exists && partial) view = new View(view.prefixPath, options);
|
||||
|
||||
view.original = orig;
|
||||
return view;
|
||||
};
|
||||
|
||||
/**
|
||||
* Partial render helper.
|
||||
*
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function renderPartial(res, view, options, parentLocals, parent){
|
||||
var collection, object, locals;
|
||||
|
||||
if (options) {
|
||||
// collection
|
||||
if (options.collection) {
|
||||
collection = options.collection;
|
||||
delete options.collection;
|
||||
} else if ('length' in options) {
|
||||
collection = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// locals
|
||||
if (options.locals) {
|
||||
locals = options.locals;
|
||||
delete options.locals;
|
||||
}
|
||||
|
||||
// object
|
||||
if ('Object' != options.constructor.name) {
|
||||
object = options;
|
||||
options = {};
|
||||
} else if (undefined != options.object) {
|
||||
object = options.object;
|
||||
delete options.object;
|
||||
}
|
||||
} else {
|
||||
options = {};
|
||||
}
|
||||
|
||||
// Inherit locals from parent
|
||||
union(options, parentLocals);
|
||||
|
||||
// Merge locals
|
||||
if (locals) merge(options, locals);
|
||||
|
||||
// Partials dont need layouts
|
||||
options.isPartial = true;
|
||||
options.layout = false;
|
||||
|
||||
// Deduce name from view path
|
||||
var name = options.as || partial.resolveObjectName(view);
|
||||
|
||||
// Render partial
|
||||
function render(){
|
||||
if (object) {
|
||||
if ('string' == typeof name) {
|
||||
options[name] = object;
|
||||
} else if (name === global) {
|
||||
merge(options, object);
|
||||
} else {
|
||||
options.scope = object;
|
||||
}
|
||||
}
|
||||
return res.render(view, options, null, parent, true);
|
||||
}
|
||||
|
||||
// Collection support
|
||||
if (collection) {
|
||||
var len = collection.length
|
||||
, buf = ''
|
||||
, keys
|
||||
, key
|
||||
, val;
|
||||
|
||||
options.collectionLength = len;
|
||||
|
||||
if ('number' == typeof len || Array.isArray(collection)) {
|
||||
for (var i = 0; i < len; ++i) {
|
||||
val = collection[i];
|
||||
options.firstInCollection = i == 0;
|
||||
options.indexInCollection = i;
|
||||
options.lastInCollection = i == len - 1;
|
||||
object = val;
|
||||
buf += render();
|
||||
}
|
||||
} else {
|
||||
keys = Object.keys(collection);
|
||||
len = keys.length;
|
||||
options.collectionLength = len;
|
||||
options.collectionKeys = keys;
|
||||
for (var i = 0; i < len; ++i) {
|
||||
key = keys[i];
|
||||
val = collection[key];
|
||||
options.keyInCollection = key;
|
||||
options.firstInCollection = i == 0;
|
||||
options.indexInCollection = i;
|
||||
options.lastInCollection = i == len - 1;
|
||||
object = val;
|
||||
buf += render();
|
||||
}
|
||||
}
|
||||
|
||||
return buf;
|
||||
} else {
|
||||
return render();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Render `view` partial with the given `options`. Optionally a
|
||||
* callback `fn(err, str)` may be passed instead of writing to
|
||||
* the socket.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `object` Single object with name derived from the view (unless `as` is present)
|
||||
*
|
||||
* - `as` Variable name for each `collection` value, defaults to the view name.
|
||||
* * as: 'something' will add the `something` local variable
|
||||
* * as: this will use the collection value as the template context
|
||||
* * as: global will merge the collection value's properties with `locals`
|
||||
*
|
||||
* - `collection` Array of objects, the name is derived from the view name itself.
|
||||
* For example _video.html_ will have a object _video_ available to it.
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object|Array|Function} options, collection, callback, or object
|
||||
* @param {Function} fn
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.partial = function(view, options, fn){
|
||||
var app = this.app
|
||||
, options = options || {}
|
||||
, viewEngine = app.set('view engine')
|
||||
, parent = {};
|
||||
|
||||
// accept callback as second argument
|
||||
if ('function' == typeof options) {
|
||||
fn = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
// root "views" option
|
||||
parent.dirname = app.set('views') || process.cwd() + '/views';
|
||||
|
||||
// utilize "view engine" option
|
||||
if (viewEngine) parent.engine = viewEngine;
|
||||
|
||||
// render the partial
|
||||
try {
|
||||
var str = renderPartial(this, view, options, null, parent);
|
||||
} catch (err) {
|
||||
if (fn) {
|
||||
fn(err);
|
||||
} else {
|
||||
this.req.next(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// callback or transfer
|
||||
if (fn) {
|
||||
fn(null, str);
|
||||
} else {
|
||||
this.send(str);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Render `view` with the given `options` and optional callback `fn`.
|
||||
* When a callback function is given a response will _not_ be made
|
||||
* automatically, however otherwise a response of _200_ and _text/html_ is given.
|
||||
*
|
||||
* Options:
|
||||
*
|
||||
* - `scope` Template evaluation context (the value of `this`)
|
||||
* - `debug` Output debugging information
|
||||
* - `status` Response status code
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object|Function} options or callback function
|
||||
* @param {Function} fn
|
||||
* @api public
|
||||
*/
|
||||
|
||||
res.render = function(view, opts, fn, parent, sub){
|
||||
// support callback function as second arg
|
||||
if ('function' == typeof opts) {
|
||||
fn = opts, opts = null;
|
||||
}
|
||||
|
||||
try {
|
||||
return this._render(view, opts, fn, parent, sub);
|
||||
} catch (err) {
|
||||
// callback given
|
||||
if (fn) {
|
||||
fn(err);
|
||||
// unwind to root call to prevent multiple callbacks
|
||||
} else if (sub) {
|
||||
throw err;
|
||||
// root template, next(err)
|
||||
} else {
|
||||
this.req.next(err);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// private render()
|
||||
|
||||
res._render = function(view, opts, fn, parent, sub){
|
||||
var options = {}
|
||||
, self = this
|
||||
, app = this.app
|
||||
, helpers = app._locals
|
||||
, dynamicHelpers = app.dynamicViewHelpers
|
||||
, viewOptions = app.set('view options')
|
||||
, root = app.set('views') || process.cwd() + '/views';
|
||||
|
||||
// cache id
|
||||
var cid = app.enabled('view cache')
|
||||
? view + (parent ? ':' + parent.path : '')
|
||||
: false;
|
||||
|
||||
// merge "view options"
|
||||
if (viewOptions) merge(options, viewOptions);
|
||||
|
||||
// merge res._locals
|
||||
if (this._locals) merge(options, this._locals);
|
||||
|
||||
// merge render() options
|
||||
if (opts) merge(options, opts);
|
||||
|
||||
// merge render() .locals
|
||||
if (opts && opts.locals) merge(options, opts.locals);
|
||||
|
||||
// status support
|
||||
if (options.status) this.statusCode = options.status;
|
||||
|
||||
// capture attempts
|
||||
options.attempts = [];
|
||||
|
||||
var partial = options.isPartial
|
||||
, layout = options.layout;
|
||||
|
||||
// Layout support
|
||||
if (true === layout || undefined === layout) {
|
||||
layout = 'layout';
|
||||
}
|
||||
|
||||
// Default execution scope to a plain object
|
||||
options.scope = options.scope || {};
|
||||
|
||||
// Populate view
|
||||
options.parentView = parent;
|
||||
|
||||
// "views" setting
|
||||
options.root = root;
|
||||
|
||||
// "view engine" setting
|
||||
options.defaultEngine = app.set('view engine');
|
||||
|
||||
// charset option
|
||||
if (options.charset) this.charset = options.charset;
|
||||
|
||||
// Dynamic helper support
|
||||
if (false !== options.dynamicHelpers) {
|
||||
// cache
|
||||
if (!this.__dynamicHelpers) {
|
||||
this.__dynamicHelpers = {};
|
||||
for (var key in dynamicHelpers) {
|
||||
this.__dynamicHelpers[key] = dynamicHelpers[key].call(
|
||||
this.app
|
||||
, this.req
|
||||
, this);
|
||||
}
|
||||
}
|
||||
|
||||
// apply
|
||||
merge(options, this.__dynamicHelpers);
|
||||
}
|
||||
|
||||
// Merge view helpers
|
||||
union(options, helpers);
|
||||
|
||||
// Always expose partial() as a local
|
||||
options.partial = function(path, opts){
|
||||
return renderPartial(self, path, opts, options, view);
|
||||
};
|
||||
|
||||
// View lookup
|
||||
options.hint = app.enabled('hints');
|
||||
view = exports.compile(view, app.cache, cid, options);
|
||||
|
||||
// layout helper
|
||||
options.layout = function(path){
|
||||
layout = path;
|
||||
};
|
||||
|
||||
// render
|
||||
var str = view.fn.call(options.scope, options);
|
||||
|
||||
// layout expected
|
||||
if (layout) {
|
||||
options.isLayout = true;
|
||||
options.layout = false;
|
||||
options.body = str;
|
||||
this.render(layout, options, fn, view, true);
|
||||
// partial return
|
||||
} else if (partial) {
|
||||
return str;
|
||||
// render complete, and
|
||||
// callback given
|
||||
} else if (fn) {
|
||||
fn(null, str);
|
||||
// respond
|
||||
} else {
|
||||
this.send(str);
|
||||
}
|
||||
function View(name, options) {
|
||||
options = options || {};
|
||||
this.name = name;
|
||||
this.root = options.root;
|
||||
var engines = options.engines;
|
||||
this.defaultEngine = options.defaultEngine;
|
||||
var ext = this.ext = extname(name);
|
||||
if (!ext) name += (ext = this.ext = '.' + this.defaultEngine);
|
||||
this.engine = engines[ext] || (engines[ext] = require(ext.slice(1)).__express);
|
||||
this.path = this.lookup(name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hint at view path resolution, outputting the
|
||||
* paths that Express has tried.
|
||||
* Lookup view by the given `path`
|
||||
*
|
||||
* @param {String} path
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function hintAtViewPaths(view, options) {
|
||||
console.error();
|
||||
console.error('failed to locate view "' + view.view + '", tried:');
|
||||
options.attempts.forEach(function(path){
|
||||
console.error(' - %s', path);
|
||||
});
|
||||
console.error();
|
||||
}
|
||||
View.prototype.lookup = function(path){
|
||||
var ext = this.ext;
|
||||
|
||||
// <path>.<engine>
|
||||
if (!utils.isAbsolute(path)) path = join(this.root, path);
|
||||
if (exists(path)) return path;
|
||||
|
||||
// <path>/index.<engine>
|
||||
path = join(dirname(path), basename(path, ext), 'index' + ext);
|
||||
if (exists(path)) return path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Render with the given `options` and callback `fn(err, str)`.
|
||||
*
|
||||
* @param {Object} options
|
||||
* @param {Function} fn
|
||||
* @api private
|
||||
*/
|
||||
|
||||
View.prototype.render = function(options, fn){
|
||||
this.engine(this.path, options, fn);
|
||||
};
|
||||
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
/*!
|
||||
* Express - view - Partial
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Memory cache.
|
||||
*/
|
||||
|
||||
var cache = {};
|
||||
|
||||
/**
|
||||
* Resolve partial object name from the view path.
|
||||
*
|
||||
* Examples:
|
||||
*
|
||||
* "user.ejs" becomes "user"
|
||||
* "forum thread.ejs" becomes "forumThread"
|
||||
* "forum/thread/post.ejs" becomes "post"
|
||||
* "blog-post.ejs" becomes "blogPost"
|
||||
*
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
exports.resolveObjectName = function(view){
|
||||
return cache[view] || (cache[view] = view
|
||||
.split('/')
|
||||
.slice(-1)[0]
|
||||
.split('.')[0]
|
||||
.replace(/^_/, '')
|
||||
.replace(/[^a-zA-Z0-9 ]+/g, ' ')
|
||||
.split(/ +/).map(function(word, i){
|
||||
return i
|
||||
? word[0].toUpperCase() + word.substr(1)
|
||||
: word;
|
||||
}).join(''));
|
||||
};
|
||||
@@ -1,209 +0,0 @@
|
||||
|
||||
/*!
|
||||
* Express - View
|
||||
* Copyright(c) 2010 TJ Holowaychuk <tj@vision-media.ca>
|
||||
* MIT Licensed
|
||||
*/
|
||||
|
||||
/**
|
||||
* Module dependencies.
|
||||
*/
|
||||
|
||||
var path = require('path')
|
||||
, extname = path.extname
|
||||
, dirname = path.dirname
|
||||
, basename = path.basename
|
||||
, fs = require('fs')
|
||||
, stat = fs.statSync;
|
||||
|
||||
/**
|
||||
* Expose `View`.
|
||||
*/
|
||||
|
||||
exports = module.exports = View;
|
||||
|
||||
/**
|
||||
* Require cache.
|
||||
*/
|
||||
|
||||
var cache = {};
|
||||
|
||||
/**
|
||||
* Initialize a new `View` with the given `view` path and `options`.
|
||||
*
|
||||
* @param {String} view
|
||||
* @param {Object} options
|
||||
* @api private
|
||||
*/
|
||||
|
||||
function View(view, options) {
|
||||
options = options || {};
|
||||
this.view = view;
|
||||
this.root = options.root;
|
||||
this.relative = false !== options.relative;
|
||||
this.defaultEngine = options.defaultEngine;
|
||||
this.parent = options.parentView;
|
||||
this.basename = basename(view);
|
||||
this.engine = this.resolveEngine();
|
||||
this.extension = '.' + this.engine;
|
||||
this.name = this.basename.replace(this.extension, '');
|
||||
this.path = this.resolvePath();
|
||||
this.dirname = dirname(this.path);
|
||||
if (options.attempts) {
|
||||
if (!~options.attempts.indexOf(this.path))
|
||||
options.attempts.push(this.path);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the view path exists.
|
||||
*
|
||||
* @return {Boolean}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('exists', function(){
|
||||
try {
|
||||
stat(this.path);
|
||||
return true;
|
||||
} catch (err) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Resolve view engine.
|
||||
*
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
View.prototype.resolveEngine = function(){
|
||||
// Explicit
|
||||
if (~this.basename.indexOf('.')) return extname(this.basename).substr(1);
|
||||
// Inherit from parent
|
||||
if (this.parent) return this.parent.engine;
|
||||
// Default
|
||||
return this.defaultEngine;
|
||||
};
|
||||
|
||||
/**
|
||||
* Resolve view path.
|
||||
*
|
||||
* @return {String}
|
||||
* @api private
|
||||
*/
|
||||
|
||||
View.prototype.resolvePath = function(){
|
||||
var path = this.view;
|
||||
// Implicit engine
|
||||
if (!~this.basename.indexOf('.')) path += this.extension;
|
||||
// Absolute
|
||||
if ('/' == path[0]) return path;
|
||||
// Relative to parent
|
||||
if (this.relative && this.parent) return this.parent.dirname + '/' + path;
|
||||
// Relative to root
|
||||
return this.root
|
||||
? this.root + '/' + path
|
||||
: path;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get view contents. This is a one-time hit, so we
|
||||
* can afford to be sync.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('contents', function(){
|
||||
return fs.readFileSync(this.path, 'utf8');
|
||||
});
|
||||
|
||||
/**
|
||||
* Get template engine api, cache exports to reduce
|
||||
* require() calls.
|
||||
*
|
||||
* @return {Object}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('templateEngine', function(){
|
||||
var ext = this.extension;
|
||||
return cache[ext] || (cache[ext] = require(this.engine));
|
||||
});
|
||||
|
||||
/**
|
||||
* Return root path alternative.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('rootPath', function(){
|
||||
this.relative = false;
|
||||
return this.resolvePath();
|
||||
});
|
||||
|
||||
/**
|
||||
* Return index path alternative.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('indexPath', function(){
|
||||
return this.dirname
|
||||
+ '/' + this.basename.replace(this.extension, '')
|
||||
+ '/index' + this.extension;
|
||||
});
|
||||
|
||||
/**
|
||||
* Return ../<name>/index path alternative.
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('upIndexPath', function(){
|
||||
return this.dirname + '/../' + this.name + '/index' + this.extension;
|
||||
});
|
||||
|
||||
/**
|
||||
* Return _ prefix path alternative
|
||||
*
|
||||
* @return {String}
|
||||
* @api public
|
||||
*/
|
||||
|
||||
View.prototype.__defineGetter__('prefixPath', function(){
|
||||
return this.dirname + '/_' + this.basename;
|
||||
});
|
||||
|
||||
/**
|
||||
* Register the given template engine `exports`
|
||||
* as `ext`. For example we may wish to map ".html"
|
||||
* files to jade:
|
||||
*
|
||||
* app.register('.html', require('jade'));
|
||||
*
|
||||
* or
|
||||
*
|
||||
* app.register('html', require('jade'));
|
||||
*
|
||||
* This is also useful for libraries that may not
|
||||
* match extensions correctly. For example my haml.js
|
||||
* library is installed from npm as "hamljs" so instead
|
||||
* of layout.hamljs, we can register the engine as ".haml":
|
||||
*
|
||||
* app.register('.haml', require('haml-js'));
|
||||
*
|
||||
* @param {String} ext
|
||||
* @param {Object} obj
|
||||
* @api public
|
||||
*/
|
||||
|
||||
exports.register = function(ext, exports) {
|
||||
if ('.' != ext[0]) ext = '.' + ext;
|
||||
cache[ext] = exports;
|
||||
};
|
||||
Alguns arquivos não foram exibidos porque demasiados arquivos foram alterados neste diff Mostrar Mais
Referência em uma Nova Issue
Bloquear um usuário