actionpack 1.13.6 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionpack might be problematic. Click here for more details.

Files changed (317) hide show
  1. data/CHANGELOG +1400 -20
  2. data/MIT-LICENSE +1 -1
  3. data/README +5 -5
  4. data/RUNNING_UNIT_TESTS +4 -5
  5. data/Rakefile +5 -6
  6. data/install.rb +2 -2
  7. data/lib/action_controller.rb +11 -15
  8. data/lib/action_controller/assertions.rb +12 -25
  9. data/lib/action_controller/assertions/dom_assertions.rb +18 -4
  10. data/lib/action_controller/assertions/model_assertions.rb +8 -1
  11. data/lib/action_controller/assertions/response_assertions.rb +35 -12
  12. data/lib/action_controller/assertions/routing_assertions.rb +56 -12
  13. data/lib/action_controller/assertions/selector_assertions.rb +105 -38
  14. data/lib/action_controller/assertions/tag_assertions.rb +28 -15
  15. data/lib/action_controller/base.rb +318 -250
  16. data/lib/action_controller/benchmarking.rb +33 -29
  17. data/lib/action_controller/caching.rb +130 -64
  18. data/lib/action_controller/cgi_ext.rb +16 -0
  19. data/lib/action_controller/cgi_ext/{cookie_performance_fix.rb → cookie.rb} +25 -40
  20. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  21. data/lib/action_controller/cgi_ext/session.rb +73 -0
  22. data/lib/action_controller/cgi_ext/stdinput.rb +23 -0
  23. data/lib/action_controller/cgi_process.rb +34 -57
  24. data/lib/action_controller/components.rb +19 -36
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/dispatcher.rb +195 -0
  27. data/lib/action_controller/filters.rb +35 -34
  28. data/lib/action_controller/flash.rb +30 -35
  29. data/lib/action_controller/helpers.rb +121 -47
  30. data/lib/action_controller/http_authentication.rb +126 -0
  31. data/lib/action_controller/integration.rb +105 -101
  32. data/lib/action_controller/layout.rb +59 -47
  33. data/lib/action_controller/mime_responds.rb +57 -68
  34. data/lib/action_controller/mime_type.rb +43 -80
  35. data/lib/action_controller/mime_types.rb +20 -0
  36. data/lib/action_controller/polymorphic_routes.rb +88 -0
  37. data/lib/action_controller/record_identifier.rb +91 -0
  38. data/lib/action_controller/request.rb +553 -88
  39. data/lib/action_controller/request_forgery_protection.rb +126 -0
  40. data/lib/action_controller/request_profiler.rb +138 -0
  41. data/lib/action_controller/rescue.rb +185 -69
  42. data/lib/action_controller/resources.rb +211 -172
  43. data/lib/action_controller/response.rb +49 -8
  44. data/lib/action_controller/routing.rb +359 -236
  45. data/lib/action_controller/routing_optimisation.rb +119 -0
  46. data/lib/action_controller/session/active_record_store.rb +3 -2
  47. data/lib/action_controller/session/cookie_store.rb +161 -0
  48. data/lib/action_controller/session/mem_cache_store.rb +9 -16
  49. data/lib/action_controller/session_management.rb +17 -8
  50. data/lib/action_controller/streaming.rb +6 -3
  51. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  52. data/lib/action_controller/templates/rescues/{_trace.rhtml → _trace.erb} +0 -0
  53. data/lib/action_controller/templates/rescues/{diagnostics.rhtml → diagnostics.erb} +2 -2
  54. data/lib/action_controller/templates/rescues/{layout.rhtml → layout.erb} +0 -0
  55. data/lib/action_controller/templates/rescues/{missing_template.rhtml → missing_template.erb} +0 -0
  56. data/lib/action_controller/templates/rescues/{routing_error.rhtml → routing_error.erb} +0 -0
  57. data/lib/action_controller/templates/rescues/{template_error.rhtml → template_error.erb} +2 -2
  58. data/lib/action_controller/templates/rescues/{unknown_action.rhtml → unknown_action.erb} +0 -0
  59. data/lib/action_controller/test_case.rb +53 -0
  60. data/lib/action_controller/test_process.rb +59 -46
  61. data/lib/action_controller/url_rewriter.rb +48 -24
  62. data/lib/action_controller/vendor/html-scanner/html/document.rb +7 -4
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +11 -6
  65. data/lib/action_controller/verification.rb +27 -21
  66. data/lib/action_pack.rb +1 -1
  67. data/lib/action_pack/version.rb +4 -4
  68. data/lib/action_view.rb +2 -3
  69. data/lib/action_view/base.rb +218 -63
  70. data/lib/action_view/compiled_templates.rb +1 -2
  71. data/lib/action_view/helpers/active_record_helper.rb +35 -17
  72. data/lib/action_view/helpers/asset_tag_helper.rb +395 -87
  73. data/lib/action_view/helpers/atom_feed_helper.rb +111 -0
  74. data/lib/action_view/helpers/benchmark_helper.rb +12 -5
  75. data/lib/action_view/helpers/cache_helper.rb +29 -0
  76. data/lib/action_view/helpers/capture_helper.rb +97 -63
  77. data/lib/action_view/helpers/date_helper.rb +295 -35
  78. data/lib/action_view/helpers/debug_helper.rb +6 -2
  79. data/lib/action_view/helpers/form_helper.rb +354 -111
  80. data/lib/action_view/helpers/form_options_helper.rb +171 -109
  81. data/lib/action_view/helpers/form_tag_helper.rb +332 -76
  82. data/lib/action_view/helpers/javascript_helper.rb +35 -11
  83. data/lib/action_view/helpers/javascripts/controls.js +484 -354
  84. data/lib/action_view/helpers/javascripts/dragdrop.js +88 -58
  85. data/lib/action_view/helpers/javascripts/effects.js +396 -364
  86. data/lib/action_view/helpers/javascripts/prototype.js +2817 -1107
  87. data/lib/action_view/helpers/number_helper.rb +84 -60
  88. data/lib/action_view/helpers/prototype_helper.rb +419 -43
  89. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  90. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  91. data/lib/action_view/helpers/sanitize_helper.rb +223 -0
  92. data/lib/action_view/helpers/scriptaculous_helper.rb +63 -4
  93. data/lib/action_view/helpers/tag_helper.rb +69 -39
  94. data/lib/action_view/helpers/text_helper.rb +221 -148
  95. data/lib/action_view/helpers/url_helper.rb +283 -165
  96. data/lib/action_view/partials.rb +134 -62
  97. data/lib/action_view/template_error.rb +4 -12
  98. data/lib/actionpack.rb +1 -0
  99. data/test/abstract_unit.rb +21 -1
  100. data/test/action_view_test.rb +26 -0
  101. data/test/active_record_unit.rb +12 -20
  102. data/test/activerecord/active_record_store_test.rb +2 -2
  103. data/test/activerecord/render_partial_with_record_identification_test.rb +74 -0
  104. data/test/controller/action_pack_assertions_test.rb +21 -152
  105. data/test/controller/addresses_render_test.rb +2 -7
  106. data/test/controller/assert_select_test.rb +120 -14
  107. data/test/controller/base_test.rb +11 -13
  108. data/test/controller/caching_test.rb +125 -5
  109. data/test/controller/capture_test.rb +23 -16
  110. data/test/controller/cgi_test.rb +66 -391
  111. data/test/controller/components_test.rb +31 -42
  112. data/test/controller/content_type_test.rb +1 -1
  113. data/test/controller/cookie_test.rb +42 -14
  114. data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -42
  115. data/test/controller/dispatcher_test.rb +123 -0
  116. data/test/controller/fake_models.rb +5 -0
  117. data/test/controller/filters_test.rb +44 -7
  118. data/test/controller/flash_test.rb +46 -2
  119. data/test/controller/fragment_store_setting_test.rb +10 -8
  120. data/test/controller/helper_test.rb +19 -2
  121. data/test/controller/html-scanner/document_test.rb +124 -0
  122. data/test/controller/html-scanner/node_test.rb +69 -0
  123. data/test/controller/html-scanner/sanitizer_test.rb +250 -0
  124. data/test/controller/html-scanner/tag_node_test.rb +239 -0
  125. data/test/controller/html-scanner/text_node_test.rb +51 -0
  126. data/test/controller/html-scanner/tokenizer_test.rb +125 -0
  127. data/test/controller/http_authentication_test.rb +54 -0
  128. data/test/controller/integration_test.rb +12 -26
  129. data/test/controller/layout_test.rb +64 -12
  130. data/test/controller/mime_responds_test.rb +193 -38
  131. data/test/controller/mime_type_test.rb +30 -8
  132. data/test/controller/new_render_test.rb +104 -22
  133. data/test/controller/polymorphic_routes_test.rb +98 -0
  134. data/test/controller/record_identifier_test.rb +103 -0
  135. data/test/controller/redirect_test.rb +120 -18
  136. data/test/controller/render_test.rb +195 -45
  137. data/test/controller/request_forgery_protection_test.rb +217 -0
  138. data/test/controller/request_test.rb +545 -27
  139. data/test/controller/rescue_test.rb +501 -0
  140. data/test/controller/resources_test.rb +258 -132
  141. data/test/controller/routing_test.rb +502 -106
  142. data/test/controller/selector_test.rb +5 -5
  143. data/test/controller/send_file_test.rb +17 -7
  144. data/test/controller/session/cookie_store_test.rb +246 -0
  145. data/test/controller/session/mem_cache_store_test.rb +182 -0
  146. data/test/controller/session_fixation_test.rb +8 -11
  147. data/test/controller/session_management_test.rb +7 -7
  148. data/test/controller/test_test.rb +150 -38
  149. data/test/controller/url_rewriter_test.rb +87 -12
  150. data/test/controller/verification_test.rb +11 -0
  151. data/test/controller/view_paths_test.rb +137 -0
  152. data/test/controller/webservice_test.rb +11 -75
  153. data/test/fixtures/addresses/{list.rhtml → list.erb} +0 -0
  154. data/test/fixtures/db_definitions/sqlite.sql +2 -1
  155. data/test/fixtures/developer.rb +2 -0
  156. data/test/fixtures/fun/games/{hello_world.rhtml → hello_world.erb} +0 -0
  157. data/test/fixtures/helpers/fun/pdf_helper.rb +1 -1
  158. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  159. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  160. data/test/fixtures/layouts/{builder.rxml → builder.builder} +0 -0
  161. data/test/fixtures/layouts/{standard.rhtml → standard.erb} +0 -0
  162. data/test/fixtures/layouts/{talk_from_action.rhtml → talk_from_action.erb} +0 -0
  163. data/test/fixtures/layouts/{yield.rhtml → yield.erb} +0 -0
  164. data/test/fixtures/multipart/binary_file +0 -0
  165. data/test/fixtures/multipart/bracketed_param +5 -0
  166. data/test/fixtures/override/test/hello_world.erb +1 -0
  167. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  168. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  169. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  170. data/test/fixtures/post_test/post/index.html.erb +1 -0
  171. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  172. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  173. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  174. data/test/fixtures/public/404.html +1 -0
  175. data/test/fixtures/public/500.html +1 -0
  176. data/test/fixtures/public/javascripts/application.js +0 -1
  177. data/test/fixtures/public/javascripts/bank.js +1 -0
  178. data/test/fixtures/public/javascripts/robber.js +1 -0
  179. data/test/fixtures/public/stylesheets/bank.css +1 -0
  180. data/test/fixtures/public/stylesheets/robber.css +1 -0
  181. data/test/fixtures/replies.yml +2 -0
  182. data/test/fixtures/reply.rb +2 -1
  183. data/test/fixtures/respond_to/{all_types_with_layout.rhtml → all_types_with_layout.html.erb} +0 -0
  184. data/test/fixtures/respond_to/{all_types_with_layout.rjs → all_types_with_layout.js.rjs} +0 -0
  185. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  186. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  187. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  188. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  189. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  190. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  191. data/test/fixtures/respond_to/{using_defaults.rhtml → using_defaults.html.erb} +0 -0
  192. data/test/fixtures/respond_to/{using_defaults.rjs → using_defaults.js.rjs} +0 -0
  193. data/test/fixtures/respond_to/{using_defaults.rxml → using_defaults.xml.builder} +0 -0
  194. data/test/fixtures/respond_to/{using_defaults_with_type_list.rhtml → using_defaults_with_type_list.html.erb} +0 -0
  195. data/test/fixtures/respond_to/{using_defaults_with_type_list.rjs → using_defaults_with_type_list.js.rjs} +0 -0
  196. data/test/fixtures/respond_to/{using_defaults_with_type_list.rxml → using_defaults_with_type_list.xml.builder} +0 -0
  197. data/test/fixtures/scope/test/{modgreet.rhtml → modgreet.erb} +0 -0
  198. data/test/fixtures/test/{_customer.rhtml → _customer.erb} +0 -0
  199. data/test/fixtures/test/{_customer_greeting.rhtml → _customer_greeting.erb} +0 -0
  200. data/test/fixtures/test/_hash_greeting.erb +1 -0
  201. data/test/fixtures/test/_hash_object.erb +2 -0
  202. data/test/fixtures/test/{_hello.rxml → _hello.builder} +0 -0
  203. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  204. data/test/fixtures/test/_partial.erb +1 -0
  205. data/test/fixtures/test/_partial.html.erb +1 -0
  206. data/test/fixtures/test/_partial.js.erb +1 -0
  207. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  208. data/test/fixtures/test/{_partial_only.rhtml → _partial_only.erb} +0 -0
  209. data/test/fixtures/test/{_person.rhtml → _person.erb} +0 -0
  210. data/test/fixtures/test/{action_talk_to_layout.rhtml → action_talk_to_layout.erb} +0 -0
  211. data/test/fixtures/test/{block_content_for.rhtml → block_content_for.erb} +0 -0
  212. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  213. data/test/fixtures/test/{capturing.rhtml → capturing.erb} +0 -0
  214. data/test/fixtures/test/{content_for.rhtml → content_for.erb} +0 -0
  215. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  216. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  217. data/test/fixtures/test/dot.directory/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
  218. data/test/fixtures/test/{erb_content_for.rhtml → erb_content_for.erb} +0 -0
  219. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  220. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  221. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  222. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  223. data/test/fixtures/test/{greeting.rhtml → greeting.erb} +0 -0
  224. data/test/fixtures/test/{hello.rxml → hello.builder} +0 -0
  225. data/test/fixtures/test/{hello_world.rxml → hello_world.builder} +0 -0
  226. data/test/fixtures/test/{hello_world.rhtml → hello_world.erb} +0 -0
  227. data/test/fixtures/test/{hello_world_container.rxml → hello_world_container.builder} +0 -0
  228. data/test/fixtures/test/{hello_world_with_layout_false.rhtml → hello_world_with_layout_false.erb} +0 -0
  229. data/test/fixtures/test/{hello_xml_world.rxml → hello_xml_world.builder} +0 -0
  230. data/test/fixtures/test/list.erb +1 -0
  231. data/test/fixtures/test/{non_erb_block_content_for.rxml → non_erb_block_content_for.builder} +0 -0
  232. data/test/fixtures/test/{potential_conflicts.rhtml → potential_conflicts.erb} +0 -0
  233. data/test/fixtures/test/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
  234. data/test/fixtures/test/{render_file_with_locals.rhtml → render_file_with_locals.erb} +0 -0
  235. data/test/fixtures/test/{render_to_string_test.rhtml → render_to_string_test.erb} +0 -0
  236. data/test/fixtures/test/{update_element_with_capture.rhtml → update_element_with_capture.erb} +0 -0
  237. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  238. data/test/fixtures/topic.rb +1 -1
  239. data/test/template/active_record_helper_test.rb +67 -20
  240. data/test/template/asset_tag_helper_test.rb +222 -54
  241. data/test/template/atom_feed_helper_test.rb +101 -0
  242. data/test/template/benchmark_helper_test.rb +2 -2
  243. data/test/template/compiled_templates_test.rb +76 -32
  244. data/test/template/date_helper_test.rb +125 -9
  245. data/test/template/form_helper_test.rb +326 -33
  246. data/test/template/form_options_helper_test.rb +822 -15
  247. data/test/template/form_tag_helper_test.rb +96 -30
  248. data/test/template/javascript_helper_test.rb +61 -13
  249. data/test/template/number_helper_test.rb +12 -11
  250. data/test/template/prototype_helper_test.rb +185 -24
  251. data/test/template/sanitize_helper_test.rb +49 -0
  252. data/test/template/scriptaculous_helper_test.rb +9 -3
  253. data/test/template/tag_helper_test.rb +13 -2
  254. data/test/template/text_helper_test.rb +38 -52
  255. data/test/template/url_helper_test.rb +216 -46
  256. metadata +144 -116
  257. data/examples/.htaccess +0 -24
  258. data/examples/address_book/index.rhtml +0 -33
  259. data/examples/address_book/layout.rhtml +0 -8
  260. data/examples/address_book_controller.cgi +0 -9
  261. data/examples/address_book_controller.fcgi +0 -6
  262. data/examples/address_book_controller.rb +0 -52
  263. data/examples/address_book_controller.rbx +0 -4
  264. data/examples/benchmark.rb +0 -52
  265. data/examples/benchmark_with_ar.fcgi +0 -89
  266. data/examples/blog_controller.cgi +0 -53
  267. data/examples/debate/index.rhtml +0 -14
  268. data/examples/debate/new_topic.rhtml +0 -22
  269. data/examples/debate/topic.rhtml +0 -32
  270. data/examples/debate_controller.cgi +0 -57
  271. data/lib/action_controller/assertions/deprecated_assertions.rb +0 -228
  272. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -36
  273. data/lib/action_controller/cgi_ext/cgi_methods.rb +0 -211
  274. data/lib/action_controller/cgi_ext/pstore_performance_fix.rb +0 -30
  275. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +0 -95
  276. data/lib/action_controller/cgi_ext/session_performance_fix.rb +0 -30
  277. data/lib/action_controller/deprecated_dependencies.rb +0 -65
  278. data/lib/action_controller/deprecated_redirects.rb +0 -17
  279. data/lib/action_controller/deprecated_request_methods.rb +0 -34
  280. data/lib/action_controller/macros/auto_complete.rb +0 -53
  281. data/lib/action_controller/macros/in_place_editing.rb +0 -33
  282. data/lib/action_controller/pagination.rb +0 -408
  283. data/lib/action_controller/scaffolding.rb +0 -189
  284. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -44
  285. data/lib/action_controller/templates/scaffolds/edit.rhtml +0 -7
  286. data/lib/action_controller/templates/scaffolds/layout.rhtml +0 -69
  287. data/lib/action_controller/templates/scaffolds/list.rhtml +0 -27
  288. data/lib/action_controller/templates/scaffolds/new.rhtml +0 -6
  289. data/lib/action_controller/templates/scaffolds/show.rhtml +0 -9
  290. data/lib/action_controller/vendor/xml_node.rb +0 -97
  291. data/lib/action_view/helpers/deprecated_helper.rb +0 -37
  292. data/lib/action_view/helpers/java_script_macros_helper.rb +0 -233
  293. data/lib/action_view/helpers/pagination_helper.rb +0 -86
  294. data/test/activerecord/active_record_assertions_test.rb +0 -92
  295. data/test/activerecord/pagination_test.rb +0 -165
  296. data/test/controller/deprecated_instance_variables_test.rb +0 -48
  297. data/test/controller/raw_post_test.rb +0 -68
  298. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +0 -1
  299. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +0 -1
  300. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +0 -1
  301. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +0 -1
  302. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +0 -1
  303. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +0 -1
  304. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +0 -1
  305. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +0 -1
  306. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +0 -1
  307. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +0 -1
  308. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +0 -1
  309. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +0 -1
  310. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +0 -1
  311. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +0 -1
  312. data/test/fixtures/respond_to/layouts/standard.rhtml +0 -1
  313. data/test/fixtures/test/_hash_object.rhtml +0 -1
  314. data/test/fixtures/test/list.rhtml +0 -1
  315. data/test/template/deprecated_helper_test.rb +0 -36
  316. data/test/template/deprecated_instance_variables_test.rb +0 -43
  317. data/test/template/java_script_macros_helper_test.rb +0 -109
