mirror of
https://github.com/github/rails.git
synced 2026-01-13 08:38:05 -05:00
Compare commits
707 Commits
3-1-github
...
v3.0.3
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0aca27d407 | ||
|
|
8279c06525 | ||
|
|
34dc030177 | ||
|
|
69f7d39945 | ||
|
|
c7dd1e4df4 | ||
|
|
ed91716439 | ||
|
|
e2266cfcbd | ||
|
|
edafb49472 | ||
|
|
fc69c64c84 | ||
|
|
bd944798a5 | ||
|
|
19ff18ed26 | ||
|
|
89b073b367 | ||
|
|
76efb921e3 | ||
|
|
fb4dc1a512 | ||
|
|
186e3c71f9 | ||
|
|
31b6a6a78b | ||
|
|
029c8ff46c | ||
|
|
65fdd284e1 | ||
|
|
1540bf5816 | ||
|
|
4369f132a6 | ||
|
|
c41d9813be | ||
|
|
7487767c31 | ||
|
|
28f9d60f77 | ||
|
|
dbc0023d6d | ||
|
|
ccc8d9ce54 | ||
|
|
43b0fd7918 | ||
|
|
86534a35f9 | ||
|
|
13f10ac4ea | ||
|
|
4c3edae2bd | ||
|
|
5d9219b1b5 | ||
|
|
70e4403e46 | ||
|
|
41e5e4a7bd | ||
|
|
bad920f423 | ||
|
|
45c3abadfa | ||
|
|
205b24064e | ||
|
|
4ffd31e195 | ||
|
|
020f74b152 | ||
|
|
7a1f22ff33 | ||
|
|
06402b03ea | ||
|
|
0db841223a | ||
|
|
34015296e0 | ||
|
|
45525713f9 | ||
|
|
422831872e | ||
|
|
e777b914c8 | ||
|
|
1287fddff6 | ||
|
|
467d5844eb | ||
|
|
afc16f6dce | ||
|
|
3551e254d2 | ||
|
|
9078041ee8 | ||
|
|
9ccaf47012 | ||
|
|
10fc93b89e | ||
|
|
fc95196df0 | ||
|
|
01ed7005e1 | ||
|
|
f8b7c74f9b | ||
|
|
4266e3e91a | ||
|
|
bdec56cbe7 | ||
|
|
b870193345 | ||
|
|
ed62b96e23 | ||
|
|
d54ab371df | ||
|
|
30ae44a326 | ||
|
|
be63e03fba | ||
|
|
86f66e9097 | ||
|
|
0de95dfb2c | ||
|
|
d5fd83f4c7 | ||
|
|
3e86e4b34e | ||
|
|
55a8bd666d | ||
|
|
1b66a646db | ||
|
|
2e4e1ed916 | ||
|
|
387a1a6365 | ||
|
|
7611a0925c | ||
|
|
bf875b273d | ||
|
|
b0f6349a07 | ||
|
|
844b9a5018 | ||
|
|
f1c13b0dd7 | ||
|
|
4b33bd9888 | ||
|
|
c7fac8c6ea | ||
|
|
ad2c0bdb9b | ||
|
|
f676beb647 | ||
|
|
ea1fde6ae7 | ||
|
|
d4b7fa5fb6 | ||
|
|
7ab940c7ad | ||
|
|
e6abfc46bf | ||
|
|
a56142cc6b | ||
|
|
b91371763e | ||
|
|
a682986d0d | ||
|
|
2c8bff3513 | ||
|
|
5cb1dad228 | ||
|
|
6c8982fa13 | ||
|
|
918bf847d8 | ||
|
|
3ca1bc381f | ||
|
|
eca3791d0f | ||
|
|
b5efa8fc67 | ||
|
|
c95f155731 | ||
|
|
29b3af539b | ||
|
|
3bc12d3ee6 | ||
|
|
ae7a354110 | ||
|
|
4d2a53d9ea | ||
|
|
4772abc7d8 | ||
|
|
4fd9c20fb3 | ||
|
|
c681df48e3 | ||
|
|
ddf73603c1 | ||
|
|
f73a3e6838 | ||
|
|
0d23c2160d | ||
|
|
63b0d0e9d2 | ||
|
|
5f6a02f4ba | ||
|
|
909c860a42 | ||
|
|
e9d593bc18 | ||
|
|
ec23bc15ba | ||
|
|
7ca9dcd434 | ||
|
|
1a618e98e0 | ||
|
|
5a4a2323b0 | ||
|
|
c53f301b48 | ||
|
|
f294540154 | ||
|
|
4480521df9 | ||
|
|
adea1467f6 | ||
|
|
14b33a4eb7 | ||
|
|
141cba8f1d | ||
|
|
0891f49075 | ||
|
|
37431bb34e | ||
|
|
f7faa86690 | ||
|
|
10d3ab79dc | ||
|
|
85ffbfef9c | ||
|
|
869118aaae | ||
|
|
1556e0874d | ||
|
|
0a8eaff3d1 | ||
|
|
dba7de0da0 | ||
|
|
0c56a27926 | ||
|
|
448a187606 | ||
|
|
0b46f72226 | ||
|
|
d0d1acab58 | ||
|
|
3021297e89 | ||
|
|
b4954e81c1 | ||
|
|
83d5180bec | ||
|
|
4245dcd521 | ||
|
|
3a6acb6162 | ||
|
|
61fc0f0ba0 | ||
|
|
b4b4a2aa28 | ||
|
|
0b9eced70d | ||
|
|
d066ad638a | ||
|
|
981942a326 | ||
|
|
2ded862226 | ||
|
|
5e685caf1c | ||
|
|
fc9e9ed406 | ||
|
|
d75244785f | ||
|
|
003d67efb2 | ||
|
|
c4528deec1 | ||
|
|
1e50fae259 | ||
|
|
9eeb24cd57 | ||
|
|
b9fd742bb6 | ||
|
|
53def60d83 | ||
|
|
444aa9c735 | ||
|
|
af53ed89c4 | ||
|
|
accc5676d6 | ||
|
|
3e958a5bf7 | ||
|
|
13a4d1436f | ||
|
|
cec7129729 | ||
|
|
da927d2e16 | ||
|
|
c13aaaa2f0 | ||
|
|
4a77431b9a | ||
|
|
86571a1f43 | ||
|
|
e5da873db1 | ||
|
|
e99c894767 | ||
|
|
2380c71601 | ||
|
|
ebfbb2a2e3 | ||
|
|
fef42738d7 | ||
|
|
f283812bed | ||
|
|
3569bde28e | ||
|
|
f9b5f63319 | ||
|
|
87104a71a9 | ||
|
|
fc1c799241 | ||
|
|
b0b2d41a48 | ||
|
|
7e73344f7a | ||
|
|
ea6305411f | ||
|
|
2221b701b4 | ||
|
|
fc1bd2bba4 | ||
|
|
de3c0d7eb4 | ||
|
|
43f404ed6e | ||
|
|
b08175825e | ||
|
|
5704aa2e1a | ||
|
|
1a2b28c9d9 | ||
|
|
9ddda10b67 | ||
|
|
f640af5a7a | ||
|
|
a35c8c6426 | ||
|
|
e080144242 | ||
|
|
8ae8125e75 | ||
|
|
ddc2f2ee50 | ||
|
|
a01547cc12 | ||
|
|
2e0114182c | ||
|
|
439a0ceb43 | ||
|
|
3daf8228f9 | ||
|
|
1685bd2e10 | ||
|
|
ede0466d13 | ||
|
|
bf388f37ac | ||
|
|
93096aedcb | ||
|
|
c493624fc2 | ||
|
|
6059c9679e | ||
|
|
721f43498a | ||
|
|
e6e90839d0 | ||
|
|
ecbfed2f83 | ||
|
|
409bd22a2d | ||
|
|
1e687e98ac | ||
|
|
cab12076d7 | ||
|
|
1efeb5c935 | ||
|
|
10d9740827 | ||
|
|
40bc0cb34d | ||
|
|
9e5545cd66 | ||
|
|
fae57ef4f5 | ||
|
|
9edaf10fee | ||
|
|
c2c6f12c84 | ||
|
|
2525bfc5dd | ||
|
|
e0411f3412 | ||
|
|
61e70bf623 | ||
|
|
6643929d5a | ||
|
|
b32bd08622 | ||
|
|
9e32b1cbe5 | ||
|
|
a56df5b052 | ||
|
|
e677275068 | ||
|
|
f38c46cfcf | ||
|
|
a88af8a669 | ||
|
|
d5e916563e | ||
|
|
d3ffea25b2 | ||
|
|
2f618bf7da | ||
|
|
f90a29853f | ||
|
|
59ef0a2a50 | ||
|
|
15fd9a21dc | ||
|
|
19b71233fe | ||
|
|
262319ab01 | ||
|
|
268319c8ba | ||
|
|
c8bf9674dd | ||
|
|
c8b34f2d9f | ||
|
|
0387d5b6f2 | ||
|
|
23f728abd5 | ||
|
|
4d023a6970 | ||
|
|
e41add0d30 | ||
|
|
8a68862020 | ||
|
|
49d54b4fb6 | ||
|
|
e66c1cee86 | ||
|
|
8105bc022e | ||
|
|
abb99e3ac0 | ||
|
|
e1e39a9a7b | ||
|
|
27cfb3e7bb | ||
|
|
093a30a019 | ||
|
|
68e1b5a397 | ||
|
|
109c37b73f | ||
|
|
bdba74819c | ||
|
|
8d14fa8959 | ||
|
|
1a61a797aa | ||
|
|
b0f059136b | ||
|
|
684bb86266 | ||
|
|
2db30351ed | ||
|
|
3c36c12a65 | ||
|
|
8765e06937 | ||
|
|
7a6c715517 | ||
|
|
bfd4022075 | ||
|
|
a0fc2f7b74 | ||
|
|
9d36e2ad2c | ||
|
|
39cb27b414 | ||
|
|
68034efea1 | ||
|
|
164e3b6612 | ||
|
|
ab59522ea1 | ||
|
|
e125bf264e | ||
|
|
b79a782a05 | ||
|
|
301462c89b | ||
|
|
7d54fba421 | ||
|
|
5391dd3821 | ||
|
|
a23bf047b6 | ||
|
|
b5a17d7648 | ||
|
|
4ef2dead68 | ||
|
|
41807eb152 | ||
|
|
75b985e4e8 | ||
|
|
91693ddfe4 | ||
|
|
14f1b09b46 | ||
|
|
3479c1ea62 | ||
|
|
89cc7426dd | ||
|
|
1817ae327e | ||
|
|
c510870a8f | ||
|
|
ceb193a201 | ||
|
|
f6d5ff2f00 | ||
|
|
b8ff3df6a4 | ||
|
|
465dd6f0e9 | ||
|
|
2ef60690ff | ||
|
|
ac89275f1d | ||
|
|
298da5b8a9 | ||
|
|
993572472a | ||
|
|
ab1ab5fcec | ||
|
|
d30fb467c9 | ||
|
|
172377df45 | ||
|
|
733e928234 | ||
|
|
742da4b0d4 | ||
|
|
2820addea7 | ||
|
|
7108daf20e | ||
|
|
a9b5b19d9a | ||
|
|
ed9d30981f | ||
|
|
3de55aafbf | ||
|
|
378728b5d1 | ||
|
|
662ce52ed7 | ||
|
|
ad781a175c | ||
|
|
16cff17e53 | ||
|
|
5297e641cd | ||
|
|
7ddf663c75 | ||
|
|
41e0f68474 | ||
|
|
a746090a31 | ||
|
|
1276c28336 | ||
|
|
9a28d42d60 | ||
|
|
442e4d087b | ||
|
|
b1c104296e | ||
|
|
ef66a08160 | ||
|
|
33b954005c | ||
|
|
3091cb386f | ||
|
|
d73fcfeb52 | ||
|
|
b8899bba12 | ||
|
|
a461c2b595 | ||
|
|
c72140198d | ||
|
|
bd1059f5ad | ||
|
|
7020c92e0f | ||
|
|
817b8f0ac4 | ||
|
|
8c49666474 | ||
|
|
9fe7ef65ea | ||
|
|
935fa9d6d5 | ||
|
|
f0113e59c2 | ||
|
|
02daf68e6a | ||
|
|
1c7a7545eb | ||
|
|
29887510bf | ||
|
|
2ad2bd3644 | ||
|
|
d7201210ba | ||
|
|
dcce930a8f | ||
|
|
b1291836c3 | ||
|
|
6a60387dce | ||
|
|
233e0e8ce8 | ||
|
|
5c06777293 | ||
|
|
86a96fb17c | ||
|
|
20866a5c95 | ||
|
|
3e931b1961 | ||
|
|
36e625df83 | ||
|
|
b227a9a948 | ||
|
|
7221d6a841 | ||
|
|
aefa11be11 | ||
|
|
bf87528b53 | ||
|
|
93a716fb9c | ||
|
|
9441eabbbf | ||
|
|
3b6d7f0df8 | ||
|
|
c25c81e7c9 | ||
|
|
d1eb9ff8b1 | ||
|
|
3df90defa8 | ||
|
|
2adad2a0cc | ||
|
|
9c5248f6f2 | ||
|
|
fed73ffecf | ||
|
|
69ae216bcf | ||
|
|
8cba115301 | ||
|
|
54b193fe16 | ||
|
|
5fc006a9a4 | ||
|
|
4144eace88 | ||
|
|
3746453c3a | ||
|
|
c2a87b45b8 | ||
|
|
031b60d270 | ||
|
|
63306de3e7 | ||
|
|
b29eb89be8 | ||
|
|
c1bafe07a9 | ||
|
|
e7b1b3f610 | ||
|
|
3e786065f5 | ||
|
|
67b4c42b89 | ||
|
|
bab620fe5c | ||
|
|
d37a44e42c | ||
|
|
c1915f5ff3 | ||
|
|
6ba646329b | ||
|
|
9017e1f03f | ||
|
|
6655a3c248 | ||
|
|
46fddc305a | ||
|
|
3e7f7668fc | ||
|
|
3a96fa485c | ||
|
|
e709c97e08 | ||
|
|
050a345972 | ||
|
|
e8d2002f21 | ||
|
|
05bb9cae95 | ||
|
|
6ed616d51a | ||
|
|
d9a323ea81 | ||
|
|
676b5bf970 | ||
|
|
fcdedb2c00 | ||
|
|
99a87e9dd0 | ||
|
|
9891ca89c6 | ||
|
|
e38fa05781 | ||
|
|
c7106e827e | ||
|
|
3d0c92868c | ||
|
|
a8a9f00058 | ||
|
|
ca73b5ba65 | ||
|
|
9f9c50f917 | ||
|
|
b63b6c4033 | ||
|
|
647ed22aad | ||
|
|
851552bd80 | ||
|
|
829df8007d | ||
|
|
a2c547f592 | ||
|
|
15e9b53a57 | ||
|
|
90d4a19285 | ||
|
|
0f94ca31b1 | ||
|
|
ef01f8840b | ||
|
|
3edd3d052e | ||
|
|
331234e0ab | ||
|
|
cc5a9c642b | ||
|
|
bf35d1e7c0 | ||
|
|
7f83aefd38 | ||
|
|
0c87873455 | ||
|
|
8da45763b2 | ||
|
|
5502a78c3e | ||
|
|
6c84fd80b7 | ||
|
|
c144f50347 | ||
|
|
e2d73f01a9 | ||
|
|
ba03dd4774 | ||
|
|
dbbf198f5c | ||
|
|
3ba8e31005 | ||
|
|
e8a083ecc2 | ||
|
|
39f2f18679 | ||
|
|
983a5e2970 | ||
|
|
2af7f29203 | ||
|
|
a2d3e6e29f | ||
|
|
473feeb32d | ||
|
|
d806aa2b68 | ||
|
|
daf2f95830 | ||
|
|
52e962086d | ||
|
|
b78011314e | ||
|
|
9df512be94 | ||
|
|
ace0f87056 | ||
|
|
1c2a2f711d | ||
|
|
a3161096c2 | ||
|
|
217fb3e9b5 | ||
|
|
dcdb8bae38 | ||
|
|
27512c2161 | ||
|
|
60685475dc | ||
|
|
3ecbae0672 | ||
|
|
59cd141c81 | ||
|
|
d7f1226412 | ||
|
|
6a035099b2 | ||
|
|
3b3cfa44f6 | ||
|
|
908544c90e | ||
|
|
2722c82f6e | ||
|
|
d810854d42 | ||
|
|
e12ff5b3bd | ||
|
|
61292a1f87 | ||
|
|
8bad8ac758 | ||
|
|
241dfa3c90 | ||
|
|
9ba46cf15a | ||
|
|
3dc9880866 | ||
|
|
da7a28027a | ||
|
|
bfe956d785 | ||
|
|
e991f39709 | ||
|
|
aec084955d | ||
|
|
55c1f351c4 | ||
|
|
d887dbc2d6 | ||
|
|
37467bf0fc | ||
|
|
30dcac2926 | ||
|
|
5deeb43fca | ||
|
|
78f6f0dc4b | ||
|
|
174d5cd7ee | ||
|
|
2ae4f01650 | ||
|
|
3a831cb7d6 | ||
|
|
0d7b8f8c83 | ||
|
|
91916e6c3c | ||
|
|
6b54a6a8ff | ||
|
|
6d1e87b16b | ||
|
|
7830f8d9f8 | ||
|
|
d8196bf994 | ||
|
|
aa384de7dd | ||
|
|
78486cb9c5 | ||
|
|
6579173814 | ||
|
|
a32f46d0ce | ||
|
|
c857bd23c0 | ||
|
|
5ccdb362b4 | ||
|
|
7a7012c717 | ||
|
|
8feb2856ea | ||
|
|
5708412052 | ||
|
|
822fa17c6c | ||
|
|
04aa14f8d7 | ||
|
|
bc61196bf5 | ||
|
|
aafb29073a | ||
|
|
880eaa145e | ||
|
|
fc6db6226f | ||
|
|
8931dd17a9 | ||
|
|
c8b84a1c8c | ||
|
|
36fcc99cce | ||
|
|
a1ca2e0a38 | ||
|
|
6a1ea881cf | ||
|
|
1cc653f9b3 | ||
|
|
eeb9b379f9 | ||
|
|
ce04ea973c | ||
|
|
ab64dc9c20 | ||
|
|
f10fb1c4e9 | ||
|
|
2570fda5ab | ||
|
|
b97ad85d44 | ||
|
|
d125687b97 | ||
|
|
2c81a31039 | ||
|
|
bedf6a0061 | ||
|
|
6edae4553e | ||
|
|
8235c9288f | ||
|
|
6ca6ef2ab0 | ||
|
|
0579963a38 | ||
|
|
7cb44a5092 | ||
|
|
df0a7bfb8f | ||
|
|
2106782828 | ||
|
|
e88f8bee5e | ||
|
|
11e9883f19 | ||
|
|
b4e5da6bde | ||
|
|
7dbc99ef0d | ||
|
|
d0e3323d5b | ||
|
|
68e2d1e496 | ||
|
|
9011f8f49c | ||
|
|
b4a520874a | ||
|
|
47f6d8b880 | ||
|
|
612c233a28 | ||
|
|
8f72ddc12b | ||
|
|
47280f083a | ||
|
|
c6391e6676 | ||
|
|
20088f6fff | ||
|
|
d033b237c4 | ||
|
|
809a04ba8f | ||
|
|
ac66de4a82 | ||
|
|
e509d4afc9 | ||
|
|
ad063263bc | ||
|
|
8f2b2781b0 | ||
|
|
6b52a58f72 | ||
|
|
1031fe1478 | ||
|
|
758f01d49e | ||
|
|
21c9795c15 | ||
|
|
49e406efb7 | ||
|
|
1ef9b98a31 | ||
|
|
bef90f8449 | ||
|
|
64f4dc68f6 | ||
|
|
a16ec2f4cf | ||
|
|
6fb6ddb9a7 | ||
|
|
632a224bd1 | ||
|
|
6580c6df36 | ||
|
|
ddce48a355 | ||
|
|
06632578c2 | ||
|
|
2f6383e340 | ||
|
|
7e85b16518 | ||
|
|
12f7f7a714 | ||
|
|
82eff0ffe9 | ||
|
|
588ac71213 | ||
|
|
0a41ece3e3 | ||
|
|
c40856c46c | ||
|
|
23303d6ab7 | ||
|
|
017840beb8 | ||
|
|
92f4cca4a3 | ||
|
|
82a58abe05 | ||
|
|
90176a6f15 | ||
|
|
4b21dfe9a7 | ||
|
|
c11ba424e7 | ||
|
|
21063e5e27 | ||
|
|
22d242c2ca | ||
|
|
6f478b0698 | ||
|
|
fc43c62fc6 | ||
|
|
b9281e8e2c | ||
|
|
5f5c508444 | ||
|
|
020aeb6192 | ||
|
|
141634ddc6 | ||
|
|
14be1789b7 | ||
|
|
1d2e075bf1 | ||
|
|
d1480926e8 | ||
|
|
047e411fd2 | ||
|
|
452a56ad51 | ||
|
|
83cb532009 | ||
|
|
c330e96a6e | ||
|
|
a34dce9717 | ||
|
|
b8ec4eaac5 | ||
|
|
688368100a | ||
|
|
19fb031d84 | ||
|
|
dbe5ae488e | ||
|
|
1091a6e9b7 | ||
|
|
8520045200 | ||
|
|
ebf7447b34 | ||
|
|
10177d3a38 | ||
|
|
f224c66a91 | ||
|
|
88fc37ff03 | ||
|
|
bdeeca358b | ||
|
|
5f7bfb1c3a | ||
|
|
ae7732f957 | ||
|
|
df8a941a43 | ||
|
|
6d68cde2c5 | ||
|
|
0f1b9bbbf8 | ||
|
|
477a9d4d86 | ||
|
|
a036999ed1 | ||
|
|
707248a629 | ||
|
|
ecf59b4776 | ||
|
|
677564f8f7 | ||
|
|
f6f7ae4020 | ||
|
|
b3ece73114 | ||
|
|
76c91a237c | ||
|
|
05ba082c6a | ||
|
|
3270c58ebb | ||
|
|
ccd4364a13 | ||
|
|
6373dd466f | ||
|
|
59e63e76c3 | ||
|
|
433d7a26fe | ||
|
|
79e15f0340 | ||
|
|
3698da65e5 | ||
|
|
108179b068 | ||
|
|
bacf78150c | ||
|
|
e86b758592 | ||
|
|
abd973689d | ||
|
|
fb6edb1769 | ||
|
|
e4283007d6 | ||
|
|
91ae6e9933 | ||
|
|
fe2d65864e | ||
|
|
9df227983f | ||
|
|
d03a1249a0 | ||
|
|
9528aa9f86 | ||
|
|
4dcce5d06e | ||
|
|
ab68d4b52e | ||
|
|
1e6e868d8c | ||
|
|
198bffe3be | ||
|
|
06af291346 | ||
|
|
30ea923040 | ||
|
|
7325dd21b3 | ||
|
|
f2d22ecbb3 | ||
|
|
1c970b8394 | ||
|
|
c8509d5303 | ||
|
|
36cb62eb9d | ||
|
|
bfd728182c | ||
|
|
ff760dd6ce | ||
|
|
5352a89d50 | ||
|
|
a56ee4c9a2 | ||
|
|
dac2b37b03 | ||
|
|
8464ee0650 | ||
|
|
e1b85c3bda | ||
|
|
1fbcd5f5fc | ||
|
|
43b8722f4b | ||
|
|
d9b77ddecd | ||
|
|
6f88b82263 | ||
|
|
b2eaac24c3 | ||
|
|
5859f5eee1 | ||
|
|
63ffec85b7 | ||
|
|
ca3fc4b325 | ||
|
|
aaa52c6d1f | ||
|
|
68bed3a4ad | ||
|
|
8968eecb93 | ||
|
|
3c404c56eb | ||
|
|
cbf89a378c | ||
|
|
2d681838c0 | ||
|
|
b02751c961 | ||
|
|
3ccf3504d2 | ||
|
|
98384b1ce8 | ||
|
|
a263a8ffd5 | ||
|
|
dd7e872e85 | ||
|
|
f85b206e7a | ||
|
|
22cbc3f0fa | ||
|
|
efb2bd0409 | ||
|
|
195e891954 | ||
|
|
28d82bd2e9 | ||
|
|
4d4b865b11 | ||
|
|
5c109e243f | ||
|
|
f7996be0c7 | ||
|
|
d55491c844 | ||
|
|
4b18d3c210 | ||
|
|
783dc5207b | ||
|
|
78c7705b32 | ||
|
|
5aec933385 | ||
|
|
413c9c8235 | ||
|
|
91930dc30b | ||
|
|
001a574785 | ||
|
|
a897a1f4a3 | ||
|
|
8fb0c9f509 | ||
|
|
0bb8d0561a | ||
|
|
bed98b9bf2 | ||
|
|
148dd2eac6 | ||
|
|
06e4c48815 | ||
|
|
0a86cb5972 | ||
|
|
e34fb808db | ||
|
|
2005f8234a | ||
|
|
05a49c7718 | ||
|
|
902d732617 | ||
|
|
84d5461d43 | ||
|
|
18bcc548bf | ||
|
|
ba9602b9e7 | ||
|
|
906ef233e4 | ||
|
|
4da32babdf | ||
|
|
87365272e9 | ||
|
|
f316a851dd | ||
|
|
19c77a0d2b | ||
|
|
2498cdaf14 | ||
|
|
963638aac8 | ||
|
|
fbc40a4d94 | ||
|
|
32d840d98a | ||
|
|
bd1cf94a29 | ||
|
|
d599e94e45 | ||
|
|
91e4249c02 | ||
|
|
07c5e5416b | ||
|
|
8158afa47e | ||
|
|
9269e55b1f | ||
|
|
b46b5a6d54 | ||
|
|
46a1da7c79 | ||
|
|
8b7219b9d6 | ||
|
|
109dc3c39c | ||
|
|
af8e085190 | ||
|
|
146a013c42 | ||
|
|
84703be5ff | ||
|
|
43cc69cb65 | ||
|
|
01186652cc | ||
|
|
7a1bba4799 | ||
|
|
4474470ffd | ||
|
|
9ae7f04cd6 | ||
|
|
1318bf6e33 | ||
|
|
5987fd4c79 | ||
|
|
677e1e58b6 | ||
|
|
3b170b2e14 | ||
|
|
79583ca9b1 | ||
|
|
257e9c4ec4 | ||
|
|
a44779e9bb | ||
|
|
503931df05 | ||
|
|
f73e9d2df8 |
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,27 +1,21 @@
|
||||
*.gem
|
||||
pkg
|
||||
.bundle
|
||||
Gemfile.lock
|
||||
.Gemfile
|
||||
debug.log
|
||||
doc/rdoc
|
||||
activemodel/doc
|
||||
activeresource/doc
|
||||
activerecord/doc
|
||||
activerecord/sqlnet.log
|
||||
actionpack/doc
|
||||
actionmailer/doc
|
||||
activesupport/doc
|
||||
activesupport/test/tmp
|
||||
activemodel/test/fixtures/fixture_database.sqlite3
|
||||
actionpack/test/tmp
|
||||
activesupport/test/fixtures/isolation_test
|
||||
dist
|
||||
railties/test/500.html
|
||||
railties/test/fixtures/tmp
|
||||
railties/test/initializer/root/log
|
||||
railties/doc
|
||||
railties/guides/output
|
||||
railties/tmp
|
||||
.rvmrc
|
||||
.rbenv-version
|
||||
RDOC_MAIN.rdoc
|
||||
|
||||
25
.travis.yml
25
.travis.yml
@@ -1,25 +0,0 @@
|
||||
script: 'ci/travis.rb'
|
||||
rvm:
|
||||
- 1.8.7
|
||||
- 1.9.2
|
||||
- 1.9.3
|
||||
env:
|
||||
- "GEM=railties"
|
||||
- "GEM=ap,am,amo,ares,as"
|
||||
- "GEM=ar:mysql"
|
||||
- "GEM=ar:mysql2"
|
||||
- "GEM=ar:sqlite3"
|
||||
- "GEM=ar:postgresql"
|
||||
notifications:
|
||||
email: false
|
||||
irc:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
channels:
|
||||
- "irc.freenode.org#rails-contrib"
|
||||
campfire:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
rooms:
|
||||
- secure: "CGWvthGkBKNnTnk9YSmf9AXKoiRI33fCl5D3jU4nx3cOPu6kv2R9nMjt9EAo\nOuS4Q85qNSf4VNQ2cUPNiNYSWQ+XiTfivKvDUw/QW9r1FejYyeWarMsSBWA+\n0fADjF1M2dkDIVLgYPfwoXEv7l+j654F1KLKB69F0F/netwP9CQ="
|
||||
bundler_args: --path vendor/bundle
|
||||
@@ -1,4 +1,3 @@
|
||||
--exclude /templates/
|
||||
--quiet
|
||||
act*/lib/**/*.rb
|
||||
railties/lib/**/*.rb
|
||||
|
||||
83
Gemfile
83
Gemfile
@@ -1,68 +1,53 @@
|
||||
source "http://rubygems.org"
|
||||
source 'http://rubygems.org'
|
||||
|
||||
gemspec
|
||||
gem "rails", :path => File.dirname(__FILE__)
|
||||
|
||||
if ENV['AREL']
|
||||
gem "arel", :path => ENV['AREL']
|
||||
end
|
||||
gem "rake", ">= 0.8.7"
|
||||
gem "mocha", ">= 0.9.8"
|
||||
gem "rdoc", ">= 2.5.10"
|
||||
gem "horo", ">= 1.0.2"
|
||||
|
||||
gem "bcrypt-ruby", "~> 3.0.0"
|
||||
gem "jquery-rails"
|
||||
# This needs to be with require false to avoid
|
||||
# it being automatically loaded by sprockets
|
||||
gem "uglifier", ">= 1.0.3", :require => false
|
||||
|
||||
gem "rake", ">= 0.8.7"
|
||||
gem "mocha", "0.10.5"
|
||||
|
||||
group :doc do
|
||||
# The current sdoc cannot generate GitHub links due
|
||||
# to a bug, but the PR that fixes it has been there
|
||||
# for some weeks unapplied. As a temporary solution
|
||||
# this is our own fork with the fix.
|
||||
gem "sdoc", :git => 'git://github.com/fxn/sdoc.git'
|
||||
gem "RedCloth", "~> 4.2" if RUBY_VERSION < "1.9.3"
|
||||
gem "w3c_validators"
|
||||
end
|
||||
# for perf tests
|
||||
gem "faker"
|
||||
gem "rbench"
|
||||
gem "addressable"
|
||||
|
||||
# AS
|
||||
gem "memcache-client", ">= 1.8.5"
|
||||
|
||||
# AM
|
||||
gem "text-format", "~> 1.0.0"
|
||||
|
||||
platforms :mri_18 do
|
||||
gem "system_timer"
|
||||
gem "json"
|
||||
gem "ruby-debug", ">= 0.10.3"
|
||||
gem 'ruby-prof'
|
||||
end
|
||||
|
||||
# Add your own local bundler stuff
|
||||
instance_eval File.read ".Gemfile" if File.exists? ".Gemfile"
|
||||
|
||||
platforms :mri do
|
||||
group :test do
|
||||
gem 'ruby-prof', '~> 0.11.2'
|
||||
end
|
||||
platforms :mri_19 do
|
||||
# TODO: Remove the conditional when ruby-debug19 supports Ruby >= 1.9.3
|
||||
gem "ruby-debug19" if RUBY_VERSION < "1.9.3"
|
||||
end
|
||||
|
||||
platforms :ruby do
|
||||
if ENV["RB_FSEVENT"]
|
||||
gem "rb-fsevent"
|
||||
end
|
||||
gem "json"
|
||||
gem "yajl-ruby"
|
||||
gem "nokogiri", ">= 1.4.4"
|
||||
gem 'json'
|
||||
gem 'yajl-ruby'
|
||||
gem "nokogiri", ">= 1.4.3.1"
|
||||
|
||||
# AR
|
||||
gem "sqlite3", "~> 1.3.4"
|
||||
gem "sqlite3-ruby", "~> 1.3.1", :require => 'sqlite3'
|
||||
|
||||
group :db do
|
||||
gem "pg", ">= 0.11.0"
|
||||
gem "pg", ">= 0.9.0"
|
||||
gem "mysql", ">= 2.8.1"
|
||||
gem "mysql2", ">= 0.3.10"
|
||||
gem "mysql2", ">= 0.2.6"
|
||||
end
|
||||
end
|
||||
|
||||
platforms :jruby do
|
||||
gem "json"
|
||||
gem "activerecord-jdbcsqlite3-adapter", ">= 1.2.0"
|
||||
gem "ruby-debug", ">= 0.10.3"
|
||||
|
||||
gem "activerecord-jdbcsqlite3-adapter"
|
||||
|
||||
# This is needed by now to let tests work on JRuby
|
||||
# TODO: When the JRuby guys merge jruby-openssl in
|
||||
@@ -70,21 +55,23 @@ platforms :jruby do
|
||||
gem "jruby-openssl"
|
||||
|
||||
group :db do
|
||||
gem "activerecord-jdbcmysql-adapter", ">= 1.2.0"
|
||||
gem "activerecord-jdbcpostgresql-adapter", ">= 1.2.0"
|
||||
gem "activerecord-jdbcmysql-adapter"
|
||||
gem "activerecord-jdbcpostgresql-adapter"
|
||||
end
|
||||
end
|
||||
|
||||
env :AREL do
|
||||
gem "arel", :path => ENV['AREL']
|
||||
end
|
||||
|
||||
# gems that are necessary for ActiveRecord tests with Oracle database
|
||||
if ENV['ORACLE_ENHANCED_PATH'] || ENV['ORACLE_ENHANCED']
|
||||
platforms :ruby do
|
||||
gem "ruby-oci8", ">= 2.0.4"
|
||||
gem 'ruby-oci8', ">= 2.0.4"
|
||||
end
|
||||
if ENV['ORACLE_ENHANCED_PATH']
|
||||
gem "activerecord-oracle_enhanced-adapter", :path => ENV['ORACLE_ENHANCED_PATH']
|
||||
gem 'activerecord-oracle_enhanced-adapter', :path => ENV['ORACLE_ENHANCED_PATH']
|
||||
else
|
||||
gem "activerecord-oracle_enhanced-adapter", :git => "git://github.com/rsim/oracle-enhanced.git"
|
||||
end
|
||||
end
|
||||
|
||||
gem 'benchmark-ips'
|
||||
|
||||
@@ -1 +1 @@
|
||||
3.1.8
|
||||
3.0.3
|
||||
|
||||
65
README.rdoc
65
README.rdoc
@@ -1,43 +1,38 @@
|
||||
== Welcome to Rails
|
||||
== Welcome to \Rails
|
||||
|
||||
Rails is a web-application framework that includes everything needed to create
|
||||
database-backed web applications according to the {Model-View-Controller (MVC)}[http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller] pattern.
|
||||
\Rails is a web-application framework that includes everything needed to create
|
||||
database-backed web applications according to the Model-View-Control pattern.
|
||||
|
||||
Understanding the MVC pattern is key to understanding Rails. MVC divides your application
|
||||
into three layers, each with a specific responsibility.
|
||||
This pattern splits the view (also called the presentation) into "dumb"
|
||||
templates that are primarily responsible for inserting pre-built data in between
|
||||
HTML tags. The model contains the "smart" domain objects (such as Account,
|
||||
Product, Person, Post) that holds all the business logic and knows how to
|
||||
persist themselves to a database. The controller handles the incoming requests
|
||||
(such as Save New Account, Update Product, Show Post) by manipulating the model
|
||||
and directing data to the view.
|
||||
|
||||
The View layer is composed of "templates" that are responsible for providing
|
||||
appropriate representations of your application's resources. Templates
|
||||
can come in a variety of formats, but most view templates are \HTML with embedded Ruby
|
||||
code (.erb files).
|
||||
|
||||
The Model layer represents your domain model (such as Account, Product, Person, Post)
|
||||
and encapsulates the business logic that is specific to your application. In Rails,
|
||||
database-backed model classes are derived from ActiveRecord::Base. Active Record allows
|
||||
you to present the data from database rows as objects and embellish these data objects
|
||||
with business logic methods. Although most Rails models are backed by a database, models
|
||||
can also be ordinary Ruby classes, or Ruby classes that implement a set of interfaces as
|
||||
provided by the ActiveModel module. You can read more about Active Record in its
|
||||
In \Rails, the model is handled by what's called an object-relational mapping
|
||||
layer entitled Active Record. This layer allows you to present the data from
|
||||
database rows as objects and embellish these data objects with business logic
|
||||
methods. You can read more about Active Record in its
|
||||
{README}[link:files/activerecord/README_rdoc.html].
|
||||
|
||||
The Controller layer is responsible for handling incoming HTTP requests and providing a
|
||||
suitable response. Usually this means returning \HTML, but Rails controllers can also
|
||||
generate XML, JSON, PDFs, mobile-specific views, and more. Controllers manipulate models
|
||||
and render view templates in order to generate the appropriate HTTP response.
|
||||
The controller and view are handled by the Action Pack, which handles both
|
||||
layers by its two parts: Action View and Action Controller. These two layers
|
||||
are bundled in a single package due to their heavy interdependence. This is
|
||||
unlike the relationship between the Active Record and Action Pack that is much
|
||||
more separate. Each of these packages can be used independently outside of
|
||||
\Rails. You can read more about Action Pack in its
|
||||
{README}[link:files/actionpack/README_rdoc.html].
|
||||
|
||||
In Rails, the Controller and View layers are handled together by Action Pack.
|
||||
These two layers are bundled in a single package due to their heavy interdependence.
|
||||
This is unlike the relationship between Active Record and Action Pack which are
|
||||
independent. Each of these packages can be used independently outside of Rails. You
|
||||
can read more about Action Pack in its {README}[link:files/actionpack/README_rdoc.html].
|
||||
|
||||
== Getting Started
|
||||
|
||||
1. Install Rails at the command prompt if you haven't yet:
|
||||
1. Install \Rails at the command prompt if you haven't yet:
|
||||
|
||||
gem install rails
|
||||
|
||||
2. At the command prompt, create a new Rails application:
|
||||
2. At the command prompt, create a new \Rails application:
|
||||
|
||||
rails new myapp
|
||||
|
||||
@@ -49,25 +44,25 @@ can read more about Action Pack in its {README}[link:files/actionpack/README_rdo
|
||||
|
||||
Run with <tt>--help</tt> for options.
|
||||
|
||||
4. Go to http://localhost:3000 and you'll see:
|
||||
4. Go to http://localhost:3000/ and you'll see:
|
||||
|
||||
"Welcome aboard: You're riding Ruby on Rails!"
|
||||
|
||||
5. Follow the guidelines to start developing your application. You may find the following resources handy:
|
||||
5. Follow the guidelines to start developing your application. You can find the following resources handy:
|
||||
|
||||
* The README file created within your application.
|
||||
* The {Getting Started with Rails}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {Ruby on Rails Tutorial}[http://railstutorial.org/book].
|
||||
* The {Ruby on Rails Guides}[http://guides.rubyonrails.org].
|
||||
* The {API Documentation}[http://api.rubyonrails.org].
|
||||
* The {Ruby on Rails guides}[http://guides.rubyonrails.org/getting_started.html].
|
||||
* The {API documentation}[http://api.rubyonrails.org].
|
||||
|
||||
|
||||
== Contributing
|
||||
|
||||
We encourage you to contribute to Ruby on Rails! Please check out the {Contributing to Rails
|
||||
guide}[http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html] for guidelines about how
|
||||
We encourage you to contribute to Ruby on \Rails! Please check out the {Contributing to Rails
|
||||
guide}[http://edgeguides.rubyonrails.org/contributing_to_rails.html] for guidelines about how
|
||||
to proceed. {Join us}[http://contributors.rubyonrails.org]!
|
||||
|
||||
== License
|
||||
|
||||
Ruby on Rails is released under the MIT license.
|
||||
Ruby on \Rails is released under the MIT license.
|
||||
|
||||
141
Rakefile
Executable file → Normal file
141
Rakefile
Executable file → Normal file
@@ -1,17 +1,34 @@
|
||||
#!/usr/bin/env rake
|
||||
gem 'rdoc', '>= 2.5.10'
|
||||
require 'rdoc'
|
||||
|
||||
require 'rake'
|
||||
require 'rdoc/task'
|
||||
require 'sdoc'
|
||||
require 'net/http'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
$:.unshift File.expand_path('..', __FILE__)
|
||||
require "tasks/release"
|
||||
# RDoc skips some files in the Rails tree due to its binary? predicate. This is a quick
|
||||
# hack for edge docs, until we decide which is the correct way to address this issue.
|
||||
# If not fixed in RDoc itself, via an option or something, we should probably move this
|
||||
# to railties and use it also in doc:rails.
|
||||
def hijack_rdoc!
|
||||
require "rdoc/parser"
|
||||
class << RDoc::Parser
|
||||
def binary?(file)
|
||||
s = File.read(file, 1024) or return false
|
||||
|
||||
desc "Build gem files for all projects"
|
||||
task :build => "all:build"
|
||||
|
||||
desc "Release all gems to gemcutter and create a tag"
|
||||
task :release => "all:release"
|
||||
if s[0, 2] == Marshal.dump('')[0, 2] then
|
||||
true
|
||||
elsif file =~ /erb\.rb$/ then
|
||||
false
|
||||
elsif s.index("\x00") then # ORIGINAL is s.scan(/<%|%>/).length >= 4 || s.index("\x00")
|
||||
true
|
||||
elsif 0.respond_to? :fdiv then
|
||||
s.count("^ -~\t\r\n").fdiv(s.size) > 0.3
|
||||
else # HACK 1.8.6
|
||||
(s.count("^ -~\t\r\n").to_f / s.size) > 0.3
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
PROJECTS = %w(activesupport activemodel actionpack actionmailer activeresource activerecord railties)
|
||||
|
||||
@@ -37,6 +54,27 @@ task :smoke do
|
||||
system %(cd activerecord && #{$0} sqlite3:isolated_test)
|
||||
end
|
||||
|
||||
spec = eval(File.read('rails.gemspec'))
|
||||
Rake::GemPackageTask.new(spec) do |pkg|
|
||||
pkg.gem_spec = spec
|
||||
end
|
||||
|
||||
desc "Release all gems to gemcutter. Package rails, package & push components, then push rails"
|
||||
task :release => :release_projects do
|
||||
require 'rake/gemcutter'
|
||||
Rake::Gemcutter::Tasks.new(spec).define
|
||||
Rake::Task['gem:push'].invoke
|
||||
end
|
||||
|
||||
desc "Release all components to gemcutter."
|
||||
task :release_projects => :package do
|
||||
errors = []
|
||||
PROJECTS.each do |project|
|
||||
system(%(cd #{project} && #{$0} release)) || errors << project
|
||||
end
|
||||
fail("Errors in #{errors.join(', ')}") unless errors.empty?
|
||||
end
|
||||
|
||||
desc "Install gems for all projects."
|
||||
task :install => :gem do
|
||||
version = File.read("RAILS_VERSION").strip
|
||||
@@ -50,67 +88,35 @@ end
|
||||
|
||||
desc "Generate documentation for the Rails framework"
|
||||
RDoc::Task.new do |rdoc|
|
||||
RDOC_MAIN = 'RDOC_MAIN.rdoc'
|
||||
|
||||
# This is a hack.
|
||||
#
|
||||
# Backslashes are needed to prevent RDoc from autolinking "Rails" to the
|
||||
# documentation of the Rails module. On the other hand, as of this
|
||||
# writing README.rdoc is displayed in the front page of the project in
|
||||
# GitHub, where backslashes are shown and look weird.
|
||||
#
|
||||
# The temporary solution is to have a README.rdoc without backslashes for
|
||||
# GitHub, and gsub it to generate the main page of the API.
|
||||
#
|
||||
# Also, relative links in GitHub have to point to blobs, whereas in the API
|
||||
# they need to point to files.
|
||||
#
|
||||
# The idea for the future is to have totally different files, since the
|
||||
# API is no longer a generic entry point to Rails and deserves a
|
||||
# dedicated main page specifically thought as an API entry point.
|
||||
rdoc.before_running_rdoc do
|
||||
rdoc_main = File.read('README.rdoc')
|
||||
|
||||
# The ^(?=\S) assertion prevents code blocks from being processed,
|
||||
# since no autolinking happens there and RDoc displays the backslash
|
||||
# otherwise.
|
||||
rdoc_main.gsub!(/^(?=\S).*?\b(?=Rails)\b/) { "#$&\\" }
|
||||
rdoc_main.gsub!(%r{link:blob/master/(\w+)/README\.rdoc}, "link:files/\\1/README_rdoc.html")
|
||||
|
||||
File.open(RDOC_MAIN, 'w') do |f|
|
||||
f.write(rdoc_main)
|
||||
end
|
||||
|
||||
rdoc.rdoc_files.include(RDOC_MAIN)
|
||||
end
|
||||
hijack_rdoc!
|
||||
|
||||
rdoc.rdoc_dir = 'doc/rdoc'
|
||||
rdoc.title = "Ruby on Rails Documentation"
|
||||
|
||||
rdoc.options << '-f' << 'sdoc'
|
||||
rdoc.options << '-T' << 'rails'
|
||||
rdoc.options << '-f' << 'horo'
|
||||
rdoc.options << '-c' << 'utf-8'
|
||||
rdoc.options << '-g' # SDoc flag, link methods to GitHub
|
||||
rdoc.options << '-m' << RDOC_MAIN
|
||||
rdoc.options << '-m' << 'README.rdoc'
|
||||
|
||||
rdoc.rdoc_files.include('railties/CHANGELOG.md')
|
||||
rdoc.rdoc_files.include('README.rdoc')
|
||||
|
||||
rdoc.rdoc_files.include('railties/CHANGELOG')
|
||||
rdoc.rdoc_files.include('railties/MIT-LICENSE')
|
||||
rdoc.rdoc_files.include('railties/README.rdoc')
|
||||
rdoc.rdoc_files.include('railties/lib/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('railties/lib/rails/generators/**/templates/*')
|
||||
|
||||
rdoc.rdoc_files.include('activerecord/README.rdoc')
|
||||
rdoc.rdoc_files.include('activerecord/CHANGELOG.md')
|
||||
rdoc.rdoc_files.include('activerecord/CHANGELOG')
|
||||
rdoc.rdoc_files.include('activerecord/lib/active_record/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('activerecord/lib/active_record/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('activeresource/README.rdoc')
|
||||
rdoc.rdoc_files.include('activeresource/CHANGELOG.md')
|
||||
rdoc.rdoc_files.include('activeresource/CHANGELOG')
|
||||
rdoc.rdoc_files.include('activeresource/lib/active_resource.rb')
|
||||
rdoc.rdoc_files.include('activeresource/lib/active_resource/*')
|
||||
|
||||
rdoc.rdoc_files.include('actionpack/README.rdoc')
|
||||
rdoc.rdoc_files.include('actionpack/CHANGELOG.md')
|
||||
rdoc.rdoc_files.include('actionpack/CHANGELOG')
|
||||
rdoc.rdoc_files.include('actionpack/lib/abstract_controller/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_controller/**/*.rb')
|
||||
rdoc.rdoc_files.include('actionpack/lib/action_dispatch/**/*.rb')
|
||||
@@ -118,18 +124,17 @@ RDoc::Task.new do |rdoc|
|
||||
rdoc.rdoc_files.exclude('actionpack/lib/action_controller/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('actionmailer/README.rdoc')
|
||||
rdoc.rdoc_files.include('actionmailer/CHANGELOG.md')
|
||||
rdoc.rdoc_files.include('actionmailer/CHANGELOG')
|
||||
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/base.rb')
|
||||
rdoc.rdoc_files.include('actionmailer/lib/action_mailer/mail_helper.rb')
|
||||
rdoc.rdoc_files.exclude('actionmailer/lib/action_mailer/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('activesupport/README.rdoc')
|
||||
rdoc.rdoc_files.include('activesupport/CHANGELOG.md')
|
||||
rdoc.rdoc_files.include('activesupport/CHANGELOG')
|
||||
rdoc.rdoc_files.include('activesupport/lib/active_support/**/*.rb')
|
||||
rdoc.rdoc_files.exclude('activesupport/lib/active_support/vendor/*')
|
||||
|
||||
rdoc.rdoc_files.include('activemodel/README.rdoc')
|
||||
rdoc.rdoc_files.include('activemodel/CHANGELOG.md')
|
||||
rdoc.rdoc_files.include('activemodel/CHANGELOG')
|
||||
rdoc.rdoc_files.include('activemodel/lib/active_model/**/*.rb')
|
||||
end
|
||||
|
||||
@@ -167,27 +172,3 @@ task :update_versions do
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# We have a webhook configured in Github that gets invoked after pushes.
|
||||
# This hook triggers the following tasks:
|
||||
#
|
||||
# * updates the local checkout
|
||||
# * updates Rails Contributors
|
||||
# * generates and publishes edge docs
|
||||
# * if there's a new stable tag, generates and publishes stable docs
|
||||
#
|
||||
# Everything is automated and you do NOT need to run this task normally.
|
||||
#
|
||||
# We publish a new version by tagging, and pushing a tag does not trigger
|
||||
# that webhook. Stable docs would be updated by any subsequent regular
|
||||
# push, but if you want that to happen right away just run this.
|
||||
#
|
||||
desc 'Publishes docs, run this AFTER a new stable tag has been pushed'
|
||||
task :publish_docs do
|
||||
Net::HTTP.new('rails-hooks.hashref.com').start do |http|
|
||||
request = Net::HTTP::Post.new('/rails-master-hook')
|
||||
response = http.request(request)
|
||||
puts response.body
|
||||
end
|
||||
end
|
||||
|
||||
424
actionmailer/CHANGELOG
Normal file
424
actionmailer/CHANGELOG
Normal file
@@ -0,0 +1,424 @@
|
||||
*Rails 3.0.3 (November 16, 2010)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.2 (November 15, 2010)*
|
||||
|
||||
* No changes.
|
||||
|
||||
|
||||
*Rails 3.0.1 (October 15, 2010)*
|
||||
|
||||
* No Changes.
|
||||
|
||||
|
||||
*Rails 3.0.0 (August 29, 2010)*
|
||||
|
||||
* subject is automatically looked up on I18n using mailer_name and action_name as scope as in t(".subject") [JK]
|
||||
|
||||
* Changed encoding behaviour of mail, so updated tests in actionmailer and bumped mail version to 2.2.1 [ML]
|
||||
|
||||
* Added ability to pass Proc objects to the defaults hash [ML]
|
||||
|
||||
* Removed all quoting.rb type files from ActionMailer and put Mail 2.2.0 in instead [ML]
|
||||
|
||||
* Lot of updates to various test cases that now work better with the new Mail and so have different expectations
|
||||
|
||||
* Added interceptors and observers from Mail [ML]
|
||||
|
||||
ActionMailer::Base.register_interceptor calls Mail.register_interceptor
|
||||
ActionMailer::Base.register_observer calls Mail.register_observer
|
||||
|
||||
* Mail::Part now no longer has nil as a default charset, it is always set to something, and defaults to UTF-8
|
||||
|
||||
* Added explict setting of charset in set_fields! method to make sure Mail has the user defined default
|
||||
|
||||
* Removed quoting.rb and refactored for Mail to take responsibility of all quoting and auto encoding requirements for the header.
|
||||
|
||||
* Fixed several tests which had incorrect encoding.
|
||||
|
||||
* Changed all utf-8 to UTF-8 for consistency
|
||||
|
||||
* Whole new API added with tests. See base.rb for full details. Old API is deprecated.
|
||||
|
||||
* The Mail::Message class has helped methods for all the field types that return 'common' defaults for the common use case, so to get the subject, mail.subject will give you a string, mail.date will give you a DateTime object, mail.from will give you an array of address specs (mikel@test.lindsaar.net) etc. If you want to access the field object itself, call mail[:field_name] which will return the field object you want, which you can then chain, like mail[:from].formatted
|
||||
|
||||
* Mail#content_type now returns the content_type field as a string. If you want the mime type of a mail, then you call Mail#mime_type (eg, text/plain), if you want the parameters of the content type field, you call Mail#content_type_parameters which gives you a hash, eg {'format' => 'flowed', 'charset' => 'utf-8'}
|
||||
|
||||
* ActionMailer::Base :default_implicit_parts_order now is in the sequence of the order you want, no reversing of ordering takes place. The default order now is text/plain, then text/enriched, then text/html and then any other part that is not one of these three.
|
||||
|
||||
* Mail does not have "quoted_body", "quoted_subject" etc. All of these are accessed via body.encoded, subject.encoded etc
|
||||
|
||||
* Every object in a Mail object returns an object, never a string. So Mail.body returns a Mail::Body class object, need to call #encoded or #decoded to get the string you want.
|
||||
* Mail::Message#set_content_type does not exist, it is simply Mail::Message#content_type
|
||||
|
||||
* Every mail message gets a unique message_id unless you specify one, had to change all the tests that check for equality with expected.encoded == actual.encoded to first replace their message_ids with control values
|
||||
|
||||
* Mail now has a proper concept of parts, remove the ActionMailer::Part and ActionMailer::PartContainer classes
|
||||
|
||||
* Calling #encoded on any object returns it as a string ready to go into the output stream of an email, this means it includes the \r\n at the end of the lines and the object is pre-wrapped with \r\n\t if it is a header field. Also, the "encoded" value includes the field name if it is a header field.
|
||||
|
||||
* Attachments are only the actual attachment, with filename etc. A part contains an attachment. The part has the content_type etc. So attachments.last.content_type is invalid. But parts.last.content_type
|
||||
|
||||
* There is no idea of a "sub_head" in Mail. A part is just a Message with some extra functionality, so it just has a "header" like a normal mail message
|
||||
|
||||
|
||||
*2.3.2 [Final] (March 15, 2009)*
|
||||
|
||||
* Fixed that ActionMailer should send correctly formatted Return-Path in MAIL FROM for SMTP #1842 [Matt Jones]
|
||||
|
||||
* Fixed RFC-2045 quoted-printable bug #1421 [squadette]
|
||||
|
||||
* Fixed that no body charset would be set when there are attachments present #740 [Paweł Kondzior]
|
||||
|
||||
|
||||
*2.2.1 [RC2] (November 14th, 2008)*
|
||||
|
||||
* Turn on STARTTLS if it is available in Net::SMTP (added in Ruby 1.8.7) and the SMTP server supports it (This is required for Gmail's SMTP server) #1336 [Grant Hollingworth]
|
||||
|
||||
|
||||
*2.2.0 [RC1] (October 24th, 2008)*
|
||||
|
||||
* Add layout functionality to mailers [Pratik Naik]
|
||||
|
||||
Mailer layouts behaves just like controller layouts, except layout names need to
|
||||
have '_mailer' postfix for them to be automatically picked up.
|
||||
|
||||
|
||||
*2.1.0 (May 31st, 2008)*
|
||||
|
||||
* Fixed that a return-path header would be ignored #7572 [joost]
|
||||
|
||||
* Less verbose mail logging: just recipients for :info log level; the whole email for :debug only. #8000 [iaddict, Tarmo Tänav]
|
||||
|
||||
* Updated TMail to version 1.2.1 [Mikel Lindsaar]
|
||||
|
||||
* Fixed that you don't have to call super in ActionMailer::TestCase#setup #10406 [jamesgolick]
|
||||
|
||||
|
||||
*2.0.2* (December 16th, 2007)
|
||||
|
||||
* Included in Rails 2.0.2
|
||||
|
||||
|
||||
*2.0.1* (December 7th, 2007)
|
||||
|
||||
* Update ActionMailer so it treats ActionView the same way that ActionController does. Closes #10244 [Rick Olson]
|
||||
|
||||
* Pass the template_root as an array as ActionView's view_path
|
||||
* Request templates with the "#{mailer_name}/#{action}" as opposed to just "#{action}"
|
||||
|
||||
* Fixed that partials would be broken when using text.plain.erb as the extension #10130 [java]
|
||||
|
||||
* Update README to use new smtp settings configuration API. Closes #10060 [psq]
|
||||
|
||||
* Allow ActionMailer subclasses to individually set their delivery method (so two subclasses can have different delivery methods) #10033 [Zach Dennis]
|
||||
|
||||
* Update TMail to v1.1.0. Use an updated version of TMail if available. [Mikel Lindsaar]
|
||||
|
||||
* Introduce a new base test class for testing Mailers. ActionMailer::TestCase [Michael Koziarski]
|
||||
|
||||
* Fix silent failure of rxml templates. #9879 [jstewart]
|
||||
|
||||
* Fix attachment decoding when using the TMail C extension. #7861 [orangechicken]
|
||||
|
||||
* Increase mail delivery test coverage. #8692 [Kamal Fariz Mahyuddin]
|
||||
|
||||
* Register alternative template engines using ActionMailer::Base.register_template_extension('haml'). #7534 [cwd, Josh Peek]
|
||||
|
||||
* Only load ActionController::UrlWriter if ActionController is present [Rick Olson]
|
||||
|
||||
* Make sure parsed emails recognized attachments nested inside multipart parts. #6714 [Jamis Buck]
|
||||
|
||||
* Allow mailer actions named send by using __send__ internally. #6467 [iGEL]
|
||||
|
||||
* Add assert_emails and assert_no_emails to test the number of emails delivered. #6479 [Jonathan Viney]
|
||||
# Assert total number of emails delivered:
|
||||
assert_emails 0
|
||||
ContactMailer.deliver_contact
|
||||
assert_emails 1
|
||||
|
||||
# Assert number of emails delivered within a block:
|
||||
assert_emails 1 do
|
||||
post :signup, :name => 'Jonathan'
|
||||
end
|
||||
|
||||
|
||||
*1.3.3* (March 12th, 2007)
|
||||
|
||||
* Depend on Action Pack 1.13.3
|
||||
|
||||
|
||||
*1.3.2* (February 5th, 2007)
|
||||
|
||||
* Deprecate server_settings renaming it to smtp_settings, add sendmail_settings to allow you to override the arguments to and location of the sendmail executable. [Michael Koziarski]
|
||||
|
||||
|
||||
*1.3.1* (January 16th, 2007)
|
||||
|
||||
* Depend on Action Pack 1.13.1
|
||||
|
||||
|
||||
*1.3.0* (January 16th, 2007)
|
||||
|
||||
* Make mime version default to 1.0. closes #2323 [ror@andreas-s.net]
|
||||
|
||||
* Make sure quoted-printable text is decoded correctly when only portions of the text are encoded. closes #3154. [jon@siliconcircus.com]
|
||||
|
||||
* Make sure DOS newlines in quoted-printable text are normalized to unix newlines before unquoting. closes #4166 and #4452. [Jamis Buck]
|
||||
|
||||
* Fixed that iconv decoding should catch InvalidEncoding #3153 [jon@siliconcircus.com]
|
||||
|
||||
* Tighten rescue clauses. #5985 [james@grayproductions.net]
|
||||
|
||||
* Automatically included ActionController::UrlWriter, such that URL generation can happen within ActionMailer controllers. [David Heinemeier Hansson]
|
||||
|
||||
* Replace Reloadable with Reloadable::Deprecated. [Nicholas Seckar]
|
||||
|
||||
* Mailer template root applies to a class and its subclasses rather than acting globally. #5555 [somekool@gmail.com]
|
||||
|
||||
* Resolve action naming collision. #5520 [ssinghi@kreeti.com]
|
||||
|
||||
* ActionMailer::Base documentation rewrite. Closes #4991 [Kevin Clark, Marcel Molina Jr.]
|
||||
|
||||
* Replace alias method chaining with Module#alias_method_chain. [Marcel Molina Jr.]
|
||||
|
||||
* Replace Ruby's deprecated append_features in favor of included. [Marcel Molina Jr.]
|
||||
|
||||
* Correct spurious documentation example code which results in a SyntaxError. [Marcel Molina Jr.]
|
||||
|
||||
|
||||
*1.2.1* (April 6th, 2006)
|
||||
|
||||
* Be part of Rails 1.1.1
|
||||
|
||||
|
||||
*1.2.0* (March 27th, 2006)
|
||||
|
||||
* Nil charset caused subject line to be improperly quoted in implicitly multipart messages #2662 [ehalvorsen+rails@runbox.com]
|
||||
|
||||
* Parse content-type apart before using it so that sub-parts of the header can be set correctly #2918 [Jamis Buck]
|
||||
|
||||
* Make custom headers work in subparts #4034 [elan@bluemandrill.com]
|
||||
|
||||
* Template paths with dot chars in them no longer mess up implicit template selection for multipart messages #3332 [Chad Fowler]
|
||||
|
||||
* Make sure anything with content-disposition of "attachment" is passed to the attachment presenter when parsing an email body [Jamis Buck]
|
||||
|
||||
* Make sure TMail#attachments includes anything with content-disposition of "attachment", regardless of content-type [Jamis Buck]
|
||||
|
||||
|
||||
*1.1.5* (December 13th, 2005)
|
||||
|
||||
* Become part of Rails 1.0
|
||||
|
||||
|
||||
*1.1.4* (December 7th, 2005)
|
||||
|
||||
* Rename Version constant to VERSION. #2802 [Marcel Molina Jr.]
|
||||
|
||||
* Stricter matching for implicitly multipart filenames excludes files ending in unsupported extensions (such as foo.rhtml.bak) and without a two-part content type (such as foo.text.rhtml or foo.text.really.plain.rhtml). #2398 [Dave Burt <dave@burt.id.au>, Jeremy Kemper]
|
||||
|
||||
|
||||
*1.1.3* (November 7th, 2005)
|
||||
|
||||
* Allow Mailers to have custom initialize methods that set default instance variables for all mail actions #2563 [mrj@bigpond.net.au]
|
||||
|
||||
|
||||
*1.1.2* (October 26th, 2005)
|
||||
|
||||
* Upgraded to Action Pack 1.10.2
|
||||
|
||||
|
||||
*1.1.1* (October 19th, 2005)
|
||||
|
||||
* Upgraded to Action Pack 1.10.1
|
||||
|
||||
|
||||
*1.1.0* (October 16th, 2005)
|
||||
|
||||
* Update and extend documentation (rdoc)
|
||||
|
||||
* Minero Aoki made TMail available to Rails/ActionMailer under the MIT license (instead of LGPL) [RubyConf '05]
|
||||
|
||||
* Austin Ziegler made Text::Simple available to Rails/ActionMailer under a MIT-like licens [See rails ML, subject "Text::Format Licence Exception" on Oct 15, 2005]
|
||||
|
||||
* Fix vendor require paths to prevent files being required twice
|
||||
|
||||
* Don't add charset to content-type header for a part that contains subparts (for AOL compatibility) #2013 [John Long]
|
||||
|
||||
* Preserve underscores when unquoting message bodies #1930
|
||||
|
||||
* Encode multibyte characters correctly #1894
|
||||
|
||||
* Multipart messages specify a MIME-Version header automatically #2003 [John Long]
|
||||
|
||||
* Add a unified render method to ActionMailer (delegates to ActionView::Base#render)
|
||||
|
||||
* Move mailer initialization to a separate (overridable) method, so that subclasses may alter the various defaults #1727
|
||||
|
||||
* Look at content-location header (if available) to determine filename of attachments #1670
|
||||
|
||||
* ActionMailer::Base.deliver(email) had been accidentally removed, but was documented in the Rails book #1849
|
||||
|
||||
* Fix problem with sendmail delivery where headers should be delimited by \n characters instead of \r\n, which confuses some mail readers #1742 [Kent Sibilev]
|
||||
|
||||
|
||||
*1.0.1* (11 July, 2005)
|
||||
|
||||
* Bind to Action Pack 1.9.1
|
||||
|
||||
|
||||
*1.0.0* (6 July, 2005)
|
||||
|
||||
* Avoid adding nil header values #1392
|
||||
|
||||
* Better multipart support with implicit multipart/alternative and sorting of subparts [John Long]
|
||||
|
||||
* Allow for nested parts in multipart mails #1570 [Flurin Egger]
|
||||
|
||||
* Normalize line endings in outgoing mail bodies to "\n" #1536 [John Long]
|
||||
|
||||
* Allow template to be explicitly specified #1448 [tuxie@dekadance.se]
|
||||
|
||||
* Allow specific "multipart/xxx" content-type to be set on multipart messages #1412 [Flurin Egger]
|
||||
|
||||
* Unquoted @ characters in headers are now accepted in spite of RFC 822 #1206
|
||||
|
||||
* Helper support (borrowed from ActionPack)
|
||||
|
||||
* Silently ignore Errno::EINVAL errors when converting text.
|
||||
|
||||
* Don't cause an error when parsing an encoded attachment name #1340 [lon@speedymac.com]
|
||||
|
||||
* Nested multipart message parts are correctly processed in TMail::Mail#body
|
||||
|
||||
* BCC headers are removed when sending via SMTP #1402
|
||||
|
||||
* Added 'content_type' accessor, to allow content type to be set on a per-message basis. content_type defaults to "text/plain".
|
||||
|
||||
* Silently ignore Iconv::IllegalSequence errors when converting text #1341 [lon@speedymac.com]
|
||||
|
||||
* Support attachments and multipart messages.
|
||||
|
||||
* Added new accessors for the various mail properties.
|
||||
|
||||
* Fix to only perform the charset conversion if a 'from' and a 'to' charset are given (make no assumptions about what the charset was) #1276 [Jamis Buck]
|
||||
|
||||
* Fix attachments and content-type problems #1276 [Jamis Buck]
|
||||
|
||||
* Fixed the TMail#body method to look at the content-transfer-encoding header and unquote the body according to the rules it specifies #1265 [Jamis Buck]
|
||||
|
||||
* Added unquoting even if the iconv lib can't be loaded--in that case, only the charset conversion is skipped #1265 [Jamis Buck]
|
||||
|
||||
* Added automatic decoding of base64 bodies #1214 [Jamis Buck]
|
||||
|
||||
* Added that delivery errors are caught in a way so the mail is still returned whether the delivery was successful or not
|
||||
|
||||
* Fixed that email address like "Jamis Buck, M.D." <wild.medicine@example.net> would cause the quoter to generate emails resulting in "bad address" errors from the mail server #1220 [Jamis Buck]
|
||||
|
||||
|
||||
*0.9.1* (20th April, 2005)
|
||||
|
||||
* Depend on Action Pack 1.8.1
|
||||
|
||||
|
||||
*0.9.0* (19th April, 2005)
|
||||
|
||||
* Added that deliver_* will now return the email that was sent
|
||||
|
||||
* Added that quoting to UTF-8 only happens if the characters used are in that range #955 [Jamis Buck]
|
||||
|
||||
* Fixed quoting for all address headers, not just to #955 [Jamis Buck]
|
||||
|
||||
* Fixed unquoting of emails that doesn't have an explicit charset #1036 [wolfgang@stufenlos.net]
|
||||
|
||||
|
||||
*0.8.1* (27th March, 2005)
|
||||
|
||||
* Fixed that if charset was found that the end of a mime part declaration TMail would throw an error #919 [lon@speedymac.com]
|
||||
|
||||
* Fixed that TMail::Unquoter would fail to recognize quoting method if it was in lowercase #919 [lon@speedymac.com]
|
||||
|
||||
* Fixed that TMail::Encoder would fail when it attempts to parse e-mail addresses which are encoded using something other than the messages encoding method #919 [lon@speedymac.com]
|
||||
|
||||
* Added rescue for missing iconv library and throws warnings if subject/body is called on a TMail object without it instead
|
||||
|
||||
|
||||
*0.8.0* (22th March, 2005)
|
||||
|
||||
* Added framework support for processing incoming emails with an Action Mailer class. See example in README.
|
||||
|
||||
|
||||
*0.7.1* (7th March, 2005)
|
||||
|
||||
* Bind to newest Action Pack (1.5.1)
|
||||
|
||||
|
||||
*0.7.0* (24th February, 2005)
|
||||
|
||||
* Added support for charsets for both subject and body. The default charset is now UTF-8 #673 [Jamis Buck]. Examples:
|
||||
|
||||
def iso_charset(recipient)
|
||||
@recipients = recipient
|
||||
@subject = "testing iso charsets"
|
||||
@from = "system@loudthinking.com"
|
||||
@body = "Nothing to see here."
|
||||
@charset = "iso-8859-1"
|
||||
end
|
||||
|
||||
def unencoded_subject(recipient)
|
||||
@recipients = recipient
|
||||
@subject = "testing unencoded subject"
|
||||
@from = "system@loudthinking.com"
|
||||
@body = "Nothing to see here."
|
||||
@encode_subject = false
|
||||
@charset = "iso-8859-1"
|
||||
end
|
||||
|
||||
|
||||
*0.6.1* (January 18th, 2005)
|
||||
|
||||
* Fixed sending of emails to use Tmail#from not the deprecated Tmail#from_address
|
||||
|
||||
|
||||
*0.6* (January 17th, 2005)
|
||||
|
||||
* Fixed that bcc and cc should be settable through @bcc and @cc -- not just @headers["Bcc"] and @headers["Cc"] #453 [Eric Hodel]
|
||||
|
||||
* Fixed Action Mailer to be "warnings safe" so you can run with ruby -w and not get framework warnings #453 [Eric Hodel]
|
||||
|
||||
|
||||
*0.5*
|
||||
|
||||
* Added access to custom headers, like cc, bcc, and reply-to #268 [Andreas Schwarz]. Example:
|
||||
|
||||
def post_notification(recipients, post)
|
||||
@recipients = recipients
|
||||
@from = post.author.email_address_with_name
|
||||
@headers["bcc"] = SYSTEM_ADMINISTRATOR_EMAIL
|
||||
@headers["reply-to"] = "notifications@example.com"
|
||||
@subject = "[#{post.account.name} #{post.title}]"
|
||||
@body["post"] = post
|
||||
end
|
||||
|
||||
*0.4* (5)
|
||||
|
||||
* Consolidated the server configuration options into Base#server_settings= and expanded that with controls for authentication and more [Marten]
|
||||
NOTE: This is an API change that could potentially break your application if you used the old application form. Please do change!
|
||||
|
||||
* Added Base#deliveries as an accessor for an array of emails sent out through that ActionMailer class when using the :test delivery option. [Jeremy Kemper]
|
||||
|
||||
* Added Base#perform_deliveries= which can be set to false to turn off the actual delivery of the email through smtp or sendmail.
|
||||
This is especially useful for functional testing that shouldn't send off real emails, but still trigger delivery_* methods.
|
||||
|
||||
* Added option to specify delivery method with Base#delivery_method=. Default is :smtp and :sendmail is currently the only other option.
|
||||
Sendmail is assumed to be present at "/usr/sbin/sendmail" if that option is used. [Kent Sibilev]
|
||||
|
||||
* Dropped "include TMail" as it added to much baggage into the default namespace (like Version) [Chad Fowler]
|
||||
|
||||
|
||||
*0.3*
|
||||
|
||||
* First release
|
||||
@@ -1,26 +0,0 @@
|
||||
## Rails 3.1.8 (Aug 9, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.1.7 (Jul 26, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.1.6 (Jun 12, 2012)
|
||||
|
||||
* No changes.
|
||||
|
||||
## Rails 3.1.5 (May 31, 2012) ##
|
||||
|
||||
* Increase minimum version of mail.
|
||||
|
||||
## Rails 3.1.1 (October 7, 2011) ##
|
||||
|
||||
* No changes
|
||||
|
||||
|
||||
## Rails 3.1.0 (August 30, 2011) ##
|
||||
|
||||
* No changes
|
||||
|
||||
Please check [3-0-stable](https://github.com/rails/rails/blob/3-0-stable/actionmailer/CHANGELOG) for previous changes.
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2011 David Heinemeier Hansson
|
||||
Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -10,7 +10,7 @@ Mail gem. It provides a way to make emails using templates in the same
|
||||
way that Action Controller renders views using templates.
|
||||
|
||||
Additionally, an Action Mailer class can be used to process incoming email,
|
||||
such as allowing a blog to accept new posts from an email (which could even
|
||||
such as allowing a weblog to accept new posts from an email (which could even
|
||||
have been sent from a phone).
|
||||
|
||||
== Sending emails
|
||||
@@ -32,7 +32,7 @@ This can be as simple as:
|
||||
end
|
||||
|
||||
The body of the email is created by using an Action View template (regular
|
||||
ERB) that has the instance variables that are declared in the mailer action.
|
||||
ERb) that has the instance variables that are declared in the mailer action.
|
||||
|
||||
So the corresponding body template for the method above could look like this:
|
||||
|
||||
@@ -59,9 +59,7 @@ generated would look like this:
|
||||
|
||||
Mr. david@loudthinking.com
|
||||
|
||||
Thank you for signing up!
|
||||
|
||||
In previous version of Rails you would call <tt>create_method_name</tt> and
|
||||
In previous version of rails you would call <tt>create_method_name</tt> and
|
||||
<tt>deliver_method_name</tt>. Rails 3.0 has a much simpler interface, you
|
||||
simply call the method and optionally call +deliver+ on the return value.
|
||||
|
||||
@@ -74,25 +72,12 @@ Or you can just chain the methods together like:
|
||||
|
||||
Notifier.welcome.deliver # Creates the email and sends it immediately
|
||||
|
||||
== Setting defaults
|
||||
|
||||
It is possible to set default values that will be used in every method in your Action Mailer class. To implement this functionality, you just call the public class method <tt>default</tt> which you get for free from ActionMailer::Base. This method accepts a Hash as the parameter. You can use any of the headers e-mail messages has, like <tt>:from</tt> as the key. You can also pass in a string as the key, like "Content-Type", but Action Mailer does this out of the box for you, so you won't need to worry about that. Finally it is also possible to pass in a Proc that will get evaluated when it is needed.
|
||||
|
||||
Note that every value you set with this method will get over written if you use the same key in your mailer method.
|
||||
|
||||
Example:
|
||||
|
||||
class Authenticationmailer < ActionMailer::Base
|
||||
default :from => "awesome@application.com", :subject => Proc.new { "E-mail was generated at #{Time.now}" }
|
||||
.....
|
||||
end
|
||||
|
||||
== Receiving emails
|
||||
|
||||
To receive emails, you need to implement a public instance method called <tt>receive</tt> that takes an
|
||||
email object as its single parameter. The Action Mailer framework has a corresponding class method,
|
||||
To receive emails, you need to implement a public instance method called <tt>receive</tt> that takes a
|
||||
tmail object as its single parameter. The Action Mailer framework has a corresponding class method,
|
||||
which is also called <tt>receive</tt>, that accepts a raw, unprocessed email as a string, which it then turns
|
||||
into the email object and calls the receive instance method.
|
||||
into the tmail object and calls the receive instance method.
|
||||
|
||||
Example:
|
||||
|
||||
@@ -104,7 +89,7 @@ Example:
|
||||
)
|
||||
|
||||
if email.has_attachments?
|
||||
email.attachments.each do |attachment|
|
||||
for attachment in email.attachments
|
||||
page.attachments.create({
|
||||
:file => attachment, :description => email.subject
|
||||
})
|
||||
@@ -119,7 +104,7 @@ trivial case like this:
|
||||
rails runner 'Mailman.receive(STDIN.read)'
|
||||
|
||||
However, invoking Rails in the runner for each mail to be received is very resource intensive. A single
|
||||
instance of Rails should be run within a daemon, if it is going to be utilized to process more than just
|
||||
instance of Rails should be run within a daemon if it is going to be utilized to process more than just
|
||||
a limited number of email.
|
||||
|
||||
== Configuration
|
||||
@@ -143,7 +128,7 @@ The latest version of Action Mailer can be installed with Rubygems:
|
||||
|
||||
Source code can be downloaded as part of the Rails project on GitHub
|
||||
|
||||
* https://github.com/rails/rails/tree/master/actionmailer
|
||||
* http://github.com/rails/rails/tree/master/actionmailer/
|
||||
|
||||
|
||||
== License
|
||||
@@ -159,5 +144,4 @@ API documentation is at
|
||||
|
||||
Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
|
||||
|
||||
* https://github.com/rails/rails/issues
|
||||
|
||||
* https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets
|
||||
|
||||
8
actionmailer/Rakefile
Executable file → Normal file
8
actionmailer/Rakefile
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env rake
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/packagetask'
|
||||
require 'rubygems/package_task'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
desc "Default Task"
|
||||
task :default => [ :test ]
|
||||
@@ -17,14 +17,14 @@ namespace :test do
|
||||
task :isolated do
|
||||
ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME'))
|
||||
Dir.glob("test/**/*_test.rb").all? do |file|
|
||||
sh(ruby, '-Ilib:test', file)
|
||||
system(ruby, '-Ilib:test', file)
|
||||
end or raise "Failures"
|
||||
end
|
||||
end
|
||||
|
||||
spec = eval(File.read('actionmailer.gemspec'))
|
||||
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
end
|
||||
|
||||
|
||||
@@ -11,11 +11,14 @@ Gem::Specification.new do |s|
|
||||
s.author = 'David Heinemeier Hansson'
|
||||
s.email = 'david@loudthinking.com'
|
||||
s.homepage = 'http://www.rubyonrails.org'
|
||||
s.rubyforge_project = 'actionmailer'
|
||||
|
||||
s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
|
||||
s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
|
||||
s.require_path = 'lib'
|
||||
s.requirements << 'none'
|
||||
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('actionpack', version)
|
||||
s.add_dependency('mail', '~> 2.5')
|
||||
s.add_dependency('mail', '~> 2.2.9')
|
||||
end
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#--
|
||||
# Copyright (c) 2004-2011 David Heinemeier Hansson
|
||||
# Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
@@ -44,6 +44,7 @@ module ActionMailer
|
||||
autoload :Collector
|
||||
autoload :Base
|
||||
autoload :DeliveryMethods
|
||||
autoload :DeprecatedApi
|
||||
autoload :MailHelper
|
||||
autoload :OldApi
|
||||
autoload :TestCase
|
||||
|
||||
@@ -1,28 +1,26 @@
|
||||
module ActionMailer
|
||||
module AdvAttrAccessor #:nodoc:
|
||||
def adv_attr_accessor(name, deprecation=nil)
|
||||
ivar = "@#{name}"
|
||||
deprecation ||= "Please pass :#{name} as hash key to mail() instead"
|
||||
def adv_attr_accessor(*names)
|
||||
names.each do |name|
|
||||
ivar = "@#{name}"
|
||||
|
||||
class_eval <<-ACCESSORS, __FILE__, __LINE__ + 1
|
||||
def #{name}=(value)
|
||||
ActiveSupport::Deprecation.warn "#{name}= is deprecated. #{deprecation}"
|
||||
#{ivar} = value
|
||||
end
|
||||
|
||||
def #{name}(*args)
|
||||
raise ArgumentError, "expected 0 or 1 parameters" unless args.length <= 1
|
||||
if args.empty?
|
||||
ActiveSupport::Deprecation.warn "#{name}() is deprecated and will be removed in future versions."
|
||||
#{ivar} if instance_variable_names.include?(#{ivar.inspect})
|
||||
else
|
||||
ActiveSupport::Deprecation.warn "#{name}(value) is deprecated. #{deprecation}"
|
||||
#{ivar} = args.first
|
||||
class_eval <<-ACCESSORS, __FILE__, __LINE__ + 1
|
||||
def #{name}=(value)
|
||||
#{ivar} = value
|
||||
end
|
||||
end
|
||||
ACCESSORS
|
||||
|
||||
self.protected_instance_variables << ivar if self.respond_to?(:protected_instance_variables)
|
||||
def #{name}(*args)
|
||||
raise ArgumentError, "expected 0 or 1 parameters" unless args.length <= 1
|
||||
if args.empty?
|
||||
#{ivar} if instance_variable_names.include?(#{ivar.inspect})
|
||||
else
|
||||
#{ivar} = args.first
|
||||
end
|
||||
end
|
||||
ACCESSORS
|
||||
|
||||
self.protected_instance_variables << ivar if self.respond_to?(:protected_instance_variables)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,8 +4,6 @@ require 'action_mailer/collector'
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/proc'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'action_mailer/log_subscriber'
|
||||
|
||||
module ActionMailer #:nodoc:
|
||||
@@ -58,7 +56,7 @@ module ActionMailer #:nodoc:
|
||||
# will accept (any valid Email header including optional fields).
|
||||
#
|
||||
# The mail method, if not passed a block, will inspect your views and send all the views with
|
||||
# the same name as the method, so the above action would send the +welcome.text.erb+ view
|
||||
# the same name as the method, so the above action would send the +welcome.text.plain.erb+ view
|
||||
# file as well as the +welcome.text.html.erb+ view file in a +multipart/alternative+ email.
|
||||
#
|
||||
# If you want to explicitly render only certain templates, pass a block:
|
||||
@@ -89,7 +87,7 @@ module ActionMailer #:nodoc:
|
||||
#
|
||||
# To define a template to be used with a mailing, create an <tt>.erb</tt> file with the same
|
||||
# name as the method in your mailer model. For example, in the mailer defined above, the template at
|
||||
# <tt>app/views/notifier/welcome.text.erb</tt> would be used to generate the email.
|
||||
# <tt>app/views/notifier/signup_notification.text.plain.erb</tt> would be used to generate the email.
|
||||
#
|
||||
# Variables defined in the model are accessible as instance variables in the view.
|
||||
#
|
||||
@@ -101,12 +99,12 @@ module ActionMailer #:nodoc:
|
||||
# You can even use Action Pack helpers in these views. For example:
|
||||
#
|
||||
# You got a new note!
|
||||
# <%= truncate(@note.body, :length => 25) %>
|
||||
# <%= truncate(@note.body, 25) %>
|
||||
#
|
||||
# If you need to access the subject, from or the recipients in the view, you can do that through message object:
|
||||
#
|
||||
# You got a new note from <%= message.from %>!
|
||||
# <%= truncate(@note.body, :length => 25) %>
|
||||
# <%= truncate(@note.body, 25) %>
|
||||
#
|
||||
#
|
||||
# = Generating URLs
|
||||
@@ -123,9 +121,8 @@ module ActionMailer #:nodoc:
|
||||
#
|
||||
# <%= users_url(:host => "example.com") %>
|
||||
#
|
||||
# You should use the <tt>named_route_url</tt> style (which generates absolute URLs) and avoid using the
|
||||
# <tt>named_route_path</tt> style (which generates relative URLs), since clients reading the mail will
|
||||
# have no concept of a current URL from which to determine a relative path.
|
||||
# You want to avoid using the <tt>name_of_route_path</tt> form of named routes because it doesn't
|
||||
# make sense to generate relative URLs in email messages.
|
||||
#
|
||||
# It is also possible to set a default host that will be used in all mailers by setting the <tt>:host</tt>
|
||||
# option as a configuration option in <tt>config/application.rb</tt>:
|
||||
@@ -153,12 +150,12 @@ module ActionMailer #:nodoc:
|
||||
#
|
||||
# = Multipart Emails
|
||||
#
|
||||
# Multipart messages can also be used implicitly because Action Mailer will automatically detect and use
|
||||
# multipart templates, where each template is named after the name of the action, followed by the content
|
||||
# type. Each such detected template will be added as a separate part to the message.
|
||||
# Multipart messages can also be used implicitly because Action Mailer will automatically
|
||||
# detect and use multipart templates, where each template is named after the name of the action, followed
|
||||
# by the content type. Each such detected template will be added as separate part to the message.
|
||||
#
|
||||
# For example, if the following templates exist:
|
||||
# * signup_notification.text.erb
|
||||
# * signup_notification.text.plain.erb
|
||||
# * signup_notification.text.html.erb
|
||||
# * signup_notification.text.xml.builder
|
||||
# * signup_notification.text.yaml.erb
|
||||
@@ -183,7 +180,7 @@ module ActionMailer #:nodoc:
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Which will (if it had both a <tt>welcome.text.erb</tt> and <tt>welcome.text.html.erb</tt>
|
||||
# Which will (if it had both a <tt>welcome.text.plain.erb</tt> and <tt>welcome.text.html.erb</tt>
|
||||
# template in the view directory), send a complete <tt>multipart/mixed</tt> email with two parts,
|
||||
# the first part being a <tt>multipart/alternative</tt> with the text and HTML email parts inside,
|
||||
# and the second being a <tt>application/pdf</tt> with a Base64 encoded copy of the file.pdf book
|
||||
@@ -225,7 +222,7 @@ module ActionMailer #:nodoc:
|
||||
#
|
||||
# An interceptor object must implement the <tt>:delivering_email(message)</tt> method which will be
|
||||
# called before the email is sent, allowing you to make modifications to the email before it hits
|
||||
# the delivery agents. Your object should make any needed modifications directly to the passed
|
||||
# the delivery agents. Your object should make and needed modifications directly to the passed
|
||||
# in Mail::Message instance.
|
||||
#
|
||||
# = Default Hash
|
||||
@@ -237,8 +234,8 @@ module ActionMailer #:nodoc:
|
||||
# default :sender => 'system@example.com'
|
||||
# end
|
||||
#
|
||||
# You can pass in any header value that a <tt>Mail::Message</tt> accepts. Out of the box,
|
||||
# <tt>ActionMailer::Base</tt> sets the following:
|
||||
# You can pass in any header value that a <tt>Mail::Message</tt>, out of the box, <tt>ActionMailer::Base</tt>
|
||||
# sets the following:
|
||||
#
|
||||
# * <tt>:mime_version => "1.0"</tt>
|
||||
# * <tt>:charset => "UTF-8",</tt>
|
||||
@@ -249,7 +246,7 @@ module ActionMailer #:nodoc:
|
||||
# but Action Mailer translates them appropriately and sets the correct values.
|
||||
#
|
||||
# As you can pass in any header, you need to either quote the header as a string, or pass it in as
|
||||
# an underscored symbol, so the following will work:
|
||||
# an underscorised symbol, so the following will work:
|
||||
#
|
||||
# class Notifier < ActionMailer::Base
|
||||
# default 'Content-Transfer-Encoding' => '7bit',
|
||||
@@ -276,7 +273,7 @@ module ActionMailer #:nodoc:
|
||||
# = Configuration options
|
||||
#
|
||||
# These options are specified on the class level, like
|
||||
# <tt>ActionMailer::Base.raise_delivery_errors = true</tt>
|
||||
# <tt>ActionMailer::Base.template_root = "/my/templates"</tt>
|
||||
#
|
||||
# * <tt>default</tt> - You can pass this in at a class level as well as within the class itself as
|
||||
# per the above section.
|
||||
@@ -293,19 +290,13 @@ module ActionMailer #:nodoc:
|
||||
# * <tt>:password</tt> - If your mail server requires authentication, set the password in this setting.
|
||||
# * <tt>:authentication</tt> - If your mail server requires authentication, you need to specify the
|
||||
# authentication type here.
|
||||
# This is a symbol and one of <tt>:plain</tt> (will send the password in the clear), <tt>:login</tt> (will
|
||||
# send password Base64 encoded) or <tt>:cram_md5</tt> (combines a Challenge/Response mechanism to exchange
|
||||
# information and a cryptographic Message Digest 5 algorithm to hash important information)
|
||||
# This is a symbol and one of <tt>:plain</tt>, <tt>:login</tt>, <tt>:cram_md5</tt>.
|
||||
# * <tt>:enable_starttls_auto</tt> - When set to true, detects if STARTTLS is enabled in your SMTP server
|
||||
# and starts to use it.
|
||||
# * <tt>:openssl_verify_mode</tt> - When using TLS, you can set how OpenSSL checks the certificate. This is
|
||||
# really useful if you need to validate a self-signed and/or a wildcard certificate. You can use the name
|
||||
# of an OpenSSL verify constant ('none', 'peer', 'client_once','fail_if_no_peer_cert') or directly the
|
||||
# constant (OpenSSL::SSL::VERIFY_NONE, OpenSSL::SSL::VERIFY_PEER,...).
|
||||
#
|
||||
# * <tt>sendmail_settings</tt> - Allows you to override options for the <tt>:sendmail</tt> delivery method.
|
||||
# * <tt>:location</tt> - The location of the sendmail executable. Defaults to <tt>/usr/sbin/sendmail</tt>.
|
||||
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@address</tt>
|
||||
# * <tt>:arguments</tt> - The command line arguments. Defaults to <tt>-i -t</tt> with <tt>-f sender@addres</tt>
|
||||
# added automatically before the message is sent.
|
||||
#
|
||||
# * <tt>file_settings</tt> - Allows you to override options for the <tt>:file</tt> delivery method.
|
||||
@@ -350,10 +341,13 @@ module ActionMailer #:nodoc:
|
||||
include AbstractController::Translation
|
||||
include AbstractController::AssetPaths
|
||||
|
||||
self.protected_instance_variables = %w(@_action_has_layout)
|
||||
|
||||
helper ActionMailer::MailHelper
|
||||
|
||||
include ActionMailer::OldApi
|
||||
include ActionMailer::DeprecatedApi
|
||||
|
||||
delegate :register_observer, :to => Mail
|
||||
delegate :register_interceptor, :to => Mail
|
||||
|
||||
private_class_method :new #:nodoc:
|
||||
|
||||
@@ -366,31 +360,6 @@ module ActionMailer #:nodoc:
|
||||
}.freeze
|
||||
|
||||
class << self
|
||||
# Register one or more Observers which will be notified when mail is delivered.
|
||||
def register_observers(*observers)
|
||||
observers.flatten.compact.each { |observer| register_observer(observer) }
|
||||
end
|
||||
|
||||
# Register one or more Interceptors which will be called before mail is sent.
|
||||
def register_interceptors(*interceptors)
|
||||
interceptors.flatten.compact.each { |interceptor| register_interceptor(interceptor) }
|
||||
end
|
||||
|
||||
# Register an Observer which will be notified when mail is delivered.
|
||||
# Either a class or a string can be passed in as the Observer. If a string is passed in
|
||||
# it will be <tt>constantize</tt>d.
|
||||
def register_observer(observer)
|
||||
delivery_observer = (observer.is_a?(String) ? observer.constantize : observer)
|
||||
Mail.register_observer(delivery_observer)
|
||||
end
|
||||
|
||||
# Register an Interceptor which will be called before mail is sent.
|
||||
# Either a class or a string can be passed in as the Interceptor. If a string is passed in
|
||||
# it will be <tt>constantize</tt>d.
|
||||
def register_interceptor(interceptor)
|
||||
delivery_interceptor = (interceptor.is_a?(String) ? interceptor.constantize : interceptor)
|
||||
Mail.register_interceptor(delivery_interceptor)
|
||||
end
|
||||
|
||||
def mailer_name
|
||||
@mailer_name ||= name.underscore
|
||||
@@ -433,14 +402,14 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
def respond_to?(method, include_private = false) #:nodoc:
|
||||
def respond_to?(method, *args) #:nodoc:
|
||||
super || action_methods.include?(method.to_s)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def set_payload_for_mail(payload, mail) #:nodoc:
|
||||
payload[:mailer] = name
|
||||
payload[:mailer] = self.name
|
||||
payload[:message_id] = mail.message_id
|
||||
payload[:subject] = mail.subject
|
||||
payload[:to] = mail.to
|
||||
@@ -452,8 +421,11 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
|
||||
def method_missing(method, *args) #:nodoc:
|
||||
return super unless respond_to?(method)
|
||||
new(method, *args).message
|
||||
if action_methods.include?(method.to_s)
|
||||
new(method, *args).message
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -474,8 +446,31 @@ module ActionMailer #:nodoc:
|
||||
super
|
||||
end
|
||||
|
||||
def mailer_name
|
||||
self.class.mailer_name
|
||||
class DeprecatedHeaderProxy < ActiveSupport::BasicObject
|
||||
def initialize(message)
|
||||
@message = message
|
||||
end
|
||||
|
||||
def []=(key, value)
|
||||
unless value.is_a?(::String)
|
||||
::ActiveSupport::Deprecation.warn("Using a non-String object for a header's value is deprecated. " \
|
||||
"You specified #{value.inspect} (a #{value.class}) for #{key}", caller)
|
||||
|
||||
value = value.to_s
|
||||
end
|
||||
|
||||
@message[key] = value
|
||||
end
|
||||
|
||||
def headers(hash = {})
|
||||
hash.each_pair do |k,v|
|
||||
self[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
def method_missing(meth, *args, &block)
|
||||
@message.send(meth, *args, &block)
|
||||
end
|
||||
end
|
||||
|
||||
# Allows you to pass random and unusual headers to the new +Mail::Message+ object
|
||||
@@ -494,9 +489,9 @@ module ActionMailer #:nodoc:
|
||||
# X-Special-Domain-Specific-Header: SecretValue
|
||||
def headers(args=nil)
|
||||
if args
|
||||
@_message.headers(args)
|
||||
DeprecatedHeaderProxy.new(@_message).headers(args)
|
||||
else
|
||||
@_message
|
||||
DeprecatedHeaderProxy.new(@_message)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -686,9 +681,6 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# Translates the +subject+ using Rails I18n class under <tt>[:actionmailer, mailer_scope, action_name]</tt> scope.
|
||||
# If it does not find a translation for the +subject+ under the specified scope it will default to a
|
||||
# humanized version of the <tt>action_name</tt>.
|
||||
def default_i18n_subject #:nodoc:
|
||||
mailer_scope = self.class.mailer_name.gsub('/', '.')
|
||||
I18n.t(:subject, :scope => [mailer_scope, action_name], :default => action_name.humanize)
|
||||
@@ -725,8 +717,15 @@ module ActionMailer #:nodoc:
|
||||
end
|
||||
|
||||
def each_template(paths, name, &block) #:nodoc:
|
||||
templates = lookup_context.find_all(name, Array.wrap(paths))
|
||||
templates.uniq_by { |t| t.formats }.each(&block)
|
||||
Array.wrap(paths).each do |path|
|
||||
templates = lookup_context.find_all(name, path)
|
||||
templates = templates.uniq_by { |t| t.formats }
|
||||
|
||||
unless templates.empty?
|
||||
templates.each(&block)
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_parts_from_responses(m, responses) #:nodoc:
|
||||
@@ -748,6 +747,28 @@ module ActionMailer #:nodoc:
|
||||
container.add_part(part)
|
||||
end
|
||||
|
||||
module DeprecatedUrlOptions
|
||||
def default_url_options
|
||||
deprecated_url_options
|
||||
end
|
||||
|
||||
def default_url_options=(val)
|
||||
deprecated_url_options
|
||||
end
|
||||
|
||||
def deprecated_url_options
|
||||
raise "You can no longer call ActionMailer::Base.default_url_options " \
|
||||
"directly. You need to set config.action_mailer.default_url_options. " \
|
||||
"If you are using ActionMailer standalone, you need to include the " \
|
||||
"routing url_helpers directly."
|
||||
end
|
||||
end
|
||||
|
||||
# This module will complain if the user tries to set default_url_options
|
||||
# directly instead of through the config object. In Action Mailer's Railtie,
|
||||
# we include the router's url_helpers, which will override this module.
|
||||
extend DeprecatedUrlOptions
|
||||
|
||||
ActiveSupport.run_load_hooks(:action_mailer, self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'tmpdir'
|
||||
|
||||
module ActionMailer
|
||||
# This module handles everything related to mail delivery, from registering new
|
||||
# This modules handles everything related to the delivery, from registering new
|
||||
# delivery methods to configuring the mail object to be sent.
|
||||
module DeliveryMethods
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
147
actionmailer/lib/action_mailer/deprecated_api.rb
Normal file
147
actionmailer/lib/action_mailer/deprecated_api.rb
Normal file
@@ -0,0 +1,147 @@
|
||||
require 'active_support/core_ext/object/try'
|
||||
|
||||
module ActionMailer
|
||||
# This is the API which is deprecated and is going to be removed on Rails 3.1 release.
|
||||
# Part of the old API will be deprecated after 3.1, for a smoother deprecation process.
|
||||
# Check those in OldApi instead.
|
||||
module DeprecatedApi #:nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
[:charset, :content_type, :mime_version, :implicit_parts_order].each do |method|
|
||||
class_eval <<-FILE, __FILE__, __LINE__ + 1
|
||||
def self.default_#{method}
|
||||
@@default_#{method}
|
||||
end
|
||||
|
||||
def self.default_#{method}=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionMailer::Base.default_#{method}=value is deprecated, " <<
|
||||
"use default :#{method} => value instead"
|
||||
@@default_#{method} = value
|
||||
end
|
||||
|
||||
@@default_#{method} = nil
|
||||
FILE
|
||||
end
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Deliver the given mail object directly. This can be used to deliver
|
||||
# a preconstructed mail object, like:
|
||||
#
|
||||
# email = MyMailer.create_some_mail(parameters)
|
||||
# email.set_some_obscure_header "frobnicate"
|
||||
# MyMailer.deliver(email)
|
||||
def deliver(mail, show_warning=true)
|
||||
if show_warning
|
||||
ActiveSupport::Deprecation.warn "#{self}.deliver is deprecated, call " <<
|
||||
"deliver in the mailer instance instead", caller[0,2]
|
||||
end
|
||||
|
||||
raise "no mail object available for delivery!" unless mail
|
||||
wrap_delivery_behavior(mail)
|
||||
mail.deliver
|
||||
mail
|
||||
end
|
||||
|
||||
def template_root
|
||||
self.view_paths && self.view_paths.first
|
||||
end
|
||||
|
||||
def template_root=(root)
|
||||
ActiveSupport::Deprecation.warn "template_root= is deprecated, use prepend_view_path instead", caller[0,2]
|
||||
self.view_paths = ActionView::Base.process_view_paths(root)
|
||||
end
|
||||
|
||||
def respond_to?(method_symbol, include_private = false)
|
||||
matches_dynamic_method?(method_symbol) || super
|
||||
end
|
||||
|
||||
def method_missing(method_symbol, *parameters)
|
||||
if match = matches_dynamic_method?(method_symbol)
|
||||
case match[1]
|
||||
when 'create'
|
||||
ActiveSupport::Deprecation.warn "#{self}.create_#{match[2]} is deprecated, " <<
|
||||
"use #{self}.#{match[2]} instead", caller[0,2]
|
||||
new(match[2], *parameters).message
|
||||
when 'deliver'
|
||||
ActiveSupport::Deprecation.warn "#{self}.deliver_#{match[2]} is deprecated, " <<
|
||||
"use #{self}.#{match[2]}.deliver instead", caller[0,2]
|
||||
new(match[2], *parameters).message.deliver
|
||||
else super
|
||||
end
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def matches_dynamic_method?(method_name)
|
||||
method_name = method_name.to_s
|
||||
/^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name)
|
||||
end
|
||||
end
|
||||
|
||||
# Delivers a Mail object. By default, it delivers the cached mail
|
||||
# object (from the <tt>create!</tt> method). If no cached mail object exists, and
|
||||
# no alternate has been given as the parameter, this will fail.
|
||||
def deliver!(mail = @_message)
|
||||
ActiveSupport::Deprecation.warn "Calling deliver in the AM::Base object is deprecated, " <<
|
||||
"please call deliver in the Mail instance", caller[0,2]
|
||||
self.class.deliver(mail, false)
|
||||
end
|
||||
alias :deliver :deliver!
|
||||
|
||||
def render(*args)
|
||||
options = args.last.is_a?(Hash) ? args.last : {}
|
||||
|
||||
if file = options[:file] and !file.index("/")
|
||||
ActiveSupport::Deprecation.warn("render :file is deprecated except for absolute paths. " \
|
||||
"Please use render :template instead")
|
||||
options[:prefix] = _prefix
|
||||
end
|
||||
|
||||
if options[:body].is_a?(Hash)
|
||||
ActiveSupport::Deprecation.warn(':body in render deprecated. Please use instance ' <<
|
||||
'variables as assigns instead', caller[0,1])
|
||||
|
||||
options[:body].each { |k,v| instance_variable_set(:"@#{k}", v) }
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
# Render a message but does not set it as mail body. Useful for rendering
|
||||
# data for part and attachments.
|
||||
#
|
||||
# Examples:
|
||||
#
|
||||
# render_message "special_message"
|
||||
# render_message :template => "special_message"
|
||||
# render_message :inline => "<%= 'Hi!' %>"
|
||||
#
|
||||
def render_message(*args)
|
||||
ActiveSupport::Deprecation.warn "render_message is deprecated, use render instead", caller[0,2]
|
||||
render(*args)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def initialize_defaults(*)
|
||||
@charset ||= self.class.default_charset.try(:dup)
|
||||
@content_type ||= self.class.default_content_type.try(:dup)
|
||||
@implicit_parts_order ||= self.class.default_implicit_parts_order.try(:dup)
|
||||
@mime_version ||= self.class.default_mime_version.try(:dup)
|
||||
super
|
||||
end
|
||||
|
||||
def create_parts
|
||||
if @body.is_a?(Hash) && !@body.empty?
|
||||
ActiveSupport::Deprecation.warn "Giving a hash to body is deprecated, please use instance variables instead", caller[0,2]
|
||||
@body.each { |k, v| instance_variable_set(:"@#{k}", v) }
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
@@ -3,8 +3,17 @@ module ActionMailer
|
||||
# Uses Text::Format to take the text and format it, indented two spaces for
|
||||
# each line, and wrapped at 72 columns.
|
||||
def block_format(text)
|
||||
begin
|
||||
require 'text/format'
|
||||
rescue LoadError => e
|
||||
$stderr.puts "You don't have text-format installed in your application. Please add it to your Gemfile and run bundle install"
|
||||
raise e
|
||||
end unless defined?(Text::Format)
|
||||
|
||||
formatted = text.split(/\n\r\n/).collect { |paragraph|
|
||||
format_paragraph(paragraph)
|
||||
Text::Format.new(
|
||||
:columns => 72, :first_indent => 2, :body_indent => 2, :text => paragraph
|
||||
).format
|
||||
}.join("\n")
|
||||
|
||||
# Make list points stand on their own line
|
||||
@@ -28,29 +37,5 @@ module ActionMailer
|
||||
def attachments
|
||||
@_message.attachments
|
||||
end
|
||||
|
||||
# Returns +text+ wrapped at +len+ columns and indented +indent+ spaces.
|
||||
#
|
||||
# === Examples
|
||||
#
|
||||
# my_text = "Here is a sample text with more than 40 characters"
|
||||
#
|
||||
# format_paragraph(my_text, 25, 4)
|
||||
# # => " Here is a sample text with\n more than 40 characters"
|
||||
def format_paragraph(text, len = 72, indent = 2)
|
||||
sentences = [[]]
|
||||
|
||||
text.split.each do |word|
|
||||
if (sentences.last + [word]).join(' ').length > len
|
||||
sentences << [word]
|
||||
else
|
||||
sentences.last << word
|
||||
end
|
||||
end
|
||||
|
||||
sentences.map { |sentence|
|
||||
"#{" " * indent}#{sentence.join(' ')}"
|
||||
}.join "\n"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -8,7 +8,9 @@ module ActionMailer
|
||||
|
||||
included do
|
||||
extend ActionMailer::AdvAttrAccessor
|
||||
self.protected_instance_variables.concat %w(@parts @mail_was_called @headers)
|
||||
|
||||
@@protected_instance_variables = %w(@parts)
|
||||
cattr_reader :protected_instance_variables
|
||||
|
||||
# Specify the BCC addresses for the message
|
||||
adv_attr_accessor :bcc
|
||||
@@ -40,11 +42,11 @@ module ActionMailer
|
||||
|
||||
# The recipient addresses for the message, either as a string (for a single
|
||||
# address) or an array (for multiple addresses).
|
||||
adv_attr_accessor :recipients, "Please pass :to as hash key to mail() instead"
|
||||
adv_attr_accessor :recipients
|
||||
|
||||
# The date on which the message was sent. If not set (the default), the
|
||||
# header will be set by the delivery agent.
|
||||
adv_attr_accessor :sent_on, "Please pass :date as hash key to mail() instead"
|
||||
adv_attr_accessor :sent_on
|
||||
|
||||
# Specify the subject of the message.
|
||||
adv_attr_accessor :subject
|
||||
@@ -52,12 +54,20 @@ module ActionMailer
|
||||
# Specify the template name to use for current message. This is the "base"
|
||||
# template name, without the extension or directory, and may be used to
|
||||
# have multiple mailer methods share the same template.
|
||||
adv_attr_accessor :template, "Please pass :template_name or :template_path as hash key to mail() instead"
|
||||
adv_attr_accessor :template
|
||||
|
||||
# Override the mailer name, which defaults to an inflected version of the
|
||||
# mailer's class name. If you want to use a template in a non-standard
|
||||
# location, you can use this to specify that location.
|
||||
adv_attr_accessor :mailer_name
|
||||
|
||||
# Define the body of the message. This is either a Hash (in which case it
|
||||
# specifies the variables to pass to the template when it is rendered),
|
||||
# or a string, in which case it specifies the actual text of the message.
|
||||
adv_attr_accessor :body
|
||||
|
||||
# Alias controller_path to mailer_name so render :partial in views work.
|
||||
alias :controller_path :mailer_name
|
||||
end
|
||||
|
||||
def process(method_name, *args)
|
||||
@@ -74,8 +84,6 @@ module ActionMailer
|
||||
# part itself is yielded to the block so that other properties (charset,
|
||||
# body, headers, etc.) can be set on it.
|
||||
def part(params)
|
||||
ActiveSupport::Deprecation.warn "part() is deprecated and will be removed in future versions. " <<
|
||||
"Please pass a block to mail() instead."
|
||||
params = {:content_type => params} if String === params
|
||||
|
||||
if custom_headers = params.delete(:headers)
|
||||
@@ -91,8 +99,6 @@ module ActionMailer
|
||||
# Add an attachment to a multipart message. This is simply a part with the
|
||||
# content-disposition set to "attachment".
|
||||
def attachment(params, &block)
|
||||
ActiveSupport::Deprecation.warn "attachment() is deprecated and will be removed in future versions. " <<
|
||||
"Please use the attachments[] API instead."
|
||||
params = { :content_type => params } if String === params
|
||||
|
||||
params[:content] ||= params.delete(:data) || params.delete(:body)
|
||||
@@ -142,11 +148,11 @@ module ActionMailer
|
||||
def create_mail
|
||||
m = @_message
|
||||
|
||||
set_fields!({:subject => @subject, :to => @recipients, :from => @from,
|
||||
:bcc => @bcc, :cc => @cc, :reply_to => @reply_to}, @charset)
|
||||
set_fields!({:subject => subject, :to => recipients, :from => from,
|
||||
:bcc => bcc, :cc => cc, :reply_to => reply_to}, charset)
|
||||
|
||||
m.mime_version = @mime_version if @mime_version
|
||||
m.date = @sent_on.to_time rescue @sent_on if @sent_on
|
||||
m.mime_version = mime_version unless mime_version.nil?
|
||||
m.date = sent_on.to_time rescue sent_on if sent_on
|
||||
|
||||
@headers.each { |k, v| m[k] = v }
|
||||
|
||||
@@ -185,8 +191,6 @@ module ActionMailer
|
||||
@implicit_parts_order ||= self.class.default[:parts_order].try(:dup)
|
||||
@mime_version ||= self.class.default[:mime_version].try(:dup)
|
||||
|
||||
@cc, @bcc, @reply_to, @subject, @from, @recipients = nil, nil, nil, nil, nil, nil
|
||||
|
||||
@mailer_name ||= self.class.mailer_name.dup
|
||||
@template ||= method_name
|
||||
@mail_was_called = false
|
||||
@@ -201,7 +205,7 @@ module ActionMailer
|
||||
if String === @body
|
||||
@parts.unshift create_inline_part(@body)
|
||||
elsif @parts.empty? || @parts.all? { |p| p.content_disposition =~ /^attachment/ }
|
||||
lookup_context.find_all(@template, [@mailer_name]).each do |template|
|
||||
lookup_context.find_all(@template, @mailer_name).each do |template|
|
||||
self.formats = template.formats
|
||||
@parts << create_inline_part(render(:template => template), template.mime_type)
|
||||
end
|
||||
@@ -212,7 +216,7 @@ module ActionMailer
|
||||
|
||||
# If this is a multipart e-mail add the mime_version if it is not
|
||||
# already set.
|
||||
@mime_version ||= "1.0" unless @parts.empty?
|
||||
@mime_version ||= "1.0" if !@parts.empty?
|
||||
end
|
||||
end
|
||||
|
||||
@@ -242,13 +246,13 @@ module ActionMailer
|
||||
ct.to_s.split("/")
|
||||
end
|
||||
|
||||
def parse_content_type
|
||||
def parse_content_type(defaults=nil)
|
||||
if @content_type.blank?
|
||||
[ nil, {} ]
|
||||
else
|
||||
ctype, *attrs = @content_type.split(/;\s*/)
|
||||
attrs = Hash[attrs.map { |attr| attr.split(/=/, 2) }]
|
||||
[ctype, {"charset" => @charset}.merge!(attrs)]
|
||||
attrs = attrs.inject({}) { |h,s| k,v = s.split(/\=/, 2); h[k] = v; h }
|
||||
[ctype, {"charset" => @charset}.merge(attrs)]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
require "action_mailer"
|
||||
require "rails"
|
||||
require "abstract_controller/railties/routes_helpers"
|
||||
|
||||
module ActionMailer
|
||||
class Railtie < Rails::Railtie
|
||||
@@ -14,30 +13,14 @@ module ActionMailer
|
||||
paths = app.config.paths
|
||||
options = app.config.action_mailer
|
||||
|
||||
options.assets_dir ||= paths["public"].first
|
||||
options.javascripts_dir ||= paths["public/javascripts"].first
|
||||
options.stylesheets_dir ||= paths["public/stylesheets"].first
|
||||
|
||||
# make sure readers methods get compiled
|
||||
options.asset_path ||= app.config.asset_path
|
||||
options.asset_host ||= app.config.asset_host
|
||||
options.assets_dir ||= paths.public.to_a.first
|
||||
options.javascripts_dir ||= paths.public.javascripts.to_a.first
|
||||
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
|
||||
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
include AbstractController::UrlFor
|
||||
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
||||
include app.routes.mounted_helpers
|
||||
|
||||
register_interceptors(options.delete(:interceptors))
|
||||
register_observers(options.delete(:observers))
|
||||
|
||||
include app.routes.url_helpers
|
||||
options.each { |k,v| send("#{k}=", v) }
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_mailer.compile_config_methods" do
|
||||
ActiveSupport.on_load(:action_mailer) do
|
||||
config.compile_methods! if config.respond_to?(:compile_methods!)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module ActionMailer
|
||||
class NonInferrableMailerError < ::StandardError
|
||||
def initialize(name)
|
||||
@@ -17,11 +15,11 @@ module ActionMailer
|
||||
|
||||
module ClassMethods
|
||||
def tests(mailer)
|
||||
self._mailer_class = mailer
|
||||
write_inheritable_attribute(:mailer_class, mailer)
|
||||
end
|
||||
|
||||
def mailer_class
|
||||
if mailer = self._mailer_class
|
||||
if mailer = read_inheritable_attribute(:mailer_class)
|
||||
mailer
|
||||
else
|
||||
tests determine_default_mailer(name)
|
||||
@@ -67,7 +65,6 @@ module ActionMailer
|
||||
end
|
||||
|
||||
included do
|
||||
class_attribute :_mailer_class
|
||||
setup :initialize_test_deliveries
|
||||
setup :set_expected_mail
|
||||
end
|
||||
|
||||
@@ -2,18 +2,16 @@ module Mail
|
||||
class Message
|
||||
|
||||
def set_content_type(*args)
|
||||
message = 'Message#set_content_type is deprecated, please just call ' <<
|
||||
'Message#content_type with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
ActiveSupport::Deprecation.warn('Message#set_content_type is deprecated, please just call ' <<
|
||||
'Message#content_type with the same arguments', caller[0,2])
|
||||
content_type(*args)
|
||||
end
|
||||
|
||||
alias :old_transfer_encoding :transfer_encoding
|
||||
def transfer_encoding(value = nil)
|
||||
if value
|
||||
message = 'Message#transfer_encoding is deprecated, ' <<
|
||||
'please call Message#content_transfer_encoding with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
ActiveSupport::Deprecation.warn('Message#transfer_encoding is deprecated, please call ' <<
|
||||
'Message#content_transfer_encoding with the same arguments', caller[0,2])
|
||||
content_transfer_encoding(value)
|
||||
else
|
||||
old_transfer_encoding
|
||||
@@ -21,17 +19,16 @@ module Mail
|
||||
end
|
||||
|
||||
def transfer_encoding=(value)
|
||||
message = 'Message#transfer_encoding= is deprecated, ' <<
|
||||
'please call Message#content_transfer_encoding= with the same arguments'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
ActiveSupport::Deprecation.warn('Message#transfer_encoding= is deprecated, please call ' <<
|
||||
'Message#content_transfer_encoding= with the same arguments', caller[0,2])
|
||||
self.content_transfer_encoding = value
|
||||
end
|
||||
|
||||
def original_filename
|
||||
message = 'Message#original_filename is deprecated, please call Message#filename'
|
||||
ActiveSupport::Deprecation.warn(message, caller[0,2])
|
||||
ActiveSupport::Deprecation.warn('Message#original_filename is deprecated, ' <<
|
||||
'please call Message#filename', caller[0,2])
|
||||
filename
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,10 +1,9 @@
|
||||
module ActionMailer
|
||||
module VERSION #:nodoc:
|
||||
MAJOR = 3
|
||||
MINOR = 1
|
||||
TINY = 8
|
||||
PRE = nil
|
||||
MINOR = 0
|
||||
TINY = 3
|
||||
|
||||
STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
|
||||
STRING = [MAJOR, MINOR, TINY].join('.')
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
Description:
|
||||
============
|
||||
Stubs out a new mailer and its views. Pass the mailer name, either
|
||||
CamelCased or under_scored, and an optional list of emails as arguments.
|
||||
|
||||
@@ -7,12 +6,10 @@ Description:
|
||||
engine and test framework generators.
|
||||
|
||||
Example:
|
||||
========
|
||||
rails generate mailer Notifications signup forgot_password invoice
|
||||
`rails generate mailer Notifications signup forgot_password invoice`
|
||||
|
||||
creates a Notifications mailer class, views, test, and fixtures:
|
||||
Mailer: app/mailers/notifications.rb
|
||||
Views: app/views/notifications/signup.erb [...]
|
||||
Test: test/functional/notifications_test.rb
|
||||
Fixtures: test/fixtures/notifications/signup [...]
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
<% module_namespacing do -%>
|
||||
class <%= class_name %> < ActionMailer::Base
|
||||
default <%= key_value :from, '"from@example.com"' %>
|
||||
<% actions.each do |action| -%>
|
||||
default :from => "from@example.com"
|
||||
<% for action in actions -%>
|
||||
|
||||
# Subject can be set in your I18n file at config/locales/en.yml
|
||||
# with the following lookup:
|
||||
@@ -11,8 +10,7 @@ class <%= class_name %> < ActionMailer::Base
|
||||
def <%= action %>
|
||||
@greeting = "Hi"
|
||||
|
||||
mail <%= key_value :to, '"to@example.org"' %>
|
||||
mail :to => "to@example.org"
|
||||
end
|
||||
<% end -%>
|
||||
end
|
||||
<% end -%>
|
||||
|
||||
@@ -23,6 +23,12 @@ if "ruby".encoding_aware?
|
||||
end
|
||||
end
|
||||
|
||||
silence_warnings do
|
||||
# These external dependencies have warnings :/
|
||||
require 'text/format'
|
||||
require 'mail'
|
||||
end
|
||||
|
||||
lib = File.expand_path("#{File.dirname(__FILE__)}/../lib")
|
||||
$:.unshift(lib) unless $:.include?('lib') || $:.include?(lib)
|
||||
|
||||
@@ -30,11 +36,6 @@ require 'test/unit'
|
||||
require 'action_mailer'
|
||||
require 'action_mailer/test_case'
|
||||
|
||||
silence_warnings do
|
||||
# These external dependencies have warnings :/
|
||||
require 'mail'
|
||||
end
|
||||
|
||||
# Show backtraces for deprecated behavior for quicker cleanup.
|
||||
ActiveSupport::Deprecation.debug = true
|
||||
|
||||
@@ -77,5 +78,3 @@ end
|
||||
def restore_delivery_method
|
||||
ActionMailer::Base.delivery_method = @old_delivery_method
|
||||
end
|
||||
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
|
||||
@@ -7,6 +7,9 @@ require 'mailers/proc_mailer'
|
||||
require 'mailers/asset_mailer'
|
||||
|
||||
class BaseTest < ActiveSupport::TestCase
|
||||
# TODO Add some tests for implicity layout render and url helpers
|
||||
# so we can get rid of old base tests altogether with old base.
|
||||
|
||||
def teardown
|
||||
ActionMailer::Base.asset_host = nil
|
||||
ActionMailer::Base.assets_dir = nil
|
||||
@@ -32,21 +35,21 @@ class BaseTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
test "mail() with bcc, cc, content_type, charset, mime_version, reply_to and date" do
|
||||
time = Time.now.beginning_of_day.to_datetime
|
||||
@time = Time.now.beginning_of_day.to_datetime
|
||||
email = BaseMailer.welcome(:bcc => 'bcc@test.lindsaar.net',
|
||||
:cc => 'cc@test.lindsaar.net',
|
||||
:content_type => 'multipart/mixed',
|
||||
:charset => 'iso-8559-1',
|
||||
:mime_version => '2.0',
|
||||
:reply_to => 'reply-to@test.lindsaar.net',
|
||||
:date => time)
|
||||
:date => @time)
|
||||
assert_equal(['bcc@test.lindsaar.net'], email.bcc)
|
||||
assert_equal(['cc@test.lindsaar.net'], email.cc)
|
||||
assert_equal('multipart/mixed; charset=iso-8559-1', email.content_type)
|
||||
assert_equal('iso-8559-1', email.charset)
|
||||
assert_equal('2.0', email.mime_version)
|
||||
assert_equal(['reply-to@test.lindsaar.net'], email.reply_to)
|
||||
assert_equal(time, email.date)
|
||||
assert_equal(@time, email.date)
|
||||
end
|
||||
|
||||
test "mail() renders the template using the method being processed" do
|
||||
@@ -73,6 +76,11 @@ class BaseTest < ActiveSupport::TestCase
|
||||
assert_equal("Not SPAM", email['X-SPAM'].decoded)
|
||||
end
|
||||
|
||||
test "deprecated non-String custom headers" do
|
||||
email = assert_deprecated { BaseMailer.welcome_with_fixnum_header }
|
||||
assert_equal("2", email['X-SPAM-COUNT'].decoded)
|
||||
end
|
||||
|
||||
test "can pass random headers in as a hash to mail" do
|
||||
hash = {'X-Special-Domain-Specific-Header' => "SecretValue",
|
||||
'In-Reply-To' => '1234@mikel.me.com' }
|
||||
@@ -153,8 +161,8 @@ class BaseTest < ActiveSupport::TestCase
|
||||
assert_equal(2, email.parts.length)
|
||||
assert_equal("multipart/related", email.mime_type)
|
||||
assert_equal("multipart/alternative", email.parts[0].mime_type)
|
||||
assert_equal("text/plain", email.parts[0].parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[0].parts[1].mime_type)
|
||||
assert_equal("text/plain", email.parts[0].parts[0].mime_type)
|
||||
assert_equal("text/html", email.parts[0].parts[1].mime_type)
|
||||
assert_equal("logo.png", email.parts[1].filename)
|
||||
end
|
||||
|
||||
@@ -431,6 +439,13 @@ class BaseTest < ActiveSupport::TestCase
|
||||
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
|
||||
end
|
||||
|
||||
test "render :file uses render :template semantics and is deprecated" do
|
||||
mail = nil
|
||||
assert_deprecated { mail = BaseMailer.implicit_different_template_with_file('implicit_multipart').deliver }
|
||||
assert_equal("HTML Implicit Multipart", mail.html_part.body.decoded)
|
||||
assert_equal("TEXT Implicit Multipart", mail.text_part.body.decoded)
|
||||
end
|
||||
|
||||
test "you can specify a different template for explicit render" do
|
||||
mail = BaseMailer.explicit_different_template('explicit_multipart_templates').deliver
|
||||
assert_equal("HTML Explicit Multipart Templates", mail.html_part.body.decoded)
|
||||
@@ -478,11 +493,6 @@ class BaseTest < ActiveSupport::TestCase
|
||||
end
|
||||
end
|
||||
|
||||
class MySecondObserver
|
||||
def self.delivered_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
test "you can register an observer to the mail object that gets informed on email delivery" do
|
||||
ActionMailer::Base.register_observer(MyObserver)
|
||||
mail = BaseMailer.welcome
|
||||
@@ -490,31 +500,11 @@ class BaseTest < ActiveSupport::TestCase
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register an observer using its stringified name to the mail object that gets informed on email delivery" do
|
||||
ActionMailer::Base.register_observer("BaseTest::MyObserver")
|
||||
mail = BaseMailer.welcome
|
||||
MyObserver.expects(:delivered_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register multiple observers to the mail object that both get informed on email delivery" do
|
||||
ActionMailer::Base.register_observers("BaseTest::MyObserver", MySecondObserver)
|
||||
mail = BaseMailer.welcome
|
||||
MyObserver.expects(:delivered_email).with(mail)
|
||||
MySecondObserver.expects(:delivered_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
class MyInterceptor
|
||||
def self.delivering_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
class MySecondInterceptor
|
||||
def self.delivering_email(mail)
|
||||
end
|
||||
end
|
||||
|
||||
test "you can register an interceptor to the mail object that gets passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptor(MyInterceptor)
|
||||
mail = BaseMailer.welcome
|
||||
@@ -522,21 +512,6 @@ class BaseTest < ActiveSupport::TestCase
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register an interceptor using its stringified name to the mail object that gets passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptor("BaseTest::MyInterceptor")
|
||||
mail = BaseMailer.welcome
|
||||
MyInterceptor.expects(:delivering_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "you can register multiple interceptors to the mail object that both get passed the mail object before delivery" do
|
||||
ActionMailer::Base.register_interceptors("BaseTest::MyInterceptor", MySecondInterceptor)
|
||||
mail = BaseMailer.welcome
|
||||
MyInterceptor.expects(:delivering_email).with(mail)
|
||||
MySecondInterceptor.expects(:delivering_email).with(mail)
|
||||
mail.deliver
|
||||
end
|
||||
|
||||
test "being able to put proc's into the defaults hash and they get evaluated on mail sending" do
|
||||
mail1 = ProcMailer.welcome
|
||||
yesterday = 1.day.ago
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
Hello there,
|
||||
|
||||
Mr. <%= @recipient %>. Be greeted, new member!
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
require 'abstract_unit'
|
||||
require 'action_controller'
|
||||
|
||||
class I18nTestMailer < ActionMailer::Base
|
||||
configure do |c|
|
||||
c.assets_dir = ''
|
||||
end
|
||||
|
||||
def mail_with_i18n_subject(recipient)
|
||||
@recipient = recipient
|
||||
I18n.locale = :de
|
||||
mail(:to => recipient, :subject => "#{I18n.t :email_subject} #{recipient}",
|
||||
:from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
|
||||
end
|
||||
end
|
||||
|
||||
class TestController < ActionController::Base
|
||||
def send_mail
|
||||
I18nTestMailer.mail_with_i18n_subject("test@localhost").deliver
|
||||
render :text => 'Mail sent'
|
||||
end
|
||||
end
|
||||
|
||||
class ActionMailerI18nWithControllerTest < ActionDispatch::IntegrationTest
|
||||
Routes = ActionDispatch::Routing::RouteSet.new
|
||||
Routes.draw do
|
||||
match ':controller(/:action(/:id))'
|
||||
end
|
||||
|
||||
def app
|
||||
Routes
|
||||
end
|
||||
|
||||
def setup
|
||||
I18n.backend.store_translations('de', :email_subject => '[Signed up] Welcome')
|
||||
end
|
||||
|
||||
def teardown
|
||||
I18n.locale = :en
|
||||
end
|
||||
|
||||
def test_send_mail
|
||||
get '/test/send_mail'
|
||||
assert_equal "Mail sent", @response.body
|
||||
end
|
||||
end
|
||||
@@ -14,14 +14,6 @@ class HelperMailer < ActionMailer::Base
|
||||
end
|
||||
end
|
||||
|
||||
def use_format_paragraph
|
||||
@text = "But soft! What light through yonder window breaks?"
|
||||
|
||||
mail_with_defaults do |format|
|
||||
format.html { render(:inline => "<%= format_paragraph @text, 15, 1 %>") }
|
||||
end
|
||||
end
|
||||
|
||||
def use_mailer
|
||||
mail_with_defaults do |format|
|
||||
format.html { render(:inline => "<%= mailer.message.subject %>") }
|
||||
@@ -58,10 +50,5 @@ class MailerHelperTest < ActionMailer::TestCase
|
||||
mail = HelperMailer.use_message
|
||||
assert_match "using helpers", mail.body.encoded
|
||||
end
|
||||
|
||||
def test_use_format_paragraph
|
||||
mail = HelperMailer.use_format_paragraph
|
||||
assert_match " But soft! What\r\n light through\r\n yonder window\r\n breaks?", mail.body.encoded
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -10,6 +10,11 @@ class BaseMailer < ActionMailer::Base
|
||||
mail({:subject => "The first email on new API!"}.merge!(hash))
|
||||
end
|
||||
|
||||
def welcome_with_fixnum_header(hash = {})
|
||||
headers['X-SPAM-COUNT'] = 2
|
||||
mail({:template_name => "welcome", :subject => "The first email on new API!"}.merge!(hash))
|
||||
end
|
||||
|
||||
def welcome_with_headers(hash = {})
|
||||
headers hash
|
||||
mail
|
||||
@@ -98,6 +103,13 @@ class BaseMailer < ActionMailer::Base
|
||||
mail(:template_name => template_name)
|
||||
end
|
||||
|
||||
def implicit_different_template_with_file(template_name='')
|
||||
mail do |format|
|
||||
format.text { render :file => template_name }
|
||||
format.html { render :file => template_name }
|
||||
end
|
||||
end
|
||||
|
||||
def explicit_different_template(template_name='')
|
||||
mail do |format|
|
||||
format.text { render :template => "#{mailer_name}/#{template_name}" }
|
||||
@@ -113,6 +125,6 @@ class BaseMailer < ActionMailer::Base
|
||||
end
|
||||
|
||||
def email_with_translations
|
||||
mail :body => render("email_with_translations.html")
|
||||
body render("email_with_translations.html")
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,14 +11,9 @@ class AdvAttrTest < ActiveSupport::TestCase
|
||||
end
|
||||
|
||||
def setup
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
@person = Person.new
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
end
|
||||
|
||||
def test_adv_attr
|
||||
assert_nil @person.name
|
||||
@person.name 'Bob'
|
||||
|
||||
@@ -3,9 +3,9 @@ require 'action_controller'
|
||||
|
||||
class AssetHostMailer < ActionMailer::Base
|
||||
def email_with_asset
|
||||
mail :to => 'test@localhost',
|
||||
:subject => 'testing email containing asset path while asset_host is set',
|
||||
:from => 'tester@example.com'
|
||||
recipients 'test@localhost'
|
||||
subject "testing email containing asset path while asset_host is set"
|
||||
from "tester@example.com"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,7 +29,7 @@ class AssetHostTest < Test::Unit::TestCase
|
||||
assert_equal %Q{<img alt="Somelogo" src="http://www.example.com/images/somelogo.png" />}, mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_asset_host_as_one_argument_proc
|
||||
def test_asset_host_as_one_arguement_proc
|
||||
AssetHostMailer.config.asset_host = Proc.new { |source|
|
||||
if source.starts_with?('/images')
|
||||
"http://images.example.com"
|
||||
@@ -41,7 +41,7 @@ class AssetHostTest < Test::Unit::TestCase
|
||||
assert_equal %Q{<img alt="Somelogo" src="http://images.example.com/images/somelogo.png" />}, mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_asset_host_as_two_argument_proc
|
||||
def test_asset_host_as_two_arguement_proc
|
||||
ActionController::Base.config.asset_host = Proc.new {|source,request|
|
||||
if request && request.ssl?
|
||||
"https://www.example.com"
|
||||
@@ -1,45 +1,53 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
class AutoLayoutMailer < ActionMailer::Base
|
||||
default :to => 'test@localhost',
|
||||
:subject => "You have a mail",
|
||||
:from => "tester@example.com"
|
||||
|
||||
def hello
|
||||
mail()
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
end
|
||||
|
||||
def spam
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
|
||||
@world = "Earth"
|
||||
mail(:body => render(:inline => "Hello, <%= @world %>", :layout => 'spam'))
|
||||
body render(:inline => "Hello, <%= @world %>", :layout => 'spam')
|
||||
end
|
||||
|
||||
def nolayout
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
|
||||
@world = "Earth"
|
||||
mail(:body => render(:inline => "Hello, <%= @world %>", :layout => false))
|
||||
body render(:inline => "Hello, <%= @world %>", :layout => false)
|
||||
end
|
||||
|
||||
def multipart(type = nil)
|
||||
mail(:content_type => type) do |format|
|
||||
format.text { render }
|
||||
format.html { render }
|
||||
end
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
|
||||
content_type(type) if type
|
||||
end
|
||||
end
|
||||
|
||||
class ExplicitLayoutMailer < ActionMailer::Base
|
||||
layout 'spam', :except => [:logout]
|
||||
|
||||
default :to => 'test@localhost',
|
||||
:subject => "You have a mail",
|
||||
:from => "tester@example.com"
|
||||
|
||||
def signup
|
||||
mail()
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
end
|
||||
|
||||
def logout
|
||||
mail()
|
||||
recipients 'test@localhost'
|
||||
subject "You have a mail"
|
||||
from "tester@example.com"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -83,6 +91,19 @@ class LayoutMailerTest < Test::Unit::TestCase
|
||||
assert_equal "Hello from layout text/html multipart", mail.parts.last.body.to_s
|
||||
end
|
||||
|
||||
def test_should_fix_multipart_layout
|
||||
mail = AutoLayoutMailer.multipart("text/plain")
|
||||
assert_equal "multipart/alternative", mail.mime_type
|
||||
assert_equal 2, mail.parts.size
|
||||
|
||||
assert_equal 'text/plain', mail.parts.first.mime_type
|
||||
assert_equal "text/plain layout - text/plain multipart", mail.parts.first.body.to_s
|
||||
|
||||
assert_equal 'text/html', mail.parts.last.mime_type
|
||||
assert_equal "Hello from layout text/html multipart", mail.parts.last.body.to_s
|
||||
end
|
||||
|
||||
|
||||
def test_should_pickup_layout_given_to_render
|
||||
mail = AutoLayoutMailer.spam
|
||||
assert_equal "Spammer layout Hello, Earth", mail.body.to_s.strip
|
||||
@@ -19,6 +19,18 @@ class RenderMailer < ActionMailer::Base
|
||||
body render(:file => "templates/signed_up")
|
||||
end
|
||||
|
||||
def rxml_template
|
||||
recipients 'test@localhost'
|
||||
subject "rendering rxml template"
|
||||
from "tester@example.com"
|
||||
end
|
||||
|
||||
def included_subtemplate
|
||||
recipients 'test@localhost'
|
||||
subject "Including another template in the one being rendered"
|
||||
from "tester@example.com"
|
||||
end
|
||||
|
||||
def no_instance_variable
|
||||
recipients 'test@localhost'
|
||||
subject "No Instance Variable"
|
||||
@@ -29,6 +41,11 @@ class RenderMailer < ActionMailer::Base
|
||||
end
|
||||
end
|
||||
|
||||
def initialize_defaults(method_name)
|
||||
super
|
||||
mailer_name "test_mailer"
|
||||
end
|
||||
|
||||
def multipart_alternative
|
||||
recipients 'test@localhost'
|
||||
subject 'multipart/alternative'
|
||||
@@ -80,13 +97,11 @@ class RenderHelperTest < Test::Unit::TestCase
|
||||
set_delivery_method :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
|
||||
@recipient = 'test@localhost'
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
@@ -100,16 +115,35 @@ class RenderHelperTest < Test::Unit::TestCase
|
||||
assert_equal "Hello there,\n\nMr. test@localhost", mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_rxml_template
|
||||
mail = RenderMailer.rxml_template.deliver
|
||||
assert_equal %(<?xml version="1.0" encoding="UTF-8"?>\n<test/>), mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_included_subtemplate
|
||||
mail = RenderMailer.included_subtemplate.deliver
|
||||
assert_equal "Hey Ho, let's go!", mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_no_instance_variable
|
||||
mail = RenderMailer.no_instance_variable.deliver
|
||||
assert_equal "Look, subject.nil? is true!", mail.body.to_s.strip
|
||||
end
|
||||
|
||||
def test_legacy_multipart_alternative
|
||||
mail = RenderMailer.multipart_alternative.deliver
|
||||
assert_equal(2, mail.parts.size)
|
||||
assert_equal("multipart/alternative", mail.mime_type)
|
||||
assert_equal("text/plain", mail.parts[0].mime_type)
|
||||
assert_equal("foo: bar", mail.parts[0].body.encoded)
|
||||
assert_equal("text/html", mail.parts[1].mime_type)
|
||||
assert_equal("<strong>foo</strong> bar", mail.parts[1].body.encoded)
|
||||
end
|
||||
end
|
||||
|
||||
class FirstSecondHelperTest < Test::Unit::TestCase
|
||||
def setup
|
||||
set_delivery_method :test
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
|
||||
@@ -117,7 +151,6 @@ class FirstSecondHelperTest < Test::Unit::TestCase
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
|
||||
@@ -328,20 +328,21 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
mail
|
||||
end
|
||||
|
||||
# Replacing logger work around for mocha bug. Should be fixed in mocha 0.3.3
|
||||
def setup
|
||||
set_delivery_method :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.raise_delivery_errors = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
ActiveSupport::Deprecation.silenced = true
|
||||
|
||||
@original_logger = TestMailer.logger
|
||||
@recipient = 'test@localhost'
|
||||
|
||||
TestMailer.delivery_method = :test
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
TestMailer.logger = @original_logger
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
@@ -514,8 +515,7 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
assert_not_nil mail
|
||||
mail, from, to = mail
|
||||
|
||||
assert_equal ['root@loudthinking.com'], to
|
||||
assert_equal 'system@loudthinking.com', from
|
||||
assert_equal 'system@loudthinking.com', from.to_s
|
||||
end
|
||||
|
||||
def test_from_with_name_for_smtp
|
||||
@@ -526,7 +526,6 @@ class ActionMailerTest < Test::Unit::TestCase
|
||||
assert_not_nil mail
|
||||
mail, from, to = mail
|
||||
|
||||
assert_equal ['root@loudthinking.com'], to
|
||||
assert_equal 'system@loudthinking.com', from
|
||||
end
|
||||
|
||||
@@ -981,7 +980,7 @@ EOF
|
||||
TestMailer.delivery_method = :file
|
||||
tmp_location = TestMailer.file_settings[:location]
|
||||
|
||||
TestMailer.cc_bcc(@recipient).deliver
|
||||
result = TestMailer.cc_bcc(@recipient).deliver
|
||||
assert File.exists?(tmp_location)
|
||||
assert File.directory?(tmp_location)
|
||||
assert File.exists?(File.join(tmp_location, @recipient))
|
||||
@@ -1097,3 +1096,111 @@ EOF
|
||||
TestMailer.smtp_settings.merge!(:enable_starttls_auto => true)
|
||||
end
|
||||
end
|
||||
|
||||
class InheritableTemplateRootTest < ActiveSupport::TestCase
|
||||
def test_attr
|
||||
expected = File.expand_path("#{File.dirname(__FILE__)}/../fixtures/path.with.dots")
|
||||
assert_equal expected, FunkyPathMailer.template_root.to_s
|
||||
|
||||
sub = Class.new(FunkyPathMailer)
|
||||
assert_deprecated do
|
||||
sub.template_root = 'test/path'
|
||||
end
|
||||
|
||||
assert_equal File.expand_path('test/path'), sub.template_root.to_s
|
||||
assert_equal expected, FunkyPathMailer.template_root.to_s
|
||||
end
|
||||
end
|
||||
|
||||
class MethodNamingTest < ActiveSupport::TestCase
|
||||
include ActionMailer::TestHelper
|
||||
|
||||
class TestMailer < ActionMailer::Base
|
||||
def send
|
||||
body 'foo'
|
||||
end
|
||||
end
|
||||
|
||||
def setup
|
||||
set_delivery_method :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
end
|
||||
|
||||
def teardown
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
def test_send_method
|
||||
assert_nothing_raised do
|
||||
assert_emails 1 do
|
||||
assert_deprecated do
|
||||
TestMailer.deliver_send
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
class RespondToTest < Test::Unit::TestCase
|
||||
class RespondToMailer < ActionMailer::Base; end
|
||||
|
||||
def setup
|
||||
set_delivery_method :test
|
||||
end
|
||||
|
||||
def teardown
|
||||
restore_delivery_method
|
||||
end
|
||||
|
||||
def test_should_respond_to_new
|
||||
assert_respond_to RespondToMailer, :new
|
||||
end
|
||||
|
||||
def test_should_respond_to_create_with_template_suffix
|
||||
assert_respond_to RespondToMailer, :create_any_old_template
|
||||
end
|
||||
|
||||
def test_should_respond_to_deliver_with_template_suffix
|
||||
assert_respond_to RespondToMailer, :deliver_any_old_template
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_new_with_template_suffix
|
||||
assert !RespondToMailer.respond_to?(:new_any_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_create_with_template_suffix_unless_it_is_separated_by_an_underscore
|
||||
assert !RespondToMailer.respond_to?(:createany_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_deliver_with_template_suffix_unless_it_is_separated_by_an_underscore
|
||||
assert !RespondToMailer.respond_to?(:deliverany_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_create_with_template_suffix_if_it_begins_with_a_uppercase_letter
|
||||
assert !RespondToMailer.respond_to?(:create_Any_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_deliver_with_template_suffix_if_it_begins_with_a_uppercase_letter
|
||||
assert !RespondToMailer.respond_to?(:deliver_Any_old_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_create_with_template_suffix_if_it_begins_with_a_digit
|
||||
assert !RespondToMailer.respond_to?(:create_1_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_deliver_with_template_suffix_if_it_begins_with_a_digit
|
||||
assert !RespondToMailer.respond_to?(:deliver_1_template)
|
||||
end
|
||||
|
||||
def test_should_not_respond_to_method_where_deliver_is_not_a_suffix
|
||||
assert !RespondToMailer.respond_to?(:foo_deliver_template)
|
||||
end
|
||||
|
||||
def test_should_still_raise_exception_with_expected_message_when_calling_an_undefined_method
|
||||
error = assert_raise NoMethodError do
|
||||
RespondToMailer.not_a_method
|
||||
end
|
||||
|
||||
assert_match(/method.*not_a_method/, error.message)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
require 'abstract_unit'
|
||||
|
||||
class TmailCompatTest < ActiveSupport::TestCase
|
||||
def setup
|
||||
@silence = ActiveSupport::Deprecation.silenced
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
end
|
||||
|
||||
def teardown
|
||||
ActiveSupport::Deprecation.silenced = @silence
|
||||
end
|
||||
|
||||
def test_set_content_type_raises_deprecation_warning
|
||||
mail = Mail.new
|
||||
@@ -39,4 +31,5 @@ class TmailCompatTest < ActiveSupport::TestCase
|
||||
end
|
||||
assert_equal mail.content_transfer_encoding, "base64"
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
@@ -18,10 +18,13 @@ class UrlTestMailer < ActionMailer::Base
|
||||
end
|
||||
|
||||
def signed_up_with_url(recipient)
|
||||
@recipients = recipient
|
||||
@subject = "[Signed up] Welcome #{recipient}"
|
||||
@from = "system@loudthinking.com"
|
||||
@sent_on = Time.local(2004, 12, 12)
|
||||
|
||||
@recipient = recipient
|
||||
@welcome_url = url_for :host => "example.com", :controller => "welcome", :action => "greeting"
|
||||
mail(:to => recipient, :subject => "[Signed up] Welcome #{recipient}",
|
||||
:from => "system@loudthinking.com", :date => Time.local(2004, 12, 12))
|
||||
end
|
||||
end
|
||||
|
||||
@@ -44,7 +47,6 @@ class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
set_delivery_method :test
|
||||
ActionMailer::Base.perform_deliveries = true
|
||||
ActionMailer::Base.deliveries.clear
|
||||
ActiveSupport::Deprecation.silenced = false
|
||||
|
||||
@recipient = 'test@localhost'
|
||||
end
|
||||
@@ -56,9 +58,11 @@ class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
def test_signed_up_with_url
|
||||
UrlTestMailer.delivery_method = :test
|
||||
|
||||
AppRoutes.draw do
|
||||
match ':controller(/:action(/:id))'
|
||||
match '/welcome' => "foo#bar", :as => "welcome"
|
||||
assert_deprecated do
|
||||
AppRoutes.draw do |map|
|
||||
map.connect ':controller/:action/:id'
|
||||
map.welcome 'welcome', :controller=>"foo", :action=>"bar"
|
||||
end
|
||||
end
|
||||
|
||||
expected = new_mail
|
||||
@@ -67,7 +71,6 @@ class ActionMailerUrlTest < ActionMailer::TestCase
|
||||
expected.body = "Hello there,\n\nMr. #{@recipient}. Please see our greeting at http://example.com/welcome/greeting http://www.basecamphq.com/welcome\n\n<img alt=\"Somelogo\" src=\"/images/somelogo.png\" />"
|
||||
expected.from = "system@loudthinking.com"
|
||||
expected.date = Time.local(2004, 12, 12)
|
||||
expected.content_type = "text/html"
|
||||
|
||||
created = nil
|
||||
assert_nothing_raised { created = UrlTestMailer.signed_up_with_url(@recipient) }
|
||||
@@ -2,10 +2,11 @@ require 'abstract_unit'
|
||||
|
||||
class TestHelperMailer < ActionMailer::Base
|
||||
def test
|
||||
recipients "test@example.com"
|
||||
from "tester@example.com"
|
||||
|
||||
@world = "Earth"
|
||||
mail :body => render(:inline => "Hello, <%= @world %>"),
|
||||
:to => "test@example.com",
|
||||
:from => "tester@example.com"
|
||||
render(:inline => "Hello, <%= @world %>")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
5336
actionpack/CHANGELOG
Normal file
5336
actionpack/CHANGELOG
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,390 +0,0 @@
|
||||
## Rails 3.1.8 (Aug 9, 2012)
|
||||
|
||||
* There is an XSS vulnerability in the strip_tags helper in Ruby on Rails, the
|
||||
helper doesn't correctly handle malformed html. As a result an attacker can
|
||||
execute arbitrary javascript through the use of specially crafted malformed
|
||||
html.
|
||||
|
||||
*Marek from Nethemba (www.nethemba.com) & Santiago Pastorino*
|
||||
|
||||
* When a "prompt" value is supplied to the `select_tag` helper, the "prompt" value is not escaped.
|
||||
If untrusted data is not escaped, and is supplied as the prompt value, there is a potential for XSS attacks.
|
||||
Vulnerable code will look something like this:
|
||||
select_tag("name", options, :prompt => UNTRUSTED_INPUT)
|
||||
|
||||
*Santiago Pastorino*
|
||||
|
||||
## Rails 3.1.7 (Jul 26, 2012)
|
||||
|
||||
* Do not convert digest auth strings to symbols. CVE-2012-3424
|
||||
|
||||
## Rails 3.1.6 (Jun 12, 2012)
|
||||
|
||||
* nil is removed from array parameter values
|
||||
|
||||
CVE-2012-2694
|
||||
|
||||
## Rails 3.1.5 (May 31, 2012) ##
|
||||
|
||||
* Detect optional glob params when adding non-greedy regexp - closes #4817.
|
||||
|
||||
* Strip null bytes from Location header
|
||||
|
||||
* Return the same session data object when setting session id
|
||||
|
||||
* Avoid inspecting the whole route set, closes #1525
|
||||
|
||||
* Strip [nil] from parameters hash. Thanks to Ben Murphy for reporting this!
|
||||
CVE-2012-2660
|
||||
|
||||
## Rails 3.1.4 (unreleased) ##
|
||||
|
||||
* :subdomain can now be specified with a value of false in url_for,
|
||||
allowing for subdomain(s) removal from the host during link generation. GH #4083
|
||||
|
||||
*Arun Agrawal*
|
||||
|
||||
* Skip assets group in Gemfile and all assets configurations options
|
||||
when the application is generated with --skip-sprockets option.
|
||||
|
||||
*Guillermo Iguaran*
|
||||
|
||||
* Use ProcessedAsset#pathname in Sprockets helpers when debugging is on. Closes #3333 #3348 #3361.
|
||||
|
||||
*Guillermo Iguaran*
|
||||
|
||||
* Allow to use asset_path on named_routes aliasing RailsHelper's
|
||||
asset_path to path_to_asset *Adrian Pike*
|
||||
|
||||
* Assets should use the request protocol by default or default to relative if no request is available *Jonathan del Strother*
|
||||
|
||||
## Rails 3.1.3 (November 20, 2011) ##
|
||||
|
||||
* Downgrade sprockets to ~> 2.0.3. Using 2.1.0 caused regressions.
|
||||
|
||||
* Fix using `tranlate` helper with a html translation which uses the `:count` option for
|
||||
pluralization.
|
||||
|
||||
*Jon Leighton*
|
||||
|
||||
## Rails 3.1.2 (November 18, 2011) ##
|
||||
|
||||
* Fix XSS security vulnerability in the `translate` helper method. When using interpolation
|
||||
in combination with HTML-safe translations, the interpolated input would not get HTML
|
||||
escaped. *GH 3664*
|
||||
|
||||
Before:
|
||||
|
||||
translate('foo_html', :something => '<script>') # => "...<script>..."
|
||||
|
||||
After:
|
||||
|
||||
translate('foo_html', :something => '<script>') # => "...<script>..."
|
||||
|
||||
*Sergey Nartimov*
|
||||
|
||||
* Upgrade sprockets dependency to ~> 2.1.0
|
||||
|
||||
* Ensure that the format isn't applied twice to the cache key, else it becomes impossible
|
||||
to target with expire_action.
|
||||
|
||||
*Christopher Meiklejohn*
|
||||
|
||||
* Swallow error when can't unmarshall object from session.
|
||||
|
||||
*Bruno Zanchet*
|
||||
|
||||
* Implement a workaround for a bug in ruby-1.9.3p0 where an error would be raised
|
||||
while attempting to convert a template from one encoding to another.
|
||||
|
||||
Please see http://redmine.ruby-lang.org/issues/5564 for details of the bug.
|
||||
|
||||
The workaround is to load all conversions into memory ahead of time, and will
|
||||
only happen if the ruby version is *exactly* 1.9.3p0. The hope is obviously that
|
||||
the underlying problem will be resolved in the next patchlevel release of
|
||||
1.9.3.
|
||||
|
||||
*Jon Leighton*
|
||||
|
||||
* Ensure users upgrading from 3.0.x to 3.1.x will properly upgrade their flash object in session (issues #3298 and #2509)
|
||||
|
||||
## Rails 3.1.1 (October 7, 2011) ##
|
||||
|
||||
* stylesheet_link_tag('/stylesheets/application') and similar helpers doesn't
|
||||
throw Sprockets::FileOutsidePaths exception anymore *Santiago Pastorino*
|
||||
|
||||
* Ensure default_asset_host_protocol is respected, closes #2980. *José Valim*
|
||||
|
||||
Changing rake db:schema:dump to run :environment as well as :load_config,
|
||||
as running :load_config alone will lead to the dumper being run without
|
||||
including extensions such as those included in foreigner and
|
||||
spatial_adapter.
|
||||
|
||||
This reverses a change made here:
|
||||
https://github.com/rails/rails/commit/5df72a238e9fcb18daf6ab6e6dc9051c9106d7bb#L0L324
|
||||
|
||||
I'm assuming here that :load_config needs to be invoked
|
||||
separately from :environment, as it is elsewhere in the
|
||||
file for db operations, if not the alternative is to go
|
||||
back to "task :dump => :environment do".
|
||||
|
||||
*Ben Woosley*
|
||||
|
||||
* Update to rack-cache 1.1.
|
||||
|
||||
Versions prior to 1.1 delete the If-Modified-Since and If-Not-Modified
|
||||
headers when config.action_controller.perform_caching is true. This has two
|
||||
problems:
|
||||
* unexpected inconsistent behaviour between development &
|
||||
production environments
|
||||
* breaks applications that use of these headers
|
||||
|
||||
*Brendan Ribera*
|
||||
|
||||
* Ensure that enhancements to assets:precompile task are only run once *Sam Pohlenz*
|
||||
|
||||
* TestCase should respect the view_assigns API instead of pulling variables on
|
||||
its own. *José Valim*
|
||||
|
||||
* javascript_path and stylesheet_path now refer to /assets if asset pipelining
|
||||
is on. *Santiago Pastorino*
|
||||
|
||||
* button_to support form option. Now you're able to pass for example
|
||||
'data-type' => 'json'. *ihower*
|
||||
|
||||
* image_path and image_tag should use /assets if asset pipelining is turned
|
||||
on. Closes #3126 *Santiago Pastorino and christos*
|
||||
|
||||
* Avoid use of existing precompiled assets during rake assets:precompile run.
|
||||
Closes #3119 *Guillermo Iguaran*
|
||||
|
||||
* Copy assets to nondigested filenames too *Santiago Pastorino*
|
||||
|
||||
* Give precedence to `config.digest = false` over the existence of
|
||||
manifest.yml asset digests *christos*
|
||||
|
||||
* escape options for the stylesheet_link_tag method *Alexey Vakhov*
|
||||
|
||||
* Re-launch assets:precompile task using (Rake.)ruby instead of Kernel.exec so
|
||||
it works on Windows *cablegram*
|
||||
|
||||
* env var passed to process shouldn't be modified in process method. [Santiago
|
||||
Pastorino]
|
||||
|
||||
* `rake assets:precompile` loads the application but does not initialize
|
||||
it.
|
||||
|
||||
To the app developer, this means configuration add in
|
||||
config/initializers/* will not be executed.
|
||||
|
||||
Plugins developers need to special case their initializers that are
|
||||
meant to be run in the assets group by adding :group => :assets. *José Valim*
|
||||
|
||||
* Sprockets uses config.assets.prefix for asset_path *asee*
|
||||
|
||||
* FileStore key_file_path properly limit filenames to 255 characters. *phuibonhoa*
|
||||
|
||||
* Fix Hash#to_query edge case with html_safe strings. *brainopia*
|
||||
|
||||
* Allow asset tag helper methods to accept :digest => false option in order to completely avoid the digest generation.
|
||||
Useful for linking assets from static html files or from emails when the user
|
||||
could probably look at an older html email with an older asset. *Santiago Pastorino*
|
||||
|
||||
* Don't mount Sprockets server at config.assets.prefix if config.assets.compile is false. *Mark J. Titorenko*
|
||||
|
||||
* Set relative url root in assets when controller isn't available for Sprockets (eg. Sass files using asset_path). Fixes #2435 *Guillermo Iguaran*
|
||||
|
||||
* Fix basic auth credential generation to not make newlines. GH #2882
|
||||
|
||||
* Fixed the behavior of asset pipeline when config.assets.digest and config.assets.compile are false and requested asset isn't precompiled.
|
||||
Before the requested asset were compiled anyway ignoring that the config.assets.compile flag is false. *Guillermo Iguaran*
|
||||
|
||||
* CookieJar is now Enumerable. Fixes #2795
|
||||
|
||||
* Fixed AssetNotPrecompiled error raised when rake assets:precompile is compiling certain .erb files. See GH #2763 #2765 #2805 *Guillermo Iguaran*
|
||||
|
||||
* Manifest is correctly placed in assets path when default assets prefix is changed. Fixes #2776 *Guillermo Iguaran*
|
||||
|
||||
* Fixed stylesheet_link_tag and javascript_include_tag to respect additional options passed by the users when debug is on. *Guillermo Iguaran*
|
||||
|
||||
* Fix ActiveRecord#exists? when passsed a nil value
|
||||
|
||||
* Fix assert_select_email to work on multipart and non-multipart emails as the method stopped working correctly in Rails 3.x due to changes in the new mail gem.
|
||||
|
||||
|
||||
## Rails 3.1.0 (August 30, 2011) ##
|
||||
|
||||
* Param values are `paramified` in controller tests. *David Chelimsky*
|
||||
|
||||
* x_sendfile_header now defaults to nil and config/environments/production.rb doesn't set a particular value for it. This allows servers to set it through X-Sendfile-Type. *Santiago Pastorino*
|
||||
|
||||
* The submit form helper does not generate an id "object_name_id" anymore. *fbrusatti*
|
||||
|
||||
* Make sure respond_with with :js tries to render a template in all cases *José Valim*
|
||||
|
||||
* json_escape will now return a SafeBuffer string if it receives SafeBuffer string *tenderlove*
|
||||
|
||||
* Make sure escape_js returns SafeBuffer string if it receives SafeBuffer string *Prem Sichanugrist*
|
||||
|
||||
* Fix escape_js to work correctly with the new SafeBuffer restriction *Paul Gallagher*
|
||||
|
||||
* Brought back alternative convention for namespaced models in i18n *thoefer*
|
||||
|
||||
Now the key can be either "namespace.model" or "namespace/model" until further deprecation.
|
||||
|
||||
* It is prohibited to perform a in-place SafeBuffer mutation *tenderlove*
|
||||
|
||||
The old behavior of SafeBuffer allowed you to mutate string in place via
|
||||
method like `sub!`. These methods can add unsafe strings to a safe buffer,
|
||||
and the safe buffer will continue to be marked as safe.
|
||||
|
||||
An example problem would be something like this:
|
||||
|
||||
<%= link_to('hello world', @user).sub!(/hello/, params[:xss]) %>
|
||||
|
||||
In the above example, an untrusted string (`params[:xss]`) is added to the
|
||||
safe buffer returned by `link_to`, and the untrusted content is successfully
|
||||
sent to the client without being escaped. To prevent this from happening
|
||||
`sub!` and other similar methods will now raise an exception when they are called on a safe buffer.
|
||||
|
||||
In addition to the in-place versions, some of the versions of these methods which return a copy of the string will incorrectly mark strings as safe. For example:
|
||||
|
||||
<%= link_to('hello world', @user).sub(/hello/, params[:xss]) %>
|
||||
|
||||
The new versions will now ensure that *all* strings returned by these methods on safe buffers are marked unsafe.
|
||||
|
||||
You can read more about this change in http://groups.google.com/group/rubyonrails-security/browse_thread/thread/2e516e7acc96c4fb
|
||||
|
||||
* Warn if we cannot verify CSRF token authenticity *José Valim*
|
||||
|
||||
* Allow AM/PM format in datetime selectors *Aditya Sanghi*
|
||||
|
||||
* Only show dump of regular env methods on exception screen (not all the rack crap) *DHH*
|
||||
|
||||
* auto_link has been removed with no replacement. If you still use auto_link
|
||||
please install the rails_autolink gem:
|
||||
http://github.com/tenderlove/rails_autolink
|
||||
|
||||
*tenderlove*
|
||||
|
||||
* Added streaming support, you can enable it with: *José Valim*
|
||||
|
||||
class PostsController < ActionController::Base
|
||||
stream :only => :index
|
||||
end
|
||||
|
||||
Please read the docs at `ActionController::Streaming` for more information.
|
||||
|
||||
* Added `ActionDispatch::Request.ignore_accept_header` to ignore accept headers and only consider the format given as parameter *José Valim*
|
||||
|
||||
* Created `ActionView::Renderer` and specified an API for `ActionView::Context`, check those objects for more information *José Valim*
|
||||
|
||||
* Added `ActionController::ParamsWrapper` to wrap parameters into a nested hash, and will be turned on for JSON request in new applications by default *Prem Sichanugrist*
|
||||
|
||||
This can be customized by setting `ActionController::Base.wrap_parameters` in `config/initializer/wrap_parameters.rb`
|
||||
|
||||
* RJS has been extracted out to a gem. *fxn*
|
||||
|
||||
* Implicit actions named not_implemented can be rendered. *Santiago Pastorino*
|
||||
|
||||
* Wildcard route will always match the optional format segment by default. *Prem Sichanugrist*
|
||||
|
||||
For example if you have this route:
|
||||
|
||||
map '*pages' => 'pages#show'
|
||||
|
||||
by requesting '/foo/bar.json', your `params[:pages]` will be equals to "foo/bar" with the request format of JSON. If you want the old 3.0.x behavior back, you could supply `:format => false` like this:
|
||||
|
||||
map '*pages' => 'pages#show', :format => false
|
||||
|
||||
* Added Base.http_basic_authenticate_with to do simple http basic authentication with a single class method call *DHH*
|
||||
|
||||
class PostsController < ApplicationController
|
||||
USER_NAME, PASSWORD = "dhh", "secret"
|
||||
|
||||
before_filter :authenticate, :except => [ :index ]
|
||||
|
||||
def index
|
||||
render :text => "Everyone can see me!"
|
||||
end
|
||||
|
||||
def edit
|
||||
render :text => "I'm only accessible if you know the password"
|
||||
end
|
||||
|
||||
private
|
||||
def authenticate
|
||||
authenticate_or_request_with_http_basic do |user_name, password|
|
||||
user_name == USER_NAME && password == PASSWORD
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
..can now be written as
|
||||
|
||||
class PostsController < ApplicationController
|
||||
http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
|
||||
|
||||
def index
|
||||
render :text => "Everyone can see me!"
|
||||
end
|
||||
|
||||
def edit
|
||||
render :text => "I'm only accessible if you know the password"
|
||||
end
|
||||
end
|
||||
|
||||
* Allow you to add `force_ssl` into controller to force browser to transfer data via HTTPS protocol on that particular controller. You can also specify `:only` or `:except` to specific it to particular action. *DHH and Prem Sichanugrist*
|
||||
|
||||
* Allow FormHelper#form_for to specify the :method as a direct option instead of through the :html hash *DHH*
|
||||
|
||||
form_for(@post, remote: true, method: :delete) instead of form_for(@post, remote: true, html: { method: :delete })
|
||||
|
||||
* Make JavaScriptHelper#j() an alias for JavaScriptHelper#escape_javascript() -- note this then supersedes the Object#j() method that the JSON gem adds within templates using the JavaScriptHelper *DHH*
|
||||
|
||||
* Sensitive query string parameters (specified in config.filter_parameters) will now be filtered out from the request paths in the log file. *Prem Sichanugrist, fxn*
|
||||
|
||||
* URL parameters which return false for to_param now appear in the query string (previously they were removed) *Andrew White*
|
||||
|
||||
* URL parameters which return nil for to_param are now removed from the query string *Andrew White*
|
||||
|
||||
* ActionDispatch::MiddlewareStack now uses composition over inheritance. It is
|
||||
no longer an array which means there may be methods missing that were not
|
||||
tested.
|
||||
|
||||
* Add an :authenticity_token option to form_tag for custom handling or to omit the token (pass :authenticity_token => false). *Jakub Kuźma, Igor Wiedler*
|
||||
|
||||
* HTML5 button_tag helper. *Rizwan Reza*
|
||||
|
||||
* Template lookup now searches further up in the inheritance chain. *Artemave*
|
||||
|
||||
* Brought back config.action_view.cache_template_loading, which allows to decide whether templates should be cached or not. *Piotr Sarnacki*
|
||||
|
||||
* url_for and named url helpers now accept :subdomain and :domain as options, *Josh Kalderimis*
|
||||
|
||||
* The redirect route method now also accepts a hash of options which will only change the parts of the url in question, or an object which responds to call, allowing for redirects to be reused (check the documentation for examples). *Josh Kalderimis*
|
||||
|
||||
* Added config.action_controller.include_all_helpers. By default 'helper :all' is done in ActionController::Base, which includes all the helpers by default. Setting include_all_helpers to false will result in including only application_helper and helper corresponding to controller (like foo_helper for foo_controller). *Piotr Sarnacki*
|
||||
|
||||
* Added a convenience idiom to generate HTML5 data-* attributes in tag helpers from a :data hash of options:
|
||||
|
||||
tag("div", :data => {:name => 'Stephen', :city_state => %w(Chicago IL)})
|
||||
# => <div data-name="Stephen" data-city-state="["Chicago","IL"]" />
|
||||
|
||||
Keys are dasherized. Values are JSON-encoded, except for strings and symbols. *Stephen Celis*
|
||||
|
||||
* Deprecate old template handler API. The new API simply requires a template handler to respond to call. *José Valim*
|
||||
|
||||
* :rhtml and :rxml were finally removed as template handlers. *José Valim*
|
||||
|
||||
* Moved etag responsibility from ActionDispatch::Response to the middleware stack. *José Valim*
|
||||
|
||||
* Rely on Rack::Session stores API for more compatibility across the Ruby world. This is backwards incompatible since Rack::Session expects #get_session to accept 4 arguments and requires #destroy_session instead of simply #destroy. *José Valim*
|
||||
|
||||
* file_field automatically adds :multipart => true to the enclosing form. *Santiago Pastorino*
|
||||
|
||||
* Renames csrf_meta_tag -> csrf_meta_tags, and aliases csrf_meta_tag for backwards compatibility. *fxn*
|
||||
|
||||
* Add Rack::Cache to the default stack. Create a Rails store that delegates to the Rails cache, so by default, whatever caching layer you are using will be used for HTTP caching. Note that Rack::Cache will be used if you use #expires_in, #fresh_when or #stale with :public => true. Otherwise, the caching rules will apply to the browser only. *Yehuda Katz, Carl Lerche*
|
||||
|
||||
Please check [3-0-stable](https://github.com/rails/rails/blob/3-0-stable/actionpack/CHANGELOG) for previous changes.
|
||||
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2004-2011 David Heinemeier Hansson
|
||||
Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
|
||||
@@ -19,8 +19,9 @@ It consists of several modules:
|
||||
|
||||
* Action View, which handles view template lookup and rendering, and provides
|
||||
view helpers that assist when building HTML forms, Atom feeds and more.
|
||||
Template formats that Action View handles are ERB (embedded Ruby, typically
|
||||
used to inline short Ruby snippets inside HTML), and XML Builder.
|
||||
Template formats that Action View handles are ERb (embedded Ruby, typically
|
||||
used to inline short Ruby snippets inside HTML), XML Builder and RJS
|
||||
(dynamically generated JavaScript from Ruby code).
|
||||
|
||||
With the Ruby on Rails framework, users only directly interface with the
|
||||
Action Controller module. Necessary Action Dispatch functionality is activated
|
||||
@@ -33,7 +34,7 @@ A short rundown of some of the major features:
|
||||
* Actions grouped in controller as methods instead of separate command objects
|
||||
and can therefore share helper methods
|
||||
|
||||
class CustomersController < ActionController::Base
|
||||
CustomersController < ActionController::Base
|
||||
def show
|
||||
@customer = find_customer
|
||||
end
|
||||
@@ -56,9 +57,9 @@ A short rundown of some of the major features:
|
||||
{Learn more}[link:classes/ActionController/Base.html]
|
||||
|
||||
|
||||
* ERB templates (static content mixed with dynamic output from ruby)
|
||||
* ERb templates (static content mixed with dynamic output from ruby)
|
||||
|
||||
<% @posts.each do |post| %>
|
||||
<% for post in @posts %>
|
||||
Title: <%= post.title %>
|
||||
<% end %>
|
||||
|
||||
@@ -81,7 +82,7 @@ A short rundown of some of the major features:
|
||||
xml.language "en-us"
|
||||
xml.ttl "40"
|
||||
|
||||
@recent_items.each do |item|
|
||||
for item in @recent_items
|
||||
xml.item do
|
||||
xml.title(item_title(item))
|
||||
xml.description(item_description(item))
|
||||
@@ -261,7 +262,7 @@ methods:
|
||||
layout "weblog/layout"
|
||||
|
||||
def index
|
||||
@posts = Post.all
|
||||
@posts = Post.find(:all)
|
||||
end
|
||||
|
||||
def show
|
||||
@@ -293,7 +294,7 @@ And the templates look like this:
|
||||
</body></html>
|
||||
|
||||
weblog/index.html.erb:
|
||||
<% @posts.each do |post| %>
|
||||
<% for post in @posts %>
|
||||
<p><%= link_to(post.title, :action => "show", :id => post.id) %></p>
|
||||
<% end %>
|
||||
|
||||
@@ -322,7 +323,7 @@ The latest version of Action Pack can be installed with Rubygems:
|
||||
|
||||
Source code can be downloaded as part of the Rails project on GitHub
|
||||
|
||||
* https://github.com/rails/rails/tree/master/actionpack
|
||||
* http://github.com/rails/rails/tree/master/actionpack/
|
||||
|
||||
|
||||
== License
|
||||
@@ -338,4 +339,4 @@ API documentation is at
|
||||
|
||||
Bug reports and feature requests can be filed with the rest for the Ruby on Rails project here:
|
||||
|
||||
* https://github.com/rails/rails/issues
|
||||
* https://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets
|
||||
|
||||
@@ -8,18 +8,15 @@ Rake can be found at http://rake.rubyforge.org
|
||||
|
||||
== Running by hand
|
||||
|
||||
To run a single test suite
|
||||
If you only want to run a single test suite, or don't want to bother with Rake,
|
||||
you can do so with something like:
|
||||
|
||||
rake test TEST=path/to/test.rb
|
||||
ruby -Itest test/controller/base_tests.rb
|
||||
|
||||
which can be further narrowed down to one test:
|
||||
|
||||
rake test TEST=path/to/test.rb TESTOPTS="--name=test_something"
|
||||
|
||||
== Dependency on Active Record and database setup
|
||||
== Dependency on ActiveRecord and database setup
|
||||
|
||||
Test cases in the test/controller/active_record/ directory depend on having
|
||||
activerecord and sqlite installed. If Active Record is not in
|
||||
activerecord and sqlite installed. If ActiveRecord is not in
|
||||
actionpack/../activerecord directory, or the sqlite rubygem is not installed,
|
||||
these tests are skipped.
|
||||
|
||||
|
||||
9
actionpack/Rakefile
Executable file → Normal file
9
actionpack/Rakefile
Executable file → Normal file
@@ -1,7 +1,7 @@
|
||||
#!/usr/bin/env rake
|
||||
require 'rake'
|
||||
require 'rake/testtask'
|
||||
require 'rake/packagetask'
|
||||
require 'rubygems/package_task'
|
||||
require 'rake/gempackagetask'
|
||||
|
||||
desc "Default Task"
|
||||
task :default => :test
|
||||
@@ -18,8 +18,7 @@ Rake::TestTask.new(:test_action_pack) do |t|
|
||||
# this will not happen automatically and the tests (as a whole) will error
|
||||
t.test_files = Dir.glob('test/{abstract,controller,dispatch,template}/**/*_test.rb').sort
|
||||
|
||||
t.warning = true
|
||||
t.verbose = true
|
||||
# t.warning = true
|
||||
end
|
||||
|
||||
namespace :test do
|
||||
@@ -36,7 +35,7 @@ end
|
||||
|
||||
spec = eval(File.read('actionpack.gemspec'))
|
||||
|
||||
Gem::PackageTask.new(spec) do |p|
|
||||
Rake::GemPackageTask.new(spec) do |p|
|
||||
p.gem_spec = spec
|
||||
end
|
||||
|
||||
|
||||
@@ -11,21 +11,21 @@ Gem::Specification.new do |s|
|
||||
s.author = 'David Heinemeier Hansson'
|
||||
s.email = 'david@loudthinking.com'
|
||||
s.homepage = 'http://www.rubyonrails.org'
|
||||
s.rubyforge_project = 'actionpack'
|
||||
|
||||
s.files = Dir['CHANGELOG.md', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
|
||||
s.files = Dir['CHANGELOG', 'README.rdoc', 'MIT-LICENSE', 'lib/**/*']
|
||||
s.require_path = 'lib'
|
||||
s.requirements << 'none'
|
||||
|
||||
s.has_rdoc = true
|
||||
|
||||
s.add_dependency('activesupport', version)
|
||||
s.add_dependency('activemodel', version)
|
||||
s.add_dependency('rack-cache', '~> 1.2')
|
||||
s.add_dependency('builder', '~> 3.2')
|
||||
s.add_dependency('i18n', '~> 0.6')
|
||||
s.add_dependency('rack', '~> 1.4.1')
|
||||
s.add_dependency('rack-test', '~> 0.6.1')
|
||||
s.add_dependency('rack-mount', '~> 0.8.2')
|
||||
s.add_dependency('sprockets', '~> 2.10')
|
||||
s.add_dependency('erubis', '~> 2.7.0')
|
||||
|
||||
s.add_development_dependency('tzinfo', '~> 0.3.29')
|
||||
s.add_dependency('builder', '~> 2.1.2')
|
||||
s.add_dependency('i18n', '~> 0.4')
|
||||
s.add_dependency('rack', '~> 1.2.1')
|
||||
s.add_dependency('rack-test', '~> 0.5.6')
|
||||
s.add_dependency('rack-mount', '~> 0.6.13')
|
||||
s.add_dependency('tzinfo', '~> 0.3.23')
|
||||
s.add_dependency('erubis', '~> 2.6.6')
|
||||
end
|
||||
|
||||
@@ -24,5 +24,4 @@ module AbstractController
|
||||
autoload :Translation
|
||||
autoload :AssetPaths
|
||||
autoload :ViewPaths
|
||||
autoload :UrlFor
|
||||
end
|
||||
|
||||
@@ -3,8 +3,7 @@ module AbstractController
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
included do
|
||||
config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir,
|
||||
:stylesheets_dir, :default_asset_host_protocol
|
||||
config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir, :stylesheets_dir
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,4 +1,3 @@
|
||||
require 'erubis'
|
||||
require 'active_support/configurable'
|
||||
require 'active_support/descendants_tracker'
|
||||
require 'active_support/core_ext/module/anonymous'
|
||||
@@ -19,7 +18,6 @@ module AbstractController
|
||||
include ActiveSupport::Configurable
|
||||
extend ActiveSupport::DescendantsTracker
|
||||
|
||||
undef_method :not_implemented
|
||||
class << self
|
||||
attr_reader :abstract
|
||||
alias_method :abstract?, :abstract
|
||||
@@ -33,9 +31,10 @@ module AbstractController
|
||||
# A list of all internal methods for a controller. This finds the first
|
||||
# abstract superclass of a controller, and gets a list of all public
|
||||
# instance methods on that abstract class. Public instance methods of
|
||||
# a controller would normally be considered action methods, so methods
|
||||
# declared on abstract classes are being removed.
|
||||
# (ActionController::Metal and ActionController::Base are defined as abstract)
|
||||
# a controller would normally be considered action methods, so we
|
||||
# are removing those methods on classes declared as abstract
|
||||
# (ActionController::Metal and ActionController::Base are defined
|
||||
# as abstract)
|
||||
def internal_methods
|
||||
controller = self
|
||||
controller = controller.superclass until controller.abstract?
|
||||
@@ -63,13 +62,13 @@ module AbstractController
|
||||
def action_methods
|
||||
@action_methods ||= begin
|
||||
# All public instance methods of this class, including ancestors
|
||||
methods = (public_instance_methods(true) -
|
||||
methods = public_instance_methods(true).map { |m| m.to_s }.to_set -
|
||||
# Except for public instance methods of Base and its ancestors
|
||||
internal_methods +
|
||||
internal_methods.map { |m| m.to_s } +
|
||||
# Be sure to include shadowed public instance methods of this class
|
||||
public_instance_methods(false)).uniq.map { |x| x.to_s } -
|
||||
public_instance_methods(false).map { |m| m.to_s } -
|
||||
# And always exclude explicitly hidden actions
|
||||
hidden_actions.to_a
|
||||
hidden_actions
|
||||
|
||||
# Clear out AS callback method pollution
|
||||
methods.reject { |method| method =~ /_one_time_conditions/ }
|
||||
@@ -130,29 +129,17 @@ module AbstractController
|
||||
self.class.action_methods
|
||||
end
|
||||
|
||||
# Returns true if a method for the action is available and
|
||||
# can be dispatched, false otherwise.
|
||||
#
|
||||
# Notice that <tt>action_methods.include?("foo")</tt> may return
|
||||
# false and <tt>available_action?("foo")</tt> returns true because
|
||||
# available action consider actions that are also available
|
||||
# through other means, for example, implicit render ones.
|
||||
def available_action?(action_name)
|
||||
method_for_action(action_name).present?
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns true if the name can be considered an action because
|
||||
# it has a method defined in the controller.
|
||||
# Returns true if the name can be considered an action. This can
|
||||
# be overridden in subclasses to modify the semantics of what
|
||||
# can be considered an action.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>name</tt> - The name of an action to be tested
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>TrueClass</tt>, <tt>FalseClass</tt>
|
||||
#
|
||||
# :api: private
|
||||
def action_method?(name)
|
||||
self.class.action_methods.include?(name)
|
||||
end
|
||||
@@ -160,9 +147,6 @@ module AbstractController
|
||||
# Call the action. Override this in a subclass to modify the
|
||||
# behavior around processing an action. This, and not #process,
|
||||
# is the intended way to override action dispatching.
|
||||
#
|
||||
# Notice that the first argument is the method to be dispatched
|
||||
# which is *not* necessarily the same as the action name.
|
||||
def process_action(method_name, *args)
|
||||
send_action(method_name, *args)
|
||||
end
|
||||
@@ -177,8 +161,8 @@ module AbstractController
|
||||
# If the action name was not found, but a method called "action_missing"
|
||||
# was found, #method_for_action will return "_handle_action_missing".
|
||||
# This method calls #action_missing with the current action name.
|
||||
def _handle_action_missing(*args)
|
||||
action_missing(@_action_name, *args)
|
||||
def _handle_action_missing
|
||||
action_missing(@_action_name)
|
||||
end
|
||||
|
||||
# Takes an action name and returns the name of the method that will
|
||||
|
||||
@@ -13,8 +13,8 @@ module AbstractController
|
||||
|
||||
# Override AbstractController::Base's process_action to run the
|
||||
# process_action callbacks around the normal behavior.
|
||||
def process_action(*args)
|
||||
run_callbacks(:process_action, action_name) do
|
||||
def process_action(method_name)
|
||||
run_callbacks(:process_action, method_name) do
|
||||
super
|
||||
end
|
||||
end
|
||||
@@ -29,7 +29,7 @@ module AbstractController
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>only</tt> - The callback should be run only for this action
|
||||
# * <tt>except</tt> - The callback should be run for all actions except this action
|
||||
# * <tt>except<tt> - The callback should be run for all actions except this action
|
||||
def _normalize_callback_options(options)
|
||||
if only = options[:only]
|
||||
only = Array(only).map {|o| "action_name == '#{o}'"}.join(" || ")
|
||||
@@ -81,29 +81,29 @@ module AbstractController
|
||||
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
||||
# Append a before, after or around filter. See _insert_callbacks
|
||||
# for details on the allowed parameters.
|
||||
def #{filter}_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options|
|
||||
options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
||||
set_callback(:process_action, :#{filter}, name, options)
|
||||
end
|
||||
end
|
||||
def #{filter}_filter(*names, &blk) # def before_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options}
|
||||
options[:if]=(Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
||||
set_callback(:process_action, :#{filter}, name, options) # set_callback(:process_action, :before_filter, name, options)
|
||||
end # end
|
||||
end # end
|
||||
|
||||
# Prepend a before, after or around filter. See _insert_callbacks
|
||||
# for details on the allowed parameters.
|
||||
def prepend_#{filter}_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options|
|
||||
options[:if] = (Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
||||
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true))
|
||||
end
|
||||
end
|
||||
def prepend_#{filter}_filter(*names, &blk) # def prepend_before_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
|
||||
options[:if]=(Array.wrap(options[:if]) << "!halted") if #{filter == :after}
|
||||
set_callback(:process_action, :#{filter}, name, options.merge(:prepend => true)) # set_callback(:process_action, :before, name, options.merge(:prepend => true))
|
||||
end # end
|
||||
end # end
|
||||
|
||||
# Skip a before, after or around filter. See _insert_callbacks
|
||||
# for details on the allowed parameters.
|
||||
def skip_#{filter}_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options|
|
||||
skip_callback(:process_action, :#{filter}, name, options)
|
||||
end
|
||||
end
|
||||
def skip_#{filter}_filter(*names, &blk) # def skip_before_filter(*names, &blk)
|
||||
_insert_callbacks(names, blk) do |name, options| # _insert_callbacks(names, blk) do |name, options|
|
||||
skip_callback(:process_action, :#{filter}, name, options) # skip_callback(:process_action, :before, name, options)
|
||||
end # end
|
||||
end # end
|
||||
|
||||
# *_filter is the same as append_*_filter
|
||||
alias_method :append_#{filter}_filter, :#{filter}_filter
|
||||
|
||||
@@ -4,6 +4,8 @@ module AbstractController
|
||||
module Helpers
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include Rendering
|
||||
|
||||
included do
|
||||
class_attribute :_helpers
|
||||
self._helpers = Module.new
|
||||
@@ -110,6 +112,17 @@ module AbstractController
|
||||
default_helper_module! unless anonymous?
|
||||
end
|
||||
|
||||
private
|
||||
# Makes all the (instance) methods in the helper module available to templates
|
||||
# rendered through this controller.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>module</tt> - The module to include into the current helper module
|
||||
# for the class
|
||||
def add_template_helper(mod)
|
||||
_helpers.module_eval { include mod }
|
||||
end
|
||||
|
||||
# Returns a list of modules, normalized from the acceptable kinds of
|
||||
# helpers with the following behavior:
|
||||
#
|
||||
@@ -142,17 +155,6 @@ module AbstractController
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
# Makes all the (instance) methods in the helper module available to templates
|
||||
# rendered through this controller.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>module</tt> - The module to include into the current helper module
|
||||
# for the class
|
||||
def add_template_helper(mod)
|
||||
_helpers.module_eval { include mod }
|
||||
end
|
||||
|
||||
def default_helper_module!
|
||||
module_name = name.sub(/Controller$/, '')
|
||||
module_path = module_name.underscore
|
||||
|
||||
@@ -81,12 +81,11 @@ module AbstractController
|
||||
# class EmployeeController < BankController
|
||||
# layout nil
|
||||
#
|
||||
# In these examples:
|
||||
# * The InformationController uses the "bank_standard" layout, inherited from BankController.
|
||||
# * The TellerController follows convention and uses +app/views/layouts/teller.html.erb+.
|
||||
# * The TillController inherits the layout from TellerController and uses +teller.html.erb+ as well.
|
||||
# * The VaultController chooses a layout dynamically by calling the <tt>access_level_layout</tt> method.
|
||||
# * The EmployeeController does not use a layout at all.
|
||||
# The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
|
||||
# and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
|
||||
#
|
||||
# The TellerController uses +teller.html.erb+, and TillController inherits that layout and
|
||||
# uses it as well.
|
||||
#
|
||||
# == Types of layouts
|
||||
#
|
||||
@@ -139,8 +138,8 @@ module AbstractController
|
||||
#
|
||||
# end
|
||||
#
|
||||
# This will assign "weblog_standard" as the WeblogController's layout for all actions except for the +rss+ action, which will
|
||||
# be rendered directly, without wrapping a layout around the rendered view.
|
||||
# This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
|
||||
# around the rendered view.
|
||||
#
|
||||
# Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
|
||||
# #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
|
||||
@@ -159,7 +158,7 @@ module AbstractController
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# This will override the controller-wide "weblog_standard" layout, and will render the help action with the "help" layout instead.
|
||||
# This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
|
||||
module Layouts
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -167,7 +166,6 @@ module AbstractController
|
||||
|
||||
included do
|
||||
class_attribute :_layout_conditions
|
||||
remove_possible_method :_layout_conditions
|
||||
delegate :_layout_conditions, :to => :'self.class'
|
||||
self._layout_conditions = {}
|
||||
_write_layout_method
|
||||
@@ -213,7 +211,7 @@ module AbstractController
|
||||
# true:: raise an ArgumentError
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>layout</tt> - The layout to use.
|
||||
# * <tt>String, Symbol, false</tt> - The layout to use.
|
||||
#
|
||||
# ==== Options (conditions)
|
||||
# * :only - A list of actions to apply this layout to.
|
||||
@@ -237,10 +235,13 @@ module AbstractController
|
||||
controller_path
|
||||
end
|
||||
|
||||
# Creates a _layout method to be called by _default_layout .
|
||||
# Takes the specified layout and creates a _layout method to be called
|
||||
# by _default_layout
|
||||
#
|
||||
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
|
||||
# if nothing is found then try same procedure to find super class's layout.
|
||||
# If there is no explicit layout specified:
|
||||
# If a layout is found in the view paths with the controller's
|
||||
# name, return that string. Otherwise, use the superclass'
|
||||
# layout (which might also be implied)
|
||||
def _write_layout_method
|
||||
remove_possible_method(:_layout)
|
||||
|
||||
@@ -267,11 +268,11 @@ module AbstractController
|
||||
raise ArgumentError, "Layouts must be specified as a String, Symbol, false, or nil"
|
||||
when nil
|
||||
if name
|
||||
_prefixes = _implied_layout_name =~ /\blayouts/ ? [] : ["layouts"]
|
||||
_prefix = "layouts" unless _implied_layout_name =~ /\blayouts/
|
||||
|
||||
self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
||||
def _layout
|
||||
if template_exists?("#{_implied_layout_name}", #{_prefixes.inspect})
|
||||
if template_exists?("#{_implied_layout_name}", #{_prefix.inspect})
|
||||
"#{_implied_layout_name}"
|
||||
else
|
||||
super
|
||||
@@ -294,15 +295,15 @@ module AbstractController
|
||||
end
|
||||
end
|
||||
|
||||
attr_internal_writer :action_has_layout
|
||||
attr_writer :action_has_layout
|
||||
|
||||
def initialize(*)
|
||||
@_action_has_layout = true
|
||||
@action_has_layout = true
|
||||
super
|
||||
end
|
||||
|
||||
def action_has_layout?
|
||||
@_action_has_layout
|
||||
@action_has_layout
|
||||
end
|
||||
|
||||
private
|
||||
@@ -310,10 +311,14 @@ module AbstractController
|
||||
# This will be overwritten by _write_layout_method
|
||||
def _layout; end
|
||||
|
||||
# Determine the layout for a given name, taking into account the name type.
|
||||
# Determine the layout for a given name and details, taking into account
|
||||
# the name type.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>name</tt> - The name of the template
|
||||
# * <tt>details</tt> - A list of details to restrict
|
||||
# the lookup to. By default, layout lookup is limited to the
|
||||
# formats specified for the current request.
|
||||
def _layout_for_option(name)
|
||||
case name
|
||||
when String then name
|
||||
@@ -326,12 +331,15 @@ module AbstractController
|
||||
end
|
||||
end
|
||||
|
||||
# Returns the default layout for this controller.
|
||||
# Returns the default layout for this controller and a given set of details.
|
||||
# Optionally raises an exception if the layout could not be found.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>require_layout</tt> - If set to true and layout is not found,
|
||||
# an ArgumentError exception is raised (defaults to false)
|
||||
# * <tt>details</tt> - A list of details to restrict the search by. This
|
||||
# might include details like the format or locale of the template.
|
||||
# * <tt>require_logout</tt> - If this is true, raise an ArgumentError
|
||||
# with details about the fact that the exception could not be
|
||||
# found (defaults to false)
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>template</tt> - The template object for the default layout (or nil)
|
||||
@@ -339,7 +347,8 @@ module AbstractController
|
||||
begin
|
||||
layout_name = _layout if action_has_layout?
|
||||
rescue NameError => e
|
||||
raise e, "Could not render layout: #{e.message}"
|
||||
raise NoMethodError,
|
||||
"You specified #{@_layout.inspect} as the layout, but no such method was found"
|
||||
end
|
||||
|
||||
if require_layout && action_has_layout? && !layout_name
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
module AbstractController
|
||||
module Railties
|
||||
module RoutesHelpers
|
||||
def self.with(routes)
|
||||
Module.new do
|
||||
define_method(:inherited) do |klass|
|
||||
super(klass)
|
||||
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
|
||||
klass.send(:include, namespace._railtie.routes.url_helpers)
|
||||
else
|
||||
klass.send(:include, routes.url_helpers)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,5 @@
|
||||
require "abstract_controller/base"
|
||||
require "action_view"
|
||||
require "active_support/core_ext/object/instance_variables"
|
||||
|
||||
module AbstractController
|
||||
class DoubleRenderError < Error
|
||||
@@ -13,16 +12,16 @@ module AbstractController
|
||||
|
||||
# This is a class to fix I18n global state. Whenever you provide I18n.locale during a request,
|
||||
# it will trigger the lookup_context and consequently expire the cache.
|
||||
# TODO Add some deprecation warnings to remove I18n.locale from controllers
|
||||
class I18nProxy < ::I18n::Config #:nodoc:
|
||||
attr_reader :original_config, :lookup_context
|
||||
attr_reader :i18n_config, :lookup_context
|
||||
|
||||
def initialize(original_config, lookup_context)
|
||||
original_config = original_config.original_config if original_config.respond_to?(:original_config)
|
||||
@original_config, @lookup_context = original_config, lookup_context
|
||||
def initialize(i18n_config, lookup_context)
|
||||
@i18n_config, @lookup_context = i18n_config, lookup_context
|
||||
end
|
||||
|
||||
def locale
|
||||
@original_config.locale
|
||||
@i18n_config.locale
|
||||
end
|
||||
|
||||
def locale=(value)
|
||||
@@ -32,12 +31,8 @@ module AbstractController
|
||||
|
||||
module Rendering
|
||||
extend ActiveSupport::Concern
|
||||
include AbstractController::ViewPaths
|
||||
|
||||
included do
|
||||
config_accessor :protected_instance_variables, :instance_reader => false
|
||||
self.protected_instance_variables = []
|
||||
end
|
||||
include AbstractController::ViewPaths
|
||||
|
||||
# Overwrite process to setup I18n proxy.
|
||||
def process(*) #:nodoc:
|
||||
@@ -50,27 +45,31 @@ module AbstractController
|
||||
module ClassMethods
|
||||
def view_context_class
|
||||
@view_context_class ||= begin
|
||||
routes = _routes if respond_to?(:_routes)
|
||||
helpers = _helpers if respond_to?(:_helpers)
|
||||
ActionView::Base.prepare(routes, helpers)
|
||||
controller = self
|
||||
Class.new(ActionView::Base) do
|
||||
if controller.respond_to?(:_routes)
|
||||
include controller._routes.url_helpers
|
||||
end
|
||||
|
||||
if controller.respond_to?(:_helpers)
|
||||
include controller._helpers
|
||||
|
||||
# TODO: Fix RJS to not require this
|
||||
self.helpers = controller._helpers
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
attr_internal_writer :view_context_class
|
||||
|
||||
# Explicitly define protected_instance_variables so it can be
|
||||
# inherited and overwritten by other modules if needed.
|
||||
def protected_instance_variables
|
||||
config.protected_instance_variables
|
||||
end
|
||||
attr_writer :view_context_class
|
||||
|
||||
def view_context_class
|
||||
@_view_context_class || self.class.view_context_class
|
||||
@view_context_class || self.class.view_context_class
|
||||
end
|
||||
|
||||
def initialize(*)
|
||||
@_view_context_class = nil
|
||||
@view_context_class = nil
|
||||
super
|
||||
end
|
||||
|
||||
@@ -84,26 +83,21 @@ module AbstractController
|
||||
#
|
||||
# Override this method in a module to change the default behavior.
|
||||
def view_context
|
||||
view_context_class.new(view_renderer, view_assigns, self)
|
||||
end
|
||||
|
||||
# Returns an object that is able to render templates.
|
||||
def view_renderer
|
||||
@_view_renderer ||= ActionView::Renderer.new(lookup_context)
|
||||
view_context_class.new(lookup_context, view_assigns, self)
|
||||
end
|
||||
|
||||
# Normalize arguments, options and then delegates render_to_body and
|
||||
# sticks the result in self.response_body.
|
||||
def render(*args, &block)
|
||||
options = _normalize_render(*args, &block)
|
||||
self.response_body = render_to_body(options)
|
||||
self.response_body = render_to_string(*args, &block)
|
||||
end
|
||||
|
||||
# Raw rendering of a template to a string. Just convert the results of
|
||||
# render_response into a String.
|
||||
# render_to_body into a String.
|
||||
# :api: plugin
|
||||
def render_to_string(*args, &block)
|
||||
options = _normalize_render(*args, &block)
|
||||
options = _normalize_args(*args, &block)
|
||||
_normalize_options(options)
|
||||
render_to_body(options)
|
||||
end
|
||||
|
||||
@@ -117,13 +111,15 @@ module AbstractController
|
||||
# Find and renders a template based on the options given.
|
||||
# :api: private
|
||||
def _render_template(options) #:nodoc:
|
||||
view_renderer.render(view_context, options)
|
||||
view_context.render(options)
|
||||
end
|
||||
|
||||
DEFAULT_PROTECTED_INSTANCE_VARIABLES = %w(
|
||||
@_action_name @_response_body @_formats @_prefixes @_config
|
||||
@_view_context_class @_view_renderer @_lookup_context
|
||||
)
|
||||
# The prefix used in render "foo" shortcuts.
|
||||
def _prefix
|
||||
controller_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# This method should return a hash with assigns.
|
||||
# You can overwrite this configuration per controller.
|
||||
@@ -131,58 +127,42 @@ module AbstractController
|
||||
def view_assigns
|
||||
hash = {}
|
||||
variables = instance_variable_names
|
||||
variables -= protected_instance_variables
|
||||
variables -= DEFAULT_PROTECTED_INSTANCE_VARIABLES
|
||||
variables.each { |name| hash[name.to_s[1, name.length]] = instance_variable_get(name) }
|
||||
variables -= protected_instance_variables if respond_to?(:protected_instance_variables)
|
||||
variables.each { |name| hash[name.to_s[1..-1]] = instance_variable_get(name) }
|
||||
hash
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Normalize args and options.
|
||||
# :api: private
|
||||
def _normalize_render(*args, &block)
|
||||
options = _normalize_args(*args, &block)
|
||||
_normalize_options(options)
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize args by converting render "foo" to render :action => "foo" and
|
||||
# Normalize options by converting render "foo" to render :action => "foo" and
|
||||
# render "foo/bar" to render :file => "foo/bar".
|
||||
# :api: plugin
|
||||
def _normalize_args(action=nil, options={})
|
||||
case action
|
||||
when NilClass
|
||||
when Hash
|
||||
options = action
|
||||
options, action = action, nil
|
||||
when String, Symbol
|
||||
action = action.to_s
|
||||
key = action.include?(?/) ? :file : :action
|
||||
options[key] = action
|
||||
else
|
||||
options[:partial] = action
|
||||
options.merge!(:partial => action)
|
||||
end
|
||||
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize options.
|
||||
# :api: plugin
|
||||
def _normalize_options(options)
|
||||
if options[:partial] == true
|
||||
options[:partial] = action_name
|
||||
end
|
||||
|
||||
if (options.keys & [:partial, :file, :template]).empty?
|
||||
options[:prefixes] ||= _prefixes
|
||||
options[:prefix] ||= _prefix
|
||||
end
|
||||
|
||||
options[:template] ||= (options[:action] || action_name).to_s
|
||||
options
|
||||
end
|
||||
|
||||
# Process extra options.
|
||||
# :api: plugin
|
||||
def _process_options(options)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
# Includes +url_for+ into the host class (e.g. an abstract controller or mailer). The class
|
||||
# has to provide a +RouteSet+ by implementing the <tt>_routes</tt> methods. Otherwise, an
|
||||
# exception will be raised.
|
||||
#
|
||||
# Note that this module is completely decoupled from HTTP - the only requirement is a valid
|
||||
# <tt>_routes</tt> implementation.
|
||||
module AbstractController
|
||||
module UrlFor
|
||||
extend ActiveSupport::Concern
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
|
||||
def _routes
|
||||
raise "In order to use #url_for, you must include routing helpers explicitly. " \
|
||||
"For instance, `include Rails.application.routes.url_helpers"
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def _routes
|
||||
nil
|
||||
end
|
||||
|
||||
def action_methods
|
||||
@action_methods ||= begin
|
||||
if _routes
|
||||
super - _routes.named_routes.helper_names
|
||||
else
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -11,36 +11,11 @@ module AbstractController
|
||||
delegate :find_template, :template_exists?, :view_paths, :formats, :formats=,
|
||||
:locale, :locale=, :to => :lookup_context
|
||||
|
||||
module ClassMethods
|
||||
def parent_prefixes
|
||||
@parent_prefixes ||= begin
|
||||
parent_controller = superclass
|
||||
prefixes = []
|
||||
|
||||
until parent_controller.abstract?
|
||||
prefixes << parent_controller.controller_path
|
||||
parent_controller = parent_controller.superclass
|
||||
end
|
||||
|
||||
prefixes
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# The prefixes used in render "foo" shortcuts.
|
||||
def _prefixes
|
||||
@_prefixes ||= begin
|
||||
parent_prefixes = self.class.parent_prefixes
|
||||
parent_prefixes.dup.unshift(controller_path)
|
||||
end
|
||||
end
|
||||
|
||||
# LookupContext is the object responsible to hold all information required to lookup
|
||||
# templates, i.e. view paths and details. Check ActionView::LookupContext for more
|
||||
# information.
|
||||
def lookup_context
|
||||
@_lookup_context ||=
|
||||
ActionView::LookupContext.new(self.class._view_paths, details_for_lookup, _prefixes)
|
||||
@lookup_context ||= ActionView::LookupContext.new(self.class._view_paths, details_for_lookup)
|
||||
end
|
||||
|
||||
def details_for_lookup
|
||||
@@ -61,7 +36,7 @@ module AbstractController
|
||||
# ==== Parameters
|
||||
# * <tt>path</tt> - If a String is provided, it gets converted into
|
||||
# the default view path. You may also provide a custom view path
|
||||
# (see ActionView::PathSet for more information)
|
||||
# (see ActionView::ViewPathSet for more information)
|
||||
def append_view_path(path)
|
||||
self.view_paths = view_paths.dup + Array(path)
|
||||
end
|
||||
@@ -71,7 +46,7 @@ module AbstractController
|
||||
# ==== Parameters
|
||||
# * <tt>path</tt> - If a String is provided, it gets converted into
|
||||
# the default view path. You may also provide a custom view path
|
||||
# (see ActionView::PathSet for more information)
|
||||
# (see ActionView::ViewPathSet for more information)
|
||||
def prepend_view_path(path)
|
||||
self.view_paths = Array(path) + view_paths.dup
|
||||
end
|
||||
@@ -84,12 +59,12 @@ module AbstractController
|
||||
# Set the view paths.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>paths</tt> - If a PathSet is provided, use that;
|
||||
# otherwise, process the parameter into a PathSet.
|
||||
# * <tt>paths</tt> - If a ViewPathSet is provided, use that;
|
||||
# otherwise, process the parameter into a ViewPathSet.
|
||||
def view_paths=(paths)
|
||||
self._view_paths = ActionView::Base.process_view_paths(paths)
|
||||
self._view_paths.freeze
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -13,9 +13,7 @@ module ActionController
|
||||
autoload :Compatibility
|
||||
autoload :ConditionalGet
|
||||
autoload :Cookies
|
||||
autoload :DataStreaming
|
||||
autoload :Flash
|
||||
autoload :ForceSSL
|
||||
autoload :Head
|
||||
autoload :Helpers
|
||||
autoload :HideActions
|
||||
@@ -23,7 +21,6 @@ module ActionController
|
||||
autoload :ImplicitRender
|
||||
autoload :Instrumentation
|
||||
autoload :MimeResponds
|
||||
autoload :ParamsWrapper
|
||||
autoload :RackDelegation
|
||||
autoload :Redirecting
|
||||
autoload :Renderers
|
||||
@@ -37,13 +34,14 @@ module ActionController
|
||||
autoload :UrlFor
|
||||
end
|
||||
|
||||
autoload :Integration, 'action_controller/deprecated/integration_test'
|
||||
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
|
||||
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
|
||||
autoload :UrlWriter, 'action_controller/deprecated'
|
||||
autoload :Routing, 'action_controller/deprecated'
|
||||
autoload :TestCase, 'action_controller/test_case'
|
||||
autoload :TemplateAssertions, 'action_controller/test_case'
|
||||
autoload :Dispatcher, 'action_controller/deprecated/dispatcher'
|
||||
autoload :UrlWriter, 'action_controller/deprecated/url_writer'
|
||||
autoload :UrlRewriter, 'action_controller/deprecated/url_writer'
|
||||
autoload :Integration, 'action_controller/deprecated/integration_test'
|
||||
autoload :IntegrationTest, 'action_controller/deprecated/integration_test'
|
||||
autoload :PerformanceTest, 'action_controller/deprecated/performance_test'
|
||||
autoload :Routing, 'action_controller/deprecated'
|
||||
autoload :TestCase, 'action_controller/test_case'
|
||||
|
||||
eager_autoload do
|
||||
autoload :RecordIdentifier
|
||||
@@ -76,5 +74,4 @@ require 'active_support/core_ext/load_error'
|
||||
require 'active_support/core_ext/module/attr_internal'
|
||||
require 'active_support/core_ext/module/delegation'
|
||||
require 'active_support/core_ext/name_error'
|
||||
require 'active_support/core_ext/uri'
|
||||
require 'active_support/inflector'
|
||||
|
||||
@@ -24,14 +24,14 @@ module ActionController
|
||||
#
|
||||
# Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
|
||||
# after executing code in the action. For example, the +index+ action of the PostsController would render the
|
||||
# template <tt>app/views/posts/index.html.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
|
||||
# template <tt>app/views/posts/index.erb</tt> by default after populating the <tt>@posts</tt> instance variable.
|
||||
#
|
||||
# Unlike index, the create action will not render a template. After performing its main purpose (creating a
|
||||
# new post), it initiates a redirect instead. This redirect works by returning an external
|
||||
# "302 Moved" HTTP response that takes the user to the index action.
|
||||
#
|
||||
# These two methods represent the two basic action archetypes used in Action Controllers. Get-and-show and do-and-redirect.
|
||||
# Most actions are variations on these themes.
|
||||
# Most actions are variations of these themes.
|
||||
#
|
||||
# == Requests
|
||||
#
|
||||
@@ -50,7 +50,7 @@ module ActionController
|
||||
#
|
||||
# All request parameters, whether they come from a GET or POST request, or from the URL, are available through the params method
|
||||
# which returns a hash. For example, an action that was performed through <tt>/posts?category=All&limit=5</tt> will include
|
||||
# <tt>{ "category" => "All", "limit" => "5" }</tt> in params.
|
||||
# <tt>{ "category" => "All", "limit" => 5 }</tt> in params.
|
||||
#
|
||||
# It's also possible to construct multi-dimensional parameter hashes by specifying keys using brackets, such as:
|
||||
#
|
||||
@@ -63,7 +63,7 @@ module ActionController
|
||||
#
|
||||
# == Sessions
|
||||
#
|
||||
# Sessions allow you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
|
||||
# Sessions allows you to store objects in between requests. This is useful for objects that are not yet ready to be persisted,
|
||||
# such as a Signup object constructed in a multi-paged process, or objects that don't change much and are needed all the time, such
|
||||
# as a User object for a system that requires login. The session should not be used, however, as a cache for objects where it's likely
|
||||
# they could be changed unknowingly. It's usually too much work to keep it all synchronized -- something databases already excel at.
|
||||
@@ -93,9 +93,9 @@ module ActionController
|
||||
# * ActiveRecord::SessionStore - Sessions are stored in your database, which works better than PStore with multiple app servers and,
|
||||
# unlike CookieStore, hides your session contents from the user. To use ActiveRecord::SessionStore, set
|
||||
#
|
||||
# MyApplication::Application.config.session_store :active_record_store
|
||||
# config.action_controller.session_store = :active_record_store
|
||||
#
|
||||
# in your <tt>config/initializers/session_store.rb</tt> and run <tt>script/rails g session_migration</tt>.
|
||||
# in your <tt>config/environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
|
||||
#
|
||||
# == Responses
|
||||
#
|
||||
@@ -105,7 +105,7 @@ module ActionController
|
||||
# == Renders
|
||||
#
|
||||
# Action Controller sends content to the user by using one of five rendering methods. The most versatile and common is the rendering
|
||||
# of a template. Included in the Action Pack is the Action View, which enables rendering of ERB templates. It's automatically configured.
|
||||
# of a template. Included in the Action Pack is the Action View, which enables rendering of ERb templates. It's automatically configured.
|
||||
# The controller passes objects to the view by assigning instance variables:
|
||||
#
|
||||
# def show
|
||||
@@ -116,8 +116,8 @@ module ActionController
|
||||
#
|
||||
# Title: <%= @post.title %>
|
||||
#
|
||||
# You don't have to rely on the automated rendering. For example, actions that could result in the rendering of different templates
|
||||
# will use the manual rendering methods:
|
||||
# You don't have to rely on the automated rendering. Especially actions that could result in the rendering of different templates will use
|
||||
# the manual rendering methods:
|
||||
#
|
||||
# def search
|
||||
# @results = Search.find(params[:query])
|
||||
@@ -128,13 +128,13 @@ module ActionController
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Read more about writing ERB and Builder templates in ActionView::Base.
|
||||
# Read more about writing ERb and Builder templates in ActionView::Base.
|
||||
#
|
||||
# == Redirects
|
||||
#
|
||||
# Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to the
|
||||
# database, we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're
|
||||
# going to reuse (and redirect to) a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
|
||||
# Redirects are used to move from one action to another. For example, after a <tt>create</tt> action, which stores a blog entry to a database,
|
||||
# we might like to show the user the new entry. Because we're following good DRY principles (Don't Repeat Yourself), we're going to reuse (and redirect to)
|
||||
# a <tt>show</tt> action that we'll assume has already been created. The code might look like this:
|
||||
#
|
||||
# def create
|
||||
# @entry = Entry.new(params[:entry])
|
||||
@@ -146,9 +146,7 @@ module ActionController
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method, which is then executed.
|
||||
# Note that this is an external HTTP-level redirection which will cause the browser to make a second request (a GET to the show action),
|
||||
# and not some internal re-routing which calls both "create" and then "show" within one request.
|
||||
# In this case, after saving our new entry to the database, the user is redirected to the <tt>show</tt> method which is then executed.
|
||||
#
|
||||
# Learn more about <tt>redirect_to</tt> and what options you have in ActionController::Redirecting.
|
||||
#
|
||||
@@ -200,28 +198,22 @@ module ActionController
|
||||
Cookies,
|
||||
Flash,
|
||||
RequestForgeryProtection,
|
||||
ForceSSL,
|
||||
Streaming,
|
||||
DataStreaming,
|
||||
RecordIdentifier,
|
||||
HttpAuthentication::Basic::ControllerMethods,
|
||||
HttpAuthentication::Digest::ControllerMethods,
|
||||
HttpAuthentication::Token::ControllerMethods,
|
||||
|
||||
# Before callbacks should also be executed the earliest as possible, so
|
||||
# also include them at the bottom.
|
||||
AbstractController::Callbacks,
|
||||
|
||||
# Append rescue at the bottom to wrap as much as possible.
|
||||
Rescue,
|
||||
|
||||
# Add instrumentations hooks at the bottom, to ensure they instrument
|
||||
# all the methods properly.
|
||||
Instrumentation,
|
||||
|
||||
# Params wrapper should come before instrumentation so they are
|
||||
# properly showed in logs
|
||||
ParamsWrapper
|
||||
# Before callbacks should also be executed the earliest as possible, so
|
||||
# also include them at the bottom.
|
||||
AbstractController::Callbacks,
|
||||
|
||||
# The same with rescue, append it at the end to wrap as much as possible.
|
||||
Rescue
|
||||
]
|
||||
|
||||
MODULES.each do |mod|
|
||||
@@ -231,6 +223,13 @@ module ActionController
|
||||
# Rails 2.x compatibility
|
||||
include ActionController::Compatibility
|
||||
|
||||
def self.inherited(klass)
|
||||
super
|
||||
klass.helper :all if klass.superclass == ActionController::Base
|
||||
end
|
||||
|
||||
require "action_controller/deprecated/base"
|
||||
ActiveSupport.run_load_hooks(:action_controller, self)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ require 'uri'
|
||||
require 'set'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
# \Caching is a cheap way of speeding up slow applications by keeping the result of
|
||||
# Caching is a cheap way of speeding up slow applications by keeping the result of
|
||||
# calculations, renderings, and database calls around for subsequent requests.
|
||||
# Action Controller affords you three approaches in varying levels of granularity:
|
||||
# Page, Action, Fragment.
|
||||
@@ -14,7 +14,7 @@ module ActionController #:nodoc:
|
||||
# Note: To turn off all caching and sweeping, set
|
||||
# config.action_controller.perform_caching = false.
|
||||
#
|
||||
# == \Caching stores
|
||||
# == Caching stores
|
||||
#
|
||||
# All the caching stores from ActiveSupport::Cache are available to be used as backends
|
||||
# for Action Controller caching. This setting only affects action and fragment caching
|
||||
|
||||
@@ -4,86 +4,73 @@ module ActionController #:nodoc:
|
||||
module Caching
|
||||
# Action caching is similar to page caching by the fact that the entire
|
||||
# output of the response is cached, but unlike page caching, every
|
||||
# request still goes through Action Pack. The key benefit of this is
|
||||
# that filters run before the cache is served, which allows for
|
||||
# authentication and other restrictions on whether someone is allowed
|
||||
# to execute such action. Example:
|
||||
# request still goes through the Action Pack. The key benefit
|
||||
# of this is that filters are run before the cache is served, which
|
||||
# allows for authentication and other restrictions on whether someone
|
||||
# is allowed to see the cache. Example:
|
||||
#
|
||||
# class ListsController < ApplicationController
|
||||
# before_filter :authenticate, :except => :public
|
||||
#
|
||||
# caches_page :public
|
||||
# caches_action :index, :show
|
||||
# caches_action :index, :show, :feed
|
||||
# end
|
||||
#
|
||||
# In this example, the +public+ action doesn't require authentication
|
||||
# so it's possible to use the faster page caching. On the other hand
|
||||
# +index+ and +show+ require authentication. They can still be cached,
|
||||
# but we need action caching for them.
|
||||
# In this example, the public action doesn't require authentication,
|
||||
# so it's possible to use the faster page caching method. But both
|
||||
# the show and feed action are to be shielded behind the authenticate
|
||||
# filter, so we need to implement those as action caches.
|
||||
#
|
||||
# Action caching uses fragment caching internally and an around
|
||||
# filter to do the job. The fragment cache is named according to
|
||||
# the host and path of the request. A page that is accessed at
|
||||
# <tt>http://david.example.com/lists/show/1</tt> will result in a fragment named
|
||||
# <tt>david.example.com/lists/show/1</tt>. This allows the cacher to
|
||||
# differentiate between <tt>david.example.com/lists/</tt> and
|
||||
# <tt>jamis.example.com/lists/</tt> -- which is a helpful way of assisting
|
||||
# Action caching internally uses the fragment caching and an around
|
||||
# filter to do the job. The fragment cache is named according to both
|
||||
# the current host and the path. So a page that is accessed at
|
||||
# http://david.somewhere.com/lists/show/1 will result in a fragment named
|
||||
# "david.somewhere.com/lists/show/1". This allows the cacher to
|
||||
# differentiate between "david.somewhere.com/lists/" and
|
||||
# "jamis.somewhere.com/lists/" -- which is a helpful way of assisting
|
||||
# the subdomain-as-account-key pattern.
|
||||
#
|
||||
# Different representations of the same resource, e.g.
|
||||
# <tt>http://david.example.com/lists</tt> and
|
||||
# <tt>http://david.example.com/lists.xml</tt>
|
||||
# <tt>http://david.somewhere.com/lists</tt> and
|
||||
# <tt>http://david.somewhere.com/lists.xml</tt>
|
||||
# are treated like separate requests and so are cached separately.
|
||||
# Keep in mind when expiring an action cache that
|
||||
# <tt>:action => 'lists'</tt> is not the same as
|
||||
# <tt>:action => 'list', :format => :xml</tt>.
|
||||
#
|
||||
# You can set modify the default action cache path by passing a
|
||||
# <tt>:cache_path</tt> option. This will be passed directly to
|
||||
# <tt>ActionCachePath.path_for</tt>. This is handy for actions with
|
||||
# multiple possible routes that should be cached differently. If a
|
||||
# block is given, it is called with the current controller instance.
|
||||
# :cache_path option. This will be passed directly to
|
||||
# ActionCachePath.path_for. This is handy for actions with multiple
|
||||
# possible routes that should be cached differently. If a block is
|
||||
# given, it is called with the current controller instance.
|
||||
#
|
||||
# And you can also use <tt>:if</tt> (or <tt>:unless</tt>) to pass a
|
||||
# proc that specifies when the action should be cached.
|
||||
# And you can also use :if (or :unless) to pass a Proc that
|
||||
# specifies when the action should be cached.
|
||||
#
|
||||
# Finally, if you are using memcached, you can also pass <tt>:expires_in</tt>.
|
||||
#
|
||||
# The following example depicts some of the points made above:
|
||||
# Finally, if you are using memcached, you can also pass :expires_in.
|
||||
#
|
||||
# class ListsController < ApplicationController
|
||||
# before_filter :authenticate, :except => :public
|
||||
#
|
||||
# caches_page :public
|
||||
#
|
||||
# caches_action :index, :if => proc do
|
||||
# !request.format.json? # cache if is not a JSON request
|
||||
# caches_page :public
|
||||
# caches_action :index, :if => proc do |c|
|
||||
# !c.request.format.json? # cache if is not a JSON request
|
||||
# end
|
||||
#
|
||||
# caches_action :show, :cache_path => { :project => 1 },
|
||||
# :expires_in => 1.hour
|
||||
#
|
||||
# caches_action :feed, :cache_path => proc do
|
||||
# if params[:user_id]
|
||||
# user_list_url(params[:user_id, params[:id])
|
||||
# caches_action :feed, :cache_path => proc do |controller|
|
||||
# if controller.params[:user_id]
|
||||
# controller.send(:user_list_url,
|
||||
# controller.params[:user_id], controller.params[:id])
|
||||
# else
|
||||
# list_url(params[:id])
|
||||
# controller.send(:list_url, controller.params[:id])
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# If you pass <tt>:layout => false</tt>, it will only cache your action
|
||||
# content. That's useful when your layout has dynamic information.
|
||||
# If you pass :layout => false, it will only cache your action
|
||||
# content. It is useful when your layout has dynamic information.
|
||||
#
|
||||
# Warning: If the format of the request is determined by the Accept HTTP
|
||||
# header the Content-Type of the cached response could be wrong because
|
||||
# no information about the MIME type is stored in the cache key. So, if
|
||||
# you first ask for MIME type M in the Accept header, a cache entry is
|
||||
# created, and then perform a second request to the same resource asking
|
||||
# for a different MIME type, you'd get the content cached for M.
|
||||
#
|
||||
# The <tt>:format</tt> parameter is taken into account though. The safest
|
||||
# way to cache by MIME type is to pass the format in the route.
|
||||
module Actions
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -102,14 +89,12 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def _save_fragment(name, options)
|
||||
return unless caching_allowed?
|
||||
|
||||
content = response_body
|
||||
content = content.join if content.is_a?(Array)
|
||||
|
||||
if caching_allowed?
|
||||
write_fragment(name, content, options)
|
||||
else
|
||||
content
|
||||
end
|
||||
write_fragment(name, content, options)
|
||||
end
|
||||
|
||||
protected
|
||||
@@ -159,7 +144,7 @@ module ActionController #:nodoc:
|
||||
attr_reader :path, :extension
|
||||
|
||||
# If +infer_extension+ is true, the cache path extension is looked up from the request's
|
||||
# path and format. This is desirable when reading and writing the cache, but not when
|
||||
# path & format. This is desirable when reading and writing the cache, but not when
|
||||
# expiring the cache - expire_action should expire the same files regardless of the
|
||||
# request format.
|
||||
def initialize(controller, options = {}, infer_extension = true)
|
||||
@@ -175,8 +160,8 @@ module ActionController #:nodoc:
|
||||
private
|
||||
def normalize!(path)
|
||||
path << 'index' if path[-1] == ?/
|
||||
path << ".#{extension}" if extension and !path.split('?').first.ends_with?(".#{extension}")
|
||||
URI.parser.unescape(path)
|
||||
path << ".#{extension}" if extension and !path.ends_with?(extension)
|
||||
URI.unescape(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,72 +1,52 @@
|
||||
module ActionController #:nodoc:
|
||||
module Caching
|
||||
# Fragment caching is used for caching various blocks within
|
||||
# views without caching the entire action as a whole. This is
|
||||
# useful when certain elements of an action change frequently or
|
||||
# depend on complicated state while other parts rarely change or
|
||||
# can be shared amongst multiple parties. The caching is done using
|
||||
# the <tt>cache</tt> helper available in the Action View. A
|
||||
# template with fragment caching might look like:
|
||||
# Fragment caching is used for caching various blocks within templates without caching the entire action as a whole. This is useful when
|
||||
# certain elements of an action change frequently or depend on complicated state while other parts rarely change or can be shared amongst multiple
|
||||
# parties. The caching is done using the cache helper available in the Action View. A template with caching might look something like:
|
||||
#
|
||||
# <b>Hello <%= @name %></b>
|
||||
#
|
||||
# <% cache do %>
|
||||
# All the topics in the system:
|
||||
# <%= render :partial => "topic", :collection => Topic.find(:all) %>
|
||||
# <% end %>
|
||||
#
|
||||
# This cache will bind the name of the action that called it, so if
|
||||
# this code was part of the view for the topics/list action, you
|
||||
# would be able to invalidate it using:
|
||||
#
|
||||
# expire_fragment(:controller => "topics", :action => "list")
|
||||
# This cache will bind to the name of the action that called it, so if this code was part of the view for the topics/list action, you would
|
||||
# be able to invalidate it using <tt>expire_fragment(:controller => "topics", :action => "list")</tt>.
|
||||
#
|
||||
# This default behavior is limited if you need to cache multiple
|
||||
# fragments per action or if the action itself is cached using
|
||||
# <tt>caches_action</tt>. To remedy this, there is an option to
|
||||
# qualify the name of the cached fragment by using the
|
||||
# <tt>:action_suffix</tt> option:
|
||||
# This default behavior is of limited use if you need to cache multiple fragments per action or if the action itself is cached using
|
||||
# <tt>caches_action</tt>, so we also have the option to qualify the name of the cached fragment with something like:
|
||||
#
|
||||
# <% cache(:action => "list", :action_suffix => "all_topics") do %>
|
||||
#
|
||||
# That would result in a name such as
|
||||
# <tt>/topics/list/all_topics</tt>, avoiding conflicts with the
|
||||
# action cache and with any fragments that use a different suffix.
|
||||
# Note that the URL doesn't have to really exist or be callable
|
||||
# - the url_for system is just used to generate unique cache names
|
||||
# that we can refer to when we need to expire the cache.
|
||||
# That would result in a name such as "/topics/list/all_topics", avoiding conflicts with the action cache and with any fragments that use a
|
||||
# different suffix. Note that the URL doesn't have to really exist or be callable - the url_for system is just used to generate unique
|
||||
# cache names that we can refer to when we need to expire the cache.
|
||||
#
|
||||
# The expiration call for this example is:
|
||||
#
|
||||
# expire_fragment(:controller => "topics",
|
||||
# :action => "list",
|
||||
# :action_suffix => "all_topics")
|
||||
# expire_fragment(:controller => "topics", :action => "list", :action_suffix => "all_topics")
|
||||
module Fragments
|
||||
# Given a key (as described in <tt>expire_fragment</tt>), returns
|
||||
# a key suitable for use in reading, writing, or expiring a
|
||||
# cached fragment. If the key is a hash, the generated key is the
|
||||
# return value of url_for on that hash (without the protocol).
|
||||
# All keys are prefixed with <tt>views/</tt> and uses
|
||||
# Given a key (as described in <tt>expire_fragment</tt>), returns a key suitable for use in reading,
|
||||
# writing, or expiring a cached fragment. If the key is a hash, the generated key is the return
|
||||
# value of url_for on that hash (without the protocol). All keys are prefixed with "views/" and uses
|
||||
# ActiveSupport::Cache.expand_cache_key for the expansion.
|
||||
def fragment_cache_key(key)
|
||||
ActiveSupport::Cache.expand_cache_key(key.is_a?(Hash) ? url_for(key).split("://").last : key, :views)
|
||||
end
|
||||
|
||||
# Writes <tt>content</tt> to the location signified by
|
||||
# <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats).
|
||||
# Writes <tt>content</tt> to the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
|
||||
def write_fragment(key, content, options = nil)
|
||||
return content unless cache_configured?
|
||||
|
||||
key = fragment_cache_key(key)
|
||||
instrument_fragment_cache :write_fragment, key do
|
||||
content = content.to_str
|
||||
content = content.html_safe.to_str if content.respond_to?(:html_safe)
|
||||
cache_store.write(key, content, options)
|
||||
end
|
||||
content
|
||||
end
|
||||
|
||||
# Reads a cached fragment from the location signified by <tt>key</tt>
|
||||
# (see <tt>expire_fragment</tt> for acceptable formats).
|
||||
# Reads a cached fragment from the location signified by <tt>key</tt> (see <tt>expire_fragment</tt> for acceptable formats)
|
||||
def read_fragment(key, options = nil)
|
||||
return unless cache_configured?
|
||||
|
||||
@@ -77,8 +57,7 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# Check if a cached fragment from the location signified by
|
||||
# <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
|
||||
# Check if a cached fragment from the location signified by <tt>key</tt> exists (see <tt>expire_fragment</tt> for acceptable formats)
|
||||
def fragment_exist?(key, options = nil)
|
||||
return unless cache_configured?
|
||||
key = fragment_cache_key(key)
|
||||
@@ -91,9 +70,8 @@ module ActionController #:nodoc:
|
||||
# Removes fragments from the cache.
|
||||
#
|
||||
# +key+ can take one of three forms:
|
||||
#
|
||||
# * String - This would normally take the form of a path, like
|
||||
# <tt>pages/45/notes</tt>.
|
||||
# <tt>"pages/45/notes"</tt>.
|
||||
# * Hash - Treated as an implicit call to +url_for+, like
|
||||
# <tt>{:controller => "pages", :action => "notes", :id => 45}</tt>
|
||||
# * Regexp - Will remove any fragment that matches, so
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
require 'fileutils'
|
||||
require 'uri'
|
||||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
@@ -70,9 +71,9 @@ module ActionController #:nodoc:
|
||||
|
||||
# Manually cache the +content+ in the key determined by +path+. Example:
|
||||
# cache_page "I'm the cached content", "/lists/show"
|
||||
def cache_page(content, path, extension = nil)
|
||||
def cache_page(content, path)
|
||||
return unless perform_caching
|
||||
path = page_cache_path(path, extension)
|
||||
path = page_cache_path(path)
|
||||
|
||||
instrument_page_cache :write_page, path do
|
||||
FileUtils.makedirs(File.dirname(path))
|
||||
@@ -97,16 +98,14 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
private
|
||||
def page_cache_file(path, extension)
|
||||
name = (path.empty? || path == "/") ? "/index" : URI.parser.unescape(path.chomp('/'))
|
||||
unless (name.split('/').last || name).include? '.'
|
||||
name << (extension || self.page_cache_extension)
|
||||
end
|
||||
def page_cache_file(path)
|
||||
name = (path.empty? || path == "/") ? "/index" : URI.unescape(path.chomp('/'))
|
||||
name << page_cache_extension unless (name.split('/').last || name).include? '.'
|
||||
return name
|
||||
end
|
||||
|
||||
def page_cache_path(path, extension = nil)
|
||||
page_cache_directory.to_s + page_cache_file(path, extension)
|
||||
def page_cache_path(path)
|
||||
page_cache_directory + page_cache_file(path)
|
||||
end
|
||||
|
||||
def instrument_page_cache(name, path)
|
||||
@@ -136,7 +135,7 @@ module ActionController #:nodoc:
|
||||
# If no options are provided, the requested url is used. Example:
|
||||
# cache_page "I'm the cached content", :controller => "lists", :action => "show"
|
||||
def cache_page(content = nil, options = nil)
|
||||
return unless self.class.perform_caching && caching_allowed?
|
||||
return unless self.class.perform_caching && caching_allowed
|
||||
|
||||
path = case options
|
||||
when Hash
|
||||
@@ -147,13 +146,13 @@ module ActionController #:nodoc:
|
||||
request.path
|
||||
end
|
||||
|
||||
if (type = Mime::LOOKUP[self.content_type]) && (type_symbol = type.symbol).present?
|
||||
extension = ".#{type_symbol}"
|
||||
end
|
||||
|
||||
self.class.cache_page(content || response.body, path, extension)
|
||||
self.class.cache_page(content || response.body, path)
|
||||
end
|
||||
|
||||
private
|
||||
def caching_allowed
|
||||
request.get? && response.status.to_i == 200
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -61,7 +61,6 @@ module ActionController #:nodoc:
|
||||
end
|
||||
|
||||
def after(controller)
|
||||
self.controller = controller
|
||||
callback(:after) if controller.perform_caching
|
||||
# Clean up, so that the controller can be collected after this request
|
||||
self.controller = nil
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
ActionController::AbstractRequest = ActionController::Request = ActionDispatch::Request
|
||||
ActionController::AbstractResponse = ActionController::Response = ActionDispatch::Response
|
||||
ActionController::Routing = ActionDispatch::Routing
|
||||
ActionController::Routing = ActionDispatch::Routing
|
||||
|
||||
143
actionpack/lib/action_controller/deprecated/base.rb
Normal file
143
actionpack/lib/action_controller/deprecated/base.rb
Normal file
@@ -0,0 +1,143 @@
|
||||
module ActionController
|
||||
class Base
|
||||
# Deprecated methods. Wrap them in a module so they can be overwritten by plugins
|
||||
# (like the verify method.)
|
||||
module DeprecatedBehavior #:nodoc:
|
||||
def relative_url_root
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root is ineffective. " <<
|
||||
"Please stop using it.", caller
|
||||
end
|
||||
|
||||
def relative_url_root=
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.relative_url_root= is ineffective. " <<
|
||||
"Please stop using it.", caller
|
||||
end
|
||||
|
||||
def consider_all_requests_local
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local is deprecated, " <<
|
||||
"use Rails.application.config.consider_all_requests_local instead", caller
|
||||
Rails.application.config.consider_all_requests_local
|
||||
end
|
||||
|
||||
def consider_all_requests_local=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.consider_all_requests_local= is deprecated. " <<
|
||||
"Please configure it on your application with config.consider_all_requests_local=", caller
|
||||
Rails.application.config.consider_all_requests_local = value
|
||||
end
|
||||
|
||||
def allow_concurrency
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency is deprecated, " <<
|
||||
"use Rails.application.config.allow_concurrency instead", caller
|
||||
Rails.application.config.allow_concurrency
|
||||
end
|
||||
|
||||
def allow_concurrency=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.allow_concurrency= is deprecated. " <<
|
||||
"Please configure it on your application with config.allow_concurrency=", caller
|
||||
Rails.application.config.allow_concurrency = value
|
||||
end
|
||||
|
||||
def ip_spoofing_check=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check= is deprecated. " <<
|
||||
"Please configure it on your application with config.action_dispatch.ip_spoofing_check=", caller
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check = value
|
||||
end
|
||||
|
||||
def ip_spoofing_check
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.ip_spoofing_check is deprecated. " <<
|
||||
"Configuring ip_spoofing_check on the application configures a middleware.", caller
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check
|
||||
end
|
||||
|
||||
def cookie_verifier_secret=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret= is deprecated. " <<
|
||||
"Please configure it on your application with config.secret_token=", caller
|
||||
end
|
||||
|
||||
def cookie_verifier_secret
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.cookie_verifier_secret is deprecated.", caller
|
||||
end
|
||||
|
||||
def trusted_proxies=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies= is deprecated. " <<
|
||||
"Please configure it on your application with config.action_dispatch.trusted_proxies=", caller
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check = value
|
||||
end
|
||||
|
||||
def trusted_proxies
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.trusted_proxies is deprecated. " <<
|
||||
"Configuring trusted_proxies on the application configures a middleware.", caller
|
||||
Rails.application.config.action_dispatch.ip_spoofing_check = value
|
||||
end
|
||||
|
||||
def session(*args)
|
||||
ActiveSupport::Deprecation.warn(
|
||||
"Disabling sessions for a single controller has been deprecated. " +
|
||||
"Sessions are now lazy loaded. So if you don't access them, " +
|
||||
"consider them off. You can still modify the session cookie " +
|
||||
"options with request.session_options.", caller)
|
||||
end
|
||||
|
||||
def session=(value)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.session= is deprecated. " <<
|
||||
"Please configure it on your application with config.session_store :cookie_store, :key => '....'", caller
|
||||
|
||||
if secret = value.delete(:secret)
|
||||
Rails.application.config.secret_token = secret
|
||||
end
|
||||
|
||||
if value.delete(:disabled)
|
||||
Rails.application.config.session_store :disabled
|
||||
else
|
||||
store = Rails.application.config.session_store
|
||||
Rails.application.config.session_store store, value
|
||||
end
|
||||
end
|
||||
|
||||
# Controls the resource action separator
|
||||
def resource_action_separator
|
||||
@resource_action_separator ||= "/"
|
||||
end
|
||||
|
||||
def resource_action_separator=(val)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.resource_action_separator is deprecated and only " \
|
||||
"works with the deprecated router DSL."
|
||||
@resource_action_separator = val
|
||||
end
|
||||
|
||||
def use_accept_header
|
||||
ActiveSupport::Deprecation.warn "ActionController::Base.use_accept_header doesn't do anything anymore. " \
|
||||
"The accept header is always taken into account."
|
||||
end
|
||||
|
||||
def use_accept_header=(val)
|
||||
use_accept_header
|
||||
end
|
||||
|
||||
# This method has been moved to ActionDispatch::Request.filter_parameters
|
||||
def filter_parameter_logging(*args, &block)
|
||||
ActiveSupport::Deprecation.warn("Setting filter_parameter_logging in ActionController is deprecated and has no longer effect, please set 'config.filter_parameters' in config/application.rb instead", caller)
|
||||
filter = Rails.application.config.filter_parameters
|
||||
filter.concat(args)
|
||||
filter << block if block
|
||||
filter
|
||||
end
|
||||
|
||||
# This was moved to a plugin
|
||||
def verify(*args)
|
||||
ActiveSupport::Deprecation.warn "verify was removed from Rails and is now available as a plugin. " \
|
||||
"Please install it with `rails plugin install git://github.com/rails/verification.git`.", caller
|
||||
end
|
||||
|
||||
def exempt_from_layout(*)
|
||||
ActiveSupport::Deprecation.warn "exempt_from_layout is no longer needed, because layouts in Rails 3 " \
|
||||
"are restricted to the content-type of the template that was rendered.", caller
|
||||
end
|
||||
end
|
||||
|
||||
extend DeprecatedBehavior
|
||||
|
||||
delegate :consider_all_requests_local, :consider_all_requests_local=,
|
||||
:allow_concurrency, :allow_concurrency=, :to => :"self.class"
|
||||
end
|
||||
end
|
||||
28
actionpack/lib/action_controller/deprecated/dispatcher.rb
Normal file
28
actionpack/lib/action_controller/deprecated/dispatcher.rb
Normal file
@@ -0,0 +1,28 @@
|
||||
module ActionController
|
||||
class Dispatcher
|
||||
class << self
|
||||
def before_dispatch(*args, &block)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.before_dispatch is deprecated. " <<
|
||||
"Please use ActionDispatch::Callbacks.before instead.", caller
|
||||
ActionDispatch::Callbacks.before(*args, &block)
|
||||
end
|
||||
|
||||
def after_dispatch(*args, &block)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.after_dispatch is deprecated. " <<
|
||||
"Please use ActionDispatch::Callbacks.after instead.", caller
|
||||
ActionDispatch::Callbacks.after(*args, &block)
|
||||
end
|
||||
|
||||
def to_prepare(*args, &block)
|
||||
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.to_prepare is deprecated. " <<
|
||||
"Please use config.to_prepare instead", caller
|
||||
ActionDispatch::Callbacks.after(*args, &block)
|
||||
end
|
||||
|
||||
def new
|
||||
ActiveSupport::Deprecation.warn "ActionController::Dispatcher.new is deprecated, use Rails.application instead."
|
||||
Rails.application
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
14
actionpack/lib/action_controller/deprecated/url_writer.rb
Normal file
14
actionpack/lib/action_controller/deprecated/url_writer.rb
Normal file
@@ -0,0 +1,14 @@
|
||||
module ActionController
|
||||
module UrlWriter
|
||||
def self.included(klass)
|
||||
ActiveSupport::Deprecation.warn "include ActionController::UrlWriter is deprecated. Instead, " \
|
||||
"include Rails.application.routes.url_helpers"
|
||||
klass.class_eval { include Rails.application.routes.url_helpers }
|
||||
end
|
||||
end
|
||||
|
||||
class UrlRewriter
|
||||
def initialize(*)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -7,10 +7,8 @@ module ActionController
|
||||
def start_processing(event)
|
||||
payload = event.payload
|
||||
params = payload[:params].except(*INTERNAL_PARAMS)
|
||||
format = payload[:format]
|
||||
format = format.to_s.upcase if format.is_a?(Symbol)
|
||||
|
||||
info " Processing by #{payload[:controller]}##{payload[:action]} as #{format}"
|
||||
info " Processing by #{payload[:controller]}##{payload[:action]} as #{payload[:formats].first.to_s.upcase}"
|
||||
info " Parameters: #{params.inspect}" unless params.empty?
|
||||
end
|
||||
|
||||
@@ -18,11 +16,7 @@ module ActionController
|
||||
payload = event.payload
|
||||
additions = ActionController::Base.log_process_action(payload)
|
||||
|
||||
status = payload[:status]
|
||||
if status.nil? && payload[:exception].present?
|
||||
status = Rack::Utils.status_code(ActionDispatch::ShowExceptions.rescue_responses[payload[:exception].first]) rescue nil
|
||||
end
|
||||
message = "Completed #{status} #{Rack::Utils::HTTP_STATUS_CODES[status]} in %.0fms" % event.duration
|
||||
message = "Completed #{payload[:status]} #{Rack::Utils::HTTP_STATUS_CODES[payload[:status]]} in %.0fms" % event.duration
|
||||
message << " (#{additions.join(" | ")})" unless additions.blank?
|
||||
|
||||
info(message)
|
||||
@@ -48,7 +42,7 @@ module ActionController
|
||||
def #{method}(event)
|
||||
key_or_path = event.payload[:key] || event.payload[:path]
|
||||
human_name = #{method.to_s.humanize.inspect}
|
||||
info("\#{human_name} \#{key_or_path} \#{"(%.1fms)" % event.duration}")
|
||||
info("\#{human_name} \#{key_or_path} (%.1fms)" % event.duration)
|
||||
end
|
||||
METHOD
|
||||
end
|
||||
|
||||
@@ -36,88 +36,35 @@ module ActionController
|
||||
action = action.to_s
|
||||
raise "MiddlewareStack#build requires an app" unless app
|
||||
|
||||
middlewares.reverse.inject(app) do |a, middleware|
|
||||
reverse.inject(app) do |a, middleware|
|
||||
middleware.valid?(action) ?
|
||||
middleware.build(a) : a
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# <tt>ActionController::Metal</tt> is the simplest possible controller, providing a
|
||||
# valid Rack interface without the additional niceties provided by
|
||||
# <tt>ActionController::Base</tt>.
|
||||
#
|
||||
# A sample metal controller might look like this:
|
||||
#
|
||||
# class HelloController < ActionController::Metal
|
||||
# def index
|
||||
# self.response_body = "Hello World!"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# And then to route requests to your metal controller, you would add
|
||||
# something like this to <tt>config/routes.rb</tt>:
|
||||
#
|
||||
# match 'hello', :to => HelloController.action(:index)
|
||||
#
|
||||
# The +action+ method returns a valid Rack application for the \Rails
|
||||
# router to dispatch to.
|
||||
#
|
||||
# == Rendering Helpers
|
||||
#
|
||||
# <tt>ActionController::Metal</tt> by default provides no utilities for rendering
|
||||
# views, partials, or other responses aside from explicitly calling of
|
||||
# <tt>response_body=</tt>, <tt>content_type=</tt>, and <tt>status=</tt>. To
|
||||
# add the render helpers you're used to having in a normal controller, you
|
||||
# can do the following:
|
||||
#
|
||||
# class HelloController < ActionController::Metal
|
||||
# include ActionController::Rendering
|
||||
# append_view_path "#{Rails.root}/app/views"
|
||||
#
|
||||
# def index
|
||||
# render "hello/index"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# == Redirection Helpers
|
||||
#
|
||||
# To add redirection helpers to your metal controller, do the following:
|
||||
#
|
||||
# class HelloController < ActionController::Metal
|
||||
# include ActionController::Redirecting
|
||||
# include Rails.application.routes.url_helpers
|
||||
#
|
||||
# def index
|
||||
# redirect_to root_url
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# == Other Helpers
|
||||
#
|
||||
# You can refer to the modules included in <tt>ActionController::Base</tt> to see
|
||||
# other features you can bring into your metal controller.
|
||||
# ActionController::Metal provides a way to get a valid Rack application from a controller.
|
||||
#
|
||||
# In AbstractController, dispatching is triggered directly by calling #process on a new controller.
|
||||
# ActionController::Metal provides an #action method that returns a valid Rack application for a
|
||||
# given action. Other rack builders, such as Rack::Builder, Rack::URLMap, and the Rails router,
|
||||
# can dispatch directly to the action returned by FooController.action(:index).
|
||||
class Metal < AbstractController::Base
|
||||
abstract!
|
||||
|
||||
attr_internal_writer :env
|
||||
|
||||
def env
|
||||
@_env ||= {}
|
||||
end
|
||||
attr_internal :env
|
||||
|
||||
# Returns the last part of the controller's name, underscored, without the ending
|
||||
# <tt>Controller</tt>. For instance, PostsController returns <tt>posts</tt>.
|
||||
# Namespaces are left out, so Admin::PostsController returns <tt>posts</tt> as well.
|
||||
# "Controller". For instance, MyApp::MyPostsController would return "my_posts" for
|
||||
# controller_name
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>string</tt>
|
||||
# String
|
||||
def self.controller_name
|
||||
@controller_name ||= self.name.demodulize.sub(/Controller$/, '').underscore
|
||||
end
|
||||
|
||||
# Delegates to the class' <tt>controller_name</tt>
|
||||
# Delegates to the class' #controller_name
|
||||
def controller_name
|
||||
self.class.controller_name
|
||||
end
|
||||
@@ -131,12 +78,9 @@ module ActionController
|
||||
attr_internal :headers, :response, :request
|
||||
delegate :session, :to => "@_request"
|
||||
|
||||
def initialize
|
||||
def initialize(*)
|
||||
@_headers = {"Content-Type" => "text/html"}
|
||||
@_status = 200
|
||||
@_request = nil
|
||||
@_response = nil
|
||||
@_routes = nil
|
||||
super
|
||||
end
|
||||
|
||||
@@ -182,11 +126,12 @@ module ActionController
|
||||
end
|
||||
|
||||
def response_body=(val)
|
||||
body = val.nil? ? nil : (val.respond_to?(:each) ? val : [val])
|
||||
body = val.respond_to?(:each) ? val : [val]
|
||||
super body
|
||||
end
|
||||
|
||||
def dispatch(name, request) #:nodoc:
|
||||
# :api: private
|
||||
def dispatch(name, request)
|
||||
@_request = request
|
||||
@_env = request.env
|
||||
@_env['action_controller.instance'] = self
|
||||
@@ -194,30 +139,27 @@ module ActionController
|
||||
to_a
|
||||
end
|
||||
|
||||
def to_a #:nodoc:
|
||||
# :api: private
|
||||
def to_a
|
||||
response ? response.to_a : [status, headers, response_body]
|
||||
end
|
||||
|
||||
class_attribute :middleware_stack
|
||||
self.middleware_stack = ActionController::MiddlewareStack.new
|
||||
|
||||
def self.inherited(base) #nodoc:
|
||||
def self.inherited(base)
|
||||
base.middleware_stack = self.middleware_stack.dup
|
||||
super
|
||||
end
|
||||
|
||||
# Adds given middleware class and its args to bottom of middleware_stack
|
||||
def self.use(*args, &block)
|
||||
middleware_stack.use(*args, &block)
|
||||
end
|
||||
|
||||
# Alias for middleware_stack
|
||||
def self.middleware
|
||||
middleware_stack
|
||||
end
|
||||
|
||||
# Makes the controller a rack endpoint that points to the action in
|
||||
# the given env's action_dispatch.request.path_parameters key.
|
||||
def self.call(env)
|
||||
action(env['action_dispatch.request.path_parameters'][:action]).call(env)
|
||||
end
|
||||
@@ -227,10 +169,10 @@ module ActionController
|
||||
# for the same action.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>action</tt> - An action name
|
||||
# action<#to_s>:: An action name
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>proc</tt> - A rack application
|
||||
# Proc:: A rack application
|
||||
def self.action(name, klass = ActionDispatch::Request)
|
||||
middleware_stack.build(name.to_s) do |env|
|
||||
new.dispatch(name, klass.new(env))
|
||||
|
||||
@@ -5,6 +5,9 @@ module ActionController
|
||||
class ::ActionController::ActionControllerError < StandardError #:nodoc:
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
end
|
||||
|
||||
# Temporary hax
|
||||
included do
|
||||
::ActionController::UnknownAction = ::AbstractController::ActionNotFound
|
||||
@@ -18,10 +21,13 @@ module ActionController
|
||||
delegate :default_charset=, :to => "ActionDispatch::Response"
|
||||
end
|
||||
|
||||
self.protected_instance_variables = %w(
|
||||
@_status @_headers @_params @_env @_response @_request
|
||||
@_view_runtime @_stream @_url_options @_action_has_layout
|
||||
)
|
||||
# TODO: Update protected instance variables list
|
||||
config_accessor :protected_instance_variables
|
||||
self.protected_instance_variables = %w(@assigns @performed_redirect @performed_render
|
||||
@variables_added @request_origin @url
|
||||
@parent_controller @action_name
|
||||
@before_filter_chain_aborted @_headers @_params
|
||||
@_response)
|
||||
|
||||
def rescue_action(env)
|
||||
raise env["action_dispatch.rescue.exception"]
|
||||
@@ -33,6 +39,12 @@ module ActionController
|
||||
def assign_shortcuts(*) end
|
||||
|
||||
def _normalize_options(options)
|
||||
if options[:action] && options[:action].to_s.include?(?/)
|
||||
ActiveSupport::Deprecation.warn "Giving a path to render :action is deprecated. " <<
|
||||
"Please use render :template instead", caller
|
||||
options[:template] = options.delete(:action)
|
||||
end
|
||||
|
||||
options[:text] = nil if options.delete(:nothing) == true
|
||||
options[:text] = " " if options.key?(:text) && options[:text].nil?
|
||||
super
|
||||
|
||||
@@ -6,7 +6,7 @@ module ActionController
|
||||
include Head
|
||||
|
||||
# Sets the etag, last_modified, or both on the response and renders a
|
||||
# <tt>304 Not Modified</tt> response if the request is already fresh.
|
||||
# "304 Not Modified" response if the request is already fresh.
|
||||
#
|
||||
# Parameters:
|
||||
# * <tt>:etag</tt>
|
||||
@@ -17,11 +17,11 @@ module ActionController
|
||||
#
|
||||
# def show
|
||||
# @article = Article.find(params[:id])
|
||||
# fresh_when(:etag => @article, :last_modified => @article.created_at, :public => true)
|
||||
# fresh_when(:etag => @article, :last_modified => @article.created_at.utc, :public => true)
|
||||
# end
|
||||
#
|
||||
# This will render the show template if the request isn't sending a matching etag or
|
||||
# If-Modified-Since header and just a <tt>304 Not Modified</tt> response if there's a match.
|
||||
# If-Modified-Since header and just a "304 Not Modified" response if there's a match.
|
||||
#
|
||||
def fresh_when(options)
|
||||
options.assert_valid_keys(:etag, :last_modified, :public)
|
||||
@@ -36,7 +36,7 @@ module ActionController
|
||||
# Sets the etag and/or last_modified on the response and checks it against
|
||||
# the client request. If the request doesn't match the options provided, the
|
||||
# request is considered stale and should be generated from scratch. Otherwise,
|
||||
# it's fresh and we don't need to generate anything and a reply of <tt>304 Not Modified</tt> is sent.
|
||||
# it's fresh and we don't need to generate anything and a reply of "304 Not Modified" is sent.
|
||||
#
|
||||
# Parameters:
|
||||
# * <tt>:etag</tt>
|
||||
@@ -48,7 +48,7 @@ module ActionController
|
||||
# def show
|
||||
# @article = Article.find(params[:id])
|
||||
#
|
||||
# if stale?(:etag => @article, :last_modified => @article.created_at)
|
||||
# if stale?(:etag => @article, :last_modified => @article.created_at.utc)
|
||||
# @statistics = @article.really_expensive_call
|
||||
# respond_to do |format|
|
||||
# # all the supported formats
|
||||
@@ -60,13 +60,13 @@ module ActionController
|
||||
!request.fresh?(response)
|
||||
end
|
||||
|
||||
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a <tt>private</tt> instruction, so that
|
||||
# intermediate caches must not cache the response.
|
||||
# Sets a HTTP 1.1 Cache-Control header. Defaults to issuing a "private" instruction, so that
|
||||
# intermediate caches shouldn't cache the response.
|
||||
#
|
||||
# Examples:
|
||||
# expires_in 20.minutes
|
||||
# expires_in 3.hours, :public => true
|
||||
# expires_in 3.hours, 'max-stale' => 5.hours, :public => true
|
||||
# expires in 3.hours, 'max-stale' => 5.hours, :public => true
|
||||
#
|
||||
# This method will overwrite an existing Cache-Control header.
|
||||
# See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.
|
||||
@@ -77,7 +77,7 @@ module ActionController
|
||||
response.cache_control[:extras] = options.map {|k,v| "#{k}=#{v}"}
|
||||
end
|
||||
|
||||
# Sets a HTTP 1.1 Cache-Control header of <tt>no-cache</tt> so no caching should occur by the browser or
|
||||
# Sets a HTTP 1.1 Cache-Control header of "no-cache" so no caching should occur by the browser or
|
||||
# intermediate caches (like caching proxy servers).
|
||||
def expires_now #:doc:
|
||||
response.cache_control.replace(:no_cache => true)
|
||||
|
||||
@@ -1,145 +0,0 @@
|
||||
require 'active_support/core_ext/file/path'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
# Methods for sending arbitrary data and for streaming files to the browser,
|
||||
# instead of rendering.
|
||||
module DataStreaming
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionController::Rendering
|
||||
|
||||
DEFAULT_SEND_FILE_OPTIONS = {
|
||||
:type => 'application/octet-stream'.freeze,
|
||||
:disposition => 'attachment'.freeze,
|
||||
}.freeze
|
||||
|
||||
protected
|
||||
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
|
||||
# via the Rack::Sendfile middleware. The header to use is set via
|
||||
# config.action_dispatch.x_sendfile_header.
|
||||
# Your server can also configure this for you by setting the X-Sendfile-Type header.
|
||||
#
|
||||
# Be careful to sanitize the path parameter if it is coming from a web
|
||||
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
|
||||
# download any file on your server.
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
||||
# Defaults to <tt>File.basename(path)</tt>.
|
||||
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
|
||||
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
|
||||
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
||||
# Valid values are 'inline' and 'attachment' (default).
|
||||
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
|
||||
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
|
||||
# the URL, which is necessary for i18n filenames on certain browsers
|
||||
# (setting <tt>:filename</tt> overrides this option).
|
||||
#
|
||||
# The default Content-Type and Content-Disposition headers are
|
||||
# set to download arbitrary binary files in as many browsers as
|
||||
# possible. IE versions 4, 5, 5.5, and 6 are all known to have
|
||||
# a variety of quirks (especially when downloading over SSL).
|
||||
#
|
||||
# Simple download:
|
||||
#
|
||||
# send_file '/path/to.zip'
|
||||
#
|
||||
# Show a JPEG in the browser:
|
||||
#
|
||||
# send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
|
||||
#
|
||||
# Show a 404 page in the browser:
|
||||
#
|
||||
# send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
|
||||
#
|
||||
# Read about the other Content-* HTTP headers if you'd like to
|
||||
# provide the user with more information (such as Content-Description) in
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
|
||||
#
|
||||
# Also be aware that the document may be cached by proxies and browsers.
|
||||
# The Pragma and Cache-Control headers declare how the file may be cached
|
||||
# by intermediaries. They default to require clients to validate with
|
||||
# the server before releasing cached responses. See
|
||||
# http://www.mnot.net/cache_docs/ for an overview of web caching and
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
||||
# for the Cache-Control header spec.
|
||||
def send_file(path, options = {}) #:doc:
|
||||
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
|
||||
|
||||
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
|
||||
send_file_headers! options
|
||||
|
||||
self.status = options[:status] || 200
|
||||
self.content_type = options[:content_type] if options.key?(:content_type)
|
||||
self.response_body = File.open(path, "rb")
|
||||
end
|
||||
|
||||
# Sends the given binary data to the browser. This method is similar to
|
||||
# <tt>render :text => data</tt>, but also allows you to specify whether
|
||||
# the browser should display the response as a file attachment (i.e. in a
|
||||
# download dialog) or as inline data. You may also set the content type,
|
||||
# the apparent file name, and other things.
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
||||
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
|
||||
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
|
||||
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
||||
# Valid values are 'inline' and 'attachment' (default).
|
||||
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to 200.
|
||||
#
|
||||
# Generic data download:
|
||||
#
|
||||
# send_data buffer
|
||||
#
|
||||
# Download a dynamically-generated tarball:
|
||||
#
|
||||
# send_data generate_tgz('dir'), :filename => 'dir.tgz'
|
||||
#
|
||||
# Display an image Active Record in the browser:
|
||||
#
|
||||
# send_data image.data, :type => image.content_type, :disposition => 'inline'
|
||||
#
|
||||
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
||||
def send_data(data, options = {}) #:doc:
|
||||
send_file_headers! options.dup
|
||||
render options.slice(:status, :content_type).merge(:text => data)
|
||||
end
|
||||
|
||||
private
|
||||
def send_file_headers!(options)
|
||||
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
|
||||
[:type, :disposition].each do |arg|
|
||||
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
|
||||
end
|
||||
|
||||
disposition = options[:disposition]
|
||||
disposition += %(; filename="#{options[:filename]}") if options[:filename]
|
||||
|
||||
content_type = options[:type]
|
||||
|
||||
if content_type.is_a?(Symbol)
|
||||
extension = Mime[content_type]
|
||||
raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
|
||||
self.content_type = extension
|
||||
else
|
||||
self.content_type = content_type
|
||||
end
|
||||
|
||||
headers.merge!(
|
||||
'Content-Disposition' => disposition,
|
||||
'Content-Transfer-Encoding' => 'binary'
|
||||
)
|
||||
|
||||
response.sending_file = true
|
||||
|
||||
# Fix a problem with IE 6.0 on opening downloaded files:
|
||||
# If Cache-Control: no-cache is set (which Rails does by default),
|
||||
# IE removes the file it just downloaded from its cache immediately
|
||||
# after it displays the "open/save" dialog, which means that if you
|
||||
# hit "open" the file isn't there anymore when the application that
|
||||
# is called for handling the download is run, so let's workaround that
|
||||
response.cache_control[:public] ||= false
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,35 +0,0 @@
|
||||
module ActionController
|
||||
# This module provides a method which will redirects browser to use HTTPS
|
||||
# protocol. This will ensure that user's sensitive information will be
|
||||
# transferred safely over the internet. You _should_ always force browser
|
||||
# to use HTTPS when you're transferring sensitive information such as
|
||||
# user authentication, account information, or credit card information.
|
||||
#
|
||||
# Note that if you really concern about your application safety, you might
|
||||
# consider using +config.force_ssl+ in your configuration config file instead.
|
||||
# That will ensure all the data transferred via HTTPS protocol and prevent
|
||||
# user from getting session hijacked when accessing the site under unsecured
|
||||
# HTTP protocol.
|
||||
module ForceSSL
|
||||
extend ActiveSupport::Concern
|
||||
include AbstractController::Callbacks
|
||||
|
||||
module ClassMethods
|
||||
# Force the request to this particular controller or specified actions to be
|
||||
# under HTTPS protocol.
|
||||
#
|
||||
# Note that this method will not be effective on development environment.
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>only</tt> - The callback should be run only for this action
|
||||
# * <tt>except<tt> - The callback should be run for all actions except this action
|
||||
def force_ssl(options = {})
|
||||
before_filter(options) do
|
||||
if !request.ssl? && !Rails.env.development?
|
||||
redirect_to :protocol => 'https://', :status => :moved_permanently
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -9,8 +9,6 @@ module ActionController
|
||||
#
|
||||
# head :created, :location => person_path(@person)
|
||||
#
|
||||
# head :created, :location => @person
|
||||
#
|
||||
# It can also be used to return exceptional conditions:
|
||||
#
|
||||
# return head(:method_not_allowed) unless request.post?
|
||||
@@ -22,7 +20,7 @@ module ActionController
|
||||
location = options.delete(:location)
|
||||
|
||||
options.each do |key, value|
|
||||
headers[key.to_s.dasherize.split('-').each { |v| v[0] = v[0].chr.upcase }.join('-')] = value.to_s
|
||||
headers[key.to_s.dasherize.split(/-/).map { |v| v.capitalize }.join("-")] = value.to_s
|
||||
end
|
||||
|
||||
self.status = status
|
||||
|
||||
@@ -2,21 +2,21 @@ require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module ActionController
|
||||
# The \Rails framework provides a large number of helpers for working with assets, dates, forms,
|
||||
# numbers and model objects, to name a few. These helpers are available to all templates
|
||||
# The Rails framework provides a large number of helpers for working with +assets+, +dates+, +forms+,
|
||||
# +numbers+ and model objects, to name a few. These helpers are available to all templates
|
||||
# by default.
|
||||
#
|
||||
# In addition to using the standard template helpers provided, creating custom helpers to
|
||||
# In addition to using the standard template helpers provided in the Rails framework, creating custom helpers to
|
||||
# extract complicated logic or reusable functionality is strongly encouraged. By default, the controller will
|
||||
# include a helper whose name matches that of the controller, e.g., <tt>MyController</tt> will automatically
|
||||
# include <tt>MyHelper</tt>.
|
||||
#
|
||||
# Additional helpers can be specified using the +helper+ class method in ActionController::Base or any
|
||||
# Additional helpers can be specified using the +helper+ class method in <tt>ActionController::Base</tt> or any
|
||||
# controller which inherits from it.
|
||||
#
|
||||
# ==== Examples
|
||||
# The +to_s+ method from the \Time class can be wrapped in a helper method to display a custom message if
|
||||
# a \Time object is blank:
|
||||
# The +to_s+ method from the Time class can be wrapped in a helper method to display a custom message if
|
||||
# the Time object is blank:
|
||||
#
|
||||
# module FormattedTimeHelper
|
||||
# def format_time(time, format=:long, blank_message=" ")
|
||||
@@ -53,20 +53,30 @@ module ActionController
|
||||
include AbstractController::Helpers
|
||||
|
||||
included do
|
||||
config_accessor :helpers_path, :include_all_helpers
|
||||
config_accessor :helpers_path
|
||||
self.helpers_path ||= []
|
||||
self.include_all_helpers = true
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def helpers_dir
|
||||
ActiveSupport::Deprecation.warn "helpers_dir is deprecated, use helpers_path instead", caller
|
||||
self.helpers_path
|
||||
end
|
||||
|
||||
def helpers_dir=(value)
|
||||
ActiveSupport::Deprecation.warn "helpers_dir= is deprecated, use helpers_path= instead", caller
|
||||
self.helpers_path = Array.wrap(value)
|
||||
end
|
||||
|
||||
# Declares helper accessors for controller attributes. For example, the
|
||||
# following adds new +name+ and <tt>name=</tt> instance methods to a
|
||||
# controller and makes them available to the view:
|
||||
# attr_accessor :name
|
||||
# helper_attr :name
|
||||
# attr_accessor :name
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>attrs</tt> - Names of attributes to be converted into helpers.
|
||||
# *attrs<Array[String, Symbol]>:: Names of attributes to be converted
|
||||
# into helpers.
|
||||
def helper_attr(*attrs)
|
||||
attrs.flatten.each { |attr| helper_method(attr, "#{attr}=") }
|
||||
end
|
||||
@@ -76,35 +86,32 @@ module ActionController
|
||||
@helper_proxy ||= ActionView::Base.new.extend(_helpers)
|
||||
end
|
||||
|
||||
# Overwrite modules_for_helpers to accept :all as argument, which loads
|
||||
# all helpers in helpers_path.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>args</tt> - A list of helpers
|
||||
#
|
||||
# ==== Returns
|
||||
# * <tt>array</tt> - A normalized list of modules for the list of helpers provided.
|
||||
def modules_for_helpers(args)
|
||||
args += all_application_helpers if args.delete(:all)
|
||||
super(args)
|
||||
end
|
||||
|
||||
def all_helpers_from_path(path)
|
||||
helpers = []
|
||||
Array.wrap(path).each do |_path|
|
||||
extract = /^#{Regexp.quote(_path.to_s)}\/?(.*)_helper.rb$/
|
||||
helpers += Dir["#{_path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
|
||||
end
|
||||
helpers.sort!
|
||||
helpers.uniq!
|
||||
helpers
|
||||
end
|
||||
|
||||
private
|
||||
# Extract helper names from files in <tt>app/helpers/**/*_helper.rb</tt>
|
||||
def all_application_helpers
|
||||
all_helpers_from_path(helpers_path)
|
||||
end
|
||||
# Overwrite modules_for_helpers to accept :all as argument, which loads
|
||||
# all helpers in helpers_dir.
|
||||
#
|
||||
# ==== Parameters
|
||||
# args<Array[String, Symbol, Module, all]>:: A list of helpers
|
||||
#
|
||||
# ==== Returns
|
||||
# Array[Module]:: A normalized list of modules for the list of
|
||||
# helpers provided.
|
||||
def modules_for_helpers(args)
|
||||
args += all_application_helpers if args.delete(:all)
|
||||
super(args)
|
||||
end
|
||||
|
||||
# Extract helper names from files in app/helpers/**/*_helper.rb
|
||||
def all_application_helpers
|
||||
helpers = []
|
||||
Array.wrap(helpers_path).each do |path|
|
||||
extract = /^#{Regexp.quote(path.to_s)}\/?(.*)_helper.rb$/
|
||||
helpers += Dir["#{path}/**/*_helper.rb"].map { |file| file.sub(extract, '\1') }
|
||||
end
|
||||
helpers.sort!
|
||||
helpers.uniq!
|
||||
helpers
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
|
||||
module ActionController
|
||||
# Adds the ability to prevent public methods on a controller to be called as actions.
|
||||
# ActionController::HideActions adds the ability to prevent public methods on a controller
|
||||
# to be called as actions.
|
||||
module HideActions
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -22,7 +23,7 @@ module ActionController
|
||||
# Sets all of the actions passed in as hidden actions.
|
||||
#
|
||||
# ==== Parameters
|
||||
# * <tt>args</tt> - A list of actions
|
||||
# *args<#to_s>:: A list of actions
|
||||
def hide_action(*args)
|
||||
self.hidden_actions = hidden_actions.dup.merge(args.map(&:to_s)).freeze
|
||||
end
|
||||
|
||||
@@ -3,12 +3,14 @@ require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionController
|
||||
module HttpAuthentication
|
||||
# Makes it dead easy to do HTTP \Basic and \Digest authentication.
|
||||
# Makes it dead easy to do HTTP Basic authentication.
|
||||
#
|
||||
# === Simple \Basic example
|
||||
# Simple Basic example:
|
||||
#
|
||||
# class PostsController < ApplicationController
|
||||
# http_basic_authenticate_with :name => "dhh", :password => "secret", :except => :index
|
||||
# USER_NAME, PASSWORD = "dhh", "secret"
|
||||
#
|
||||
# before_filter :authenticate, :except => [ :index ]
|
||||
#
|
||||
# def index
|
||||
# render :text => "Everyone can see me!"
|
||||
@@ -17,11 +19,17 @@ module ActionController
|
||||
# def edit
|
||||
# render :text => "I'm only accessible if you know the password"
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# === Advanced \Basic example
|
||||
# private
|
||||
# def authenticate
|
||||
# authenticate_or_request_with_http_basic do |user_name, password|
|
||||
# user_name == USER_NAME && password == PASSWORD
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Here is a more advanced \Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
|
||||
#
|
||||
# Here is a more advanced Basic example where only Atom feeds and the XML API is protected by HTTP authentication,
|
||||
# the regular HTML interface is protected by a session approach:
|
||||
#
|
||||
# class ApplicationController < ActionController::Base
|
||||
@@ -61,13 +69,13 @@ module ActionController
|
||||
# assert_equal 200, status
|
||||
# end
|
||||
#
|
||||
# === Simple \Digest example
|
||||
# Simple Digest example:
|
||||
#
|
||||
# require 'digest/md5'
|
||||
# class PostsController < ApplicationController
|
||||
# REALM = "SuperSecret"
|
||||
# USERS = {"dhh" => "secret", #plain text password
|
||||
# "dap" => Digest::MD5.hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password
|
||||
# "dap" => Digest:MD5::hexdigest(["dap",REALM,"secret"].join(":")) #ha1 digest password
|
||||
#
|
||||
# before_filter :authenticate, :except => [:index]
|
||||
#
|
||||
@@ -93,30 +101,15 @@ module ActionController
|
||||
# or the ha1 digest hash so the framework can appropriately hash to check the user's
|
||||
# credentials. Returning +nil+ will cause authentication to fail.
|
||||
#
|
||||
# Storing the ha1 hash: MD5(username:realm:password), is better than storing a plain password. If
|
||||
# the password file or database is compromised, the attacker would be able to use the ha1 hash to
|
||||
# authenticate as the user at this +realm+, but would not have the user's password to try using at
|
||||
# other sites.
|
||||
# On shared hosts, Apache sometimes doesn't pass authentication headers to
|
||||
# FCGI instances. If your environment matches this description and you cannot
|
||||
# authenticate, try this rule in your Apache setup:
|
||||
#
|
||||
# In rare instances, web servers or front proxies strip authorization headers before
|
||||
# they reach your application. You can debug this situation by logging all environment
|
||||
# variables, and check for HTTP_AUTHORIZATION, amongst others.
|
||||
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
|
||||
module Basic
|
||||
extend self
|
||||
|
||||
module ControllerMethods
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
module ClassMethods
|
||||
def http_basic_authenticate_with(options = {})
|
||||
before_filter(options.except(:name, :password, :realm)) do
|
||||
authenticate_or_request_with_http_basic(options[:realm] || "Application") do |name, password|
|
||||
name == options[:name] && password == options[:password]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def authenticate_or_request_with_http_basic(realm = "Application", &login_procedure)
|
||||
authenticate_with_http_basic(&login_procedure) || request_http_basic_authentication(realm)
|
||||
end
|
||||
@@ -145,7 +138,7 @@ module ActionController
|
||||
end
|
||||
|
||||
def encode_credentials(user_name, password)
|
||||
"Basic #{ActiveSupport::Base64.encode64s("#{user_name}:#{password}")}"
|
||||
"Basic #{ActiveSupport::Base64.encode64("#{user_name}:#{password}")}"
|
||||
end
|
||||
|
||||
def authentication_request(controller, realm)
|
||||
@@ -192,15 +185,12 @@ module ActionController
|
||||
return false unless password
|
||||
|
||||
method = request.env['rack.methodoverride.original_method'] || request.env['REQUEST_METHOD']
|
||||
uri = credentials[:uri][0,1] == '/' ? request.original_fullpath : request.original_url
|
||||
uri = credentials[:uri][0,1] == '/' ? request.fullpath : request.url
|
||||
|
||||
[true, false].any? do |trailing_question_mark|
|
||||
[true, false].any? do |password_is_ha1|
|
||||
_uri = trailing_question_mark ? uri + "?" : uri
|
||||
expected = expected_response(method, _uri, credentials, password, password_is_ha1)
|
||||
expected == credentials[:response]
|
||||
end
|
||||
end
|
||||
[true, false].any? do |password_is_ha1|
|
||||
expected = expected_response(method, uri, credentials, password, password_is_ha1)
|
||||
expected == credentials[:response]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -219,7 +209,7 @@ module ActionController
|
||||
|
||||
def encode_credentials(http_method, credentials, password, password_is_ha1)
|
||||
credentials[:response] = expected_response(http_method, credentials[:uri], credentials, password, password_is_ha1)
|
||||
"Digest " + credentials.sort_by {|x| x[0].to_s }.map {|v| "#{v[0]}='#{v[1]}'" }.join(', ')
|
||||
"Digest " + credentials.sort_by {|x| x[0].to_s }.inject([]) {|a, v| a << "#{v[0]}='#{v[1]}'" }.join(', ')
|
||||
end
|
||||
|
||||
def decode_credentials_header(request)
|
||||
@@ -227,9 +217,9 @@ module ActionController
|
||||
end
|
||||
|
||||
def decode_credentials(header)
|
||||
HashWithIndifferentAccess[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
|
||||
Hash[header.to_s.gsub(/^Digest\s+/,'').split(',').map do |pair|
|
||||
key, value = pair.split('=', 2)
|
||||
[key.strip, value.to_s.gsub(/^"|"$/,'').delete('\'')]
|
||||
[key.strip.to_sym, value.to_s.gsub(/^"|"$/,'').gsub(/'/, '')]
|
||||
end]
|
||||
end
|
||||
|
||||
@@ -383,6 +373,7 @@ module ActionController
|
||||
#
|
||||
# RewriteRule ^(.*)$ dispatch.fcgi [E=X-HTTP_AUTHORIZATION:%{HTTP:Authorization},QSA,L]
|
||||
module Token
|
||||
|
||||
extend self
|
||||
|
||||
module ControllerMethods
|
||||
@@ -411,7 +402,7 @@ module ActionController
|
||||
# Returns nil if no token is found.
|
||||
def authenticate(controller, &login_procedure)
|
||||
token, options = token_and_options(controller.request)
|
||||
unless token.blank?
|
||||
if !token.blank?
|
||||
login_procedure.call(token, options)
|
||||
end
|
||||
end
|
||||
@@ -421,19 +412,20 @@ module ActionController
|
||||
# Authorization: Token token="abc", nonce="def"
|
||||
# Then the returned token is "abc", and the options is {:nonce => "def"}
|
||||
#
|
||||
# request - ActionDispatch::Request instance with the current headers.
|
||||
# request - ActionController::Request instance with the current headers.
|
||||
#
|
||||
# Returns an Array of [String, Hash] if a token is present.
|
||||
# Returns nil if no token is found.
|
||||
def token_and_options(request)
|
||||
if header = request.authorization.to_s[/^Token (.*)/]
|
||||
values = Hash[$1.split(',').map do |value|
|
||||
value.strip! # remove any spaces between commas and values
|
||||
key, value = value.split(/\=\"?/) # split key=value pairs
|
||||
value.chomp!('"') # chomp trailing " in value
|
||||
value.gsub!(/\\\"/, '"') # unescape remaining quotes
|
||||
[key, value]
|
||||
end]
|
||||
values = $1.split(',').
|
||||
inject({}) do |memo, value|
|
||||
value.strip! # remove any spaces between commas and values
|
||||
key, value = value.split(/\=\"?/) # split key=value pairs
|
||||
value.chomp!('"') # chomp trailing " in value
|
||||
value.gsub!(/\\\"/, '"') # unescape remaining quotes
|
||||
memo.update(key => value)
|
||||
end
|
||||
[values.delete("token"), values.with_indifferent_access]
|
||||
end
|
||||
end
|
||||
@@ -445,8 +437,9 @@ module ActionController
|
||||
#
|
||||
# Returns String.
|
||||
def encode_credentials(token, options = {})
|
||||
values = ["token=#{token.to_s.inspect}"] + options.map do |key, value|
|
||||
"#{key}=#{value.to_s.inspect}"
|
||||
values = ["token=#{token.to_s.inspect}"]
|
||||
options.each do |key, value|
|
||||
values << "#{key}=#{value.to_s.inspect}"
|
||||
end
|
||||
"Token #{values * ", "}"
|
||||
end
|
||||
@@ -462,5 +455,6 @@ module ActionController
|
||||
controller.__send__ :render, :text => "HTTP Token: Access denied.\n", :status => :unauthorized
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
module ActionController
|
||||
module ImplicitRender
|
||||
def send_action(method, *args)
|
||||
def send_action(*)
|
||||
ret = super
|
||||
default_render unless response_body
|
||||
ret
|
||||
end
|
||||
|
||||
def default_render(*args)
|
||||
render(*args)
|
||||
def default_render
|
||||
render
|
||||
end
|
||||
|
||||
def method_for_action(action_name)
|
||||
super || if template_exists?(action_name.to_s, _prefixes)
|
||||
"default_render"
|
||||
super || begin
|
||||
if template_exists?(action_name.to_s, _prefix)
|
||||
"default_render"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -14,12 +14,12 @@ module ActionController
|
||||
|
||||
attr_internal :view_runtime
|
||||
|
||||
def process_action(*args)
|
||||
def process_action(action, *args)
|
||||
raw_payload = {
|
||||
:controller => self.class.name,
|
||||
:action => self.action_name,
|
||||
:params => request.filtered_parameters,
|
||||
:format => request.format.try(:ref),
|
||||
:formats => request.formats.map(&:to_sym),
|
||||
:method => request.method,
|
||||
:path => (request.fullpath rescue "unknown")
|
||||
}
|
||||
@@ -78,7 +78,7 @@ module ActionController
|
||||
yield
|
||||
end
|
||||
|
||||
# Every time after an action is processed, this method is invoked
|
||||
# Everytime after an action is processed, this method is invoked
|
||||
# with the payload, so you can add more information.
|
||||
# :api: plugin
|
||||
def append_info_to_payload(payload) #:nodoc:
|
||||
|
||||
@@ -1,13 +1,10 @@
|
||||
require 'abstract_controller/collector'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/object/inclusion'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
module MimeResponds
|
||||
module MimeResponds #:nodoc:
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionController::ImplicitRender
|
||||
|
||||
included do
|
||||
class_attribute :responder, :mimes_for_respond_to
|
||||
self.responder = ActionController::Responder
|
||||
@@ -35,10 +32,10 @@ module ActionController #:nodoc:
|
||||
# and all actions except <tt>:edit</tt> respond to <tt>:xml</tt> and
|
||||
# <tt>:json</tt>.
|
||||
#
|
||||
# respond_to :json, :only => :create
|
||||
# respond_to :rjs, :only => :create
|
||||
#
|
||||
# This specifies that the <tt>:create</tt> action and no other responds
|
||||
# to <tt>:json</tt>.
|
||||
# to <tt>:rjs</tt>.
|
||||
def respond_to(*mimes)
|
||||
options = mimes.extract_options!
|
||||
|
||||
@@ -66,13 +63,13 @@ module ActionController #:nodoc:
|
||||
# might look something like this:
|
||||
#
|
||||
# def index
|
||||
# @people = Person.all
|
||||
# @people = Person.find(:all)
|
||||
# end
|
||||
#
|
||||
# Here's the same action, with web-service support baked in:
|
||||
#
|
||||
# def index
|
||||
# @people = Person.all
|
||||
# @people = Person.find(:all)
|
||||
#
|
||||
# respond_to do |format|
|
||||
# format.html
|
||||
@@ -108,8 +105,8 @@ module ActionController #:nodoc:
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# If the client wants HTML, we just redirect them back to the person list. If they want JavaScript,
|
||||
# then it is an Ajax request and we render the JavaScript template associated with this action.
|
||||
# If the client wants HTML, we just redirect them back to the person list. If they want Javascript
|
||||
# (format.js), then it is an RJS request and we render the RJS template associated with this action.
|
||||
# Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
|
||||
# include the person's company in the rendered XML, so you get something like this:
|
||||
#
|
||||
@@ -158,7 +155,7 @@ module ActionController #:nodoc:
|
||||
# Respond to also allows you to specify a common block for different formats by using any:
|
||||
#
|
||||
# def index
|
||||
# @people = Person.all
|
||||
# @people = Person.find(:all)
|
||||
#
|
||||
# respond_to do |format|
|
||||
# format.html
|
||||
@@ -181,7 +178,7 @@ module ActionController #:nodoc:
|
||||
# respond_to :html, :xml, :json
|
||||
#
|
||||
# def index
|
||||
# @people = Person.all
|
||||
# @people = Person.find(:all)
|
||||
# respond_with(@person)
|
||||
# end
|
||||
# end
|
||||
@@ -192,7 +189,7 @@ module ActionController #:nodoc:
|
||||
raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?
|
||||
|
||||
if response = retrieve_response_from_mimes(mimes, &block)
|
||||
response.call(nil)
|
||||
response.call
|
||||
end
|
||||
end
|
||||
|
||||
@@ -211,8 +208,8 @@ module ActionController #:nodoc:
|
||||
# It also accepts a block to be given. It's used to overwrite a default
|
||||
# response:
|
||||
#
|
||||
# def create
|
||||
# @user = User.new(params[:user])
|
||||
# def destroy
|
||||
# @user = User.find(params[:id])
|
||||
# flash[:notice] = "User was successfully created." if @user.save
|
||||
#
|
||||
# respond_with(@user) do |format|
|
||||
@@ -225,9 +222,6 @@ module ActionController #:nodoc:
|
||||
# is quite simple (it just needs to respond to call), you can even give
|
||||
# a proc to it.
|
||||
#
|
||||
# In order to use respond_with, first you need to declare the formats your
|
||||
# controller responds to in the class level with a call to <tt>respond_to</tt>.
|
||||
#
|
||||
def respond_with(*resources, &block)
|
||||
raise "In order to use respond_with, first you need to declare the formats your " <<
|
||||
"controller responds to in the class level" if self.class.mimes_for_respond_to.empty?
|
||||
@@ -251,9 +245,9 @@ module ActionController #:nodoc:
|
||||
config = self.class.mimes_for_respond_to[mime]
|
||||
|
||||
if config[:except]
|
||||
!action.in?(config[:except])
|
||||
!config[:except].include?(action)
|
||||
elsif config[:only]
|
||||
action.in?(config[:only])
|
||||
config[:only].include?(action)
|
||||
else
|
||||
true
|
||||
end
|
||||
@@ -263,9 +257,10 @@ module ActionController #:nodoc:
|
||||
# Collects mimes and return the response for the negotiated format. Returns
|
||||
# nil if :not_acceptable was sent to the client.
|
||||
#
|
||||
def retrieve_response_from_mimes(mimes=nil, &block) #:nodoc:
|
||||
def retrieve_response_from_mimes(mimes=nil, &block)
|
||||
collector = Collector.new { default_render }
|
||||
mimes ||= collect_mimes_from_class_level
|
||||
collector = Collector.new(mimes) { |options| default_render(options || {}) }
|
||||
mimes.each { |mime| collector.send(mime) }
|
||||
block.call(collector) if block_given?
|
||||
|
||||
if format = request.negotiate_mime(collector.order)
|
||||
@@ -282,9 +277,8 @@ module ActionController #:nodoc:
|
||||
include AbstractController::Collector
|
||||
attr_accessor :order
|
||||
|
||||
def initialize(mimes, &block)
|
||||
def initialize(&block)
|
||||
@order, @responses, @default_response = [], {}, block
|
||||
mimes.each { |mime| send(mime) }
|
||||
end
|
||||
|
||||
def any(*args, &block)
|
||||
@@ -297,7 +291,7 @@ module ActionController #:nodoc:
|
||||
alias :all :any
|
||||
|
||||
def custom(mime_type, &block)
|
||||
mime_type = Mime::Type.lookup(mime_type.to_s) unless mime_type.is_a?(Mime::Type)
|
||||
mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
|
||||
@order << mime_type
|
||||
@responses[mime_type] ||= block
|
||||
end
|
||||
|
||||
@@ -1,234 +0,0 @@
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/hash/slice'
|
||||
require 'active_support/core_ext/hash/except'
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/module/anonymous'
|
||||
require 'action_dispatch/http/mime_types'
|
||||
|
||||
module ActionController
|
||||
# Wraps the parameters hash into a nested hash. This will allow clients to submit
|
||||
# POST requests without having to specify any root elements.
|
||||
#
|
||||
# This functionality is enabled in +config/initializers/wrap_parameters.rb+
|
||||
# and can be customized. If you are upgrading to \Rails 3.1, this file will
|
||||
# need to be created for the functionality to be enabled.
|
||||
#
|
||||
# You could also turn it on per controller by setting the format array to
|
||||
# a non-empty array:
|
||||
#
|
||||
# class UsersController < ApplicationController
|
||||
# wrap_parameters :format => [:json, :xml]
|
||||
# end
|
||||
#
|
||||
# If you enable +ParamsWrapper+ for +:json+ format, instead of having to
|
||||
# send JSON parameters like this:
|
||||
#
|
||||
# {"user": {"name": "Konata"}}
|
||||
#
|
||||
# You can send parameters like this:
|
||||
#
|
||||
# {"name": "Konata"}
|
||||
#
|
||||
# And it will be wrapped into a nested hash with the key name matching the
|
||||
# controller's name. For example, if you're posting to +UsersController+,
|
||||
# your new +params+ hash will look like this:
|
||||
#
|
||||
# {"name" => "Konata", "user" => {"name" => "Konata"}}
|
||||
#
|
||||
# You can also specify the key in which the parameters should be wrapped to,
|
||||
# and also the list of attributes it should wrap by using either +:include+ or
|
||||
# +:exclude+ options like this:
|
||||
#
|
||||
# class UsersController < ApplicationController
|
||||
# wrap_parameters :person, :include => [:username, :password]
|
||||
# end
|
||||
#
|
||||
# If you're going to pass the parameters to an +ActiveModel+ object (such as
|
||||
# +User.new(params[:user])+), you might consider passing the model class to
|
||||
# the method instead. The +ParamsWrapper+ will actually try to determine the
|
||||
# list of attribute names from the model and only wrap those attributes:
|
||||
#
|
||||
# class UsersController < ApplicationController
|
||||
# wrap_parameters Person
|
||||
# end
|
||||
#
|
||||
# You still could pass +:include+ and +:exclude+ to set the list of attributes
|
||||
# you want to wrap.
|
||||
#
|
||||
# By default, if you don't specify the key in which the parameters would be
|
||||
# wrapped to, +ParamsWrapper+ will actually try to determine if there's
|
||||
# a model related to it or not. This controller, for example:
|
||||
#
|
||||
# class Admin::UsersController < ApplicationController
|
||||
# end
|
||||
#
|
||||
# will try to check if +Admin::User+ or +User+ model exists, and use it to
|
||||
# determine the wrapper key respectively. If both models don't exist,
|
||||
# it will then fallback to use +user+ as the key.
|
||||
module ParamsWrapper
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
EXCLUDE_PARAMETERS = %w(authenticity_token _method utf8)
|
||||
|
||||
included do
|
||||
class_attribute :_wrapper_options
|
||||
self._wrapper_options = { :format => [] }
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
# Sets the name of the wrapper key, or the model which +ParamsWrapper+
|
||||
# would use to determine the attribute names from.
|
||||
#
|
||||
# ==== Examples
|
||||
# wrap_parameters :format => :xml
|
||||
# # enables the parameter wrapper for XML format
|
||||
#
|
||||
# wrap_parameters :person
|
||||
# # wraps parameters into +params[:person]+ hash
|
||||
#
|
||||
# wrap_parameters Person
|
||||
# # wraps parameters by determining the wrapper key from Person class
|
||||
# (+person+, in this case) and the list of attribute names
|
||||
#
|
||||
# wrap_parameters :include => [:username, :title]
|
||||
# # wraps only +:username+ and +:title+ attributes from parameters.
|
||||
#
|
||||
# wrap_parameters false
|
||||
# # disables parameters wrapping for this controller altogether.
|
||||
#
|
||||
# ==== Options
|
||||
# * <tt>:format</tt> - The list of formats in which the parameters wrapper
|
||||
# will be enabled.
|
||||
# * <tt>:include</tt> - The list of attribute names which parameters wrapper
|
||||
# will wrap into a nested hash.
|
||||
# * <tt>:exclude</tt> - The list of attribute names which parameters wrapper
|
||||
# will exclude from a nested hash.
|
||||
def wrap_parameters(name_or_model_or_options, options = {})
|
||||
model = nil
|
||||
|
||||
case name_or_model_or_options
|
||||
when Hash
|
||||
options = name_or_model_or_options
|
||||
when false
|
||||
options = options.merge(:format => [])
|
||||
when Symbol, String
|
||||
options = options.merge(:name => name_or_model_or_options)
|
||||
else
|
||||
model = name_or_model_or_options
|
||||
end
|
||||
|
||||
_set_wrapper_defaults(_wrapper_options.slice(:format).merge(options), model)
|
||||
end
|
||||
|
||||
# Sets the default wrapper key or model which will be used to determine
|
||||
# wrapper key and attribute names. Will be called automatically when the
|
||||
# module is inherited.
|
||||
def inherited(klass)
|
||||
if klass._wrapper_options[:format].present?
|
||||
klass._set_wrapper_defaults(klass._wrapper_options.slice(:format))
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Determine the wrapper model from the controller's name. By convention,
|
||||
# this could be done by trying to find the defined model that has the
|
||||
# same singularize name as the controller. For example, +UsersController+
|
||||
# will try to find if the +User+ model exists.
|
||||
#
|
||||
# This method also does namespace lookup. Foo::Bar::UsersController will
|
||||
# try to find Foo::Bar::User, Foo::User and finally User.
|
||||
def _default_wrap_model #:nodoc:
|
||||
return nil if self.anonymous?
|
||||
|
||||
model_name = self.name.sub(/Controller$/, '').singularize
|
||||
|
||||
begin
|
||||
model_klass = model_name.constantize
|
||||
rescue NameError, ArgumentError => e
|
||||
if e.message =~ /is not missing constant|uninitialized constant #{model_name}/
|
||||
namespaces = model_name.split("::")
|
||||
namespaces.delete_at(-2)
|
||||
break if namespaces.last == model_name
|
||||
model_name = namespaces.join("::")
|
||||
else
|
||||
raise
|
||||
end
|
||||
end until model_klass
|
||||
|
||||
model_klass
|
||||
end
|
||||
|
||||
def _set_wrapper_defaults(options, model=nil)
|
||||
options = options.dup
|
||||
|
||||
unless options[:include] || options[:exclude]
|
||||
model ||= _default_wrap_model
|
||||
if model.respond_to?(:attribute_names) && model.attribute_names.present?
|
||||
options[:include] = model.attribute_names
|
||||
end
|
||||
end
|
||||
|
||||
unless options[:name] || self.anonymous?
|
||||
model ||= _default_wrap_model
|
||||
options[:name] = model ? model.to_s.demodulize.underscore :
|
||||
controller_name.singularize
|
||||
end
|
||||
|
||||
options[:include] = Array.wrap(options[:include]).collect(&:to_s) if options[:include]
|
||||
options[:exclude] = Array.wrap(options[:exclude]).collect(&:to_s) if options[:exclude]
|
||||
options[:format] = Array.wrap(options[:format])
|
||||
|
||||
self._wrapper_options = options
|
||||
end
|
||||
end
|
||||
|
||||
# Performs parameters wrapping upon the request. Will be called automatically
|
||||
# by the metal call stack.
|
||||
def process_action(*args)
|
||||
if _wrapper_enabled?
|
||||
wrapped_hash = _wrap_parameters request.request_parameters
|
||||
wrapped_filtered_hash = _wrap_parameters request.filtered_parameters
|
||||
|
||||
# This will make the wrapped hash accessible from controller and view
|
||||
request.parameters.merge! wrapped_hash
|
||||
request.request_parameters.merge! wrapped_hash
|
||||
|
||||
# This will make the wrapped hash displayed in the log file
|
||||
request.filtered_parameters.merge! wrapped_filtered_hash
|
||||
end
|
||||
super
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns the wrapper key which will use to stored wrapped parameters.
|
||||
def _wrapper_key
|
||||
_wrapper_options[:name]
|
||||
end
|
||||
|
||||
# Returns the list of enabled formats.
|
||||
def _wrapper_formats
|
||||
_wrapper_options[:format]
|
||||
end
|
||||
|
||||
# Returns the list of parameters which will be selected for wrapped.
|
||||
def _wrap_parameters(parameters)
|
||||
value = if include_only = _wrapper_options[:include]
|
||||
parameters.slice(*include_only)
|
||||
else
|
||||
exclude = _wrapper_options[:exclude] || []
|
||||
parameters.except(*(exclude + EXCLUDE_PARAMETERS))
|
||||
end
|
||||
|
||||
{ _wrapper_key => value }
|
||||
end
|
||||
|
||||
# Checks if we should perform parameters wrapping.
|
||||
def _wrapper_enabled?
|
||||
ref = request.content_mime_type.try(:ref)
|
||||
_wrapper_formats.include?(ref) && _wrapper_key && !request.request_parameters[_wrapper_key]
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,7 +20,6 @@ module ActionController
|
||||
# * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
|
||||
# * <tt>String</tt> starting with <tt>protocol://</tt> (like <tt>http://</tt>) - Is passed straight through as the target for redirection.
|
||||
# * <tt>String</tt> not containing a protocol - The current protocol and host is prepended to the string.
|
||||
# * <tt>Proc</tt> - A block that will be executed in the controller's context. Should return any option accepted by +redirect_to+.
|
||||
# * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
|
||||
# Short-hand for <tt>redirect_to(request.env["HTTP_REFERER"])</tt>
|
||||
#
|
||||
@@ -31,7 +30,6 @@ module ActionController
|
||||
# redirect_to "/images/screenshot.jpg"
|
||||
# redirect_to articles_url
|
||||
# redirect_to :back
|
||||
# redirect_to proc { edit_post_url(@post) }
|
||||
#
|
||||
# The redirection happens as a "302 Moved" header unless otherwise specified.
|
||||
#
|
||||
@@ -40,12 +38,11 @@ module ActionController
|
||||
# redirect_to :action=>'atom', :status => :moved_permanently
|
||||
# redirect_to post_url(@post), :status => 301
|
||||
# redirect_to :action=>'atom', :status => 302
|
||||
#
|
||||
#
|
||||
# The status code can either be a standard {HTTP Status code}[http://www.iana.org/assignments/http-status-codes] as an
|
||||
# integer, or a symbol representing the downcased, underscored and symbolized description.
|
||||
# Note that the status code must be a 3xx HTTP code, or redirection will not occur.
|
||||
#
|
||||
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for the commonly used flash names
|
||||
# It is also possible to assign a flash message as part of the redirection. There are two special accessors for commonly used the flash names
|
||||
# +alert+ and +notice+ as well as a general purpose +flash+ bucket.
|
||||
#
|
||||
# Examples:
|
||||
@@ -88,11 +85,9 @@ module ActionController
|
||||
when :back
|
||||
raise RedirectBackError unless refer = request.headers["Referer"]
|
||||
refer
|
||||
when Proc
|
||||
_compute_redirect_to_location options.call
|
||||
else
|
||||
url_for(options)
|
||||
end.gsub(/[\0\r\n]/, '')
|
||||
end.gsub(/[\r\n]/, '')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,7 +2,6 @@ require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
|
||||
module ActionController
|
||||
# See <tt>Renderers.add</tt>
|
||||
def self.add_renderer(key, &block)
|
||||
Renderers.add(key, &block)
|
||||
end
|
||||
@@ -16,12 +15,30 @@ module ActionController
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def _write_render_options
|
||||
renderers = _renderers.map do |name, value|
|
||||
<<-RUBY_EVAL
|
||||
if options.key?(:#{name})
|
||||
_process_options(options)
|
||||
return _render_option_#{name}(options.delete(:#{name}), options)
|
||||
end
|
||||
RUBY_EVAL
|
||||
end
|
||||
|
||||
class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
|
||||
def _handle_render_options(options)
|
||||
#{renderers.join}
|
||||
end
|
||||
RUBY_EVAL
|
||||
end
|
||||
|
||||
def use_renderers(*args)
|
||||
new = _renderers.dup
|
||||
args.each do |key|
|
||||
new[key] = RENDERERS[key]
|
||||
end
|
||||
self._renderers = new.freeze
|
||||
_write_render_options
|
||||
end
|
||||
alias use_renderer use_renderers
|
||||
end
|
||||
@@ -30,82 +47,51 @@ module ActionController
|
||||
_handle_render_options(options) || super
|
||||
end
|
||||
|
||||
def _handle_render_options(options)
|
||||
_renderers.each do |name, value|
|
||||
if options.key?(name.to_sym)
|
||||
_process_options(options)
|
||||
return send("_render_option_#{name}", options.delete(name.to_sym), options)
|
||||
end
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
# Hash of available renderers, mapping a renderer name to its proc.
|
||||
# Default keys are :json, :js, :xml.
|
||||
RENDERERS = {}
|
||||
|
||||
# Adds a new renderer to call within controller actions.
|
||||
# A renderer is invoked by passing its name as an option to
|
||||
# <tt>AbstractController::Rendering#render</tt>. To create a renderer
|
||||
# pass it a name and a block. The block takes two arguments, the first
|
||||
# is the value paired with its key and the second is the remaining
|
||||
# hash of options passed to +render+.
|
||||
#
|
||||
# === Example
|
||||
# Create a csv renderer:
|
||||
#
|
||||
# ActionController::Renderers.add :csv do |obj, options|
|
||||
# filename = options[:filename] || 'data'
|
||||
# str = obj.respond_to?(:to_csv) ? obj.to_csv : obj.to_s
|
||||
# send_data str, :type => Mime::CSV,
|
||||
# :disposition => "attachment; filename=#{filename}.csv"
|
||||
# end
|
||||
#
|
||||
# Note that we used Mime::CSV for the csv mime type as it comes with Rails.
|
||||
# For a custom renderer, you'll need to register a mime type with
|
||||
# <tt>Mime::Type.register</tt>.
|
||||
#
|
||||
# To use the csv renderer in a controller action:
|
||||
#
|
||||
# def show
|
||||
# @csvable = Csvable.find(params[:id])
|
||||
# respond_to do |format|
|
||||
# format.html
|
||||
# format.csv { render :csv => @csvable, :filename => @csvable.name }
|
||||
# }
|
||||
# end
|
||||
# To use renderers and their mime types in more concise ways, see
|
||||
# <tt>ActionController::MimeResponds::ClassMethods.respond_to</tt> and
|
||||
# <tt>ActionController::MimeResponds#respond_with</tt>
|
||||
def self.add(key, &block)
|
||||
define_method("_render_option_#{key}", &block)
|
||||
RENDERERS[key] = block
|
||||
All._write_render_options
|
||||
end
|
||||
|
||||
module All
|
||||
extend ActiveSupport::Concern
|
||||
include Renderers
|
||||
|
||||
INCLUDED = []
|
||||
included do
|
||||
self._renderers = RENDERERS
|
||||
_write_render_options
|
||||
INCLUDED << self
|
||||
end
|
||||
|
||||
def self._write_render_options
|
||||
INCLUDED.each(&:_write_render_options)
|
||||
end
|
||||
end
|
||||
|
||||
add :json do |json, options|
|
||||
json = json.to_json(options) unless json.kind_of?(String)
|
||||
json = json.to_json(options) unless json.respond_to?(:to_str)
|
||||
json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
|
||||
self.content_type ||= Mime::JSON
|
||||
json
|
||||
self.response_body = json
|
||||
end
|
||||
|
||||
add :js do |js, options|
|
||||
self.content_type ||= Mime::JS
|
||||
js.respond_to?(:to_js) ? js.to_js(options) : js
|
||||
self.response_body = js.respond_to?(:to_js) ? js.to_js(options) : js
|
||||
end
|
||||
|
||||
add :xml do |xml, options|
|
||||
self.content_type ||= Mime::XML
|
||||
xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
|
||||
self.response_body = xml.respond_to?(:to_xml) ? xml.to_xml(options) : xml
|
||||
end
|
||||
|
||||
add :update do |proc, options|
|
||||
view_context = self.view_context
|
||||
generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(view_context, &proc)
|
||||
self.content_type = Mime::JS
|
||||
self.response_body = generator.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,11 +2,12 @@ module ActionController
|
||||
module Rendering
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include ActionController::RackDelegation
|
||||
include AbstractController::Rendering
|
||||
|
||||
# Before processing, set the request formats in current controller formats.
|
||||
def process_action(*) #:nodoc:
|
||||
self.formats = request.formats.map { |x| x.ref }
|
||||
self.formats = request.formats.map { |x| x.to_sym }
|
||||
super
|
||||
end
|
||||
|
||||
@@ -18,48 +19,38 @@ module ActionController
|
||||
response_body
|
||||
end
|
||||
|
||||
# Overwrite render_to_string because body can now be set to a rack body.
|
||||
def render_to_string(*)
|
||||
if self.response_body = super
|
||||
string = ""
|
||||
response_body.each { |r| string << r }
|
||||
string
|
||||
end
|
||||
ensure
|
||||
self.response_body = nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Normalize arguments by catching blocks and setting them on :update.
|
||||
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
|
||||
options = super
|
||||
options[:update] = blk if block_given?
|
||||
options
|
||||
end
|
||||
|
||||
# Normalize both text and status options.
|
||||
def _normalize_options(options) #:nodoc:
|
||||
if options.key?(:text) && options[:text].respond_to?(:to_text)
|
||||
options[:text] = options[:text].to_text
|
||||
# Normalize arguments by catching blocks and setting them on :update.
|
||||
def _normalize_args(action=nil, options={}, &blk) #:nodoc:
|
||||
options = super
|
||||
options[:update] = blk if block_given?
|
||||
options
|
||||
end
|
||||
|
||||
if options[:status]
|
||||
options[:status] = Rack::Utils.status_code(options[:status])
|
||||
# Normalize both text and status options.
|
||||
def _normalize_options(options) #:nodoc:
|
||||
if options.key?(:text) && options[:text].respond_to?(:to_text)
|
||||
options[:text] = options[:text].to_text
|
||||
end
|
||||
|
||||
if options[:status]
|
||||
options[:status] = Rack::Utils.status_code(options[:status])
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
# Process controller specific options, as status, content-type and location.
|
||||
def _process_options(options) #:nodoc:
|
||||
status, content_type, location = options.values_at(:status, :content_type, :location)
|
||||
|
||||
# Process controller specific options, as status, content-type and location.
|
||||
def _process_options(options) #:nodoc:
|
||||
status, content_type, location = options.values_at(:status, :content_type, :location)
|
||||
self.status = status if status
|
||||
self.content_type = content_type if content_type
|
||||
self.headers["Location"] = url_for(location) if location
|
||||
|
||||
self.status = status if status
|
||||
self.content_type = content_type if content_type
|
||||
self.headers["Location"] = url_for(location) if location
|
||||
super
|
||||
end
|
||||
|
||||
super
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,26 +4,45 @@ module ActionController #:nodoc:
|
||||
class InvalidAuthenticityToken < ActionControllerError #:nodoc:
|
||||
end
|
||||
|
||||
# Controller actions are protected from Cross-Site Request Forgery (CSRF) attacks
|
||||
# by including a token in the rendered html for your application. This token is
|
||||
# stored as a random string in the session, to which an attacker does not have
|
||||
# access. When a request reaches your application, \Rails verifies the received
|
||||
# token with the token in the session. Only HTML and JavaScript requests are checked,
|
||||
# so this will not protect your XML API (presumably you'll have a different
|
||||
# authentication scheme there anyway). Also, GET requests are not protected as these
|
||||
# should be idempotent.
|
||||
# Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current
|
||||
# web application, not a forged link from another site, is done by embedding a token based on a random
|
||||
# string stored in the session (which an attacker wouldn't know) in all forms and Ajax requests generated
|
||||
# by Rails and then verifying the authenticity of that token in the controller. Only HTML/JavaScript
|
||||
# requests are checked, so this will not protect your XML API (presumably you'll have a different
|
||||
# authentication scheme there anyway). Also, GET requests are not protected as these should be
|
||||
# idempotent anyway.
|
||||
#
|
||||
# CSRF protection is turned on with the <tt>protect_from_forgery</tt> method,
|
||||
# which checks the token and resets the session if it doesn't match what was expected.
|
||||
# A call to this method is generated for new \Rails applications by default.
|
||||
# You can customize the error message by editing public/422.html.
|
||||
# This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
|
||||
# ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the
|
||||
# error message in production by editing public/422.html. A call to this method in ApplicationController is
|
||||
# generated by default in post-Rails 2.0 applications.
|
||||
#
|
||||
# The token parameter is named <tt>authenticity_token</tt> by default. The name and
|
||||
# value of this token must be added to every layout that renders forms by including
|
||||
# <tt>csrf_meta_tags</tt> in the html +head+.
|
||||
# The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form
|
||||
# manually (without the use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to
|
||||
# include a hidden field named like that and set its value to what is returned by
|
||||
# <tt>form_authenticity_token</tt>.
|
||||
#
|
||||
# Request forgery protection is disabled by default in test environment. If you are upgrading from Rails
|
||||
# 1.x, add this to config/environments/test.rb:
|
||||
#
|
||||
# # Disable request forgery protection in test environment
|
||||
# config.action_controller.allow_forgery_protection = false
|
||||
#
|
||||
# == Learn more about CSRF (Cross-Site Request Forgery) attacks
|
||||
#
|
||||
# Here are some resources:
|
||||
# * http://isc.sans.org/diary.html?storyid=1750
|
||||
# * http://en.wikipedia.org/wiki/Cross-site_request_forgery
|
||||
#
|
||||
# Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
|
||||
# There are a few guidelines you should follow:
|
||||
#
|
||||
# * Keep your GET requests safe and idempotent. More reading material:
|
||||
# * http://www.xml.com/pub/a/2002/04/24/deviant.html
|
||||
# * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
|
||||
# * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look
|
||||
# for "Expires: at end of session"
|
||||
#
|
||||
# Learn more about CSRF attacks and securing your application in the
|
||||
# {Ruby on Rails Security Guide}[http://guides.rubyonrails.org/security.html].
|
||||
module RequestForgeryProtection
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -52,52 +71,44 @@ module ActionController #:nodoc:
|
||||
# class FooController < ApplicationController
|
||||
# protect_from_forgery :except => :index
|
||||
#
|
||||
# You can disable csrf protection on controller-by-controller basis:
|
||||
#
|
||||
# skip_before_filter :verify_authenticity_token
|
||||
#
|
||||
# It can also be disabled for specific controller actions:
|
||||
#
|
||||
# skip_before_filter :verify_authenticity_token, :except => [:create]
|
||||
# # you can disable csrf protection on controller-by-controller basis:
|
||||
# skip_before_filter :verify_authenticity_token
|
||||
# end
|
||||
#
|
||||
# Valid Options:
|
||||
#
|
||||
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
|
||||
# * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
|
||||
def protect_from_forgery(options = {})
|
||||
self.request_forgery_protection_token ||= :authenticity_token
|
||||
prepend_before_filter :verify_authenticity_token, options
|
||||
before_filter :verify_authenticity_token, options
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
||||
|
||||
def protect_from_forgery(options = {})
|
||||
self.request_forgery_protection_token ||= :authenticity_token
|
||||
before_filter :verify_authenticity_token, options
|
||||
end
|
||||
|
||||
# The actual before_filter that is used. Modify this to change how you handle unverified requests.
|
||||
def verify_authenticity_token
|
||||
unless verified_request?
|
||||
logger.debug "WARNING: Can't verify CSRF token authenticity" if logger
|
||||
handle_unverified_request
|
||||
end
|
||||
verified_request? || raise(ActionController::InvalidAuthenticityToken)
|
||||
end
|
||||
|
||||
# This is the method that defines the application behavior when a request is found to be unverified.
|
||||
# By default, \Rails resets the session when it finds an unverified request.
|
||||
def handle_unverified_request
|
||||
reset_session
|
||||
end
|
||||
|
||||
# Returns true or false if a request is verified. Checks:
|
||||
# Returns true or false if a request is verified. Checks:
|
||||
#
|
||||
# * is the format restricted? By default, only HTML requests are checked.
|
||||
# * is it a GET request? Gets should be safe and idempotent
|
||||
# * Does the form_authenticity_token match the given token value from the params?
|
||||
# * Does the X-CSRF-Token header match the form_authenticity_token
|
||||
def verified_request?
|
||||
!protect_against_forgery? || request.get? ||
|
||||
form_authenticity_token == params[request_forgery_protection_token] ||
|
||||
form_authenticity_token == request.headers['X-CSRF-Token']
|
||||
!protect_against_forgery? || request.forgery_whitelisted? ||
|
||||
form_authenticity_token == params[request_forgery_protection_token]
|
||||
end
|
||||
|
||||
# Sets the token value for the current session.
|
||||
def form_authenticity_token
|
||||
session[:_csrf_token] ||= SecureRandom.base64(32)
|
||||
session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
|
||||
end
|
||||
|
||||
# The form's authenticity parameter. Override to provide your own.
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
require 'active_support/json'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
# Responsible for exposing a resource to different mime requests,
|
||||
# Responder is responsible for exposing a resource to different mime requests,
|
||||
# usually depending on the HTTP verb. The responder is triggered when
|
||||
# <code>respond_with</code> is called. The simplest case to study is a GET request:
|
||||
#
|
||||
@@ -24,10 +24,10 @@ module ActionController #:nodoc:
|
||||
#
|
||||
# === Builtin HTTP verb semantics
|
||||
#
|
||||
# The default \Rails responder holds semantics for each HTTP verb. Depending on the
|
||||
# The default Rails responder holds semantics for each HTTP verb. Depending on the
|
||||
# content type, verb and the resource status, it will behave differently.
|
||||
#
|
||||
# Using \Rails default responder, a POST request for creating an object could
|
||||
# Using Rails default responder, a POST request for creating an object could
|
||||
# be written as:
|
||||
#
|
||||
# def create
|
||||
@@ -68,7 +68,7 @@ module ActionController #:nodoc:
|
||||
# respond_with(@project, @task)
|
||||
# end
|
||||
#
|
||||
# Giving several resources ensures that the responder will redirect to
|
||||
# Giving an array of resources, you ensure that the responder will redirect to
|
||||
# <code>project_task_url</code> instead of <code>task_url</code>.
|
||||
#
|
||||
# Namespaced and singleton resources require a symbol to be given, as in
|
||||
@@ -77,42 +77,8 @@ module ActionController #:nodoc:
|
||||
#
|
||||
# respond_with(@project, :manager, @task)
|
||||
#
|
||||
# Note that if you give an array, it will be treated as a collection,
|
||||
# so the following is not equivalent:
|
||||
# Check <code>polymorphic_url</code> documentation for more examples.
|
||||
#
|
||||
# respond_with [@project, :manager, @task]
|
||||
#
|
||||
# === Custom options
|
||||
#
|
||||
# <code>respond_with</code> also allow you to pass options that are forwarded
|
||||
# to the underlying render call. Those options are only applied success
|
||||
# scenarios. For instance, you can do the following in the create method above:
|
||||
#
|
||||
# def create
|
||||
# @project = Project.find(params[:project_id])
|
||||
# @task = @project.comments.build(params[:task])
|
||||
# flash[:notice] = 'Task was successfully created.' if @task.save
|
||||
# respond_with(@project, @task, :status => 201)
|
||||
# end
|
||||
#
|
||||
# This will return status 201 if the task was saved with success. If not,
|
||||
# it will simply ignore the given options and return status 422 and the
|
||||
# resource errors. To customize the failure scenario, you can pass a
|
||||
# a block to <code>respond_with</code>:
|
||||
#
|
||||
# def create
|
||||
# @project = Project.find(params[:project_id])
|
||||
# @task = @project.comments.build(params[:task])
|
||||
# respond_with(@project, @task, :status => 201) do |format|
|
||||
# if @task.save
|
||||
# flash[:notice] = 'Task was successfully created.'
|
||||
# else
|
||||
# format.html { render "some_special_template" }
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# Using <code>respond_with</code> with a block follows the same syntax as <code>respond_to</code>.
|
||||
class Responder
|
||||
attr_reader :controller, :request, :format, :resource, :resources, :options
|
||||
|
||||
@@ -149,7 +115,7 @@ module ActionController #:nodoc:
|
||||
# Main entry point for responder responsible to dispatch to the proper format.
|
||||
#
|
||||
def respond
|
||||
method = "to_#{format}"
|
||||
method = :"to_#{format}"
|
||||
respond_to?(method) ? send(method) : to_format
|
||||
end
|
||||
|
||||
@@ -162,28 +128,19 @@ module ActionController #:nodoc:
|
||||
navigation_behavior(e)
|
||||
end
|
||||
|
||||
# to_js simply tries to render a template. If no template is found, raises the error.
|
||||
def to_js
|
||||
default_render
|
||||
end
|
||||
|
||||
# All other formats follow the procedure below. First we try to render a
|
||||
# template, if the template is not available, we verify if the resource
|
||||
# responds to :to_format and display it.
|
||||
#
|
||||
def to_format
|
||||
if get? || !has_errors?
|
||||
default_render
|
||||
else
|
||||
display_errors
|
||||
end
|
||||
default_render
|
||||
rescue ActionView::MissingTemplate => e
|
||||
api_behavior(e)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# This is the common behavior for formats associated with browsing, like :html, :iphone and so forth.
|
||||
# This is the common behavior for "navigation" requests, like :html, :iphone and so forth.
|
||||
def navigation_behavior(error)
|
||||
if get?
|
||||
raise error
|
||||
@@ -194,12 +151,14 @@ module ActionController #:nodoc:
|
||||
end
|
||||
end
|
||||
|
||||
# This is the common behavior for formats associated with APIs, such as :xml and :json.
|
||||
# This is the common behavior for "API" requests, like :xml and :json.
|
||||
def api_behavior(error)
|
||||
raise error unless resourceful?
|
||||
|
||||
if get?
|
||||
display resource
|
||||
elsif has_errors?
|
||||
display resource.errors, :status => :unprocessable_entity
|
||||
elsif post?
|
||||
display resource, :status => :created, :location => api_location
|
||||
elsif has_empty_resource_definition?
|
||||
@@ -212,7 +171,7 @@ module ActionController #:nodoc:
|
||||
# Checks whether the resource responds to the current format or not.
|
||||
#
|
||||
def resourceful?
|
||||
resource.respond_to?("to_#{format}")
|
||||
resource.respond_to?(:"to_#{format}")
|
||||
end
|
||||
|
||||
# Returns the resource location by retrieving it from the options or
|
||||
@@ -228,7 +187,7 @@ module ActionController #:nodoc:
|
||||
# controller.
|
||||
#
|
||||
def default_render
|
||||
@default_response.call(options)
|
||||
@default_response.call
|
||||
end
|
||||
|
||||
# Display is just a shortcut to render a resource with the current format.
|
||||
@@ -252,10 +211,6 @@ module ActionController #:nodoc:
|
||||
controller.render given_options.merge!(options).merge!(format => resource)
|
||||
end
|
||||
|
||||
def display_errors
|
||||
controller.render format => resource.errors, :status => :unprocessable_entity
|
||||
end
|
||||
|
||||
# Check whether the resource has errors.
|
||||
#
|
||||
def has_errors?
|
||||
@@ -272,7 +227,7 @@ module ActionController #:nodoc:
|
||||
# Check whether resource needs a specific definition of empty resource to be valid
|
||||
#
|
||||
def has_empty_resource_definition?
|
||||
respond_to?("empty_#{format}_resource", true)
|
||||
respond_to?("empty_#{format}_resource")
|
||||
end
|
||||
|
||||
# Delegate to proper empty resource method
|
||||
|
||||
@@ -1,254 +1,157 @@
|
||||
require 'active_support/core_ext/file/path'
|
||||
require 'rack/chunked'
|
||||
|
||||
module ActionController #:nodoc:
|
||||
# Allows views to be streamed back to the client as they are rendered.
|
||||
#
|
||||
# The default way Rails renders views is by first rendering the template
|
||||
# and then the layout. The response is sent to the client after the whole
|
||||
# template is rendered, all queries are made, and the layout is processed.
|
||||
#
|
||||
# Streaming inverts the rendering flow by rendering the layout first and
|
||||
# streaming each part of the layout as they are processed. This allows the
|
||||
# header of the HTML (which is usually in the layout) to be streamed back
|
||||
# to client very quickly, allowing JavaScripts and stylesheets to be loaded
|
||||
# earlier than usual.
|
||||
#
|
||||
# This approach was introduced in Rails 3.1 and is still improving. Several
|
||||
# Rack middlewares may not work and you need to be careful when streaming.
|
||||
# Those points are going to be addressed soon.
|
||||
#
|
||||
# In order to use streaming, you will need to use a Ruby version that
|
||||
# supports fibers (fibers are supported since version 1.9.2 of the main
|
||||
# Ruby implementation).
|
||||
#
|
||||
# == Examples
|
||||
#
|
||||
# Streaming can be added to a given template easily, all you need to do is
|
||||
# to pass the :stream option.
|
||||
#
|
||||
# class PostsController
|
||||
# def index
|
||||
# @posts = Post.scoped
|
||||
# render :stream => true
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# == When to use streaming
|
||||
#
|
||||
# Streaming may be considered to be overkill for lightweight actions like
|
||||
# +new+ or +edit+. The real benefit of streaming is on expensive actions
|
||||
# that, for example, do a lot of queries on the database.
|
||||
#
|
||||
# In such actions, you want to delay queries execution as much as you can.
|
||||
# For example, imagine the following +dashboard+ action:
|
||||
#
|
||||
# def dashboard
|
||||
# @posts = Post.all
|
||||
# @pages = Page.all
|
||||
# @articles = Article.all
|
||||
# end
|
||||
#
|
||||
# Most of the queries here are happening in the controller. In order to benefit
|
||||
# from streaming you would want to rewrite it as:
|
||||
#
|
||||
# def dashboard
|
||||
# # Allow lazy execution of the queries
|
||||
# @posts = Post.scoped
|
||||
# @pages = Page.scoped
|
||||
# @articles = Article.scoped
|
||||
# render :stream => true
|
||||
# end
|
||||
#
|
||||
# Notice that :stream only works with templates. Rendering :json
|
||||
# or :xml with :stream won't work.
|
||||
#
|
||||
# == Communication between layout and template
|
||||
#
|
||||
# When streaming, rendering happens top-down instead of inside-out.
|
||||
# Rails starts with the layout, and the template is rendered later,
|
||||
# when its +yield+ is reached.
|
||||
#
|
||||
# This means that, if your application currently relies on instance
|
||||
# variables set in the template to be used in the layout, they won't
|
||||
# work once you move to streaming. The proper way to communicate
|
||||
# between layout and template, regardless of whether you use streaming
|
||||
# or not, is by using +content_for+, +provide+ and +yield+.
|
||||
#
|
||||
# Take a simple example where the layout expects the template to tell
|
||||
# which title to use:
|
||||
#
|
||||
# <html>
|
||||
# <head><title><%= yield :title %></title></head>
|
||||
# <body><%= yield %></body>
|
||||
# </html>
|
||||
#
|
||||
# You would use +content_for+ in your template to specify the title:
|
||||
#
|
||||
# <%= content_for :title, "Main" %>
|
||||
# Hello
|
||||
#
|
||||
# And the final result would be:
|
||||
#
|
||||
# <html>
|
||||
# <head><title>Main</title></head>
|
||||
# <body>Hello</body>
|
||||
# </html>
|
||||
#
|
||||
# However, if +content_for+ is called several times, the final result
|
||||
# would have all calls concatenated. For instance, if we have the following
|
||||
# template:
|
||||
#
|
||||
# <%= content_for :title, "Main" %>
|
||||
# Hello
|
||||
# <%= content_for :title, " page" %>
|
||||
#
|
||||
# The final result would be:
|
||||
#
|
||||
# <html>
|
||||
# <head><title>Main page</title></head>
|
||||
# <body>Hello</body>
|
||||
# </html>
|
||||
#
|
||||
# This means that, if you have <code>yield :title</code> in your layout
|
||||
# and you want to use streaming, you would have to render the whole template
|
||||
# (and eventually trigger all queries) before streaming the title and all
|
||||
# assets, which kills the purpose of streaming. For this reason Rails 3.1
|
||||
# introduces a new helper called +provide+ that does the same as +content_for+
|
||||
# but tells the layout to stop searching for other entries and continue rendering.
|
||||
#
|
||||
# For instance, the template above using +provide+ would be:
|
||||
#
|
||||
# <%= provide :title, "Main" %>
|
||||
# Hello
|
||||
# <%= content_for :title, " page" %>
|
||||
#
|
||||
# Giving:
|
||||
#
|
||||
# <html>
|
||||
# <head><title>Main</title></head>
|
||||
# <body>Hello</body>
|
||||
# </html>
|
||||
#
|
||||
# That said, when streaming, you need to properly check your templates
|
||||
# and choose when to use +provide+ and +content_for+.
|
||||
#
|
||||
# == Headers, cookies, session and flash
|
||||
#
|
||||
# When streaming, the HTTP headers are sent to the client right before
|
||||
# it renders the first line. This means that, modifying headers, cookies,
|
||||
# session or flash after the template starts rendering will not propagate
|
||||
# to the client.
|
||||
#
|
||||
# If you try to modify cookies, session or flash, an +ActionDispatch::ClosedError+
|
||||
# will be raised, showing those objects are closed for modification.
|
||||
#
|
||||
# == Middlewares
|
||||
#
|
||||
# Middlewares that need to manipulate the body won't work with streaming.
|
||||
# You should disable those middlewares whenever streaming in development
|
||||
# or production. For instance, +Rack::Bug+ won't work when streaming as it
|
||||
# needs to inject contents in the HTML body.
|
||||
#
|
||||
# Also +Rack::Cache+ won't work with streaming as it does not support
|
||||
# streaming bodies yet. Whenever streaming Cache-Control is automatically
|
||||
# set to "no-cache".
|
||||
#
|
||||
# == Errors
|
||||
#
|
||||
# When it comes to streaming, exceptions get a bit more complicated. This
|
||||
# happens because part of the template was already rendered and streamed to
|
||||
# the client, making it impossible to render a whole exception page.
|
||||
#
|
||||
# Currently, when an exception happens in development or production, Rails
|
||||
# will automatically stream to the client:
|
||||
#
|
||||
# "><script type="text/javascript">window.location = "/500.html"</script></html>
|
||||
#
|
||||
# The first two characters (">) are required in case the exception happens
|
||||
# while rendering attributes for a given tag. You can check the real cause
|
||||
# for the exception in your logger.
|
||||
#
|
||||
# == Web server support
|
||||
#
|
||||
# Not all web servers support streaming out-of-the-box. You need to check
|
||||
# the instructions for each of them.
|
||||
#
|
||||
# ==== Unicorn
|
||||
#
|
||||
# Unicorn supports streaming but it needs to be configured. For this, you
|
||||
# need to create a config file as follow:
|
||||
#
|
||||
# # unicorn.config.rb
|
||||
# listen 3000, :tcp_nopush => false
|
||||
#
|
||||
# And use it on initialization:
|
||||
#
|
||||
# unicorn_rails --config-file unicorn.config.rb
|
||||
#
|
||||
# You may also want to configure other parameters like <tt>:tcp_nodelay</tt>.
|
||||
# Please check its documentation for more information: http://unicorn.bogomips.org/Unicorn/Configurator.html#method-i-listen
|
||||
#
|
||||
# If you are using Unicorn with Nginx, you may need to tweak Nginx.
|
||||
# Streaming should work out of the box on Rainbows.
|
||||
#
|
||||
# ==== Passenger
|
||||
#
|
||||
# To be described.
|
||||
#
|
||||
# Methods for sending arbitrary data and for streaming files to the browser,
|
||||
# instead of rendering.
|
||||
module Streaming
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include AbstractController::Rendering
|
||||
attr_internal :stream
|
||||
include ActionController::Rendering
|
||||
|
||||
module ClassMethods
|
||||
# Render streaming templates. It accepts :only, :except, :if and :unless as options
|
||||
# to specify when to stream, as in ActionController filters.
|
||||
def stream(options={})
|
||||
ActiveSupport::Deprecation.warn "stream class method is deprecated. Please give the :stream option to render instead"
|
||||
if defined?(Fiber)
|
||||
before_filter :_stream_filter, options
|
||||
else
|
||||
raise "You cannot use streaming if Fiber is not available."
|
||||
end
|
||||
end
|
||||
end
|
||||
DEFAULT_SEND_FILE_OPTIONS = {
|
||||
:type => 'application/octet-stream'.freeze,
|
||||
:disposition => 'attachment'.freeze,
|
||||
}.freeze
|
||||
|
||||
protected
|
||||
# Sends the file. This uses a server-appropriate method (such as X-Sendfile)
|
||||
# via the Rack::Sendfile middleware. The header to use is set via
|
||||
# config.action_dispatch.x_sendfile_header, and defaults to "X-Sendfile".
|
||||
# Your server can also configure this for you by setting the X-Sendfile-Type header.
|
||||
#
|
||||
# Be careful to sanitize the path parameter if it is coming from a web
|
||||
# page. <tt>send_file(params[:path])</tt> allows a malicious user to
|
||||
# download any file on your server.
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
||||
# Defaults to <tt>File.basename(path)</tt>.
|
||||
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
|
||||
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
|
||||
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
||||
# Valid values are 'inline' and 'attachment' (default).
|
||||
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
|
||||
# * <tt>:url_based_filename</tt> - set to +true+ if you want the browser guess the filename from
|
||||
# the URL, which is necessary for i18n filenames on certain browsers
|
||||
# (setting <tt>:filename</tt> overrides this option).
|
||||
#
|
||||
# The default Content-Type and Content-Disposition headers are
|
||||
# set to download arbitrary binary files in as many browsers as
|
||||
# possible. IE versions 4, 5, 5.5, and 6 are all known to have
|
||||
# a variety of quirks (especially when downloading over SSL).
|
||||
#
|
||||
# Simple download:
|
||||
#
|
||||
# send_file '/path/to.zip'
|
||||
#
|
||||
# Show a JPEG in the browser:
|
||||
#
|
||||
# send_file '/path/to.jpeg', :type => 'image/jpeg', :disposition => 'inline'
|
||||
#
|
||||
# Show a 404 page in the browser:
|
||||
#
|
||||
# send_file '/path/to/404.html', :type => 'text/html; charset=utf-8', :status => 404
|
||||
#
|
||||
# Read about the other Content-* HTTP headers if you'd like to
|
||||
# provide the user with more information (such as Content-Description) in
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.11.
|
||||
#
|
||||
# Also be aware that the document may be cached by proxies and browsers.
|
||||
# The Pragma and Cache-Control headers declare how the file may be cached
|
||||
# by intermediaries. They default to require clients to validate with
|
||||
# the server before releasing cached responses. See
|
||||
# http://www.mnot.net/cache_docs/ for an overview of web caching and
|
||||
# http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
|
||||
# for the Cache-Control header spec.
|
||||
def send_file(path, options = {}) #:doc:
|
||||
raise MissingFile, "Cannot read file #{path}" unless File.file?(path) and File.readable?(path)
|
||||
|
||||
# Mark following render calls as streaming.
|
||||
def _stream_filter #:nodoc:
|
||||
self.stream = true
|
||||
end
|
||||
options[:filename] ||= File.basename(path) unless options[:url_based_filename]
|
||||
send_file_headers! options
|
||||
|
||||
# Consider the stream option when normalazing options.
|
||||
def _normalize_options(options) #:nodoc:
|
||||
super
|
||||
options[:stream] = self.stream unless options.key?(:stream)
|
||||
end
|
||||
|
||||
# Set proper cache control and transfer encoding when streaming
|
||||
def _process_options(options) #:nodoc:
|
||||
super
|
||||
if options[:stream]
|
||||
if env["HTTP_VERSION"] == "HTTP/1.0"
|
||||
options.delete(:stream)
|
||||
else
|
||||
headers["Cache-Control"] ||= "no-cache"
|
||||
headers["Transfer-Encoding"] = "chunked"
|
||||
headers.delete("Content-Length")
|
||||
if options[:x_sendfile]
|
||||
ActiveSupport::Deprecation.warn(":x_sendfile is no longer needed in send_file", caller)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Call render_to_body if we are streaming instead of usual +render+.
|
||||
def _render_template(options) #:nodoc:
|
||||
if options.delete(:stream)
|
||||
Rack::Chunked::Body.new view_renderer.render_body(view_context, options)
|
||||
else
|
||||
super
|
||||
self.status = options[:status] || 200
|
||||
self.content_type = options[:content_type] if options.key?(:content_type)
|
||||
self.response_body = File.open(path, "rb")
|
||||
end
|
||||
|
||||
# Sends the given binary data to the browser. This method is similar to
|
||||
# <tt>render :text => data</tt>, but also allows you to specify whether
|
||||
# the browser should display the response as a file attachment (i.e. in a
|
||||
# download dialog) or as inline data. You may also set the content type,
|
||||
# the apparent file name, and other things.
|
||||
#
|
||||
# Options:
|
||||
# * <tt>:filename</tt> - suggests a filename for the browser to use.
|
||||
# * <tt>:type</tt> - specifies an HTTP content type. Defaults to 'application/octet-stream'. You can specify
|
||||
# either a string or a symbol for a registered type register with <tt>Mime::Type.register</tt>, for example :json
|
||||
# * <tt>:disposition</tt> - specifies whether the file will be shown inline or downloaded.
|
||||
# Valid values are 'inline' and 'attachment' (default).
|
||||
# * <tt>:status</tt> - specifies the status code to send with the response. Defaults to '200 OK'.
|
||||
#
|
||||
# Generic data download:
|
||||
#
|
||||
# send_data buffer
|
||||
#
|
||||
# Download a dynamically-generated tarball:
|
||||
#
|
||||
# send_data generate_tgz('dir'), :filename => 'dir.tgz'
|
||||
#
|
||||
# Display an image Active Record in the browser:
|
||||
#
|
||||
# send_data image.data, :type => image.content_type, :disposition => 'inline'
|
||||
#
|
||||
# See +send_file+ for more information on HTTP Content-* headers and caching.
|
||||
#
|
||||
# <b>Tip:</b> if you want to stream large amounts of on-the-fly generated
|
||||
# data to the browser, then use <tt>render :text => proc { ... }</tt>
|
||||
# instead. See ActionController::Base#render for more information.
|
||||
def send_data(data, options = {}) #:doc:
|
||||
send_file_headers! options.dup
|
||||
render options.slice(:status, :content_type).merge(:text => data)
|
||||
end
|
||||
|
||||
private
|
||||
def send_file_headers!(options)
|
||||
options.update(DEFAULT_SEND_FILE_OPTIONS.merge(options))
|
||||
[:type, :disposition].each do |arg|
|
||||
raise ArgumentError, ":#{arg} option required" if options[arg].nil?
|
||||
end
|
||||
|
||||
if options.key?(:length)
|
||||
ActiveSupport::Deprecation.warn("You do not need to provide the file's length", caller)
|
||||
end
|
||||
|
||||
disposition = options[:disposition]
|
||||
disposition += %(; filename="#{options[:filename]}") if options[:filename]
|
||||
|
||||
content_type = options[:type]
|
||||
|
||||
if content_type.is_a?(Symbol)
|
||||
extension = Mime[content_type]
|
||||
raise ArgumentError, "Unknown MIME type #{options[:type]}" unless extension
|
||||
self.content_type = extension
|
||||
else
|
||||
self.content_type = content_type
|
||||
end
|
||||
|
||||
headers.merge!(
|
||||
'Content-Disposition' => disposition,
|
||||
'Content-Transfer-Encoding' => 'binary'
|
||||
)
|
||||
|
||||
response.sending_file = true
|
||||
|
||||
# Fix a problem with IE 6.0 on opening downloaded files:
|
||||
# If Cache-Control: no-cache is set (which Rails does by default),
|
||||
# IE removes the file it just downloaded from its cache immediately
|
||||
# after it displays the "open/save" dialog, which means that if you
|
||||
# hit "open" the file isn't there anymore when the application that
|
||||
# is called for handling the download is run, so let's workaround that
|
||||
response.cache_control[:public] ||= false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,11 +4,6 @@ module ActionController
|
||||
|
||||
include RackDelegation
|
||||
|
||||
def recycle!
|
||||
@_url_options = nil
|
||||
end
|
||||
|
||||
|
||||
# TODO: Clean this up
|
||||
def process_with_new_base_test(request, response)
|
||||
@_request = request
|
||||
@@ -19,9 +14,18 @@ module ActionController
|
||||
cookies.write(@_response)
|
||||
end
|
||||
@_response.prepare!
|
||||
set_test_assigns
|
||||
ret
|
||||
end
|
||||
|
||||
def set_test_assigns
|
||||
@assigns = {}
|
||||
(instance_variable_names - self.class.protected_instance_variables).each do |var|
|
||||
name, value = var[1..-1], instance_variable_get(var)
|
||||
@assigns[name] = value
|
||||
end
|
||||
end
|
||||
|
||||
# TODO : Rewrite tests using controller.headers= to use Rack env
|
||||
def headers=(new_headers)
|
||||
@_response ||= ActionDispatch::Response.new
|
||||
|
||||
@@ -1,47 +1,28 @@
|
||||
# Includes +url_for+ into the host class. The class has to provide a +RouteSet+ by implementing
|
||||
# the <tt>_routes</tt> method. Otherwise, an exception will be raised.
|
||||
#
|
||||
# In addition to <tt>AbstractController::UrlFor</tt>, this module accesses the HTTP layer to define
|
||||
# url options like the +host+. In order to do so, this module requires the host class
|
||||
# to implement +env+ and +request+, which need to be a Rack-compatible.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# class RootUrl
|
||||
# include ActionController::UrlFor
|
||||
# include Rails.application.routes.url_helpers
|
||||
#
|
||||
# delegate :env, :request, :to => :controller
|
||||
#
|
||||
# def initialize(controller)
|
||||
# @controller = controller
|
||||
# @url = root_path # named route from the application.
|
||||
# end
|
||||
# end
|
||||
#
|
||||
module ActionController
|
||||
module UrlFor
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
include AbstractController::UrlFor
|
||||
include ActionDispatch::Routing::UrlFor
|
||||
|
||||
def url_options
|
||||
@_url_options ||= super.reverse_merge(
|
||||
:host => request.host,
|
||||
:port => request.optional_port,
|
||||
super.reverse_merge(
|
||||
:host => request.host_with_port,
|
||||
:protocol => request.protocol,
|
||||
:_path_segments => request.symbolized_path_parameters
|
||||
).freeze
|
||||
|
||||
if _routes.equal?(env["action_dispatch.routes"])
|
||||
@_url_options.dup.tap do |options|
|
||||
options[:script_name] = request.script_name.dup
|
||||
options.freeze
|
||||
end
|
||||
else
|
||||
@_url_options
|
||||
end
|
||||
).merge(:script_name => request.script_name)
|
||||
end
|
||||
|
||||
def _routes
|
||||
raise "In order to use #url_for, you must include routing helpers explicitly. " \
|
||||
"For instance, `include Rails.application.routes.url_helpers"
|
||||
end
|
||||
|
||||
module ClassMethods
|
||||
def action_methods
|
||||
@action_methods ||= begin
|
||||
super - _routes.named_routes.helper_names
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -2,13 +2,35 @@ require "rails"
|
||||
require "action_controller"
|
||||
require "action_dispatch/railtie"
|
||||
require "action_view/railtie"
|
||||
require "abstract_controller/railties/routes_helpers"
|
||||
require "action_controller/railties/paths"
|
||||
require "active_support/deprecation/proxy_wrappers"
|
||||
require "active_support/deprecation"
|
||||
|
||||
module ActionController
|
||||
class Railtie < Rails::Railtie
|
||||
config.action_controller = ActiveSupport::OrderedOptions.new
|
||||
|
||||
config.action_controller.singleton_class.tap do |d|
|
||||
d.send(:define_method, :session) do
|
||||
ActiveSupport::Deprecation.warn "config.action_controller.session has been deprecated. " <<
|
||||
"Please use Rails.application.config.session_store instead.", caller
|
||||
end
|
||||
|
||||
d.send(:define_method, :session=) do |val|
|
||||
ActiveSupport::Deprecation.warn "config.action_controller.session= has been deprecated. " <<
|
||||
"Please use config.session_store(name, options) instead.", caller
|
||||
end
|
||||
|
||||
d.send(:define_method, :session_store) do
|
||||
ActiveSupport::Deprecation.warn "config.action_controller.session_store has been deprecated. " <<
|
||||
"Please use Rails.application.config.session_store instead.", caller
|
||||
end
|
||||
|
||||
d.send(:define_method, :session_store=) do |val|
|
||||
ActiveSupport::Deprecation.warn "config.action_controller.session_store= has been deprecated. " <<
|
||||
"Please use config.session_store(name, options) instead.", caller
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_controller.logger" do
|
||||
ActiveSupport.on_load(:action_controller) { self.logger ||= Rails.logger }
|
||||
end
|
||||
@@ -21,27 +43,24 @@ module ActionController
|
||||
paths = app.config.paths
|
||||
options = app.config.action_controller
|
||||
|
||||
options.assets_dir ||= paths["public"].first
|
||||
options.javascripts_dir ||= paths["public/javascripts"].first
|
||||
options.stylesheets_dir ||= paths["public/stylesheets"].first
|
||||
options.page_cache_directory ||= paths["public"].first
|
||||
|
||||
# make sure readers methods get compiled
|
||||
options.asset_path ||= app.config.asset_path
|
||||
options.asset_host ||= app.config.asset_host
|
||||
options.assets_dir ||= paths.public.to_a.first
|
||||
options.javascripts_dir ||= paths.public.javascripts.to_a.first
|
||||
options.stylesheets_dir ||= paths.public.stylesheets.to_a.first
|
||||
options.page_cache_directory ||= paths.public.to_a.first
|
||||
options.helpers_path ||= paths.app.helpers.to_a
|
||||
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
include app.routes.mounted_helpers
|
||||
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes)
|
||||
extend ::ActionController::Railties::Paths.with(app)
|
||||
include app.routes.url_helpers
|
||||
options.each { |k,v| send("#{k}=", v) }
|
||||
end
|
||||
end
|
||||
|
||||
initializer "action_controller.compile_config_methods" do
|
||||
ActiveSupport.on_load(:action_controller) do
|
||||
config.compile_methods! if config.respond_to?(:compile_methods!)
|
||||
end
|
||||
initializer "action_controller.deprecated_routes" do |app|
|
||||
message = "ActionController::Routing::Routes is deprecated. " \
|
||||
"Instead, use Rails.application.routes"
|
||||
|
||||
proxy = ActiveSupport::Deprecation::DeprecatedObjectProxy.new(app.routes, message)
|
||||
ActionController::Routing::Routes = proxy
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,24 +0,0 @@
|
||||
module ActionController
|
||||
module Railties
|
||||
module Paths
|
||||
def self.with(app)
|
||||
Module.new do
|
||||
define_method(:inherited) do |klass|
|
||||
super(klass)
|
||||
|
||||
if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) }
|
||||
paths = namespace._railtie.paths["app/helpers"].existent
|
||||
else
|
||||
paths = app.config.helpers_paths
|
||||
end
|
||||
|
||||
klass.helpers_path = paths
|
||||
if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers
|
||||
klass.helper :all
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -18,12 +18,18 @@ module ActionController
|
||||
# post = Post.find(params[:id])
|
||||
# post.destroy
|
||||
#
|
||||
# redirect_to(post) # Calls polymorphic_url(post) which in turn calls post_url(post)
|
||||
# respond_to do |format|
|
||||
# format.html { redirect_to(post) } # Calls polymorphic_url(post) which in turn calls post_url(post)
|
||||
# format.js do
|
||||
# # Calls: new Effect.fade('post_45');
|
||||
# render(:update) { |page| page[post].visual_effect(:fade) }
|
||||
# end
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# As the example above shows, you can stop caring to a large extent what the actual id of the post is.
|
||||
# You just know that one is being assigned and that the subsequent calls in redirect_to expect that
|
||||
# same naming convention and allows you to write less code if you follow it.
|
||||
# As the example above shows, you can stop caring to a large extent what the actual id of the post is. You just know
|
||||
# that one is being assigned and that the subsequent calls in redirect_to and the RJS expect that same naming
|
||||
# convention and allows you to write less code if you follow it.
|
||||
module RecordIdentifier
|
||||
extend self
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
require 'rack/session/abstract/id'
|
||||
require 'active_support/core_ext/object/blank'
|
||||
require 'active_support/core_ext/object/to_query'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/module/anonymous'
|
||||
|
||||
module ActionController
|
||||
module TemplateAssertions
|
||||
@@ -130,13 +128,13 @@ module ActionController
|
||||
super
|
||||
|
||||
self.session = TestSession.new
|
||||
self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => SecureRandom.hex(16))
|
||||
self.session_options = TestSession::DEFAULT_OPTIONS.merge(:id => ActiveSupport::SecureRandom.hex(16))
|
||||
end
|
||||
|
||||
class Result < ::Array #:nodoc:
|
||||
def to_s() join '/' end
|
||||
def self.new_escaped(strings)
|
||||
new strings.collect {|str| uri_parser.unescape str}
|
||||
new strings.collect {|str| URI.unescape str}
|
||||
end
|
||||
end
|
||||
|
||||
@@ -145,23 +143,15 @@ module ActionController
|
||||
extra_keys = routes.extra_keys(parameters)
|
||||
non_path_parameters = get? ? query_parameters : request_parameters
|
||||
parameters.each do |key, value|
|
||||
if value.is_a?(Array) && (value.frozen? || value.any?(&:frozen?))
|
||||
value = value.map{ |v| v.duplicable? ? v.dup : v }
|
||||
elsif value.is_a?(Hash) && (value.frozen? || value.any?{ |k,v| v.frozen? })
|
||||
value = Hash[value.map{ |k,v| [k, v.duplicable? ? v.dup : v] }]
|
||||
elsif value.frozen? && value.duplicable?
|
||||
value = value.dup
|
||||
if value.is_a? Fixnum
|
||||
value = value.to_s
|
||||
elsif value.is_a? Array
|
||||
value = Result.new(value)
|
||||
end
|
||||
|
||||
if extra_keys.include?(key.to_sym)
|
||||
non_path_parameters[key] = value
|
||||
else
|
||||
if value.is_a?(Array)
|
||||
value = Result.new(value.map(&:to_param))
|
||||
else
|
||||
value = value.to_param
|
||||
end
|
||||
|
||||
path_parameters[key.to_s] = value
|
||||
end
|
||||
end
|
||||
@@ -181,16 +171,12 @@ module ActionController
|
||||
end
|
||||
|
||||
def recycle!
|
||||
write_cookies!
|
||||
@env.delete('HTTP_COOKIE') if @cookies.blank?
|
||||
@env.delete('action_dispatch.cookies')
|
||||
@cookies = nil
|
||||
@formats = nil
|
||||
@env.delete_if { |k, v| k =~ /^(action_dispatch|rack)\.request/ }
|
||||
@env.delete_if { |k, v| k =~ /^action_dispatch\.rescue/ }
|
||||
@symbolized_path_params = nil
|
||||
@method = @request_method = nil
|
||||
@fullpath = @ip = @remote_ip = @protocol = nil
|
||||
@fullpath = @ip = @remote_ip = nil
|
||||
@env['action_dispatch.request.query_parameters'] = {}
|
||||
end
|
||||
end
|
||||
@@ -208,23 +194,20 @@ module ActionController
|
||||
end
|
||||
end
|
||||
|
||||
class TestSession < Rack::Session::Abstract::SessionHash #:nodoc:
|
||||
DEFAULT_OPTIONS = Rack::Session::Abstract::ID::DEFAULT_OPTIONS
|
||||
class TestSession < ActionDispatch::Session::AbstractStore::SessionHash #:nodoc:
|
||||
DEFAULT_OPTIONS = ActionDispatch::Session::AbstractStore::DEFAULT_OPTIONS
|
||||
|
||||
def initialize(session = {})
|
||||
@env, @by = nil, nil
|
||||
replace(session.stringify_keys)
|
||||
@loaded = true
|
||||
end
|
||||
|
||||
def exists?
|
||||
true
|
||||
end
|
||||
def exists?; true; end
|
||||
end
|
||||
|
||||
# Superclass for ActionController functional tests. Functional tests allow you to
|
||||
# test a single controller action per test method. This should not be confused with
|
||||
# integration tests (see ActionDispatch::IntegrationTest), which are more like
|
||||
# integration tests (see ActionController::IntegrationTest), which are more like
|
||||
# "stories" that can involve multiple controllers and multiple actions (i.e. multiple
|
||||
# different HTTP requests).
|
||||
#
|
||||
@@ -269,7 +252,7 @@ module ActionController
|
||||
# after calling +post+. If the various assert methods are not sufficient, then you
|
||||
# may use this object to inspect the HTTP response in detail.
|
||||
#
|
||||
# (Earlier versions of \Rails required each functional test to subclass
|
||||
# (Earlier versions of Rails required each functional test to subclass
|
||||
# Test::Unit::TestCase and define @controller, @request, @response in +setup+.)
|
||||
#
|
||||
# == Controller is automatically inferred
|
||||
@@ -282,7 +265,7 @@ module ActionController
|
||||
# tests WidgetController
|
||||
# end
|
||||
#
|
||||
# == \Testing controller internals
|
||||
# == Testing controller internals
|
||||
#
|
||||
# In addition to these specific assertions, you also have easy access to various collections that the regular test/unit assertions
|
||||
# can be used against. These collections are:
|
||||
@@ -290,7 +273,7 @@ module ActionController
|
||||
# * assigns: Instance variables assigned in the action that are available for the view.
|
||||
# * session: Objects being saved in the session.
|
||||
# * flash: The flash objects currently in the session.
|
||||
# * cookies: \Cookies being sent to the user on this request.
|
||||
# * cookies: Cookies being sent to the user on this request.
|
||||
#
|
||||
# These collections can be used just like any other hash:
|
||||
#
|
||||
@@ -314,13 +297,9 @@ module ActionController
|
||||
# and cookies, though. For sessions, you just do:
|
||||
#
|
||||
# @request.session[:key] = "value"
|
||||
# @request.cookies[:key] = "value"
|
||||
# @request.cookies["key"] = "value"
|
||||
#
|
||||
# To clear the cookies for a test just clear the request's cookies hash:
|
||||
#
|
||||
# @request.cookies.clear
|
||||
#
|
||||
# == \Testing named routes
|
||||
# == Testing named routes
|
||||
#
|
||||
# If you're using named routes, they can be easily tested using the original named routes' methods straight in the test case.
|
||||
# Example:
|
||||
@@ -343,11 +322,11 @@ module ActionController
|
||||
|
||||
def controller_class=(new_class)
|
||||
prepare_controller_class(new_class) if new_class
|
||||
self._controller_class = new_class
|
||||
write_inheritable_attribute(:controller_class, new_class)
|
||||
end
|
||||
|
||||
def controller_class
|
||||
if current_controller_class = self._controller_class
|
||||
if current_controller_class = read_inheritable_attribute(:controller_class)
|
||||
current_controller_class
|
||||
else
|
||||
self.controller_class = determine_default_controller_class(name)
|
||||
@@ -401,24 +380,7 @@ module ActionController
|
||||
end
|
||||
alias xhr :xml_http_request
|
||||
|
||||
def paramify_values(hash_or_array_or_value)
|
||||
case hash_or_array_or_value
|
||||
when Hash
|
||||
Hash[hash_or_array_or_value.map{|key, value| [key, paramify_values(value)] }]
|
||||
when Array
|
||||
hash_or_array_or_value.map {|i| paramify_values(i)}
|
||||
when Rack::Test::UploadedFile
|
||||
hash_or_array_or_value
|
||||
else
|
||||
hash_or_array_or_value.to_param
|
||||
end
|
||||
end
|
||||
|
||||
def process(action, parameters = nil, session = nil, flash = nil, http_method = 'GET')
|
||||
# Ensure that numbers and symbols passed as params are converted to
|
||||
# proper params, as is the case when engaging rack.
|
||||
parameters = paramify_values(parameters) if html_format?(parameters)
|
||||
|
||||
# Sanity check for required instance variables so we can give an
|
||||
# understandable error message.
|
||||
%w(@routes @controller @request @response).each do |iv_name|
|
||||
@@ -437,23 +399,18 @@ module ActionController
|
||||
@request.env['REQUEST_METHOD'] = http_method
|
||||
|
||||
parameters ||= {}
|
||||
controller_class_name = @controller.class.anonymous? ?
|
||||
"anonymous_controller" :
|
||||
@controller.class.name.underscore.sub(/_controller$/, '')
|
||||
@request.assign_parameters(@routes, @controller.class.name.underscore.sub(/_controller$/, ''), action.to_s, parameters)
|
||||
|
||||
@request.assign_parameters(@routes, controller_class_name, action.to_s, parameters)
|
||||
|
||||
@request.session = ActionController::TestSession.new(session) if session
|
||||
@request.session = ActionController::TestSession.new(session) unless session.nil?
|
||||
@request.session["flash"] = @request.flash.update(flash || {})
|
||||
@request.session["flash"].sweep
|
||||
|
||||
@controller.request = @request
|
||||
@controller.params.merge!(parameters)
|
||||
build_request_uri(action, parameters)
|
||||
@controller.class.class_eval { include Testing }
|
||||
@controller.recycle!
|
||||
Base.class_eval { include Testing }
|
||||
@controller.process_with_new_base_test(@request, @response)
|
||||
@assigns = @controller.respond_to?(:view_assigns) ? @controller.view_assigns : {}
|
||||
@request.session.delete('flash') if @request.session['flash'].blank?
|
||||
@request.cookies.merge!(@response.cookies)
|
||||
@response
|
||||
end
|
||||
|
||||
@@ -467,7 +424,7 @@ module ActionController
|
||||
|
||||
@request.env.delete('PATH_INFO')
|
||||
|
||||
if defined?(@controller) && @controller
|
||||
if @controller
|
||||
@controller.request = @request
|
||||
@controller.params = {}
|
||||
end
|
||||
@@ -481,7 +438,6 @@ module ActionController
|
||||
included do
|
||||
include ActionController::TemplateAssertions
|
||||
include ActionDispatch::Assertions
|
||||
class_attribute :_controller_class
|
||||
setup :setup_controller_request_and_response
|
||||
end
|
||||
|
||||
@@ -489,7 +445,7 @@ module ActionController
|
||||
|
||||
def build_request_uri(action, parameters)
|
||||
unless @request.env["PATH_INFO"]
|
||||
options = @controller.respond_to?(:url_options) ? @controller.__send__(:url_options).merge(parameters) : parameters
|
||||
options = @controller.__send__(:url_options).merge(parameters)
|
||||
options.update(
|
||||
:only_path => true,
|
||||
:action => action,
|
||||
@@ -503,12 +459,6 @@ module ActionController
|
||||
@request.env["QUERY_STRING"] = query_string || ""
|
||||
end
|
||||
end
|
||||
|
||||
def html_format?(parameters)
|
||||
return true unless parameters.is_a?(Hash)
|
||||
format = Mime[parameters[:format]]
|
||||
format.nil? || format.html?
|
||||
end
|
||||
end
|
||||
|
||||
# When the request.remote_addr remains the default for testing, which is 0.0.0.0, the exception is simply raised inline
|
||||
@@ -519,11 +469,9 @@ module ActionController
|
||||
# The exception is stored in the exception accessor for further inspection.
|
||||
module RaiseActionExceptions
|
||||
def self.included(base)
|
||||
unless base.method_defined?(:exception) && base.method_defined?(:exception=)
|
||||
base.class_eval do
|
||||
attr_accessor :exception
|
||||
protected :exception, :exception=
|
||||
end
|
||||
base.class_eval do
|
||||
attr_accessor :exception
|
||||
protected :exception, :exception=
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ require 'html/selector'
|
||||
require 'html/sanitizer'
|
||||
|
||||
module HTML #:nodoc:
|
||||
# A top-level HTML document. You give it a body of text, and it will parse that
|
||||
# A top-level HTMl document. You give it a body of text, and it will parse that
|
||||
# text into a tree of nodes.
|
||||
class Document #:nodoc:
|
||||
|
||||
|
||||
@@ -18,14 +18,14 @@ module HTML #:nodoc:
|
||||
hash[k] = Conditions.new(v)
|
||||
when :children
|
||||
hash[k] = v = keys_to_symbols(v)
|
||||
v.each do |key,value|
|
||||
case key
|
||||
v.each do |k,v2|
|
||||
case k
|
||||
when :count, :greater_than, :less_than
|
||||
# keys are valid, and require no further processing
|
||||
when :only
|
||||
v[key] = Conditions.new(value)
|
||||
v[k] = Conditions.new(v2)
|
||||
else
|
||||
raise "illegal key #{key.inspect} => #{value.inspect}"
|
||||
raise "illegal key #{k.inspect} => #{v2.inspect}"
|
||||
end
|
||||
end
|
||||
else
|
||||
@@ -156,7 +156,7 @@ module HTML #:nodoc:
|
||||
end
|
||||
|
||||
closing = ( scanner.scan(/\//) ? :close : nil )
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[^\s!>\/]+/)
|
||||
return Text.new(parent, line, pos, content) unless name = scanner.scan(/[\w:-]+/)
|
||||
name.downcase!
|
||||
|
||||
unless closing
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
require 'set'
|
||||
require 'cgi'
|
||||
require 'active_support/core_ext/class/attribute'
|
||||
require 'active_support/core_ext/class/inheritable_attributes'
|
||||
|
||||
module HTML
|
||||
class Sanitizer
|
||||
@@ -34,7 +33,7 @@ module HTML
|
||||
result = super
|
||||
# strip any comments, and if they have a newline at the end (ie. line with
|
||||
# only a comment) strip that too
|
||||
result = result.gsub(/<!--(.*?)-->[\n]?/m, "") if (result && result =~ /<!--(.*?)-->[\n]?/m)
|
||||
result.gsub!(/<!--(.*?)-->[\n]?/m, "") if result
|
||||
# Recurse - handle all dirty nested tags
|
||||
result == text ? result : sanitize(result, options)
|
||||
end
|
||||
@@ -61,7 +60,7 @@ module HTML
|
||||
class WhiteListSanitizer < Sanitizer
|
||||
[:protocol_separator, :uri_attributes, :allowed_attributes, :allowed_tags, :allowed_protocols, :bad_tags,
|
||||
:allowed_css_properties, :allowed_css_keywords, :shorthand_css_properties].each do |attr|
|
||||
class_attribute attr, :instance_writer => false
|
||||
class_inheritable_accessor attr, :instance_writer => false
|
||||
end
|
||||
|
||||
# A regular expression of the valid characters used to separate protocols like
|
||||
@@ -171,7 +170,7 @@ module HTML
|
||||
|
||||
def contains_bad_protocols?(attr_name, value)
|
||||
uri_attributes.include?(attr_name) &&
|
||||
(value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first.downcase))
|
||||
(value =~ /(^[^\/:]*):|(�*58)|(p)|(%|%)3A/ && !allowed_protocols.include?(value.split(protocol_separator).first))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -128,8 +128,6 @@ module HTML
|
||||
# (no parent element).
|
||||
# * <tt>:empty</tt> -- Match the element only if it has no child elements,
|
||||
# and no text content.
|
||||
# * <tt>:content(string)</tt> -- Match the element only if it has <tt>string</tt>
|
||||
# as its text content (ignoring leading and trailing whitespace).
|
||||
# * <tt>:only-child</tt> -- Match the element if it is the only child (element)
|
||||
# of its parent element.
|
||||
# * <tt>:only-of-type</tt> -- Match the element if it is the only child (element)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#--
|
||||
# Copyright (c) 2004-2011 David Heinemeier Hansson
|
||||
# Copyright (c) 2004-2010 David Heinemeier Hansson
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining
|
||||
# a copy of this software and associated documentation files (the
|
||||
@@ -53,14 +53,12 @@ module ActionDispatch
|
||||
autoload :Flash
|
||||
autoload :Head
|
||||
autoload :ParamsParser
|
||||
autoload :Reloader
|
||||
autoload :RemoteIp
|
||||
autoload :Rescue
|
||||
autoload :ShowExceptions
|
||||
autoload :Static
|
||||
end
|
||||
|
||||
autoload :ClosedError, 'action_dispatch/middleware/closed_error'
|
||||
autoload :MiddlewareStack, 'action_dispatch/middleware/stack'
|
||||
autoload :Routing
|
||||
|
||||
@@ -87,7 +85,6 @@ module ActionDispatch
|
||||
autoload_under 'testing' do
|
||||
autoload :Assertions
|
||||
autoload :Integration
|
||||
autoload :IntegrationTest, 'action_dispatch/testing/integration'
|
||||
autoload :PerformanceTest
|
||||
autoload :TestProcess
|
||||
autoload :TestRequest
|
||||
|
||||
@@ -39,8 +39,22 @@ module ActionDispatch
|
||||
end
|
||||
|
||||
module Response
|
||||
attr_reader :cache_control, :etag
|
||||
alias :etag? :etag
|
||||
attr_reader :cache_control
|
||||
|
||||
def initialize(*)
|
||||
status, header, body = super
|
||||
|
||||
@cache_control = {}
|
||||
@etag = self["ETag"]
|
||||
|
||||
if cache_control = self["Cache-Control"]
|
||||
cache_control.split(/,\s*/).each do |segment|
|
||||
first, last = segment.split("=")
|
||||
last ||= true
|
||||
@cache_control[first.to_sym] = last
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def last_modified
|
||||
if last = headers['Last-Modified']
|
||||
@@ -56,6 +70,14 @@ module ActionDispatch
|
||||
headers['Last-Modified'] = utc_time.httpdate
|
||||
end
|
||||
|
||||
def etag
|
||||
@etag
|
||||
end
|
||||
|
||||
def etag?
|
||||
@etag
|
||||
end
|
||||
|
||||
def etag=(etag)
|
||||
key = ActiveSupport::Cache.expand_cache_key(etag)
|
||||
@etag = self["ETag"] = %("#{Digest::MD5.hexdigest(key)}")
|
||||
@@ -63,24 +85,31 @@ module ActionDispatch
|
||||
|
||||
private
|
||||
|
||||
def prepare_cache_control!
|
||||
@cache_control = {}
|
||||
@etag = self["ETag"]
|
||||
|
||||
if cache_control = self["Cache-Control"]
|
||||
cache_control.split(/,\s*/).each do |segment|
|
||||
first, last = segment.split("=")
|
||||
@cache_control[first.to_sym] = last || true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_conditional_get!
|
||||
if etag? || last_modified? || !@cache_control.empty?
|
||||
set_conditional_cache_control!
|
||||
elsif nonempty_ok_response?
|
||||
self.etag = body
|
||||
|
||||
if request && request.etag_matches?(etag)
|
||||
self.status = 304
|
||||
self.body = []
|
||||
end
|
||||
|
||||
set_conditional_cache_control!
|
||||
else
|
||||
headers["Cache-Control"] = "no-cache"
|
||||
end
|
||||
end
|
||||
|
||||
def nonempty_ok_response?
|
||||
@status == 200 && string_body?
|
||||
end
|
||||
|
||||
def string_body?
|
||||
!@blank && @body.respond_to?(:all?) && @body.all? { |part| part.is_a?(String) }
|
||||
end
|
||||
|
||||
DEFAULT_CACHE_CONTROL = "max-age=0, private, must-revalidate"
|
||||
|
||||
def set_conditional_cache_control!
|
||||
@@ -90,7 +119,7 @@ module ActionDispatch
|
||||
|
||||
if control.empty?
|
||||
headers["Cache-Control"] = DEFAULT_CACHE_CONTROL
|
||||
elsif control[:no_cache]
|
||||
elsif @cache_control[:no_cache]
|
||||
headers["Cache-Control"] = "no-cache"
|
||||
else
|
||||
extras = control[:extras]
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user