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
@@ -64,13 +64,13 @@ module ActionController #:nodoc:
64
64
  #
65
65
  # If there is a template in <tt>app/views/layouts/</tt> with the same name as the current controller then it will be automatically
66
66
  # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
67
- # <tt>app/views/layouts/weblog.rhtml</tt> or <tt>app/views/layouts/weblog.rxml</tt> exists then it will be automatically set as
68
- # the layout for your WeblogController. You can create a layout with the name <tt>application.rhtml</tt> or <tt>application.rxml</tt>
67
+ # <tt>app/views/layouts/weblog.erb</tt> or <tt>app/views/layouts/weblog.builder</tt> exists then it will be automatically set as
68
+ # the layout for your WeblogController. You can create a layout with the name <tt>application.erb</tt> or <tt>application.builder</tt>
69
69
  # and this will be set as the default controller if there is no layout with the same name as the current controller and there is
70
70
  # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout.
71
- # assignment. So an Admin::WeblogController will look for a template named <tt>app/views/layouts/admin/weblog.rhtml</tt>.
71
+ # assignment. So an Admin::WeblogController will look for a template named <tt>app/views/layouts/admin/weblog.erb</tt>.
72
72
  # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set.
73
- # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignement if the child
73
+ # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child
74
74
  # class has a layout with the same name.
75
75
  #
76
76
  # == Inheritance for layouts
@@ -165,30 +165,39 @@ module ActionController #:nodoc:
165
165
  # variable. The preferred notation now is to use <tt>yield</tt>, as documented above.
166
166
  module ClassMethods
167
167
  # If a layout is specified, all rendered actions will have their result rendered
168
- # when the layout<tt>yield</tt>'s. This layout can itself depend on instance variables assigned during action
168
+ # when the layout <tt>yield</tt>s. This layout can itself depend on instance variables assigned during action
169
169
  # performance and have access to them as any normal template would.
170
- def layout(template_name, conditions = {})
170
+ def layout(template_name, conditions = {}, auto = false)
171
171
  add_layout_conditions(conditions)
172
172
  write_inheritable_attribute "layout", template_name
173
+ write_inheritable_attribute "auto_layout", auto
173
174
  end
174
175
 
175
176
  def layout_conditions #:nodoc:
176
177
  @layout_conditions ||= read_inheritable_attribute("layout_conditions")
177
178
  end
178
179
 
179
- def default_layout #:nodoc:
180
- @default_layout ||= read_inheritable_attribute("layout")
180
+ def default_layout(format) #:nodoc:
181
+ layout = read_inheritable_attribute("layout")
182
+ return layout unless read_inheritable_attribute("auto_layout")
183
+ @default_layout ||= {}
184
+ @default_layout[format] ||= default_layout_with_format(format, layout)
185
+ @default_layout[format]
181
186
  end
182
-
187
+
188
+ def layout_list #:nodoc:
189
+ view_paths.collect do |path|
190
+ Dir["#{path}/layouts/**/*"]
191
+ end.flatten
192
+ end
193
+
183
194
  private
184
195
  def inherited_with_layout(child)
185
196
  inherited_without_layout(child)