@@ -4,7 +4,7 @@
4
4
  #++
5
5
 
6
6
  require 'rexml/document'
7
- require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
7
+ require 'html/document'
8
8
 
9
9
  module ActionController
10
10
  module Assertions
@@ -13,15 +13,13 @@ module ActionController
13
13
  end
14
14
 
15
15
  # Adds the #assert_select method for use in Rails functional
16
- # test cases.
17
- #
18
- # Use #assert_select to make assertions on the response HTML of a controller
16
+ # test cases, which can be used to make assertions on the response HTML of a controller
19
17
  # action. You can also call #assert_select within another #assert_select to
20
18
  # make assertions on elements selected by the enclosing assertion.
21
19
  #
22
20
  # Use #css_select to select elements without making an assertions, either
23
21
  # from the response HTML or elements selected by the enclosing assertion.
24
- #
22
+ #
25
23
  # In addition to HTML responses, you can make the following assertions:
26
24
  # * #assert_select_rjs -- Assertions on HTML content of RJS update and
27
25
  # insertion operations.
@@ -29,7 +27,7 @@ module ActionController
29
27
  # for example for dealing with feed item descriptions.
30
28
  # * #assert_select_email -- Assertions on the HTML body of an e-mail.
31
29
  #
32
- # Also see HTML::Selector for learning how to use selectors.
30
+ # Also see HTML::Selector to learn how to use selectors.
33
31
  module SelectorAssertions
