eactionpack 2.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. data/CHANGELOG +7 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +469 -0
  4. data/RUNNING_UNIT_TESTS +24 -0
  5. data/Rakefile +146 -0
  6. data/install.rb +30 -0
  7. data/lib/action_controller.rb +79 -0
  8. data/lib/action_controller/assertions.rb +69 -0
  9. data/lib/action_controller/assertions/dom_assertions.rb +39 -0
  10. data/lib/action_controller/assertions/model_assertions.rb +20 -0
  11. data/lib/action_controller/assertions/response_assertions.rb +172 -0
  12. data/lib/action_controller/assertions/routing_assertions.rb +146 -0
  13. data/lib/action_controller/assertions/selector_assertions.rb +491 -0
  14. data/lib/action_controller/assertions/tag_assertions.rb +130 -0
  15. data/lib/action_controller/base.rb +1288 -0
  16. data/lib/action_controller/benchmarking.rb +94 -0
  17. data/lib/action_controller/caching.rb +72 -0
  18. data/lib/action_controller/caching/actions.rb +144 -0
  19. data/lib/action_controller/caching/fragments.rb +138 -0
  20. data/lib/action_controller/caching/pages.rb +154 -0
  21. data/lib/action_controller/caching/sql_cache.rb +18 -0
  22. data/lib/action_controller/caching/sweeping.rb +97 -0
  23. data/lib/action_controller/cgi_ext.rb +16 -0
  24. data/lib/action_controller/cgi_ext/cookie.rb +110 -0
  25. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  26. data/lib/action_controller/cgi_ext/session.rb +73 -0
  27. data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
  28. data/lib/action_controller/cgi_process.rb +223 -0
  29. data/lib/action_controller/components.rb +166 -0
  30. data/lib/action_controller/cookies.rb +96 -0
  31. data/lib/action_controller/dispatcher.rb +162 -0
  32. data/lib/action_controller/filters.rb +642 -0
  33. data/lib/action_controller/flash.rb +172 -0
  34. data/lib/action_controller/headers.rb +31 -0
  35. data/lib/action_controller/helpers.rb +221 -0
  36. data/lib/action_controller/http_authentication.rb +124 -0
  37. data/lib/action_controller/integration.rb +634 -0
  38. data/lib/action_controller/layout.rb +309 -0
  39. data/lib/action_controller/mime_responds.rb +173 -0
  40. data/lib/action_controller/mime_type.rb +186 -0
  41. data/lib/action_controller/mime_types.rb +20 -0
  42. data/lib/action_controller/polymorphic_routes.rb +191 -0
  43. data/lib/action_controller/record_identifier.rb +102 -0
  44. data/lib/action_controller/request.rb +764 -0
  45. data/lib/action_controller/request_forgery_protection.rb +140 -0
  46. data/lib/action_controller/request_profiler.rb +169 -0
  47. data/lib/action_controller/rescue.rb +258 -0
  48. data/lib/action_controller/resources.rb +572 -0
  49. data/lib/action_controller/response.rb +76 -0
  50. data/lib/action_controller/routing.rb +387 -0
  51. data/lib/action_controller/routing/builder.rb +203 -0
  52. data/lib/action_controller/routing/optimisations.rb +120 -0
  53. data/lib/action_controller/routing/recognition_optimisation.rb +162 -0
  54. data/lib/action_controller/routing/route.rb +240 -0
  55. data/lib/action_controller/routing/route_set.rb +436 -0
  56. data/lib/action_controller/routing/routing_ext.rb +46 -0
  57. data/lib/action_controller/routing/segments.rb +283 -0
  58. data/lib/action_controller/session/active_record_store.rb +340 -0
  59. data/lib/action_controller/session/cookie_store.rb +166 -0
  60. data/lib/action_controller/session/drb_server.rb +32 -0
  61. data/lib/action_controller/session/drb_store.rb +35 -0
  62. data/lib/action_controller/session/mem_cache_store.rb +98 -0
  63. data/lib/action_controller/session_management.rb +158 -0
  64. data/lib/action_controller/status_codes.rb +88 -0
  65. data/lib/action_controller/streaming.rb +155 -0
  66. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  67. data/lib/action_controller/templates/rescues/_trace.erb +26 -0
  68. data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
  69. data/lib/action_controller/templates/rescues/layout.erb +29 -0
  70. data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
  71. data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
  72. data/lib/action_controller/templates/rescues/template_error.erb +21 -0
  73. data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
  74. data/lib/action_controller/test_case.rb +83 -0
  75. data/lib/action_controller/test_process.rb +526 -0
  76. data/lib/action_controller/url_rewriter.rb +142 -0
  77. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  78. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  79. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  80. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  81. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  82. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  83. data/lib/action_controller/verification.rb +130 -0
  84. data/lib/action_pack.rb +24 -0
  85. data/lib/action_pack/version.rb +9 -0
  86. data/lib/action_view.rb +44 -0
  87. data/lib/action_view/base.rb +335 -0
  88. data/lib/action_view/helpers/active_record_helper.rb +276 -0
  89. data/lib/action_view/helpers/asset_tag_helper.rb +599 -0
  90. data/lib/action_view/helpers/atom_feed_helper.rb +143 -0
  91. data/lib/action_view/helpers/benchmark_helper.rb +33 -0
  92. data/lib/action_view/helpers/cache_helper.rb +40 -0
  93. data/lib/action_view/helpers/capture_helper.rb +161 -0
  94. data/lib/action_view/helpers/date_helper.rb +711 -0
  95. data/lib/action_view/helpers/debug_helper.rb +31 -0
  96. data/lib/action_view/helpers/form_helper.rb +767 -0
  97. data/lib/action_view/helpers/form_options_helper.rb +458 -0
  98. data/lib/action_view/helpers/form_tag_helper.rb +458 -0
  99. data/lib/action_view/helpers/javascript_helper.rb +148 -0
  100. data/lib/action_view/helpers/number_helper.rb +186 -0
  101. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  102. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  103. data/lib/action_view/helpers/sanitize_helper.rb +229 -0
  104. data/lib/action_view/helpers/tag_helper.rb +134 -0
  105. data/lib/action_view/helpers/text_helper.rb +507 -0
  106. data/lib/action_view/helpers/url_helper.rb +573 -0
  107. data/lib/action_view/inline_template.rb +20 -0
  108. data/lib/action_view/partial_template.rb +70 -0
  109. data/lib/action_view/partials.rb +158 -0
  110. data/lib/action_view/template.rb +125 -0
  111. data/lib/action_view/template_error.rb +110 -0
  112. data/lib/action_view/template_finder.rb +176 -0
  113. data/lib/action_view/template_handler.rb +34 -0
  114. data/lib/action_view/template_handlers/builder.rb +27 -0
  115. data/lib/action_view/template_handlers/compilable.rb +128 -0
  116. data/lib/action_view/template_handlers/erb.rb +56 -0
  117. data/lib/action_view/test_case.rb +58 -0
  118. data/lib/actionpack.rb +1 -0
  119. data/test/abstract_unit.rb +36 -0
  120. data/test/active_record_unit.rb +105 -0
  121. data/test/activerecord/active_record_store_test.rb +141 -0
  122. data/test/activerecord/render_partial_with_record_identification_test.rb +191 -0
  123. data/test/adv_attr_test.rb +20 -0
  124. data/test/controller/action_pack_assertions_test.rb +543 -0
  125. data/test/controller/addresses_render_test.rb +43 -0
  126. data/test/controller/assert_select_test.rb +331 -0
  127. data/test/controller/base_test.rb +219 -0
  128. data/test/controller/benchmark_test.rb +32 -0
  129. data/test/controller/caching_test.rb +581 -0
  130. data/test/controller/capture_test.rb +89 -0
  131. data/test/controller/cgi_test.rb +116 -0
  132. data/test/controller/components_test.rb +140 -0
  133. data/test/controller/content_type_test.rb +139 -0
  134. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  135. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  136. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  137. data/test/controller/cookie_test.rb +146 -0
  138. data/test/controller/custom_handler_test.rb +45 -0
  139. data/test/controller/deprecation/deprecated_base_methods_test.rb +37 -0
  140. data/test/controller/dispatcher_test.rb +105 -0
  141. data/test/controller/fake_controllers.rb +33 -0
  142. data/test/controller/fake_models.rb +11 -0
  143. data/test/controller/filter_params_test.rb +49 -0
  144. data/test/controller/filters_test.rb +881 -0
  145. data/test/controller/flash_test.rb +146 -0
  146. data/test/controller/header_test.rb +14 -0
  147. data/test/controller/helper_test.rb +210 -0
  148. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  149. data/test/controller/html-scanner/document_test.rb +148 -0
  150. data/test/controller/html-scanner/node_test.rb +89 -0
  151. data/test/controller/html-scanner/sanitizer_test.rb +269 -0
  152. data/test/controller/html-scanner/tag_node_test.rb +238 -0
  153. data/test/controller/html-scanner/text_node_test.rb +50 -0
  154. data/test/controller/html-scanner/tokenizer_test.rb +131 -0
  155. data/test/controller/http_authentication_test.rb +54 -0
  156. data/test/controller/integration_test.rb +252 -0
  157. data/test/controller/integration_upload_test.rb +43 -0
  158. data/test/controller/layout_test.rb +255 -0
  159. data/test/controller/mime_responds_test.rb +514 -0
  160. data/test/controller/mime_type_test.rb +84 -0
  161. data/test/controller/new_render_test.rb +843 -0
  162. data/test/controller/polymorphic_routes_test.rb +174 -0
  163. data/test/controller/record_identifier_test.rb +139 -0
  164. data/test/controller/redirect_test.rb +289 -0
  165. data/test/controller/render_test.rb +484 -0
  166. data/test/controller/request_forgery_protection_test.rb +305 -0
  167. data/test/controller/request_test.rb +928 -0
  168. data/test/controller/rescue_test.rb +517 -0
  169. data/test/controller/resources_test.rb +873 -0
  170. data/test/controller/routing_test.rb +2464 -0
  171. data/test/controller/selector_test.rb +628 -0
  172. data/test/controller/send_file_test.rb +138 -0
  173. data/test/controller/session/cookie_store_test.rb +258 -0
  174. data/test/controller/session/mem_cache_store_test.rb +181 -0
  175. data/test/controller/session_fixation_test.rb +89 -0
  176. data/test/controller/session_management_test.rb +178 -0
  177. data/test/controller/test_test.rb +695 -0
  178. data/test/controller/url_rewriter_test.rb +310 -0
  179. data/test/controller/verification_test.rb +270 -0
  180. data/test/controller/view_paths_test.rb +140 -0
  181. data/test/controller/webservice_test.rb +229 -0
  182. data/test/fixtures/addresses/list.erb +1 -0
  183. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  184. data/test/fixtures/companies.yml +24 -0
  185. data/test/fixtures/company.rb +10 -0
  186. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  187. data/test/fixtures/content_type/render_default_for_js.js.erb +1 -0
  188. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  189. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  190. data/test/fixtures/customers/_customer.html.erb +1 -0
  191. data/test/fixtures/db_definitions/sqlite.sql +49 -0
  192. data/test/fixtures/developer.rb +9 -0
  193. data/test/fixtures/developers.yml +21 -0
  194. data/test/fixtures/developers_projects.yml +13 -0
  195. data/test/fixtures/fun/games/hello_world.erb +1 -0
  196. data/test/fixtures/functional_caching/_partial.erb +3 -0
  197. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  198. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  199. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  200. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  201. data/test/fixtures/helpers/abc_helper.rb +5 -0
  202. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  203. data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
  204. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  205. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  206. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  207. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  208. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  209. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  210. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  211. data/test/fixtures/layouts/block_with_layout.erb +3 -0
  212. data/test/fixtures/layouts/builder.builder +3 -0
  213. data/test/fixtures/layouts/partial_with_layout.erb +3 -0
  214. data/test/fixtures/layouts/standard.erb +1 -0
  215. data/test/fixtures/layouts/talk_from_action.erb +2 -0
  216. data/test/fixtures/layouts/yield.erb +2 -0
  217. data/test/fixtures/mascot.rb +3 -0
  218. data/test/fixtures/mascots.yml +4 -0
  219. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  220. data/test/fixtures/multipart/binary_file +0 -0
  221. data/test/fixtures/multipart/boundary_problem_file +10 -0
  222. data/test/fixtures/multipart/bracketed_param +5 -0
  223. data/test/fixtures/multipart/large_text_file +10 -0
  224. data/test/fixtures/multipart/mixed_files +0 -0
  225. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  226. data/test/fixtures/multipart/single_parameter +5 -0
  227. data/test/fixtures/multipart/text_file +10 -0
  228. data/test/fixtures/override/test/hello_world.erb +1 -0
  229. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  230. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  231. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  232. data/test/fixtures/post_test/post/index.html.erb +1 -0
  233. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  234. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  235. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  236. data/test/fixtures/project.rb +3 -0
  237. data/test/fixtures/projects.yml +7 -0
  238. data/test/fixtures/public/404.html +1 -0
  239. data/test/fixtures/public/500.html +1 -0
  240. data/test/fixtures/public/images/rails.png +0 -0
  241. data/test/fixtures/public/javascripts/application.js +1 -0
  242. data/test/fixtures/public/javascripts/bank.js +1 -0
  243. data/test/fixtures/public/javascripts/robber.js +1 -0
  244. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  245. data/test/fixtures/public/stylesheets/bank.css +1 -0
  246. data/test/fixtures/public/stylesheets/robber.css +1 -0
  247. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  248. data/test/fixtures/replies.yml +15 -0
  249. data/test/fixtures/reply.rb +7 -0
  250. data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
  251. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  252. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  253. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  254. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  255. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  256. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  257. data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
  258. data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
  259. data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
  260. data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
  261. data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
  262. data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
  263. data/test/fixtures/scope/test/modgreet.erb +1 -0
  264. data/test/fixtures/shared.html.erb +1 -0
  265. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  266. data/test/fixtures/test/_customer.erb +1 -0
  267. data/test/fixtures/test/_customer_counter.erb +1 -0
  268. data/test/fixtures/test/_customer_greeting.erb +1 -0
  269. data/test/fixtures/test/_form.erb +1 -0
  270. data/test/fixtures/test/_hash_greeting.erb +1 -0
  271. data/test/fixtures/test/_hash_object.erb +2 -0
  272. data/test/fixtures/test/_hello.builder +1 -0
  273. data/test/fixtures/test/_labelling_form.erb +1 -0
  274. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  275. data/test/fixtures/test/_partial.erb +1 -0
  276. data/test/fixtures/test/_partial.html.erb +1 -0
  277. data/test/fixtures/test/_partial.js.erb +1 -0
  278. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  279. data/test/fixtures/test/_partial_only.erb +1 -0
  280. data/test/fixtures/test/_person.erb +2 -0
  281. data/test/fixtures/test/_raise.html.erb +1 -0
  282. data/test/fixtures/test/action_talk_to_layout.erb +2 -0
  283. data/test/fixtures/test/block_content_for.erb +2 -0
  284. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  285. data/test/fixtures/test/capturing.erb +4 -0
  286. data/test/fixtures/test/content_for.erb +2 -0
  287. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  288. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  289. data/test/fixtures/test/delete_with_js.rjs +2 -0
  290. data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
  291. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  292. data/test/fixtures/test/erb_content_for.erb +2 -0
  293. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  294. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  295. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  296. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  297. data/test/fixtures/test/greeting.erb +1 -0
  298. data/test/fixtures/test/greeting.js.rjs +1 -0
  299. data/test/fixtures/test/hello.builder +4 -0
  300. data/test/fixtures/test/hello_world.erb +1 -0
  301. data/test/fixtures/test/hello_world_container.builder +3 -0
  302. data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
  303. data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
  304. data/test/fixtures/test/hello_xml_world.builder +11 -0
  305. data/test/fixtures/test/list.erb +1 -0
  306. data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
  307. data/test/fixtures/test/potential_conflicts.erb +4 -0
  308. data/test/fixtures/test/render_file_from_template.html.erb +1 -0
  309. data/test/fixtures/test/render_file_with_ivar.erb +1 -0
  310. data/test/fixtures/test/render_file_with_locals.erb +1 -0
  311. data/test/fixtures/test/render_to_string_test.erb +1 -0
  312. data/test/fixtures/test/update_element_with_capture.erb +9 -0
  313. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  314. data/test/fixtures/topic.rb +3 -0
  315. data/test/fixtures/topics.yml +22 -0
  316. data/test/fixtures/topics/_topic.html.erb +1 -0
  317. data/test/template/active_record_helper_test.rb +268 -0
  318. data/test/template/asset_tag_helper_test.rb +514 -0
  319. data/test/template/atom_feed_helper_test.rb +179 -0
  320. data/test/template/benchmark_helper_test.rb +60 -0
  321. data/test/template/date_helper_test.rb +1791 -0
  322. data/test/template/deprecated_erb_variable_test.rb +9 -0
  323. data/test/template/erb_util_test.rb +24 -0
  324. data/test/template/form_helper_test.rb +885 -0
  325. data/test/template/form_options_helper_test.rb +1333 -0
  326. data/test/template/form_tag_helper_test.rb +272 -0
  327. data/test/template/javascript_helper_test.rb +73 -0
  328. data/test/template/number_helper_test.rb +97 -0
  329. data/test/template/record_tag_helper_test.rb +54 -0
  330. data/test/template/sanitize_helper_test.rb +48 -0
  331. data/test/template/tag_helper_test.rb +77 -0
  332. data/test/template/template_finder_test.rb +73 -0
  333. data/test/template/template_object_test.rb +95 -0
  334. data/test/template/test_test.rb +56 -0
  335. data/test/template/text_helper_test.rb +367 -0
  336. data/test/template/url_helper_test.rb +544 -0
  337. data/test/testing_sandbox.rb +15 -0
  338. metadata +469 -0