186
- layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
187
- child.layout(layout_match) unless layout_list.grep(%r{layouts/#{layout_match}\.[a-z][0-9a-z]*$}).empty?
188
- end
189
-
190
- def layout_list
191
- Dir.glob("#{template_root}/layouts/**/*")
197
+ unless child.name.blank?
198
+ layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
199
+ child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
200
+ end
192
201
  end
193
202
 
194
203
  def add_layout_conditions(conditions)
@@ -204,6 +213,15 @@ module ActionController #:nodoc:
204
213
  h[dirname] = File.directory? dirname
205
214
  end
206
215
  end
216
+
217
+ def default_layout_with_format(format, layout)
218
+ list = layout_list
219
+ if list.grep(%r{layouts/#{layout}\.#{format}(\.[a-z][0-9a-z]*)+$}).empty?
220
+ (!list.grep(%r{layouts/#{layout}\.([a-z][0-9a-z]*)+$}).empty? && format == :html) ? layout : nil
221
+ else
222
+ layout
223
+ end
224
+ end
207
225
  end
208
226
 
209
227
  # Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
@@ -211,11 +229,10 @@ module ActionController #:nodoc:
211
229
  # object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
212
230
  # weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
213
231
  def active_layout(passed_layout = nil)
214
- layout = passed_layout || self.class.default_layout
215
-
232
+ layout = passed_layout || self.class.default_layout(response.template.template_format)
216
233
  active_layout = case layout
217
234
  when String then layout
218
- when Symbol then send(layout)
235
+ when Symbol then send!(layout)
219
236
  when Proc then layout.call(self)
220
237
  end
221
238
 
@@ -231,34 +248,30 @@ module ActionController #:nodoc:
231
248
  end
232
249
  end
233
250
 
234
- def render_with_a_layout(options = nil, deprecated_status = nil, deprecated_layout = nil, &block) #:nodoc:
235
- template_with_options = options.is_a?(Hash)
236
-
237
- if apply_layout?(template_with_options, options) && (layout = pick_layout(template_with_options, options, deprecated_layout))
238
- assert_existence_of_template_file(layout)
251
+ protected
252
+ def render_with_a_layout(options = nil, &block) #:nodoc:
253
+ template_with_options = options.is_a?(Hash)
254
+
255
+ if apply_layout?(template_with_options, options) && (layout = pick_layout(template_with_options, options))
256
+ assert_existence_of_template_file(layout)
239
257
 
240
- options = options.merge :layout => false if template_with_options
241
- logger.info("Rendering #{options} within #{layout}") if logger
258
+ options = options.merge :layout => false if template_with_options
259
+ logger.info("Rendering template within #{layout}") if logger
242
260
 
243
- if template_with_options
244
261
  content_for_layout = render_with_no_layout(options, &block)
245
- deprecated_status = options[:status] || deprecated_status
262
+ erase_render_results
263
+ add_variables_to_assigns
264
+ @template.instance_variable_set("@content_for_layout", content_for_layout)
265
+ response.layout = layout
266
+ status = template_with_options ? options[:status] : nil
267
+ render_for_text(@template.render_file(layout, true), status)
246
268
  else
247
- content_for_layout = render_with_no_layout(options, deprecated_status, &block)
269
+ render_with_no_layout(options, &block)
248
270
  end
249
-
250
- erase_render_results
251
- add_variables_to_assigns
252
- @template.instance_variable_set("@content_for_layout", content_for_layout)
253
- response.layout = layout
254
- render_text(@template.render_file(layout, true), deprecated_status)
255
- else
256
- render_with_no_layout(options, deprecated_status, &block)
257
271
  end
258
- end
272
+
259
273
 
260
274
  private
261
-
262
275
  def apply_layout?(template_with_options, options)
263
276
  return false if options == :update
264
277
  template_with_options ? candidate_for_layout?(options) : !template_exempt_from_layout?
@@ -266,14 +279,12 @@ module ActionController #:nodoc:
266
279
 
267
280
  def candidate_for_layout?(options)
268
281
  (options.has_key?(:layout) && options[:layout] != false) ||
269
- options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing).compact.empty? &&
270
- !template_exempt_from_layout?(default_template_name(options[:action] || options[:template]))
282
+ options.values_at(:text, :xml, :json, :file, :inline, :partial, :nothing).compact.empty? &&
283
+ !template_exempt_from_layout?(options[:template] || default_template_name(options[:action]))
271
284
  end
272
285
 
273
- def pick_layout(template_with_options, options, deprecated_layout)
274
- if deprecated_layout
275
- deprecated_layout
276
- elsif template_with_options
286
+ def pick_layout(template_with_options, options)
287
+ if template_with_options
277
288
  case layout = options[:layout]
278
289
  when FalseClass
279
290
  nil
@@ -305,9 +316,10 @@ module ActionController #:nodoc:
305
316
  # Does a layout directory for this class exist?
306
317
  # we cache this info in a class level hash
307
318
  def layout_directory?(layout_name)
308
- template_path = File.join(self.class.view_root, 'layouts', layout_name)
309
- dirname = File.dirname(template_path)
310
- self.class.send(:layout_directory_exists_cache)[dirname]
319
+ view_paths.find do |path|
320
+ next unless template_path = Dir[File.join(path, 'layouts', layout_name) + ".*"].first
321
+ self.class.send!(:layout_directory_exists_cache)[File.dirname(template_path)]
322
+ end
311
323
  end
312
324
  end
313
325
  end
@@ -1,7 +1,9 @@
1
1
  module ActionController #:nodoc:
2
2
  module MimeResponds #:nodoc:
3
3
  def self.included(base)
4
- base.send(:include, ActionController::MimeResponds::InstanceMethods)
4
+ base.module_eval do
5
+ include ActionController::MimeResponds::InstanceMethods
6
+ end
5
7
  end
6
8
 
7
9
  module InstanceMethods
@@ -11,51 +13,51 @@ module ActionController #:nodoc:
11
13
  # def index
12
14
  # @people = Person.find(:all)
13
15
  # end
14
- #
16
+ #
15
17
  # Here's the same action, with web-service support baked in:
16
- #
18
+ #
17
19
  # def index
18
20
  # @people = Person.find(:all)
19
- #
21
+ #
20
22
  # respond_to do |format|
21
23
  # format.html
22
24
  # format.xml { render :xml => @people.to_xml }
23
25
  # end
24
26
  # end
25
- #
26
- # What that says is, "if the client wants HTML in response to this action, just respond as we
27
- # would have before, but if the client wants XML, return them the list of people in XML format."
27
+ #
28
+ # What that says is, "if the client wants HTML in response to this action, just respond as we
29
+ # would have before, but if the client wants XML, return them the list of people in XML format."
28
30
  # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
29
- #
30
- # Supposing you have an action that adds a new person, optionally creating their company
31
+ #
32
+ # Supposing you have an action that adds a new person, optionally creating their company
31
33
  # (by name) if it does not already exist, without web-services, it might look like this:
32
- #
34
+ #
33
35
  # def create
34
36
  # @company = Company.find_or_create_by_name(params[:company][:name])
35
37
  # @person = @company.people.create(params[:person])
36
- #
38
+ #
37
39
  # redirect_to(person_list_url)
38
40
  # end
39
- #
41
+ #
40
42
  # Here's the same action, with web-service support baked in:
41
- #
43
+ #
42
44
  # def create
43
45
  # company = params[:person].delete(:company)
44
46
  # @company = Company.find_or_create_by_name(company[:name])
45
47
  # @person = @company.people.create(params[:person])
46
- #
48
+ #
47
49
  # respond_to do |format|
48
50
  # format.html { redirect_to(person_list_url) }
49
51
  # format.js
50
52
  # format.xml { render :xml => @person.to_xml(:include => @company) }
51
53
  # end
52
54
  # end
53
- #
54
- # If the client wants HTML, we just redirect them back to the person list. If they want Javascript
55
- # (wants.js), then it is an RJS request and we render the RJS template associated with this action.
56
- # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
57
- # include the persons company in the rendered XML, so you get something like this:
58
- #
55
+ #
56
+ # If the client wants HTML, we just redirect them back to the person list. If they want Javascript
57
+ # (format.js), then it is an RJS request and we render the RJS template associated with this action.
58
+ # Lastly, if the client wants XML, we render the created person as XML, but with a twist: we also
59
+ # include the person's company in the rendered XML, so you get something like this:
60
+ #
59
61
  # <person>
60
62
  # <id>...</id>
61
63
  # ...
@@ -65,60 +67,54 @@ module ActionController #:nodoc:
65
67
  # ...
66
68
  # </company>
67
69
  # </person>
68
- #
70
+ #
69
71
  # Note, however, the extra bit at the top of that action:
70
- #
72
+ #
71
73
  # company = params[:person].delete(:company)
72
74
  # @company = Company.find_or_create_by_name(company[:name])
73
- #
74
- # This is because the incoming XML document (if a web-service request is in process) can only contain a
75
+ #
76
+ # This is because the incoming XML document (if a web-service request is in process) can only contain a
75
77
  # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
76
- #
78
+ #
77
79
  # person[name]=...&person[company][name]=...&...
78
- #
80
+ #
79
81
  # And, like this (xml-encoded):
80
- #
82
+ #
81
83
  # <person>
82
84
  # <name>...</name>
83
85
  # <company>
84
86
  # <name>...</name>
85
87
  # </company>
86
88
  # </person>
87
- #
88
- # In other words, we make the request so that it operates on a single entity—a person. Then, in the action,
89
- # we extract the company data from the request, find or create the company, and then create the new person
89
+ #
90
+ # In other words, we make the request so that it operates on a single entity's person. Then, in the action,
91
+ # we extract the company data from the request, find or create the company, and then create the new person
90
92
  # with the remaining data.
91
- #
92
- # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
93
- # in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow
93
+ #
94
+ # Note that you can define your own XML parameter parser which would allow you to describe multiple entities
95
+ # in a single request (i.e., by wrapping them all in a single root note), but if you just go with the flow
94
96
  # and accept Rails' defaults, life will be much easier.
95
- #
97
+ #
96
98
  # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
97
99
  # environment.rb as follows.
98
- #
100
+ #
99
101
  # Mime::Type.register "image/jpg", :jpg
100
102
  def respond_to(*types, &block)
101
103
  raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
102
104
  block ||= lambda { |responder| types.each { |type| responder.send(type) } }
103
- responder = Responder.new(block.binding)
105
+ responder = Responder.new(self)
104
106
  block.call(responder)
105
107
  responder.respond
106
108
  end
107
109
  end
108
-
110
+
109
111
  class Responder #:nodoc:
110
- DEFAULT_BLOCKS = [:html, :js, :xml].inject({}) do |blocks, ext|
111
- template_extension = (ext == :html ? '' : ".r#{ext}")
112
- blocks.update ext => %(Proc.new { render :action => "\#{action_name}#{template_extension}", :content_type => Mime::#{ext.to_s.upcase} })
113
- end
114
-
115
- def initialize(block_binding)
116
- @block_binding = block_binding
117
- @mime_type_priority = eval(
118
- "(params[:format] && Mime::EXTENSION_LOOKUP[params[:format]]) ? " +
119
- "[ Mime::EXTENSION_LOOKUP[params[:format]] ] : request.accepts",
120
- block_binding
121
- )
112
+ def initialize(controller)
113
+ @controller = controller
114
+ @request = controller.request
115
+ @response = controller.response
116
+
117
+ @mime_type_priority = Array(Mime::Type.lookup_by_extension(@request.parameters[:format]) || @request.accepts)
122
118
 
123
119
  @order = []
124
120
  @responses = {}
@@ -126,54 +122,47 @@ module ActionController #:nodoc:
126
122
 
127
123
  def custom(mime_type, &block)
128
124
  mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
129
-
125
+
130
126
  @order << mime_type
131
-
132
- if block_given?
133
- @responses[mime_type] = Proc.new do
134
- eval "response.content_type = '#{mime_type.to_s}'", @block_binding
135
- block.call
136
- end
137
- else
138
- if source = DEFAULT_BLOCKS[mime_type.to_sym]
139
- @responses[mime_type] = eval(source, @block_binding)
140
- else
141
- raise ActionController::RenderError, "Expected a block but none was given for custom mime handler #{mime_type}"
142
- end
127
+
128
+ @responses[mime_type] = Proc.new do
129
+ @response.template.template_format = mime_type.to_sym
130
+ @response.content_type = mime_type.to_s
131
+ block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
143
132
  end
144
133
  end
145
134
 
146
135
  def any(*args, &block)
147
136
  args.each { |type| send(type, &block) }
148
137
  end
149
-
138
+
150
139
  def method_missing(symbol, &block)
151
140
  mime_constant = symbol.to_s.upcase
152
-
141
+
153
142
  if Mime::SET.include?(Mime.const_get(mime_constant))
154
143
  custom(Mime.const_get(mime_constant), &block)
155
144
  else
156
145
  super
157
146
  end
158
147
  end
159
-
148
+
160
149
  def respond
161
150
  for priority in @mime_type_priority
162
151
  if priority == Mime::ALL
163
152
  @responses[@order.first].call
164
153
  return
165
154
  else
166
- if priority === @order
155
+ if @responses[priority]
167
156
  @responses[priority].call
168
157
  return # mime type match found, be happy and return
169
158
  end
170
159
  end
171
160
  end
172
-
161
+
173
162
  if @order.include?(Mime::ALL)
174
163
  @responses[Mime::ALL].call
175
164
  else
176
- eval 'render(:nothing => true, :status => "406 Not Acceptable")', @block_binding
165
+ @controller.send :head, :not_acceptable
177
166
  end
178
167
  end
179
168
  end
@@ -1,4 +1,8 @@
1
1
  module Mime
2
+ SET = []
3
+ EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
4
+ LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k.blank? }
5
+
2
6
  # Encapsulates the notion of a mime type. Can be used at render time, for example, with:
3
7
  #
4
8
  # class PostsController < ActionController::Base
@@ -20,7 +24,7 @@ module Mime
20
24
  def initialize(order, name, q=nil)
21
25
  @order = order
22
26
  @name = name.strip
23
- q ||= 0.0 if @name == "*/*" # default "*/*" to end of list
27
+ q ||= 0.0 if @name == Mime::ALL # default wilcard match to end of list
24
28
  @q = ((q || 1.0).to_f * 100).to_i
25
29
  end
26
30
 
@@ -44,22 +48,37 @@ module Mime
44
48
  LOOKUP[string]
45
49
  end
46
50
 
47
- def register(string, symbol, synonyms = [])
48
- Mime.send :const_set, symbol.to_s.upcase, Type.new(string, symbol, synonyms)
49
- SET << Mime.send(:const_get, symbol.to_s.upcase)
50
- LOOKUP[string] = EXTENSION_LOOKUP[symbol.to_s] = SET.last
51
+ def lookup_by_extension(extension)
52
+ EXTENSION_LOOKUP[extension]
53
+ end
54
+
55
+ # Registers an alias that's not used on mime type lookup, but can be referenced directly. Especially useful for
56
+ # rendering different HTML versions depending on the user agent, like an iPhone.
57
+ def register_alias(string, symbol, extension_synonyms = [])
58
+ register(string, symbol, [], extension_synonyms, true)
59
+ end
60
+
61
+ def register(string, symbol, mime_type_synonyms = [], extension_synonyms = [], skip_lookup = false)
62
+ Mime.instance_eval { const_set symbol.to_s.upcase, Type.new(string, symbol, mime_type_synonyms) }
63
+
64
+ SET << Mime.const_get(symbol.to_s.upcase)
65
+
66
+ ([string] + mime_type_synonyms).each { |string| LOOKUP[string] = SET.last } unless skip_lookup
67
+ ([symbol.to_s] + extension_synonyms).each { |ext| EXTENSION_LOOKUP[ext] = SET.last }
51
68
  end
52
69
 
53
70
  def parse(accept_header)
54
71
  # keep track of creation order to keep the subsequent sort stable
55
- index = 0
56
- list = accept_header.split(/,/).map! do |i|
57
- AcceptItem.new(index += 1, *i.split(/;\s*q=/))
58
- end.sort!
72
+ list = []
73
+ accept_header.split(/,/).each_with_index do |header, index|
74
+ params = header.split(/;\s*q=/)
75
+ list << AcceptItem.new(index, *params) unless params.empty?
76
+ end
77
+ list.sort!
59
78
 
60
79
  # Take care of the broken text/xml entry by renaming or deleting it
61
80
  text_xml = list.index("text/xml")
62
- app_xml = list.index("application/xml")
81
+ app_xml = list.index(Mime::XML.to_s)
63
82
 
64
83
  if text_xml && app_xml
65
84
  # set the q value to the max of the two
@@ -73,9 +92,9 @@ module Mime
73
92
 
74
93
  # delete text_xml from the list
75
94
  list.delete_at(text_xml)
76
-
95
+
77
96
  elsif text_xml
78
- list[text_xml].name = "application/xml"
97
+ list[text_xml].name = Mime::XML.to_s
79
98
  end
80
99
 
81
100
  # Look for more specific xml-based types and sort them ahead of app/xml
@@ -128,73 +147,17 @@ module Mime
128
147
  def ==(mime_type)
129
148
  (@synonyms + [ self ]).any? { |synonym| synonym.to_s == mime_type.to_s } if mime_type
130
149
  end
150
+
151
+ private
152
+ def method_missing(method, *args)
153
+ if method.to_s =~ /(\w+)\?$/
154
+ mime_type = $1.downcase.to_sym
155
+ mime_type == @symbol || (mime_type == :html && @symbol == :all)
156
+ else
157
+ super
158
+ end
159
+ end
131
160
  end
132
-
133
- ALL = Type.new "*/*", :all
134
- TEXT = Type.new "text/plain", :text
135
- HTML = Type.new "text/html", :html, %w( application/xhtml+xml )
136
- JS = Type.new "text/javascript", :js, %w( application/javascript application/x-javascript )
137
- ICS = Type.new "text/calendar", :ics
138
- CSV = Type.new "text/csv", :csv
139
- XML = Type.new "application/xml", :xml, %w( text/xml application/x-xml )
140
- RSS = Type.new "application/rss+xml", :rss
141
- ATOM = Type.new "application/atom+xml", :atom
142
- YAML = Type.new "application/x-yaml", :yaml, %w( text/yaml )
143
- JSON = Type.new "application/json", :json, %w( text/x-json )
144
-
145
- SET = [ ALL, TEXT, HTML, JS, ICS, XML, RSS, ATOM, YAML, JSON ]
146
-
147
- LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k == "" }
148
-
149
- LOOKUP["*/*"] = ALL
150
-
151
- LOOKUP["text/plain"] = TEXT
152
-
153
- LOOKUP["text/html"] = HTML
154
- LOOKUP["application/xhtml+xml"] = HTML
155
-
156
- LOOKUP["text/javascript"] = JS
157
- LOOKUP["application/javascript"] = JS
158
- LOOKUP["application/x-javascript"] = JS
159
-
160
- LOOKUP["text/calendar"] = ICS
161
-
162
- LOOKUP["text/csv"] = CSV
163
-
164
- LOOKUP["application/xml"] = XML
165
- LOOKUP["text/xml"] = XML
166
- LOOKUP["application/x-xml"] = XML
167
-
168
- LOOKUP["text/yaml"] = YAML
169
- LOOKUP["application/x-yaml"] = YAML
170
-
171
- LOOKUP["application/rss+xml"] = RSS
172
- LOOKUP["application/atom+xml"] = ATOM
173
-
174
- LOOKUP["application/json"] = JSON
175
- LOOKUP["text/x-json"] = JSON
176
-
177
-
178
- EXTENSION_LOOKUP = Hash.new { |h, k| h[k] = Type.new(k) unless k == "" }
179
-
180
- EXTENSION_LOOKUP["html"] = HTML
181
- EXTENSION_LOOKUP["xhtml"] = HTML
182
-
183
- EXTENSION_LOOKUP["txt"] = TEXT
184
-
185
- EXTENSION_LOOKUP["xml"] = XML
186
-
187
- EXTENSION_LOOKUP["js"] = JS
188
-
189
- EXTENSION_LOOKUP["ics"] = ICS
190
-
191
- EXTENSION_LOOKUP["csv"] = CSV
192
-
193
- EXTENSION_LOOKUP["yml"] = YAML
194
- EXTENSION_LOOKUP["yaml"] = YAML
195
-
196
- EXTENSION_LOOKUP["rss"] = RSS
197
- EXTENSION_LOOKUP["atom"] = ATOM
198
-
199
- EXTENSION_LOOKUP["json"] = JSON
200
161
  end
162
+
163
+ require 'action_controller/mime_types'