34
32
  # :call-seq:
35
33
  # css_select(selector) => array
@@ -49,12 +47,26 @@ module ActionController
49
47
  # The selector may be a CSS selector expression (+String+), an expression
50
48
  # with substitution values (+Array+) or an HTML::Selector object.
51
49
  #
52
- # For example:
50
+ # ==== Examples
51
+ # # Selects all div tags
52
+ # divs = css_select("div")
53
+ #
54
+ # # Selects all paragraph tags and does something interesting
55
+ # pars = css_select("p")
56
+ # pars.each do |par|
57
+ # # Do something fun with paragraphs here...
58
+ # end
59
+ #
60
+ # # Selects all list items in unordered lists
61
+ # items = css_select("ul>li")
62
+ #
63
+ # # Selects all form tags and then all inputs inside the form
53
64
  # forms = css_select("form")
54
65
  # forms.each do |form|
55
66
  # inputs = css_select(form, "input")
56
67
  # ...
57
68
  # end
69
+ #
58
70
  def css_select(*args)
59
71
  # See assert_select to understand what's going on here.
60
72
  arg = args.shift
@@ -66,6 +78,7 @@ module ActionController
66
78
  raise ArgumentError, "First argument is either selector or element to select, but nil found. Perhaps you called assert_select with an element that does not exist?"
67
79
  elsif @selected
68
80
  matches = []
81
+
69
82
  @selected.each do |selected|
70
83
  subset = css_select(selected, HTML::Selector.new(arg.dup, args.dup))
71
84
  subset.each do |match|
@@ -105,12 +118,13 @@ module ActionController
105
118
  # response HTML. Calling #assert_select inside an #assert_select block will
106
119
  # run the assertion for each element selected by the enclosing assertion.
107
120
  #
108
- # For example:
121
+ # ==== Example
109
122
  # assert_select "ol>li" do |elements|
110
123
  # elements.each do |element|
111
124
  # assert_select element, "li"
112
125
  # end
113
126
  # end
127
+ #
114
128
  # Or for short:
115
129
  # assert_select "ol>li" do
116
130
  # assert_select "li"
@@ -148,7 +162,7 @@ module ActionController
148
162
  # If the method is called with a block, once all equality tests are
149
163
  # evaluated the block is called with an array of all matched elements.
150
164
  #
151
- # === Examples
165
+ # ==== Examples
152
166
  #
153
167
  # # At least one form element
154
168
  # assert_select "form"
@@ -196,7 +210,7 @@ module ActionController
196
210
  # Otherwise just operate on the response document.
197
211
  root = response_from_page_or_rjs
198
212
  end
199
-
213
+
200
214
  # First or second argument is the selector: string and we pass
201
215
  # all remaining arguments. Array and we pass the argument. Also
202
216
  # accepts selector itself.
@@ -209,7 +223,7 @@ module ActionController
209
223
  selector = arg
210
224
  else raise ArgumentError, "Expecting a selector as the first argument"
211
225
  end
212
-
226
+
213
227
  # Next argument is used for equality tests.
214
228
  equals = {}
215
229
  case arg = args.shift
@@ -277,14 +291,10 @@ module ActionController
277
291
  # found one but expecting two.
278
292
  message ||= content_mismatch if matches.empty?
279
293
  # Test minimum/maximum occurrence.