@@ -0,0 +1,309 @@
1
+ module ActionController #:nodoc:
2
+ module Layout #:nodoc:
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+ base.class_eval do
6
+ # NOTE: Can't use alias_method_chain here because +render_without_layout+ is already
7
+ # defined as a publicly exposed method
8
+ alias_method :render_with_no_layout, :render
9
+ alias_method :render, :render_with_a_layout
10
+
11
+ class << self
12
+ alias_method_chain :inherited, :layout
13
+ end
14
+ end
15
+ end
16
+
17
+ # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
18
+ # repeated setups. The inclusion pattern has pages that look like this:
19
+ #
20
+ # <%= render "shared/header" %>
21
+ # Hello World
22
+ # <%= render "shared/footer" %>
23
+ #
24
+ # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
25
+ # and if you ever want to change the structure of these two includes, you'll have to change all the templates.
26
+ #
27
+ # With layouts, you can flip it around and have the common structure know where to insert changing content. This means
28
+ # that the header and footer are only mentioned in one place, like this:
29
+ #
30
+ # // The header part of this layout
31
+ # <%= yield %>
32
+ # // The footer part of this layout
33
+ #
34
+ # And then you have content pages that look like this:
35
+ #
36
+ # hello world
37
+ #
38
+ # At rendering time, the content page is computed and then inserted in the layout, like this:
39
+ #
40
+ # // The header part of this layout
41
+ # hello world
42
+ # // The footer part of this layout
43
+ #
44
+ # NOTE: The old notation for rendering the view from a layout was to expose the magic <tt>@content_for_layout</tt> instance
45
+ # variable. The preferred notation now is to use <tt>yield</tt>, as documented above.
46
+ #
47
+ # == Accessing shared variables
48
+ #
49
+ # Layouts have access to variables specified in the content pages and vice versa. This allows you to have layouts with
50
+ # references that won't materialize before rendering time:
51
+ #
52
+ # <h1><%= @page_title %></h1>
53
+ # <%= yield %>
54
+ #
55
+ # ...and content pages that fulfill these references _at_ rendering time:
56
+ #
57
+ # <% @page_title = "Welcome" %>
58
+ # Off-world colonies offers you a chance to start a new life
59
+ #
60
+ # The result after rendering is:
61
+ #
62
+ # <h1>Welcome</h1>
63
+ # Off-world colonies offers you a chance to start a new life
64
+ #
65
+ # == Automatic layout assignment
66
+ #
67
+ # If there is a template in <tt>app/views/layouts/</tt> with the same name as the current controller then it will be automatically
68
+ # set as that controller's layout unless explicitly told otherwise. Say you have a WeblogController, for example. If a template named
69
+ # <tt>app/views/layouts/weblog.erb</tt> or <tt>app/views/layouts/weblog.builder</tt> exists then it will be automatically set as
70
+ # the layout for your WeblogController. You can create a layout with the name <tt>application.erb</tt> or <tt>application.builder</tt>
71
+ # 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
72
+ # no layout explicitly assigned with the +layout+ method. Nested controllers use the same folder structure for automatic layout.
73
+ # assignment. So an Admin::WeblogController will look for a template named <tt>app/views/layouts/admin/weblog.erb</tt>.
74
+ # Setting a layout explicitly will always override the automatic behaviour for the controller where the layout is set.
75
+ # Explicitly setting the layout in a parent class, though, will not override the child class's layout assignment if the child
76
+ # class has a layout with the same name.
77
+ #
78
+ # == Inheritance for layouts
79
+ #
80
+ # Layouts are shared downwards in the inheritance hierarchy, but not upwards. Examples:
81
+ #
82
+ # class BankController < ActionController::Base
83
+ # layout "bank_standard"
84
+ #
85
+ # class InformationController < BankController
86
+ #
87
+ # class VaultController < BankController
88
+ # layout :access_level_layout
89
+ #
90
+ # class EmployeeController < BankController
91
+ # layout nil
92
+ #
93
+ # The InformationController uses "bank_standard" inherited from the BankController, the VaultController overwrites
94
+ # and picks the layout dynamically, and the EmployeeController doesn't want to use a layout at all.
95
+ #
96
+ # == Types of layouts
97
+ #
98
+ # Layouts are basically just regular templates, but the name of this template needs not be specified statically. Sometimes
99
+ # you want to alternate layouts depending on runtime information, such as whether someone is logged in or not. This can
100
+ # be done either by specifying a method reference as a symbol or using an inline method (as a proc).
101
+ #
102
+ # The method reference is the preferred approach to variable layouts and is used like this:
103
+ #
104
+ # class WeblogController < ActionController::Base
105
+ # layout :writers_and_readers
106
+ #
107
+ # def index
108
+ # # fetching posts
109
+ # end
110
+ #
111
+ # private
112
+ # def writers_and_readers
113
+ # logged_in? ? "writer_layout" : "reader_layout"
114
+ # end
115
+ #
116
+ # Now when a new request for the index action is processed, the layout will vary depending on whether the person accessing
117
+ # is logged in or not.
118
+ #
119
+ # If you want to use an inline method, such as a proc, do something like this:
120
+ #
121
+ # class WeblogController < ActionController::Base
122
+ # layout proc{ |controller| controller.logged_in? ? "writer_layout" : "reader_layout" }
123
+ #
124
+ # Of course, the most common way of specifying a layout is still just as a plain template name:
125
+ #
126
+ # class WeblogController < ActionController::Base
127
+ # layout "weblog_standard"
128
+ #
129
+ # If no directory is specified for the template name, the template will by default be looked for in <tt>app/views/layouts/</tt>.
130
+ # Otherwise, it will be looked up relative to the template root.
131
+ #
132
+ # == Conditional layouts
133
+ #
134
+ # If you have a layout that by default is applied to all the actions of a controller, you still have the option of rendering
135
+ # a given action or set of actions without a layout, or restricting a layout to only a single action or a set of actions. The
136
+ # <tt>:only</tt> and <tt>:except</tt> options can be passed to the layout call. For example:
137
+ #
138
+ # class WeblogController < ActionController::Base
139
+ # layout "weblog_standard", :except => :rss
140
+ #
141
+ # # ...
142
+ #
143
+ # end
144
+ #
145
+ # This will assign "weblog_standard" as the WeblogController's layout except for the +rss+ action, which will not wrap a layout
146
+ # around the rendered view.
147
+ #
148
+ # Both the <tt>:only</tt> and <tt>:except</tt> condition can accept an arbitrary number of method references, so
149
+ # #<tt>:except => [ :rss, :text_only ]</tt> is valid, as is <tt>:except => :rss</tt>.
150
+ #
151
+ # == Using a different layout in the action render call
152
+ #
153
+ # If most of your actions use the same layout, it makes perfect sense to define a controller-wide layout as described above.
154
+ # Sometimes you'll have exceptions where one action wants to use a different layout than the rest of the controller.
155
+ # You can do this by passing a <tt>:layout</tt> option to the <tt>render</tt> call. For example:
156
+ #
157
+ # class WeblogController < ActionController::Base
158
+ # layout "weblog_standard"
159
+ #
160
+ # def help
161
+ # render :action => "help", :layout => "help"
162
+ # end
163
+ # end
164
+ #
165
+ # This will render the help action with the "help" layout instead of the controller-wide "weblog_standard" layout.
166
+ module ClassMethods
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
169
+ # performance and have access to them as any normal template would.
170
+ def layout(template_name, conditions = {}, auto = false)
171
+ add_layout_conditions(conditions)
172
+ write_inheritable_attribute "layout", template_name
173
+ write_inheritable_attribute "auto_layout", auto
174
+ end
175
+
176
+ def layout_conditions #:nodoc:
177
+ @layout_conditions ||= read_inheritable_attribute("layout_conditions")
178
+ end
179
+
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]
186
+ end
187
+
188
+ def layout_list #:nodoc:
189
+ Array(view_paths).sum([]) { |path| Dir["#{path}/layouts/**/*"] }
190
+ end
191
+
192
+ private
193
+ def inherited_with_layout(child)
194
+ inherited_without_layout(child)
195
+ unless child.name.blank?
196
+ layout_match = child.name.underscore.sub(/_controller$/, '').sub(/^controllers\//, '')
197
+ child.layout(layout_match, {}, true) unless child.layout_list.grep(%r{layouts/#{layout_match}(\.[a-z][0-9a-z]*)+$}).empty?
198
+ end
199
+ end
200
+
201
+ def add_layout_conditions(conditions)
202
+ write_inheritable_hash "layout_conditions", normalize_conditions(conditions)
203
+ end
204
+
205
+ def normalize_conditions(conditions)
206
+ conditions.inject({}) {|hash, (key, value)| hash.merge(key => [value].flatten.map {|action| action.to_s})}
207
+ end
208
+
209
+ def default_layout_with_format(format, layout)
210
+ list = layout_list
211
+ if list.grep(%r{layouts/#{layout}\.#{format}(\.[a-z][0-9a-z]*)+$}).empty?
212
+ (!list.grep(%r{layouts/#{layout}\.([a-z][0-9a-z]*)+$}).empty? && format == :html) ? layout : nil
213
+ else
214
+ layout
215
+ end
216
+ end
217
+ end
218
+
219
+ # Returns the name of the active layout. If the layout was specified as a method reference (through a symbol), this method
220
+ # is called and the return value is used. Likewise if the layout was specified as an inline method (through a proc or method
221
+ # object). If the layout was defined without a directory, layouts is assumed. So <tt>layout "weblog/standard"</tt> will return
222
+ # weblog/standard, but <tt>layout "standard"</tt> will return layouts/standard.
223
+ def active_layout(passed_layout = nil)
224
+ layout = passed_layout || self.class.default_layout(response.template.template_format)
225
+ active_layout = case layout
226
+ when String then layout
227
+ when Symbol then send!(layout)
228
+ when Proc then layout.call(self)
229
+ end
230
+
231
+ # Explicitly passed layout names with slashes are looked up relative to the template root,
232
+ # but auto-discovered layouts derived from a nested controller will contain a slash, though be relative
233
+ # to the 'layouts' directory so we have to check the file system to infer which case the layout name came from.
234
+ if active_layout
235
+ if active_layout.include?('/') && ! layout_directory?(active_layout)
236
+ active_layout
237
+ else
238
+ "layouts/#{active_layout}"
239
+ end
240
+ end
241
+ end
242
+
243
+ protected
244
+ def render_with_a_layout(options = nil, extra_options = {}, &block) #:nodoc:
245
+ template_with_options = options.is_a?(Hash)
246
+
247
+ if (layout = pick_layout(template_with_options, options)) && apply_layout?(template_with_options, options)
248
+ options = options.merge :layout => false if template_with_options
249
+ logger.info("Rendering template within #{layout}") if logger
250
+
251
+ content_for_layout = render_with_no_layout(options, extra_options, &block)
252
+ erase_render_results
253
+ add_variables_to_assigns
254
+ @template.instance_variable_set("@content_for_layout", content_for_layout)
255
+ response.layout = layout
256
+ status = template_with_options ? options[:status] : nil
257
+ render_for_text(@template.render_file(layout, true), status)
258
+ else
259
+ render_with_no_layout(options, extra_options, &block)
260
+ end
261
+ end
262
+
263
+
264
+ private
265
+ def apply_layout?(template_with_options, options)
266
+ template_with_options ? candidate_for_layout?(options) : !template_exempt_from_layout?
267
+ end
268
+
269
+ def candidate_for_layout?(options)
270
+ (options.has_key?(:layout) && options[:layout] != false) ||
271
+ options.values_at(:text, :xml, :json, :js, :file, :inline, :partial, :nothing).compact.empty? &&
272
+ !template_exempt_from_layout?(options[:template] || default_template_name(options[:action]))
273
+ end
274
+
275
+ def pick_layout(template_with_options, options)
276
+ if template_with_options
277
+ case layout = options[:layout]
278
+ when FalseClass
279
+ nil
280
+ when NilClass, TrueClass
281
+ active_layout if action_has_layout?
282
+ else
283
+ active_layout(layout)
284
+ end
285
+ else
286
+ active_layout if action_has_layout?
287
+ end
288
+ end
289
+
290
+ def action_has_layout?
291
+ if conditions = self.class.layout_conditions
292
+ case
293
+ when only = conditions[:only]
294
+ only.include?(action_name)
295
+ when except = conditions[:except]
296
+ !except.include?(action_name)
297
+ else
298
+ true
299
+ end
300
+ else
301
+ true
302
+ end
303
+ end
304
+
305
+ def layout_directory?(layout_name)
306
+ @template.finder.find_template_extension_from_handler(File.join('layouts', layout_name))
307
+ end
308
+ end
309
+ end
@@ -0,0 +1,173 @@
1
+ module ActionController #:nodoc:
2
+ module MimeResponds #:nodoc:
3
+ def self.included(base)
4
+ base.module_eval do
5
+ include ActionController::MimeResponds::InstanceMethods
6
+ end
7
+ end
8
+
9
+ module InstanceMethods
10
+ # Without web-service support, an action which collects the data for displaying a list of people
11
+ # might look something like this:
12
+ #
13
+ # def index
14
+ # @people = Person.find(:all)
15
+ # end
16
+ #
17
+ # Here's the same action, with web-service support baked in:
18
+ #
19
+ # def index
20
+ # @people = Person.find(:all)
21
+ #
22
+ # respond_to do |format|
23
+ # format.html
24
+ # format.xml { render :xml => @people.to_xml }
25
+ # end
26
+ # end
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."
30
+ # (Rails determines the desired response format from the HTTP Accept header submitted by the client.)
31
+ #
32
+ # Supposing you have an action that adds a new person, optionally creating their company
33
+ # (by name) if it does not already exist, without web-services, it might look like this:
34
+ #
35
+ # def create
36
+ # @company = Company.find_or_create_by_name(params[:company][:name])
37
+ # @person = @company.people.create(params[:person])
38
+ #
39
+ # redirect_to(person_list_url)
40
+ # end
41
+ #
42
+ # Here's the same action, with web-service support baked in:
43
+ #
44
+ # def create
45
+ # company = params[:person].delete(:company)
46
+ # @company = Company.find_or_create_by_name(company[:name])
47
+ # @person = @company.people.create(params[:person])
48
+ #
49
+ # respond_to do |format|
50
+ # format.html { redirect_to(person_list_url) }
51
+ # format.js
52
+ # format.xml { render :xml => @person.to_xml(:include => @company) }
53
+ # end
54
+ # end
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 JS request and we render the js.erb 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
+ #
61
+ # <person>
62
+ # <id>...</id>
63
+ # ...
64
+ # <company>
65
+ # <id>...</id>
66
+ # <name>...</name>
67
+ # ...
68
+ # </company>
69
+ # </person>
70
+ #
71
+ # Note, however, the extra bit at the top of that action:
72
+ #
73
+ # company = params[:person].delete(:company)
74
+ # @company = Company.find_or_create_by_name(company[:name])
75
+ #
76
+ # This is because the incoming XML document (if a web-service request is in process) can only contain a
77
+ # single root-node. So, we have to rearrange things so that the request looks like this (url-encoded):
78
+ #
79
+ # person[name]=...&person[company][name]=...&...
80
+ #
81
+ # And, like this (xml-encoded):
82
+ #
83
+ # <person>
84
+ # <name>...</name>
85
+ # <company>
86
+ # <name>...</name>
87
+ # </company>
88
+ # </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
92
+ # with the remaining data.
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 node), but if you just go with the flow
96
+ # and accept Rails' defaults, life will be much easier.
97
+ #
98
+ # If you need to use a MIME type which isn't supported by default, you can register your own handlers in
99
+ # environment.rb as follows.
100
+ #
101
+ # Mime::Type.register "image/jpg", :jpg
102
+ def respond_to(*types, &block)
103
+ raise ArgumentError, "respond_to takes either types or a block, never both" unless types.any? ^ block
104
+ block ||= lambda { |responder| types.each { |type| responder.send(type) } }
105
+ responder = Responder.new(self)
106
+ block.call(responder)
107
+ responder.respond
108
+ end
109
+ end
110
+
111
+ class Responder #:nodoc:
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)
118
+
119
+ @order = []
120
+ @responses = {}
121
+ end
122
+
123
+ def custom(mime_type, &block)
124
+ mime_type = mime_type.is_a?(Mime::Type) ? mime_type : Mime::Type.lookup(mime_type.to_s)
125
+
126
+ @order << mime_type
127
+ @responses[mime_type] ||= Proc.new do
128
+ @response.template.template_format = mime_type.to_sym
129
+ @response.content_type = mime_type.to_s
130
+ block_given? ? block.call : @controller.send(:render, :action => @controller.action_name)
131
+ end
132
+ end
133
+
134
+ def any(*args, &block)
135
+ if args.any?
136
+ args.each { |type| send(type, &block) }
137
+ else
138
+ custom(@mime_type_priority.first, &block)
139
+ end
140
+ end
141
+
142
+ def method_missing(symbol, &block)
143
+ mime_constant = symbol.to_s.upcase
144
+
145
+ if Mime::SET.include?(Mime.const_get(mime_constant))
146
+ custom(Mime.const_get(mime_constant), &block)
147
+ else
148
+ super
149
+ end
150
+ end
151
+
152
+ def respond
153
+ for priority in @mime_type_priority
154
+ if priority == Mime::ALL
155
+ @responses[@order.first].call
156
+ return
157
+ else
158
+ if @responses[priority]
159
+ @responses[priority].call
160
+ return # mime type match found, be happy and return
161
+ end
162
+ end
163
+ end
164
+
165
+ if @order.include?(Mime::ALL)
166
+ @responses[Mime::ALL].call
167
+ else
168
+ @controller.send :head, :not_acceptable
169
+ end
170
+ end
171
+ end
172
+ end
173
+ end