280
- if equals[:minimum]
281
- assert matches.size >= equals[:minimum], message ||
282
- "Expected at least #{equals[:minimum]} elements, found #{matches.size}."
283
- end
284
- if equals[:maximum]
285
- assert matches.size <= equals[:maximum], message ||
286
- "Expected at most #{equals[:maximum]} elements, found #{matches.size}."
287
- end
294
+ min, max = equals[:minimum], equals[:maximum]
295
+ message = message || %(Expected #{count_description(min, max)} matching "#{selector.to_s}", found #{matches.size}.)
296
+ assert matches.size >= min, message if min
297
+ assert matches.size <= max, message if max
288
298
 
289
299
  # If a block is given call that block. Set @selected to allow
290
300
  # nested assert_select, which can be nested several levels deep.
@@ -300,7 +310,19 @@ module ActionController
300
310
  # Returns all matches elements.
301
311
  matches
302
312
  end
303
-
313
+
314
+ def count_description(min, max) #:nodoc:
315
+ pluralize = lambda {|word, quantity| word << (quantity == 1 ? '' : 's')}
316
+
317
+ if min && max && (max != min)
318
+ "between #{min} and #{max} elements"
319
+ elsif min && !(min == 1 && max == 1)
320
+ "at least #{min} #{pluralize['element', min]}"
321
+ elsif max
322
+ "at most #{max} #{pluralize['element', max]}"
323
+ end
324
+ end
325
+
304
326
  # :call-seq:
305
327
  # assert_select_rjs(id?) { |elements| ... }
306
328
  # assert_select_rjs(statement, id?) { |elements| ... }
@@ -317,12 +339,17 @@ module ActionController
317
339
  # that update or insert an element with that identifier.
318
340
  #
319
341
  # Use the first argument to narrow down assertions to only statements
320
- # of that type. Possible values are +:replace+, +:replace_html+ and
321
- # +:insert_html+.
342
+ # of that type. Possible values are <tt>:replace</tt>, <tt>:replace_html</tt>,
343
+ # <tt>:show</tt>, <tt>:hide</tt>, <tt>:toggle</tt>, <tt>:remove</tt> and
344
+ # <tt>:insert_html</tt>.
322
345
  #
323
- # Use the argument +:insert+ followed by an insertion position to narrow
346
+ # Use the argument <tt>:insert</tt> followed by an insertion position to narrow
324
347
  # down the assertion to only statements that insert elements in that
325
- # position. Possible values are +:top+, +:bottom+, +:before+ and +:after+.
348
+ # position. Possible values are <tt>:top</tt>, <tt>:bottom</tt>, <tt>:before</tt>
349
+ # and <tt>:after</tt>.
350
+ #
351
+ # Using the <tt>:remove</tt> statement, you will be able to pass a block, but it will
352
+ # be ignored as there is no HTML passed for this statement.
326
353
  #
327
354
  # === Using blocks
328
355
  #
@@ -339,7 +366,7 @@ module ActionController
339
366
  # but without distinguishing whether the content is returned in an HTML
340
367
  # or JavaScript.
341
368
  #
342
- # === Examples
369
+ # ==== Examples
343
370
  #
344
371
  # # Replacing the element foo.
345
372
  # # page.replace 'foo', ...
@@ -352,6 +379,9 @@ module ActionController
352
379
  # # Inserting into the element bar, top position.
353
380
  # assert_select_rjs :insert, :top, "bar"
354
381
  #
382
+ # # Remove the element bar
383
+ # assert_select_rjs :remove, "bar"
384
+ #
355
385
  # # Changing the element foo, with an image.
356
386
  # assert_select_rjs "foo" do
357
387
  # assert_select "img[src=/images/logo.gif""
@@ -373,6 +403,7 @@ module ActionController
373
403
  # any RJS statement.
374
404
  if arg.is_a?(Symbol)
375
405
  rjs_type = arg
406
+
376
407
  if rjs_type == :insert
377
408
  arg = args.shift
378
409
  insertion = "insert_#{arg}".to_sym
@@ -400,20 +431,29 @@ module ActionController
400
431
  case rjs_type
401
432
  when :chained_replace, :chained_replace_html
402
433
  Regexp.new("\\$\\(\"#{id}\"\\)#{statement}\\(#{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
434
+ when :remove, :show, :hide, :toggle
435
+ Regexp.new("#{statement}\\(\"#{id}\"\\)")
403
436
  else
404
437
  Regexp.new("#{statement}\\(\"#{id}\", #{RJS_PATTERN_HTML}\\)", Regexp::MULTILINE)
405
438
  end
406
439
 
407
440
  # Duplicate the body since the next step involves destroying it.
408
441
  matches = nil
409
- @response.body.gsub(pattern) do |match|
410
- html = unescape_rjs($2)
411
- matches ||= []
412
- matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
413
- ""
442
+ case rjs_type
443
+ when :remove, :show, :hide, :toggle
444
+ matches = @response.body.match(pattern)
445
+ else
446
+ @response.body.gsub(pattern) do |match|
447
+ html = unescape_rjs($2)
448
+ matches ||= []
449
+ matches.concat HTML::Document.new(html).root.children.select { |n| n.tag? }
450
+ ""
451
+ end
414
452
  end
453
+
415
454
  if matches
416
- if block_given?
455
+ assert_block("") { true } # to count the assertion
456
+ if block_given? && !([:remove, :show, :hide, :toggle].include? rjs_type)
417
457
  begin
418
458
  in_scope, @selected = @selected, matches
419
459
  yield matches
@@ -441,8 +481,20 @@ module ActionController
441
481
  # The content of each element is un-encoded, and wrapped in the root
442
482
  # element +encoded+. It then calls the block with all un-encoded elements.
443
483
  #
444
- # === Example
484
+ # ==== Examples
485
+ # # Selects all bold tags from within the title of an ATOM feed's entries (perhaps to nab a section name prefix)
486
+ # assert_select_feed :atom, 1.0 do
487
+ # # Select each entry item and then the title item
488
+ # assert_select "entry>title" do
489
+ # # Run assertions on the encoded title elements
490
+ # assert_select_encoded do
491
+ # assert_select "b"
492
+ # end
493
+ # end
494
+ # end
495
+ #
445
496
  #
497
+ # # Selects all paragraph tags from within the description of an RSS feed
446
498
  # assert_select_feed :rss, 2.0 do
447
499
  # # Select description element of each feed item.
448
500
  # assert_select "channel>item>description" do
@@ -493,11 +545,19 @@ module ActionController
493
545
  # You must enable deliveries for this assertion to work, use:
494
546
  # ActionMailer::Base.perform_deliveries = true
495
547
  #
496
- # === Example
548
+ # ==== Examples
549
+ #
550
+ # assert_select_email do
551
+ # assert_select "h1", "Email alert"
552
+ # end
553
+ #
554
+ # assert_select_email do
555
+ # items = assert_select "ol>li"
556
+ # items.each do
557
+ # # Work with items here...
558
+ # end
559
+ # end
497
560
  #
498
- # assert_select_email do
499
- # assert_select "h1", "Email alert"
500
- # end
501
561
  def assert_select_email(&block)
502
562
  deliveries = ActionMailer::Base.deliveries
503
563
  assert !deliveries.empty?, "No e-mail in delivery list"
@@ -519,6 +579,10 @@ module ActionController
519
579
  :replace_html => /Element\.update/,
520
580
  :chained_replace => /\.replace/,
521
581
  :chained_replace_html => /\.update/,
582
+ :remove => /Element\.remove/,
583
+ :show => /Element\.show/,
584
+ :hide => /Element\.hide/,
585
+ :toggle => /Element\.toggle/
522
586
  }
523
587
  RJS_INSERTIONS = [:top, :bottom, :before, :after]
524
588
  RJS_INSERTIONS.each do |insertion|
@@ -537,10 +601,12 @@ module ActionController
537
601
  # #assert_select and #css_select call this to obtain the content in the HTML
538
602
  # page, or from all the RJS statements, depending on the type of response.
539
603
  def response_from_page_or_rjs()
540
- content_type = @response.headers["Content-Type"]
604
+ content_type = @response.content_type
605
+
541
606
  if content_type && content_type =~ /text\/javascript/
542
607
  body = @response.body.dup
543
608
  root = HTML::Node.new(nil)
609
+
544
610
  while true
545
611
  next if body.sub!(RJS_PATTERN_EVERYTHING) do |match|
546
612
  html = unescape_rjs($3)
@@ -550,6 +616,7 @@ module ActionController
550
616
  end
551
617
  break
552
618
  end
619
+
553
620
  root
554
621
  else
555
622
  html_document.root
@@ -560,6 +627,7 @@ module ActionController
560
627
  def unescape_rjs(rjs_string)
561
628
  # RJS encodes double quotes and line breaks.
562
629
  unescaped= rjs_string.gsub('\"', '"')
630
+ unescaped.gsub!(/\\\//, '/')
563
631
  unescaped.gsub!('\n', "\n")
564
632
  unescaped.gsub!('\076', '>')
565
633
  unescaped.gsub!('\074', '<')
@@ -567,7 +635,6 @@ module ActionController
567
635
  unescaped.gsub!(RJS_PATTERN_UNICODE_ESCAPED_CHAR) {|u| [$1.hex].pack('U*')}
568
636
  unescaped
569
637
  end
570
-
571
638
  end
572
639
  end
573
640
  end
@@ -1,8 +1,9 @@
1
1
  require 'rexml/document'
2
- require File.dirname(__FILE__) + "/../vendor/html-scanner/html/document"
2
+ require 'html/document'
3
3
 
4
4
  module ActionController
5
5
  module Assertions
6
+ # Pair of assertions to testing elements in the HTML output of the response.
6
7
  module TagAssertions
7
8
  # Asserts that there is a tag/node/element in the body of the response
8
9
  # that meets all of the given conditions. The +conditions+ parameter must
@@ -37,8 +38,8 @@ module ActionController
37
38
  # to match on the children, and only matching children will be
38
39
  # counted.
39
40
  # * <tt>:content</tt>: the textual content of the node must match the
40
- # given value. This will not match HTML tags in the body of a
41
- # tag--only text.
41
+ # given value. This will not match HTML tags in the body of a
42
+ # tag--only text.
42
43
  #
43
44
  # Conditions are matched using the following algorithm:
44
45
  #
@@ -48,39 +49,39 @@ module ActionController
48
49
  # * if the condition is +true+, the value must not be +nil+.
49
50
  # * if the condition is +false+ or +nil+, the value must be +nil+.
50
51
  #
51
- # Usage:
52
+ # === Examples
52
53
  #
53
- # # assert that there is a "span" tag
54
+ # # Assert that there is a "span" tag
54
55
  # assert_tag :tag => "span"
55
56
  #
56
- # # assert that there is a "span" tag with id="x"
57
+ # # Assert that there is a "span" tag with id="x"
57
58
  # assert_tag :tag => "span", :attributes => { :id => "x" }
58
59
  #
59
- # # assert that there is a "span" tag using the short-hand
60
+ # # Assert that there is a "span" tag using the short-hand
60
61
  # assert_tag :span
61
62
  #
62
- # # assert that there is a "span" tag with id="x" using the short-hand
63
+ # # Assert that there is a "span" tag with id="x" using the short-hand
63
64
  # assert_tag :span, :attributes => { :id => "x" }
64
65
  #
65
- # # assert that there is a "span" inside of a "div"
66
+ # # Assert that there is a "span" inside of a "div"
66
67
  # assert_tag :tag => "span", :parent => { :tag => "div" }
67
68
  #
68
- # # assert that there is a "span" somewhere inside a table
69
+ # # Assert that there is a "span" somewhere inside a table
69
70
  # assert_tag :tag => "span", :ancestor => { :tag => "table" }
70
71
  #
71
- # # assert that there is a "span" with at least one "em" child
72
+ # # Assert that there is a "span" with at least one "em" child
72
73
  # assert_tag :tag => "span", :child => { :tag => "em" }
73
74
  #
74
- # # assert that there is a "span" containing a (possibly nested)
75
+ # # Assert that there is a "span" containing a (possibly nested)
75
76
  # # "strong" tag.
76
77
  # assert_tag :tag => "span", :descendant => { :tag => "strong" }
77
78
  #
78
- # # assert that there is a "span" containing between 2 and 4 "em" tags
79
+ # # Assert that there is a "span" containing between 2 and 4 "em" tags
79
80
  # # as immediate children
80
81
  # assert_tag :tag => "span",
81
82
  # :children => { :count => 2..4, :only => { :tag => "em" } }
82
83
  #
83
- # # get funky: assert that there is a "div", with an "ul" ancestor
84
+ # # Get funky: assert that there is a "div", with an "ul" ancestor
84
85
  # # and an "li" parent (with "class" = "enum"), and containing a
85
86
  # # "span" descendant that contains text matching /hello world/
86
87
  # assert_tag :tag => "div",
@@ -90,7 +91,7 @@ module ActionController
90
91
  # :descendant => { :tag => "span",
91
92
  # :child => /hello world/ }
92
93
  #
93
- # <strong>Please note</strong: #assert_tag and #assert_no_tag only work
94
+ # <b>Please note</b>: #assert_tag and #assert_no_tag only work
94
95
  # with well-formed XHTML. They recognize a few tags as implicitly self-closing
95
96
  # (like br and hr and such) but will not work correctly with tags
96
97
  # that allow optional closing tags (p, li, td). <em>You must explicitly
@@ -105,6 +106,18 @@ module ActionController
105
106
 
106
107
  # Identical to #assert_tag, but asserts that a matching tag does _not_
107
108
  # exist. (See #assert_tag for a full discussion of the syntax.)
109
+ #
110
+ # === Examples
111
+ # # Assert that there is not a "div" containing a "p"
112
+ # assert_no_tag :tag => "div", :descendant => { :tag => "p" }
113
+ #
114
+ # # Assert that an unordered list is empty
115
+ # assert_no_tag :tag => "ul", :descendant => { :tag => "li" }
116
+ #
117
+ # # Assert that there is not a "p" tag with between 1 to 3 "img" tags
118
+ # # as immediate children
119
+ # assert_no_tag :tag => "p",
120
+ # :children => { :count => 1..3, :only => { :tag => "img" } }
108
121
  def assert_no_tag(*opts)
109
122
  clean_backtrace do
110
123
  opts = opts.size > 1 ? opts.last.merge({ :tag => opts.first.to_s }) : opts.first
@@ -11,10 +11,16 @@ require 'set'
11
11
  module ActionController #:nodoc:
12
12
  class ActionControllerError < StandardError #:nodoc:
13
13
  end
14
+
14
15
  class SessionRestoreError < ActionControllerError #:nodoc:
15
16
  end
17
+
16
18
  class MissingTemplate < ActionControllerError #:nodoc:
17
19
  end
20
+
21
+ class RenderError < ActionControllerError #:nodoc:
22
+ end
23
+
18
24
  class RoutingError < ActionControllerError #:nodoc:
19
25
  attr_reader :failures
20
26
  def initialize(message, failures=[])
@@ -22,14 +28,39 @@ module ActionController #:nodoc:
22
28
  @failures = failures
23
29
  end
24
30
  end
31
+
32
+ class MethodNotAllowed < ActionControllerError #:nodoc:
33
+ attr_reader :allowed_methods
34
+
35
+ def initialize(*allowed_methods)
36
+ super("Only #{allowed_methods.to_sentence} requests are allowed.")
37
+ @allowed_methods = allowed_methods
38
+ end
39
+
40
+ def allowed_methods_header
41
+ allowed_methods.map { |method_symbol| method_symbol.to_s.upcase } * ', '
42
+ end
43
+
44
+ def handle_response!(response)
45
+ response.headers['Allow'] ||= allowed_methods_header
46
+ end
47
+ end
48
+
49
+ class NotImplemented < MethodNotAllowed #:nodoc:
50
+ end
51
+
25
52
  class UnknownController < ActionControllerError #:nodoc:
26
53
  end
54
+
27
55
  class UnknownAction < ActionControllerError #:nodoc:
28
56
  end
57
+
29
58
  class MissingFile < ActionControllerError #:nodoc:
30
59
  end
60
+
31
61
  class RenderError < ActionControllerError #:nodoc:
32
62
  end
63
+
33
64
  class SessionOverflowError < ActionControllerError #:nodoc:
34
65
  DEFAULT_MESSAGE = 'Your session data is larger than the data column in which it is to be stored. You must increase the size of your data column if you intend to store large data.'
35
66
 
@@ -37,13 +68,15 @@ module ActionController #:nodoc:
37
68
  super(message || DEFAULT_MESSAGE)
38
69
  end
39
70
  end
71
+
40
72
  class DoubleRenderError < ActionControllerError #:nodoc:
41
- DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and only once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\". Finally, note that to cause a before filter to halt execution of the rest of the filter chain, the filter must return false, explicitly, so \"render(...) and return false\"."
73
+ DEFAULT_MESSAGE = "Render and/or redirect were called multiple times in this action. Please note that you may only call render OR redirect, and at most once per action. Also note that neither redirect nor render terminate execution of the action, so if you want to exit an action after redirecting, you need to do something like \"redirect_to(...) and return\". Finally, note that to cause a before filter to halt execution of the rest of the filter chain, the filter must return false, explicitly, so \"render(...) and return false\"."
42
74
 
43
75
  def initialize(message = nil)
44
76
  super(message || DEFAULT_MESSAGE)
45
77
  end
46
78
  end
79
+
47
80
  class RedirectBackError < ActionControllerError #:nodoc:
48
81
  DEFAULT_MESSAGE = 'No HTTP_REFERER was set in the request to this action, so redirect_to :back could not be called successfully. If this is a test, make sure to specify request.env["HTTP_REFERER"].'
49
82
 
@@ -52,6 +85,9 @@ module ActionController #:nodoc:
52
85
  end
53
86
  end
54
87
 
88
+ class UnknownHttpMethod < ActionControllerError #:nodoc:
89
+ end
90
+
55
91
  # Action Controllers are the core of a web request in Rails. They are made up of one or more actions that are executed
56
92
  # on request and then either render a template or redirect to another action. An action is defined as a public method
57
93
  # on the controller, which will automatically be made accessible to the web-server through Rails Routes.
@@ -71,7 +107,7 @@ module ActionController #:nodoc:
71
107
  #
72
108
  # Actions, by default, render a template in the <tt>app/views</tt> directory corresponding to the name of the controller and action
73
109
  # after executing code in the action. For example, the +index+ action of the +GuestBookController+ would render the
74
- # template <tt>app/views/guestbook/index.rhtml</tt> by default after populating the <tt>@entries</tt> instance variable.
110
+ # template <tt>app/views/guestbook/index.erb</tt> by default after populating the <tt>@entries</tt> instance variable.
75
111
  #
76
112
  # Unlike index, the sign action will not render a template. After performing its main purpose (creating a
77
113
  # new entry in the guest book), it initiates a redirect instead. This redirect works by returning an external
@@ -128,17 +164,26 @@ module ActionController #:nodoc:
128
164
  # For removing objects from the session, you can either assign a single key to nil, like <tt>session[:person] = nil</tt>, or you can
129
165
  # remove the entire session with reset_session.
130
166
  #
131
- # By default, sessions are stored on the file system in <tt>RAILS_ROOT/tmp/sessions</tt>. Any object can be placed in the session
132
- # (as long as it can be Marshalled). But remember that 1000 active sessions each storing a 50kb object could lead to a 50MB store on the filesystem.
133
- # In other words, think carefully about size and caching before resorting to the use of the session on the filesystem.
167
+ # Sessions are stored in a browser cookie that's cryptographically signed, but unencrypted, by default. This prevents
168
+ # the user from tampering with the session but also allows him to see its contents.
169
+ #
170
+ # Do not put secret information in session!
171
+ #
172
+ # Other options for session storage are:
134
173
  #
135
- # An alternative to storing sessions on disk is to use ActiveRecordStore to store sessions in your database, which can solve problems
136
- # caused by storing sessions in the file system and may speed up your application. To use ActiveRecordStore, uncomment the line:
174
+ # ActiveRecordStore: sessions are stored in your database, which works better than PStore with multiple app servers and,
175
+ # unlike CookieStore, hides your session contents from the user. To use ActiveRecordStore, set
137
176
  #
138
177
  # config.action_controller.session_store = :active_record_store
139
178
  #
140
179
  # in your <tt>environment.rb</tt> and run <tt>rake db:sessions:create</tt>.
141
180
  #
181
+ # MemCacheStore: sessions are stored as entries in your memcached cache. Set the session store type in <tt>environment.rb</tt>:
182
+ #
183
+ # config.action_controller.session_store = :mem_cache_store
184
+ #
185
+ # This assumes that memcached has been installed and configured properly. See the MemCacheStore docs for more information.
186
+ #
142
187
  # == Responses
143
188
  #
144
189
  # Each action results in a response, which holds the headers and document to be sent to the user's browser. The actual response
@@ -192,7 +237,7 @@ module ActionController #:nodoc:
192
237
  #
193
238
  # == Calling multiple redirects or renders
194
239
  #
195
- # An action should conclude with a single render or redirect. Attempting to try to do either again will result in a DoubleRenderError:
240
+ # An action may contain only a single render or a single redirect. Attempting to try to do either again will result in a DoubleRenderError:
196
241
  #
197
242
  # def do_something
198
243
  # redirect_to :action => "elsewhere"
@@ -209,7 +254,6 @@ module ActionController #:nodoc:
209
254
  class Base
210
255
  DEFAULT_RENDER_STATUS_CODE = "200 OK"
211
256
 
212
- include Reloadable::Deprecated
213
257
  include StatusCodes
214
258
 
215
259
  # Determines whether the view has access to controller internals @request, @response, @session, and @template.
@@ -249,7 +293,7 @@ module ActionController #:nodoc:
249
293
  # The param_parsers hash lets you register handlers which will process the http body and add parameters to the
250
294
  # <tt>params</tt> hash. These handlers are invoked for post and put requests.
251
295
  #
252
- # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instanciated
296
+ # By default application/xml is enabled. A XmlSimple class with the same param name as the root will be instantiated
253
297
  # in the <tt>params</tt>. This allows XML requests to mask themselves as regular form submissions, so you can have one
254
298
  # action serve both regular forms and web service requests.
255
299
  #
@@ -271,17 +315,15 @@ module ActionController #:nodoc:
271
315
  # A YAML parser is also available and can be turned on with:
272
316
  #
273
317
  # ActionController::Base.param_parsers[Mime::YAML] = :yaml
274
- @@param_parsers = { Mime::XML => :xml_simple }
318
+ @@param_parsers = { Mime::MULTIPART_FORM => :multipart_form,
319
+ Mime::URL_ENCODED_FORM => :url_encoded_form,
320
+ Mime::XML => :xml_simple }
275
321
  cattr_accessor :param_parsers
276
322
 
277
323
  # Controls the default charset for all renders.
278
324
  @@default_charset = "utf-8"
279
325
  cattr_accessor :default_charset
280
-
281
- # Template root determines the base from which template references will be made. So a call to render("test/template")
282
- # will be converted to "#{template_root}/test/template.rhtml".
283
- class_inheritable_accessor :template_root
284
-
326
+
285
327
  # The logger is used for generating information on the action run-time (including benchmarking) if available.
286
328
  # Can be set to nil for no logging. Compatible with both Ruby's own Logger and Log4r loggers.
287
329
  cattr_accessor :logger
@@ -295,6 +337,18 @@ module ActionController #:nodoc:
295
337
  # Controls the resource action separator
296
338
  @@resource_action_separator = "/"
297
339
  cattr_accessor :resource_action_separator
340
+
341
+ # Sets the token parameter name for RequestForgery. Calling #protect_from_forgery sets it to :authenticity_token by default
342
+ cattr_accessor :request_forgery_protection_token
343
+
344
+ # Indicates whether or not optimise the generated named
345
+ # route helper methods
346
+ cattr_accessor :optimise_named_routes
347
+ self.optimise_named_routes = true
348
+
349
+ # Controls whether request forgergy protection is turned on or not. Turned off by default only in test mode.
350
+ class_inheritable_accessor :allow_forgery_protection
351
+ self.allow_forgery_protection = true
298
352
 
299
353
  # Holds the request object that's primarily used to get environment variables through access like
300
354
  # <tt>request.env["REQUEST_URI"]</tt>.
@@ -353,19 +407,57 @@ module ActionController #:nodoc:
353
407
  # By default, all methods defined in ActionController::Base and included modules are hidden.
354
408
  # More methods can be hidden using <tt>hide_actions</tt>.
355
409
  def hidden_actions
356
- write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods) unless read_inheritable_attribute(:hidden_actions)
410
+ unless read_inheritable_attribute(:hidden_actions)
411
+ write_inheritable_attribute(:hidden_actions, ActionController::Base.public_instance_methods.map(&:to_s))
412
+ end
413
+
357
414
  read_inheritable_attribute(:hidden_actions)
358
415
  end
359
416
 
360
417
  # Hide each of the given methods from being callable as actions.
361
418
  def hide_action(*names)
362
- write_inheritable_attribute(:hidden_actions, hidden_actions | names.collect { |n| n.to_s })
419
+ write_inheritable_attribute(:hidden_actions, hidden_actions | names.map(&:to_s))
420
+ end
421
+
422
+ ## View load paths determine the bases from which template references can be made. So a call to
423
+ ## render("test/template") will be looked up in the view load paths array and the closest match will be
424
+ ## returned.
425
+ def view_paths
426
+ @view_paths || superclass.view_paths
427
+ end
428
+
429
+ def view_paths=(value)
430
+ @view_paths = value
363
431
  end
364
432
 
365
- # Replace sensitive paramater data from the request log.
366
- # Filters paramaters that have any of the arguments as a substring.
433
+ # Adds a view_path to the front of the view_paths array.
434
+ # If the current class has no view paths, copy them from
435
+ # the superclass. This change will be visible for all future requests.
436
+ #
437
+ # ArticleController.prepend_view_path("views/default")
438
+ # ArticleController.prepend_view_path(["views/default", "views/custom"])
439
+ #
440
+ def prepend_view_path(path)
441
+ @view_paths = superclass.view_paths.dup if @view_paths.nil?
442
+ view_paths.unshift(*path)
443
+ end
444
+
445
+ # Adds a view_path to the end of the view_paths array.
446
+ # If the current class has no view paths, copy them from
447
+ # the superclass. This change will be visible for all future requests.
448
+ #
449
+ # ArticleController.append_view_path("views/default")
450
+ # ArticleController.append_view_path(["views/default", "views/custom"])
451
+ #
452
+ def append_view_path(path)
453
+ @view_paths = superclass.view_paths.dup if @view_paths.nil?
454
+ view_paths.push(*path)
455
+ end
456
+
457
+ # Replace sensitive parameter data from the request log.
458
+ # Filters parameters that have any of the arguments as a substring.
367
459
  # Looks in all subhashes of the param hash for keys to filter.
368
- # If a block is given, each key and value of the paramater hash and all
460
+ # If a block is given, each key and value of the parameter hash and all
369
461
  # subhashes is passed to it, the value or key
370
462
  # can be replaced using String#replace or similar method.
371
463
  #
@@ -412,13 +504,10 @@ module ActionController #:nodoc:
412
504
 
413
505
  # Don't render layouts for templates with the given extensions.
414
506
  def exempt_from_layout(*extensions)
415
- @@exempt_from_layout.merge extensions.collect { |extension|
416
- if extension.is_a?(Regexp)
417
- extension
418
- else
419
- /\.#{Regexp.escape(extension.to_s)}$/
420
- end
421
- }
507
+ regexps = extensions.collect do |extension|
508
+ extension.is_a?(Regexp) ? extension : /\.#{Regexp.escape(extension.to_s)}$/
509
+ end
510
+ @@exempt_from_layout.merge regexps
422
511
  end
423
512
  end
424
513
 
@@ -435,6 +524,9 @@ module ActionController #:nodoc:
435
524
  send(method, *arguments)
436
525
 
437
526
  assign_default_content_type_and_charset
527
+
528
+ response.request = request
529
+ response.prepare! unless component_request?
438
530
  response
439
531
  ensure
440
532
  process_cleanup
@@ -452,18 +544,25 @@ module ActionController #:nodoc:
452
544
  # * <tt>:only_path</tt> -- if true, returns the relative URL (omitting the protocol, host name, and port) (<tt>false</tt> by default)
453
545
  # * <tt>:trailing_slash</tt> -- if true, adds a trailing slash, as in "/archive/2005/". Note that this
454
546
  # is currently not recommended since it breaks caching.
455
- # * <tt>:host</tt> -- overrides the default (current) host if provided
456
- # * <tt>:protocol</tt> -- overrides the default (current) protocol if provided
547
+ # * <tt>:host</tt> -- overrides the default (current) host if provided.
548
+ # * <tt>:protocol</tt> -- overrides the default (current) protocol if provided.
549
+ # * <tt>:port</tt> -- optionally specify the port to connect to.
550
+ # * <tt>:user</tt> -- Inline HTTP authentication (only plucked out if :password is also present).
551
+ # * <tt>:password</tt> -- Inline HTTP authentication (only plucked out if :user is also present).
552
+ # * <tt>:skip_relative_url_root</tt> -- if true, the url is not constructed using the relative_url_root of the request so the path
553
+ # will include the web server relative installation directory.
457
554
  #
458
555
  # The URL is generated from the remaining keys in the hash. A URL contains two key parts: the <base> and a query string.
459
556
  # Routes composes a query string as the key/value pairs not included in the <base>.
460
557
  #
461
558
  # The default Routes setup supports a typical Rails path of "controller/action/id" where action and id are optional, with
462
559
  # action defaulting to 'index' when not given. Here are some typical url_for statements and their corresponding URLs:
463
- #  
464
- # url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent'
465
- # url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts'
466
- # url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10'
560
+ #
561
+ # url_for :controller => 'posts', :action => 'recent' # => 'proto://host.com/posts/recent'
562
+ # url_for :controller => 'posts', :action => 'index' # => 'proto://host.com/posts'
563
+ # url_for :controller => 'posts', :action => 'index', :port=>'8033' # => 'proto://host.com:8033/posts'
564
+ # url_for :controller => 'posts', :action => 'show', :id => 10 # => 'proto://host.com/posts/show/10'
565
+ # url_for :controller => 'posts', :user => 'd', :password => '123' # => 'proto://d:123@host.com/posts'
467
566
  #
468
567
  # When generating a new URL, missing values may be filled in from the current request's parameters. For example,
469
568
  # <tt>url_for :action => 'some_action'</tt> will retain the current controller, as expected. This behavior extends to
@@ -491,9 +590,9 @@ module ActionController #:nodoc:
491
590
  # However, you might ask why the action from the current request, 'contacts', isn't carried over into the new URL. The
492
591
  # answer has to do with the order in which the parameters appear in the generated path. In a nutshell, since the
493
592
  # value that appears in the slot for <tt>:first</tt> is not equal to default value for <tt>:first</tt> we stop using
494
- # defaults. On it's own, this rule can account for much of the typical Rails URL behavior.
593
+ # defaults. On its own, this rule can account for much of the typical Rails URL behavior.
495
594
  #  
496
- # Although a convienence, defaults can occasionaly get in your way. In some cases a default persists longer than desired.
595
+ # Although a convenience, defaults can occasionally get in your way. In some cases a default persists longer than desired.
497
596
  # The default may be cleared by adding <tt>:name => nil</tt> to <tt>url_for</tt>'s options.
498
597
  # This is often required when writing form helpers, since the defaults in play may vary greatly depending upon where the
499
598
  # helper is used from. The following line will redirect to PostController's default action, regardless of the page it is
@@ -509,22 +608,14 @@ module ActionController #:nodoc:
509
608
  #
510
609
  # This takes the current URL as is and only exchanges the action. In contrast, <tt>url_for :action => 'print'</tt>
511
610
  # would have slashed-off the path components after the changed action.
512
- def url_for(options = {}, *parameters_for_method_reference) #:doc:
513
- case options
611
+ def url_for(options = nil) #:doc:
612
+ case options || {}
514
613
  when String
515
614
  options
516
-
517
- when Symbol
518
- ActiveSupport::Deprecation.warn(
519
- "You called url_for(:#{options}), which is a deprecated API call. Instead you should use the named " +
520
- "route directly, like #{options}(). Using symbols and parameters with url_for will be removed from Rails 2.0.",
521
- caller
522
- )
523
-
524
- send(options, *parameters_for_method_reference)
525
-
526
615
  when Hash
527
616
  @url.rewrite(rewrite_options(options))
617
+ else
618
+ polymorphic_url(options)
528
619
  end
529
620
  end
530
621
 
@@ -543,11 +634,41 @@ module ActionController #:nodoc:
543
634
  self.class.controller_path
544
635
  end
545
636
 
546
- # Test whether the session is enabled for this request.
547
637
  def session_enabled?
548
638
  request.session_options && request.session_options[:disabled] != false
549
639
  end
550
640
 
641
+ self.view_paths = []
642
+
643
+ # View load paths for controller.
644
+ def view_paths
645
+ (@template || self.class).view_paths
646
+ end
647
+
648
+ def view_paths=(value)
649
+ (@template || self.class).view_paths = value
650
+ end
651
+
652
+ # Adds a view_path to the front of the view_paths array.
653
+ # This change affects the current request only.
654
+ #
655
+ # self.prepend_view_path("views/default")
656
+ # self.prepend_view_path(["views/default", "views/custom"])
657
+ #
658
+ def prepend_view_path(path)
659
+ (@template || self.class).prepend_view_path(path)
660
+ end
661
+
662
+ # Adds a view_path to the end of the view_paths array.
663
+ # This change affects the current request only.
664
+ #
665
+ # self.append_view_path("views/default")
666
+ # self.append_view_path(["views/default", "views/custom"])
667
+ #
668
+ def append_view_path(path)
669
+ (@template || self.class).append_view_path(path)
670
+ end
671
+
551
672
  protected
552
673
  # Renders the content that will be returned to the browser as the response body.
553
674
  #
@@ -567,10 +688,6 @@ module ActionController #:nodoc:
567
688
  # # but with a custom layout
568
689
  # render :action => "long_goal", :layout => "spectacular"
569
690
  #
570
- # _Deprecation_ _notice_: This used to have the signatures <tt>render_action("action", status = 200)</tt>,
571
- # <tt>render_without_layout("controller/action", status = 200)</tt>, and
572
- # <tt>render_with_layout("controller/action", status = 200, layout)</tt>.
573
- #
574
691
  # === Rendering partials
575
692
  #
576
693
  # Partial rendering in a controller is most commonly used together with Ajax calls that only update one or a few elements on a page
@@ -581,6 +698,10 @@ module ActionController #:nodoc:
581
698
  # # Renders the same partial with a local variable.
582
699
  # render :partial => "person", :locals => { :name => "david" }
583
700
  #
701
+ # # Renders the partial, making @new_person available through
702
+ # # the local variable 'person'
703
+ # render :partial => "person", :object => @new_person
704
+ #
584
705
  # # Renders a collection of the same partial by making each element
585
706
  # # of @winners available through the local variable "person" as it
586
707
  # # builds the complete response.
@@ -602,16 +723,19 @@ module ActionController #:nodoc:
602
723
  # Note that the partial filename must also be a valid Ruby variable name,
603
724
  # so e.g. 2005 and register-user are invalid.
604
725
  #
605
- # _Deprecation_ _notice_: This used to have the signatures
606
- # <tt>render_partial(partial_path = default_template_name, object = nil, local_assigns = {})</tt> and
607
- # <tt>render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = {})</tt>.
726
+ #
727
+ # == Automatic etagging
728
+ #
729
+ # Rendering will automatically insert the etag header on 200 OK responses. The etag is calculated using MD5 of the
730
+ # response body. If a request comes in that has a matching etag, the response will be changed to a 304 Not Modified
731
+ # and the response body will be set to an empty string. No etag header will be inserted if it's already set.
608
732
  #
609
733
  # === Rendering a template
610
734
  #
611
735
  # Template rendering works just like action rendering except that it takes a path relative to the template root.
612
736
  # The current layout is automatically applied.
613
737
  #
614
- # # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.rhtml)
738
+ # # Renders the template located in [TEMPLATE_ROOT]/weblog/show.r(html|xml) (in Rails, app/views/weblog/show.erb)
615
739
  # render :template => "weblog/show"
616
740
  #
617
741
  # === Rendering a file
@@ -620,18 +744,16 @@ module ActionController #:nodoc:
620
744
  # is assumed to be absolute, and the current layout is not applied.
621
745
  #
622
746
  # # Renders the template located at the absolute filesystem path
623
- # render :file => "/path/to/some/template.rhtml"
624
- # render :file => "c:/path/to/some/template.rhtml"
747
+ # render :file => "/path/to/some/template.erb"
748
+ # render :file => "c:/path/to/some/template.erb"
625
749
  #
626
750
  # # Renders a template within the current layout, and with a 404 status code
627
- # render :file => "/path/to/some/template.rhtml", :layout => true, :status => 404
628
- # render :file => "c:/path/to/some/template.rhtml", :layout => true, :status => 404
751
+ # render :file => "/path/to/some/template.erb", :layout => true, :status => 404
752
+ # render :file => "c:/path/to/some/template.erb", :layout => true, :status => 404
629
753
  #
630
754
  # # Renders a template relative to the template root and chooses the proper file extension
631
755
  # render :file => "some/template", :use_full_path => true
632
756
  #
633
- # _Deprecation_ _notice_: This used to have the signature <tt>render_file(path, status = 200)</tt>
634
- #
635
757
  # === Rendering text
636
758
  #
637
759
  # Rendering of text is usually used for tests or for rendering prepared content, such as a cache. By default, text
@@ -644,11 +766,11 @@ module ActionController #:nodoc:
644
766
  # render :text => "Explosion!", :status => 500
645
767
  #
646
768
  # # Renders the clear text "Hi there!" within the current active layout (if one exists)
647
- # render :text => "Explosion!", :layout => true
769
+ # render :text => "Hi there!", :layout => true
648
770
  #
649
771
  # # Renders the clear text "Hi there!" within the layout
650
772
  # # placed in "app/views/layouts/special.r(html|xml)"
651
- # render :text => "Explosion!", :layout => "special"
773
+ # render :text => "Hi there!", :layout => "special"
652
774
  #
653
775
  # The :text option can also accept a Proc object, which can be used to manually control the page generation. This should
654
776
  # generally be avoided, as it violates the separation between code and content, and because almost everything that can be
@@ -657,20 +779,24 @@ module ActionController #:nodoc:
657
779
  # # Renders "Hello from code!"
658
780
  # render :text => proc { |response, output| output.write("Hello from code!") }
659
781
  #
660
- # _Deprecation_ _notice_: This used to have the signature <tt>render_text("text", status = 200)</tt>
661
- #
662
782
  # === Rendering JSON
663
783
  #
664
- # Rendering JSON sets the content type to text/x-json and optionally wraps the JSON in a callback. It is expected
665
- # that the response will be eval'd for use as a data structure.
784
+ # Rendering JSON sets the content type to application/json and optionally wraps the JSON in a callback. It is expected
785
+ # that the response will be parsed (or eval'd) for use as a data structure.
666
786
  #
667
- # # Renders '{name: "David"}'
787
+ # # Renders '{"name": "David"}'
668
788
  # render :json => {:name => "David"}.to_json
669
789
  #
790
+ # It's not necessary to call <tt>to_json</tt> on the object you want to render, since <tt>render</tt> will
791
+ # automatically do that for you:
792
+ #
793
+ # # Also renders '{"name": "David"}'
794
+ # render :json => {:name => "David"}
795
+ #
670
796
  # Sometimes the result isn't handled directly by a script (such as when the request comes from a SCRIPT tag),
671
- # so the callback option is provided for these cases.
797
+ # so the <tt>:callback</tt> option is provided for these cases.
672
798
  #
673
- # # Renders 'show({name: "David"})'
799
+ # # Renders 'show({"name": "David"})'
674
800
  # render :json => {:name => "David"}.to_json, :callback => 'show'
675
801
  #
676
802
  # === Rendering an inline template
@@ -683,13 +809,11 @@ module ActionController #:nodoc:
683
809
  # render :inline => "<%= 'hello, ' * 3 + 'again' %>"
684
810
  #
685
811
  # # Renders "<p>Good seeing you!</p>" using Builder
686
- # render :inline => "xml.p { 'Good seeing you!' }", :type => :rxml
812
+ # render :inline => "xml.p { 'Good seeing you!' }", :type => :builder
687
813
  #
688
814
  # # Renders "hello david"
689
815
  # render :inline => "<%= 'hello ' + name %>", :locals => { :name => "david" }
690
816
  #
691
- # _Deprecation_ _notice_: This used to have the signature <tt>render_template(template, status = 200, type = :rhtml)</tt>
692
- #
693
817
  # === Rendering inline JavaScriptGenerator page updates
694
818
  #
695
819
  # In addition to rendering JavaScriptGenerator page updates with Ajax in RJS templates (see ActionView::Base for details),
@@ -700,35 +824,21 @@ module ActionController #:nodoc:
700
824
  # page.visual_effect :highlight, 'user_list'
701
825
  # end
702
826
  #
703
- # === Rendering nothing
827
+ # === Rendering with status and location headers
704
828
  #
705
- # Rendering nothing is often convenient in combination with Ajax calls that perform their effect client-side or
706
- # when you just want to communicate a status code. Due to a bug in Safari, nothing actually means a single space.
829
+ # All renders take the :status and :location options and turn them into headers. They can even be used together:
707
830
  #
708
- # # Renders an empty response with status code 200
709
- # render :nothing => true
710
- #
711
- # # Renders an empty response with status code 401 (access denied)
712
- # render :nothing => true, :status => 401
713
- def render(options = nil, deprecated_status = nil, &block) #:doc:
831
+ # render :xml => post.to_xml, :status => :created, :location => post_url(post)
832
+ def render(options = nil, &block) #:doc:
714
833
  raise DoubleRenderError, "Can only render or redirect once per action" if performed?
715
834
 
716
835
  if options.nil?
717
- return render_file(default_template_name, deprecated_status, true)
836
+ return render_for_file(default_template_name, nil, true)
718
837
  else
719
- # Backwards compatibility
720
- unless options.is_a?(Hash)
721
- if options == :update
722
- options = { :update => true }
723
- else
724
- ActiveSupport::Deprecation.warn(
725
- "You called render('#{options}'), which is a deprecated API call. Instead you use " +
726
- "render :file => #{options}. Calling render with just a string will be removed from Rails 2.0.",
727
- caller
728
- )
729
-
730
- return render_file(options, deprecated_status, true)
731
- end
838
+ if options == :update
839
+ options = { :update => true }
840
+ elsif !options.is_a?(Hash)
841
+ raise RenderError, "You called render with invalid options : #{options}"
732
842
  end
733
843
  end
734
844
 
@@ -736,52 +846,72 @@ module ActionController #:nodoc:
736
846
  response.content_type = content_type.to_s
737
847
  end
738
848
 
849
+ if location = options[:location]
850
+ response.headers["Location"] = url_for(location)
851
+ end
852
+
739
853
  if text = options[:text]
740
- render_text(text, options[:status])
854
+ render_for_text(text, options[:status])
741
855
 
742
856
  else
743
857
  if file = options[:file]
744
- render_file(file, options[:status], options[:use_full_path], options[:locals] || {})
858
+ render_for_file(file, options[:status], options[:use_full_path], options[:locals] || {})
745
859
 
746
860
  elsif template = options[:template]
747
- render_file(template, options[:status], true)
861
+ render_for_file(template, options[:status], true)
748
862
 
749
863
  elsif inline = options[:inline]
750
- render_template(inline, options[:status], options[:type], options[:locals] || {})
864
+ add_variables_to_assigns
865
+ render_for_text(@template.render_template(options[:type] || :erb, inline, nil, options[:locals] || {}), options[:status])
751
866
 
752
867
  elsif action_name = options[:action]
753
- ActiveSupport::Deprecation.silence do
754
- render_action(action_name, options[:status], options[:layout])
755
- end
868
+ template = default_template_name(action_name.to_s)
869
+ if options[:layout] && !template_exempt_from_layout?(template)
870
+ render_with_a_layout(:file => template, :status => options[:status], :use_full_path => true, :layout => true)
871
+ else
872
+ render_with_no_layout(:file => template, :status => options[:status], :use_full_path => true)
873
+ end
756
874
 
757
875
  elsif xml = options[:xml]
758
- render_xml(xml, options[:status])
876
+ response.content_type = Mime::XML
877
+ render_for_text(xml.respond_to?(:to_xml) ? xml.to_xml : xml, options[:status])
759
878
 
760
879
  elsif json = options[:json]
761
- render_json(json, options[:callback], options[:status])
880
+ json = json.to_json unless json.is_a?(String)
881
+ json = "#{options[:callback]}(#{json})" unless options[:callback].blank?
882
+ response.content_type = Mime::JSON
883
+ render_for_text(json, options[:status])
762
884
 
763
885
  elsif partial = options[:partial]
764
886
  partial = default_template_name if partial == true
887
+ add_variables_to_assigns
888
+
765
889
  if collection = options[:collection]
766
- render_partial_collection(partial, collection, options[:spacer_template], options[:locals], options[:status])
890
+ render_for_text(
891
+ @template.send!(:render_partial_collection, partial, collection,
892
+ options[:spacer_template], options[:locals]), options[:status]
893
+ )
767
894
  else
768
- render_partial(partial, ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals], options[:status])
895
+ render_for_text(
896
+ @template.send!(:render_partial, partial,
897
+ ActionView::Base::ObjectWrapper.new(options[:object]), options[:locals]), options[:status]
898
+ )
769
899
  end
770
900
 
771
901
  elsif options[:update]
772
902
  add_variables_to_assigns
773
- @template.send :evaluate_assigns
903
+ @template.send! :evaluate_assigns
774
904
 
775
905
  generator = ActionView::Helpers::PrototypeHelper::JavaScriptGenerator.new(@template, &block)
776
- render_javascript(generator.to_s)
906
+ response.content_type = Mime::JS
907
+ render_for_text(generator.to_s)
777
908
 
778
909
  elsif options[:nothing]
779
910
  # Safari doesn't pass the headers of the return if the response is zero length
780
- render_text(" ", options[:status])
911
+ render_for_text(" ", options[:status])
781
912
 
782
913
  else
783
- render_file(default_template_name, options[:status], true)
784
-
914
+ render_for_file(default_template_name, options[:status], true)
785
915
  end
786
916
  end
787
917
  end
@@ -789,87 +919,13 @@ module ActionController #:nodoc:
789
919
  # Renders according to the same rules as <tt>render</tt>, but returns the result in a string instead
790
920
  # of sending it as the response body to the browser.
791
921
  def render_to_string(options = nil, &block) #:doc:
792
- ActiveSupport::Deprecation.silence { render(options, &block) }
922
+ render(options, &block)
793
923
  ensure
794
924
  erase_render_results
795
925
  forget_variables_added_to_assigns
796
926
  reset_variables_added_to_assigns
797
927
  end
798
928
 
799
- def render_action(action_name, status = nil, with_layout = true) #:nodoc:
800
- template = default_template_name(action_name.to_s)
801
- if with_layout && !template_exempt_from_layout?(template)
802
- render_with_layout(:file => template, :status => status, :use_full_path => true, :layout => true)
803
- else
804
- render_without_layout(:file => template, :status => status, :use_full_path => true)
805
- end
806
- end
807
-
808
- def render_file(template_path, status = nil, use_full_path = false, locals = {}) #:nodoc:
809
- add_variables_to_assigns
810
- assert_existence_of_template_file(template_path) if use_full_path
811
- logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
812
- render_text(@template.render_file(template_path, use_full_path, locals), status)
813
- end
814
-
815
- def render_template(template, status = nil, type = :rhtml, local_assigns = {}) #:nodoc:
816
- add_variables_to_assigns
817
- render_text(@template.render_template(type, template, nil, local_assigns), status)
818
- end
819
-
820
- def render_text(text = nil, status = nil, append_response = false) #:nodoc:
821
- @performed_render = true
822
-
823
- response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
824
-
825
- if append_response
826
- response.body ||= ''
827
- response.body << text
828
- else
829
- response.body = text
830
- end
831
- end
832
-
833
- def render_javascript(javascript, status = nil, append_response = true) #:nodoc:
834
- response.content_type = Mime::JS
835
- render_text(javascript, status, append_response)
836
- end
837
-
838
- def render_xml(xml, status = nil) #:nodoc:
839
- response.content_type = Mime::XML
840
- render_text(xml, status)
841
- end
842
-
843
- def render_json(json, callback = nil, status = nil) #:nodoc:
844
- json = "#{callback}(#{json})" unless callback.blank?
845
-
846
- response.content_type = Mime::JSON
847
- render_text(json, status)
848
- end
849
-
850
- def render_nothing(status = nil) #:nodoc:
851
- render_text(' ', status)
852
- end
853
-
854
- def render_partial(partial_path = default_template_name, object = nil, local_assigns = nil, status = nil) #:nodoc:
855
- add_variables_to_assigns
856
- render_text(@template.render_partial(partial_path, object, local_assigns), status)
857
- end
858
-
859
- def render_partial_collection(partial_name, collection, partial_spacer_template = nil, local_assigns = nil, status = nil) #:nodoc:
860
- add_variables_to_assigns
861
- render_text(@template.render_partial_collection(partial_name, collection, partial_spacer_template, local_assigns), status)
862
- end
863
-
864
- def render_with_layout(template_name = default_template_name, status = nil, layout = nil) #:nodoc:
865
- render_with_a_layout(template_name, status, layout)
866
- end
867
-
868
- def render_without_layout(template_name = default_template_name, status = nil) #:nodoc:
869
- render_with_no_layout(template_name, status)
870
- end
871
-
872
-
873
929
  # Return a response that has no content (merely headers). The options
874
930
  # argument is interpreted to be a hash of header names and values.
875
931
  # This allows you to easily return a response that consists only of
@@ -956,47 +1012,62 @@ module ActionController #:nodoc:
956
1012
 
957
1013
  # Redirects the browser to the target specified in +options+. This parameter can take one of three forms:
958
1014
  #
959
- # * <tt>Hash</tt>: The URL will be generated by calling url_for with the +options+.
960
- # * <tt>String starting with protocol:// (like http://)</tt>: Is passed straight through as the target for redirection.
961
- # * <tt>String not containing a protocol</tt>: The current protocol and host is prepended to the string.
962
- # * <tt>:back</tt>: Back to the page that issued the request. Useful for forms that are triggered from multiple places.
1015
+ # * <tt>Hash</tt> - The URL will be generated by calling url_for with the +options+.
1016
+ # * <tt>Record</tt> - The URL will be generated by calling url_for with the +options+, which will reference a named URL for that record.
1017
+ # * <tt>String starting with protocol:// (like http://)</tt> - Is passed straight through as the target for redirection.
1018
+ # * <tt>String not containing a protocol</tt> - The current protocol and host is prepended to the string.
1019
+ # * <tt>:back</tt> - Back to the page that issued the request. Useful for forms that are triggered from multiple places.
963
1020
  # Short-hand for redirect_to(request.env["HTTP_REFERER"])
964
1021
  #
965
1022
  # Examples:
966
1023
  # redirect_to :action => "show", :id => 5
1024
+ # redirect_to post
967
1025
  # redirect_to "http://www.rubyonrails.org"
968
1026
  # redirect_to "/images/screenshot.jpg"
1027
+ # redirect_to articles_url
969
1028
  # redirect_to :back
970
1029
  #
971
- # The redirection happens as a "302 Moved" header.
1030
+ # The redirection happens as a "302 Moved" header unless otherwise specified.
1031
+ #
1032
+ # Examples:
1033
+ # redirect_to post_url(@post), :status=>:found
1034
+ # redirect_to :action=>'atom', :status=>:moved_permanently
1035
+ # redirect_to post_url(@post), :status=>301
1036
+ # redirect_to :action=>'atom', :status=>302
972
1037
  #
973
1038
  # When using <tt>redirect_to :back</tt>, if there is no referrer,
974
1039
  # RedirectBackError will be raised. You may specify some fallback
975
- # behavior for this case by rescueing RedirectBackError.
976
- def redirect_to(options = {}, *parameters_for_method_reference) #:doc:
1040
+ # behavior for this case by rescuing RedirectBackError.
1041
+ def redirect_to(options = {}, response_status = {}) #:doc:
1042
+
1043
+ if options.is_a?(Hash) && options[:status]
1044
+ status = options.delete(:status)
1045
+ elsif response_status[:status]
1046
+ status = response_status[:status]
1047
+ else
1048
+ status = 302
1049
+ end
1050
+
977
1051
  case options
978
1052
  when %r{^\w+://.*}
979
1053
  raise DoubleRenderError if performed?
980
- logger.info("Redirected to #{options}") if logger
981
- response.redirect(options)
1054
+ logger.info("Redirected to #{options}") if logger && logger.info?
1055
+ response.redirect(options, interpret_status(status))
982
1056
  response.redirected_to = options
983
1057
  @performed_redirect = true
984
1058
 
985
1059
  when String
986
- redirect_to(request.protocol + request.host_with_port + options)
1060
+ redirect_to(request.protocol + request.host_with_port + options, :status=>status)
987
1061
 
988
1062
  when :back
989
- request.env["HTTP_REFERER"] ? redirect_to(request.env["HTTP_REFERER"]) : raise(RedirectBackError)
1063
+ request.env["HTTP_REFERER"] ? redirect_to(request.env["HTTP_REFERER"], :status=>status) : raise(RedirectBackError)
1064
+
1065
+ when Hash
1066
+ redirect_to(url_for(options), :status=>status)
1067
+ response.redirected_to = options
990
1068
 
991
1069
  else
992
- if parameters_for_method_reference.empty?
993
- redirect_to(url_for(options))
994
- response.redirected_to = options
995
- else
996
- # TOOD: Deprecate me!
997
- redirect_to(url_for(options, *parameters_for_method_reference))
998
- response.redirected_to, response.redirected_to_method_params = options, parameters_for_method_reference
999
- end
1070
+ redirect_to(url_for(options), :status=>status)
1000
1071
  end
1001
1072
  end
1002
1073
 
@@ -1030,23 +1101,35 @@ module ActionController #:nodoc:
1030
1101
  response.session = @_session
1031
1102
  end
1032
1103
 
1104
+
1033
1105
  private
1034
- def self.view_class
1035
- @view_class ||=
1036
- # create a new class based on the default template class and include helper methods
1037
- returning Class.new(ActionView::Base) do |view_class|
1038
- view_class.send(:include, master_helper_module)
1039
- end
1106
+ def render_for_file(template_path, status = nil, use_full_path = false, locals = {}) #:nodoc:
1107
+ add_variables_to_assigns
1108
+ assert_existence_of_template_file(template_path) if use_full_path
1109
+ logger.info("Rendering #{template_path}" + (status ? " (#{status})" : '')) if logger
1110
+ render_for_text(@template.render_file(template_path, use_full_path, locals), status)
1040
1111
  end
1041
1112
 
1042
- def self.view_root
1043
- @view_root ||= template_root
1044
- end
1113
+ def render_for_text(text = nil, status = nil, append_response = false) #:nodoc:
1114
+ @performed_render = true
1045
1115
 
1116
+ response.headers['Status'] = interpret_status(status || DEFAULT_RENDER_STATUS_CODE)
1117
+
1118
+ if append_response
1119
+ response.body ||= ''
1120
+ response.body << text.to_s
1121
+ else
1122
+ response.body = text.is_a?(Proc) ? text : text.to_s
1123
+ end
1124
+ end
1125
+
1046
1126
  def initialize_template_class(response)
1047
- raise "You must assign a template class through ActionController.template_class= before processing a request" unless @@template_class
1127
+ unless @@template_class
1128
+ raise "You must assign a template class through ActionController.template_class= before processing a request"
1129
+ end
1048
1130
 
1049
- response.template = self.class.view_class.new(self.class.view_root, {}, self)
1131
+ response.template = ActionView::Base.new(view_paths, {}, self)
1132
+ response.template.extend self.class.master_helper_module
1050
1133
  response.redirected_to = nil
1051
1134
  @performed_render = @performed_redirect = false
1052
1135
  end
@@ -1062,26 +1145,6 @@ module ActionController #:nodoc:
1062
1145
  @assigns = @_response.template.assigns
1063
1146
 
1064
1147
  @_headers = @_response.headers
1065
-
1066
- assign_deprecated_shortcuts(request, response)
1067
- end
1068
-
1069
-
1070
- # TODO: assigns cookies headers params request response template
1071
- DEPRECATED_INSTANCE_VARIABLES = %w(cookies flash headers params request response session)
1072
-
1073
- # Gone after 1.2.
1074
- def assign_deprecated_shortcuts(request, response)
1075
- DEPRECATED_INSTANCE_VARIABLES.each do |method|
1076
- var = "@#{method}"
1077
- if instance_variables.include?(var)
1078
- value = instance_variable_get(var)
1079
- unless ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy === value
1080
- raise "Deprecating #{var}, but it's already set to #{value.inspect}! Use the #{method}= writer method instead of setting #{var} directly."
1081
- end
1082
- end
1083
- instance_variable_set var, ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, method)
1084
- end
1085
1148
  end
1086
1149
 
1087
1150
  def initialize_current_url
@@ -1089,22 +1152,26 @@ module ActionController #:nodoc:
1089
1152
  end
1090
1153
 
1091
1154
  def log_processing
1092
- if logger
1155
+ if logger && logger.info?
1093
1156
  logger.info "\n\nProcessing #{controller_class_name}\##{action_name} (for #{request_origin}) [#{request.method.to_s.upcase}]"
1094
1157
  logger.info " Session ID: #{@_session.session_id}" if @_session and @_session.respond_to?(:session_id)
1095
1158
  logger.info " Parameters: #{respond_to?(:filter_parameters) ? filter_parameters(params).inspect : params.inspect}"
1096
1159
  end
1097
1160
  end
1098
1161
 
1162
+ def default_render #:nodoc:
1163
+ render
1164
+ end
1165
+
1099
1166
  def perform_action
1100
1167
  if self.class.action_methods.include?(action_name)
1101
1168
  send(action_name)
1102
- render unless performed?
1169
+ default_render unless performed?
1103
1170
  elsif respond_to? :method_missing
1104
- send(:method_missing, action_name)
1105
- render unless performed?
1171
+ method_missing action_name
1172
+ default_render unless performed?
1106
1173
  elsif template_exists? && template_public?
1107
- render
1174
+ default_render
1108
1175
  else
1109
1176
  raise UnknownAction, "No action responded to #{action_name}", caller
1110
1177
  end
@@ -1132,7 +1199,7 @@ module ActionController #:nodoc:
1132
1199
  end
1133
1200
 
1134
1201
  def self.action_methods
1135
- @action_methods ||= Set.new(public_instance_methods - hidden_actions)
1202
+ @action_methods ||= Set.new(public_instance_methods.map(&:to_s)) - hidden_actions
1136
1203
  end
1137
1204
 
1138
1205
  def add_variables_to_assigns
@@ -1160,7 +1227,7 @@ module ActionController #:nodoc:
1160
1227
  end
1161
1228
 
1162
1229
  def add_class_variables_to_assigns
1163
- %w(template_root logger template_class ignore_missing_templates).each do |cvar|
1230
+ %w(view_paths logger template_class ignore_missing_templates).each do |cvar|
1164
1231
  @assigns[cvar] = self.send(cvar)
1165
1232
  end
1166
1233
  end
@@ -1199,16 +1266,17 @@ module ActionController #:nodoc:
1199
1266
  end
1200
1267
 
1201
1268
  def template_exempt_from_layout?(template_name = default_template_name)
1202
- extension = @template.pick_template_extension(template_name) rescue nil
1269
+ extension = @template && @template.pick_template_extension(template_name)
1203
1270
  name_with_extension = !template_name.include?('.') && extension ? "#{template_name}.#{extension}" : template_name
1204
- extension == :rjs || @@exempt_from_layout.any? { |ext| name_with_extension =~ ext }
1271
+ @@exempt_from_layout.any? { |ext| name_with_extension =~ ext }
1205
1272
  end
1206
1273
 
1207
1274
  def assert_existence_of_template_file(template_name)
1208
1275
  unless template_exists?(template_name) || ignore_missing_templates
1209
- full_template_path = @template.send(:full_template_path, template_name, 'rhtml')
1276
+ full_template_path = template_name.include?('.') ? template_name : "#{template_name}.#{@template.template_format}.erb"
1277
+ display_paths = view_paths.join(':')
1210
1278
  template_type = (template_name =~ /layouts/i) ? 'layout' : 'template'
1211
- raise(MissingTemplate, "Missing #{template_type} #{full_template_path}")
1279
+ raise(MissingTemplate, "Missing #{template_type} #{full_template_path} in view path #{display_paths}")
1212
1280
  end
1213
1281
  end
1214
1282