actionpack-2.3.17-rack-upgrade 2.3.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (441) hide show
  1. data/CHANGELOG +5250 -0
  2. data/MIT-LICENSE +21 -0
  3. data/README +409 -0
  4. data/RUNNING_UNIT_TESTS +24 -0
  5. data/Rakefile +158 -0
  6. data/install.rb +30 -0
  7. data/lib/action_controller.rb +113 -0
  8. data/lib/action_controller/assertions/dom_assertions.rb +55 -0
  9. data/lib/action_controller/assertions/model_assertions.rb +21 -0
  10. data/lib/action_controller/assertions/response_assertions.rb +169 -0
  11. data/lib/action_controller/assertions/routing_assertions.rb +146 -0
  12. data/lib/action_controller/assertions/selector_assertions.rb +638 -0
  13. data/lib/action_controller/assertions/tag_assertions.rb +127 -0
  14. data/lib/action_controller/base.rb +1425 -0
  15. data/lib/action_controller/benchmarking.rb +107 -0
  16. data/lib/action_controller/caching.rb +71 -0
  17. data/lib/action_controller/caching/actions.rb +177 -0
  18. data/lib/action_controller/caching/fragments.rb +120 -0
  19. data/lib/action_controller/caching/pages.rb +152 -0
  20. data/lib/action_controller/caching/sweeper.rb +45 -0
  21. data/lib/action_controller/caching/sweeping.rb +55 -0
  22. data/lib/action_controller/cgi_ext.rb +15 -0
  23. data/lib/action_controller/cgi_ext/cookie.rb +112 -0
  24. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  25. data/lib/action_controller/cgi_ext/stdinput.rb +24 -0
  26. data/lib/action_controller/cgi_process.rb +77 -0
  27. data/lib/action_controller/cookies.rb +197 -0
  28. data/lib/action_controller/dispatcher.rb +133 -0
  29. data/lib/action_controller/failsafe.rb +87 -0
  30. data/lib/action_controller/filters.rb +680 -0
  31. data/lib/action_controller/flash.rb +213 -0
  32. data/lib/action_controller/headers.rb +33 -0
  33. data/lib/action_controller/helpers.rb +225 -0
  34. data/lib/action_controller/http_authentication.rb +309 -0
  35. data/lib/action_controller/integration.rb +708 -0
  36. data/lib/action_controller/layout.rb +286 -0
  37. data/lib/action_controller/middleware_stack.rb +119 -0
  38. data/lib/action_controller/middlewares.rb +14 -0
  39. data/lib/action_controller/mime_responds.rb +193 -0
  40. data/lib/action_controller/mime_type.rb +212 -0
  41. data/lib/action_controller/mime_types.rb +21 -0
  42. data/lib/action_controller/params_parser.rb +77 -0
  43. data/lib/action_controller/performance_test.rb +15 -0
  44. data/lib/action_controller/polymorphic_routes.rb +189 -0
  45. data/lib/action_controller/rack_lint_patch.rb +36 -0
  46. data/lib/action_controller/record_identifier.rb +104 -0
  47. data/lib/action_controller/reloader.rb +54 -0
  48. data/lib/action_controller/request.rb +518 -0
  49. data/lib/action_controller/request_forgery_protection.rb +116 -0
  50. data/lib/action_controller/rescue.rb +183 -0
  51. data/lib/action_controller/resources.rb +682 -0
  52. data/lib/action_controller/response.rb +237 -0
  53. data/lib/action_controller/routing.rb +388 -0
  54. data/lib/action_controller/routing/builder.rb +197 -0
  55. data/lib/action_controller/routing/optimisations.rb +130 -0
  56. data/lib/action_controller/routing/recognition_optimisation.rb +167 -0
  57. data/lib/action_controller/routing/route.rb +265 -0
  58. data/lib/action_controller/routing/route_set.rb +503 -0
  59. data/lib/action_controller/routing/routing_ext.rb +49 -0
  60. data/lib/action_controller/routing/segments.rb +343 -0
  61. data/lib/action_controller/session/abstract_store.rb +276 -0
  62. data/lib/action_controller/session/cookie_store.rb +240 -0
  63. data/lib/action_controller/session/mem_cache_store.rb +60 -0
  64. data/lib/action_controller/session_management.rb +54 -0
  65. data/lib/action_controller/status_codes.rb +88 -0
  66. data/lib/action_controller/streaming.rb +181 -0
  67. data/lib/action_controller/string_coercion.rb +29 -0
  68. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  69. data/lib/action_controller/templates/rescues/_trace.erb +26 -0
  70. data/lib/action_controller/templates/rescues/diagnostics.erb +11 -0
  71. data/lib/action_controller/templates/rescues/layout.erb +29 -0
  72. data/lib/action_controller/templates/rescues/missing_template.erb +2 -0
  73. data/lib/action_controller/templates/rescues/routing_error.erb +10 -0
  74. data/lib/action_controller/templates/rescues/template_error.erb +21 -0
  75. data/lib/action_controller/templates/rescues/unknown_action.erb +2 -0
  76. data/lib/action_controller/test_case.rb +209 -0
  77. data/lib/action_controller/test_process.rb +580 -0
  78. data/lib/action_controller/translation.rb +13 -0
  79. data/lib/action_controller/uploaded_file.rb +44 -0
  80. data/lib/action_controller/url_rewriter.rb +229 -0
  81. data/lib/action_controller/vendor/html-scanner.rb +16 -0
  82. data/lib/action_controller/vendor/html-scanner/html/document.rb +68 -0
  83. data/lib/action_controller/vendor/html-scanner/html/node.rb +537 -0
  84. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  85. data/lib/action_controller/vendor/html-scanner/html/selector.rb +828 -0
  86. data/lib/action_controller/vendor/html-scanner/html/tokenizer.rb +105 -0
  87. data/lib/action_controller/vendor/html-scanner/html/version.rb +11 -0
  88. data/lib/action_controller/verification.rb +130 -0
  89. data/lib/action_pack.rb +24 -0
  90. data/lib/action_pack/version.rb +9 -0
  91. data/lib/action_view.rb +58 -0
  92. data/lib/action_view/base.rb +362 -0
  93. data/lib/action_view/helpers.rb +61 -0
  94. data/lib/action_view/helpers/active_record_helper.rb +305 -0
  95. data/lib/action_view/helpers/asset_tag_helper.rb +695 -0
  96. data/lib/action_view/helpers/atom_feed_helper.rb +198 -0
  97. data/lib/action_view/helpers/benchmark_helper.rb +54 -0
  98. data/lib/action_view/helpers/cache_helper.rb +39 -0
  99. data/lib/action_view/helpers/capture_helper.rb +136 -0
  100. data/lib/action_view/helpers/csrf_helper.rb +14 -0
  101. data/lib/action_view/helpers/date_helper.rb +989 -0
  102. data/lib/action_view/helpers/debug_helper.rb +38 -0
  103. data/lib/action_view/helpers/form_helper.rb +1118 -0
  104. data/lib/action_view/helpers/form_options_helper.rb +599 -0
  105. data/lib/action_view/helpers/form_tag_helper.rb +490 -0
  106. data/lib/action_view/helpers/javascript_helper.rb +208 -0
  107. data/lib/action_view/helpers/number_helper.rb +308 -0
  108. data/lib/action_view/helpers/prototype_helper.rb +1305 -0
  109. data/lib/action_view/helpers/raw_output_helper.rb +9 -0
  110. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  111. data/lib/action_view/helpers/record_tag_helper.rb +58 -0
  112. data/lib/action_view/helpers/sanitize_helper.rb +251 -0
  113. data/lib/action_view/helpers/scriptaculous_helper.rb +226 -0
  114. data/lib/action_view/helpers/tag_helper.rb +151 -0
  115. data/lib/action_view/helpers/text_helper.rb +597 -0
  116. data/lib/action_view/helpers/translation_helper.rb +67 -0
  117. data/lib/action_view/helpers/url_helper.rb +637 -0
  118. data/lib/action_view/inline_template.rb +19 -0
  119. data/lib/action_view/locale/en.yml +117 -0
  120. data/lib/action_view/partials.rb +241 -0
  121. data/lib/action_view/paths.rb +77 -0
  122. data/lib/action_view/reloadable_template.rb +117 -0
  123. data/lib/action_view/renderable.rb +109 -0
  124. data/lib/action_view/renderable_partial.rb +53 -0
  125. data/lib/action_view/template.rb +252 -0
  126. data/lib/action_view/template_error.rb +99 -0
  127. data/lib/action_view/template_handler.rb +34 -0
  128. data/lib/action_view/template_handlers.rb +48 -0
  129. data/lib/action_view/template_handlers/builder.rb +17 -0
  130. data/lib/action_view/template_handlers/erb.rb +25 -0
  131. data/lib/action_view/template_handlers/rjs.rb +13 -0
  132. data/lib/action_view/test_case.rb +162 -0
  133. data/lib/actionpack.rb +2 -0
  134. data/test/abstract_unit.rb +78 -0
  135. data/test/active_record_unit.rb +104 -0
  136. data/test/activerecord/active_record_store_test.rb +221 -0
  137. data/test/activerecord/render_partial_with_record_identification_test.rb +188 -0
  138. data/test/adv_attr_test.rb +20 -0
  139. data/test/controller/action_pack_assertions_test.rb +545 -0
  140. data/test/controller/addresses_render_test.rb +37 -0
  141. data/test/controller/assert_select_test.rb +735 -0
  142. data/test/controller/base_test.rb +217 -0
  143. data/test/controller/benchmark_test.rb +32 -0
  144. data/test/controller/caching_test.rb +743 -0
  145. data/test/controller/capture_test.rb +66 -0
  146. data/test/controller/content_type_test.rb +178 -0
  147. data/test/controller/controller_fixtures/app/controllers/admin/user_controller.rb +0 -0
  148. data/test/controller/controller_fixtures/app/controllers/user_controller.rb +0 -0
  149. data/test/controller/controller_fixtures/vendor/plugins/bad_plugin/lib/plugin_controller.rb +0 -0
  150. data/test/controller/cookie_test.rb +208 -0
  151. data/test/controller/deprecation/deprecated_base_methods_test.rb +32 -0
  152. data/test/controller/dispatcher_test.rb +144 -0
  153. data/test/controller/dom_assertions_test.rb +53 -0
  154. data/test/controller/failsafe_test.rb +60 -0
  155. data/test/controller/fake_controllers.rb +33 -0
  156. data/test/controller/fake_models.rb +19 -0
  157. data/test/controller/filter_params_test.rb +52 -0
  158. data/test/controller/filters_test.rb +885 -0
  159. data/test/controller/flash_test.rb +174 -0
  160. data/test/controller/header_test.rb +14 -0
  161. data/test/controller/helper_test.rb +224 -0
  162. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  163. data/test/controller/html-scanner/document_test.rb +148 -0
  164. data/test/controller/html-scanner/node_test.rb +89 -0
  165. data/test/controller/html-scanner/sanitizer_test.rb +281 -0
  166. data/test/controller/html-scanner/tag_node_test.rb +238 -0
  167. data/test/controller/html-scanner/text_node_test.rb +50 -0
  168. data/test/controller/html-scanner/tokenizer_test.rb +131 -0
  169. data/test/controller/http_basic_authentication_test.rb +113 -0
  170. data/test/controller/http_digest_authentication_test.rb +254 -0
  171. data/test/controller/integration_test.rb +526 -0
  172. data/test/controller/layout_test.rb +215 -0
  173. data/test/controller/localized_templates_test.rb +24 -0
  174. data/test/controller/logging_test.rb +46 -0
  175. data/test/controller/middleware_stack_test.rb +90 -0
  176. data/test/controller/mime_responds_test.rb +536 -0
  177. data/test/controller/mime_type_test.rb +93 -0
  178. data/test/controller/output_escaping_test.rb +19 -0
  179. data/test/controller/polymorphic_routes_test.rb +297 -0
  180. data/test/controller/rack_test.rb +308 -0
  181. data/test/controller/record_identifier_test.rb +139 -0
  182. data/test/controller/redirect_test.rb +285 -0
  183. data/test/controller/reloader_test.rb +125 -0
  184. data/test/controller/render_test.rb +1783 -0
  185. data/test/controller/request/json_params_parsing_test.rb +65 -0
  186. data/test/controller/request/multipart_params_parsing_test.rb +177 -0
  187. data/test/controller/request/query_string_parsing_test.rb +129 -0
  188. data/test/controller/request/test_request_test.rb +35 -0
  189. data/test/controller/request/url_encoded_params_parsing_test.rb +146 -0
  190. data/test/controller/request/xml_params_parsing_test.rb +103 -0
  191. data/test/controller/request_forgery_protection_test.rb +233 -0
  192. data/test/controller/request_test.rb +398 -0
  193. data/test/controller/rescue_test.rb +541 -0
  194. data/test/controller/resources_test.rb +1393 -0
  195. data/test/controller/routing_test.rb +2592 -0
  196. data/test/controller/selector_test.rb +628 -0
  197. data/test/controller/send_file_test.rb +171 -0
  198. data/test/controller/session/abstract_store_test.rb +64 -0
  199. data/test/controller/session/cookie_store_test.rb +354 -0
  200. data/test/controller/session/mem_cache_store_test.rb +187 -0
  201. data/test/controller/session/test_session_test.rb +58 -0
  202. data/test/controller/test_test.rb +700 -0
  203. data/test/controller/translation_test.rb +26 -0
  204. data/test/controller/url_rewriter_test.rb +395 -0
  205. data/test/controller/verification_test.rb +270 -0
  206. data/test/controller/view_paths_test.rb +141 -0
  207. data/test/controller/webservice_test.rb +273 -0
  208. data/test/fixtures/_top_level_partial.html.erb +1 -0
  209. data/test/fixtures/_top_level_partial_only.erb +1 -0
  210. data/test/fixtures/addresses/list.erb +1 -0
  211. data/test/fixtures/alternate_helpers/foo_helper.rb +3 -0
  212. data/test/fixtures/bad_customers/_bad_customer.html.erb +1 -0
  213. data/test/fixtures/companies.yml +24 -0
  214. data/test/fixtures/company.rb +10 -0
  215. data/test/fixtures/content_type/render_default_content_types_for_respond_to.rhtml +1 -0
  216. data/test/fixtures/content_type/render_default_for_rhtml.rhtml +1 -0
  217. data/test/fixtures/content_type/render_default_for_rjs.rjs +1 -0
  218. data/test/fixtures/content_type/render_default_for_rxml.rxml +1 -0
  219. data/test/fixtures/customers/_customer.html.erb +1 -0
  220. data/test/fixtures/db_definitions/sqlite.sql +49 -0
  221. data/test/fixtures/developer.rb +9 -0
  222. data/test/fixtures/developers.yml +21 -0
  223. data/test/fixtures/developers/_developer.erb +1 -0
  224. data/test/fixtures/developers_projects.yml +13 -0
  225. data/test/fixtures/failsafe/500.html +1 -0
  226. data/test/fixtures/fun/games/_game.erb +1 -0
  227. data/test/fixtures/fun/games/hello_world.erb +1 -0
  228. data/test/fixtures/fun/serious/games/_game.erb +1 -0
  229. data/test/fixtures/functional_caching/_partial.erb +3 -0
  230. data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
  231. data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
  232. data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -0
  233. data/test/fixtures/functional_caching/fragment_cached.html.erb +2 -0
  234. data/test/fixtures/functional_caching/html_fragment_cached_with_partial.html.erb +1 -0
  235. data/test/fixtures/functional_caching/inline_fragment_cached.html.erb +2 -0
  236. data/test/fixtures/functional_caching/js_fragment_cached_with_partial.js.rjs +1 -0
  237. data/test/fixtures/good_customers/_good_customer.html.erb +1 -0
  238. data/test/fixtures/helpers/abc_helper.rb +5 -0
  239. data/test/fixtures/helpers/fun/games_helper.rb +3 -0
  240. data/test/fixtures/helpers/fun/pdf_helper.rb +3 -0
  241. data/test/fixtures/layout_tests/abs_path_layout.rhtml +1 -0
  242. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  243. data/test/fixtures/layout_tests/alt/layouts/alt.rhtml +0 -0
  244. data/test/fixtures/layout_tests/layouts/controller_name_space/nested.rhtml +1 -0
  245. data/test/fixtures/layout_tests/layouts/item.rhtml +1 -0
  246. data/test/fixtures/layout_tests/layouts/layout_test.rhtml +1 -0
  247. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  248. data/test/fixtures/layout_tests/layouts/third_party_template_library.mab +1 -0
  249. data/test/fixtures/layout_tests/views/hello.rhtml +1 -0
  250. data/test/fixtures/layouts/_column.html.erb +2 -0
  251. data/test/fixtures/layouts/block_with_layout.erb +3 -0
  252. data/test/fixtures/layouts/builder.builder +3 -0
  253. data/test/fixtures/layouts/default_html.html.erb +1 -0
  254. data/test/fixtures/layouts/partial_with_layout.erb +3 -0
  255. data/test/fixtures/layouts/standard.erb +1 -0
  256. data/test/fixtures/layouts/talk_from_action.erb +2 -0
  257. data/test/fixtures/layouts/xhr.html.erb +2 -0
  258. data/test/fixtures/layouts/yield.erb +2 -0
  259. data/test/fixtures/localized/hello_world.de.html +1 -0
  260. data/test/fixtures/localized/hello_world.en.html +1 -0
  261. data/test/fixtures/mascot.rb +3 -0
  262. data/test/fixtures/mascots.yml +4 -0
  263. data/test/fixtures/mascots/_mascot.html.erb +1 -0
  264. data/test/fixtures/multipart/binary_file +0 -0
  265. data/test/fixtures/multipart/boundary_problem_file +10 -0
  266. data/test/fixtures/multipart/bracketed_param +5 -0
  267. data/test/fixtures/multipart/empty +10 -0
  268. data/test/fixtures/multipart/hello.txt +1 -0
  269. data/test/fixtures/multipart/large_text_file +10 -0
  270. data/test/fixtures/multipart/mixed_files +0 -0
  271. data/test/fixtures/multipart/mona_lisa.jpg +0 -0
  272. data/test/fixtures/multipart/none +9 -0
  273. data/test/fixtures/multipart/single_parameter +5 -0
  274. data/test/fixtures/multipart/text_file +10 -0
  275. data/test/fixtures/override/test/hello_world.erb +1 -0
  276. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  277. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  278. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  279. data/test/fixtures/post_test/post/index.html.erb +1 -0
  280. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  281. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  282. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  283. data/test/fixtures/project.rb +3 -0
  284. data/test/fixtures/projects.yml +7 -0
  285. data/test/fixtures/projects/_project.erb +1 -0
  286. data/test/fixtures/public/404.html +1 -0
  287. data/test/fixtures/public/500.da.html +1 -0
  288. data/test/fixtures/public/500.html +1 -0
  289. data/test/fixtures/public/absolute/test.css +23 -0
  290. data/test/fixtures/public/absolute/test.js +63 -0
  291. data/test/fixtures/public/images/rails.png +0 -0
  292. data/test/fixtures/public/javascripts/application.js +1 -0
  293. data/test/fixtures/public/javascripts/bank.js +1 -0
  294. data/test/fixtures/public/javascripts/controls.js +1 -0
  295. data/test/fixtures/public/javascripts/dragdrop.js +1 -0
  296. data/test/fixtures/public/javascripts/effects.js +1 -0
  297. data/test/fixtures/public/javascripts/prototype.js +1 -0
  298. data/test/fixtures/public/javascripts/robber.js +1 -0
  299. data/test/fixtures/public/javascripts/subdir/subdir.js +1 -0
  300. data/test/fixtures/public/javascripts/version.1.0.js +1 -0
  301. data/test/fixtures/public/stylesheets/bank.css +1 -0
  302. data/test/fixtures/public/stylesheets/robber.css +1 -0
  303. data/test/fixtures/public/stylesheets/subdir/subdir.css +1 -0
  304. data/test/fixtures/public/stylesheets/version.1.0.css +1 -0
  305. data/test/fixtures/quiz/questions/_question.html.erb +1 -0
  306. data/test/fixtures/replies.yml +15 -0
  307. data/test/fixtures/replies/_reply.erb +1 -0
  308. data/test/fixtures/reply.rb +7 -0
  309. data/test/fixtures/respond_to/all_types_with_layout.html.erb +1 -0
  310. data/test/fixtures/respond_to/all_types_with_layout.js.rjs +1 -0
  311. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  312. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  313. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  314. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  315. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  316. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  317. data/test/fixtures/respond_to/using_defaults.html.erb +1 -0
  318. data/test/fixtures/respond_to/using_defaults.js.rjs +1 -0
  319. data/test/fixtures/respond_to/using_defaults.xml.builder +1 -0
  320. data/test/fixtures/respond_to/using_defaults_with_type_list.html.erb +1 -0
  321. data/test/fixtures/respond_to/using_defaults_with_type_list.js.rjs +1 -0
  322. data/test/fixtures/respond_to/using_defaults_with_type_list.xml.builder +1 -0
  323. data/test/fixtures/scope/test/modgreet.erb +1 -0
  324. data/test/fixtures/session_autoload_test/session_autoload_test/foo.rb +10 -0
  325. data/test/fixtures/shared.html.erb +1 -0
  326. data/test/fixtures/symlink_parent/symlinked_layout.erb +5 -0
  327. data/test/fixtures/test/_counter.html.erb +1 -0
  328. data/test/fixtures/test/_customer.erb +1 -0
  329. data/test/fixtures/test/_customer_counter.erb +1 -0
  330. data/test/fixtures/test/_customer_counter_with_as.erb +1 -0
  331. data/test/fixtures/test/_customer_greeting.erb +1 -0
  332. data/test/fixtures/test/_customer_with_var.erb +1 -0
  333. data/test/fixtures/test/_form.erb +1 -0
  334. data/test/fixtures/test/_from_helper.erb +1 -0
  335. data/test/fixtures/test/_hash_greeting.erb +1 -0
  336. data/test/fixtures/test/_hash_object.erb +2 -0
  337. data/test/fixtures/test/_hello.builder +1 -0
  338. data/test/fixtures/test/_labelling_form.erb +1 -0
  339. data/test/fixtures/test/_layout_for_block_with_args.html.erb +3 -0
  340. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  341. data/test/fixtures/test/_local_inspector.html.erb +1 -0
  342. data/test/fixtures/test/_one.html.erb +1 -0
  343. data/test/fixtures/test/_partial.erb +1 -0
  344. data/test/fixtures/test/_partial.html.erb +1 -0
  345. data/test/fixtures/test/_partial.js.erb +1 -0
  346. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  347. data/test/fixtures/test/_partial_only.erb +1 -0
  348. data/test/fixtures/test/_partial_with_only_html_version.html.erb +1 -0
  349. data/test/fixtures/test/_person.erb +2 -0
  350. data/test/fixtures/test/_raise.html.erb +1 -0
  351. data/test/fixtures/test/_two.html.erb +1 -0
  352. data/test/fixtures/test/_utf8_partial.html.erb +1 -0
  353. data/test/fixtures/test/_utf8_partial_magic.html.erb +2 -0
  354. data/test/fixtures/test/action_talk_to_layout.erb +2 -0
  355. data/test/fixtures/test/array_translation.erb +1 -0
  356. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  357. data/test/fixtures/test/capturing.erb +4 -0
  358. data/test/fixtures/test/content_for.erb +2 -0
  359. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  360. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  361. data/test/fixtures/test/delete_with_js.rjs +2 -0
  362. data/test/fixtures/test/dont_pick_me +1 -0
  363. data/test/fixtures/test/dot.directory/render_file_with_ivar.erb +1 -0
  364. data/test/fixtures/test/enum_rjs_test.rjs +6 -0
  365. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  366. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  367. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  368. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  369. data/test/fixtures/test/greeting.erb +1 -0
  370. data/test/fixtures/test/greeting.js.rjs +1 -0
  371. data/test/fixtures/test/hello.builder +4 -0
  372. data/test/fixtures/test/hello_world.da.html.erb +1 -0
  373. data/test/fixtures/test/hello_world.erb +1 -0
  374. data/test/fixtures/test/hello_world.erb~ +1 -0
  375. data/test/fixtures/test/hello_world.pt-BR.html.erb +1 -0
  376. data/test/fixtures/test/hello_world_container.builder +3 -0
  377. data/test/fixtures/test/hello_world_from_rxml.builder +4 -0
  378. data/test/fixtures/test/hello_world_with_layout_false.erb +1 -0
  379. data/test/fixtures/test/hello_xml_world.builder +11 -0
  380. data/test/fixtures/test/hyphen-ated.erb +1 -0
  381. data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
  382. data/test/fixtures/test/list.erb +1 -0
  383. data/test/fixtures/test/malformed/malformed.en.html.erb~ +1 -0
  384. data/test/fixtures/test/malformed/malformed.erb~ +1 -0
  385. data/test/fixtures/test/malformed/malformed.html.erb~ +1 -0
  386. data/test/fixtures/test/nested_layout.erb +3 -0
  387. data/test/fixtures/test/non_erb_block_content_for.builder +4 -0
  388. data/test/fixtures/test/potential_conflicts.erb +4 -0
  389. data/test/fixtures/test/render_explicit_html_template.js.rjs +1 -0
  390. data/test/fixtures/test/render_file_from_template.html.erb +1 -0
  391. data/test/fixtures/test/render_file_with_ivar.erb +1 -0
  392. data/test/fixtures/test/render_file_with_locals.erb +1 -0
  393. data/test/fixtures/test/render_implicit_html_template.js.rjs +1 -0
  394. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.da.html.erb +1 -0
  395. data/test/fixtures/test/render_implicit_html_template_from_xhr_request.html.erb +1 -0
  396. data/test/fixtures/test/render_implicit_js_template_without_layout.js.erb +1 -0
  397. data/test/fixtures/test/render_to_string_test.erb +1 -0
  398. data/test/fixtures/test/scoped_array_translation.erb +1 -0
  399. data/test/fixtures/test/sub_template_raise.html.erb +1 -0
  400. data/test/fixtures/test/template.erb +1 -0
  401. data/test/fixtures/test/translation.erb +1 -0
  402. data/test/fixtures/test/update_element_with_capture.erb +9 -0
  403. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  404. data/test/fixtures/test/using_layout_around_block_with_args.html.erb +1 -0
  405. data/test/fixtures/test/utf8.html.erb +4 -0
  406. data/test/fixtures/test/utf8_magic.html.erb +5 -0
  407. data/test/fixtures/test/utf8_magic_with_bare_partial.html.erb +5 -0
  408. data/test/fixtures/topic.rb +3 -0
  409. data/test/fixtures/topics.yml +22 -0
  410. data/test/fixtures/topics/_topic.html.erb +1 -0
  411. data/test/template/active_record_helper_i18n_test.rb +51 -0
  412. data/test/template/active_record_helper_test.rb +302 -0
  413. data/test/template/asset_tag_helper_test.rb +770 -0
  414. data/test/template/atom_feed_helper_test.rb +315 -0
  415. data/test/template/benchmark_helper_test.rb +86 -0
  416. data/test/template/compiled_templates_test.rb +204 -0
  417. data/test/template/date_helper_i18n_test.rb +121 -0
  418. data/test/template/date_helper_test.rb +2603 -0
  419. data/test/template/erb_util_test.rb +36 -0
  420. data/test/template/form_helper_test.rb +1447 -0
  421. data/test/template/form_options_helper_i18n_test.rb +27 -0
  422. data/test/template/form_options_helper_test.rb +811 -0
  423. data/test/template/form_tag_helper_test.rb +356 -0
  424. data/test/template/javascript_helper_test.rb +106 -0
  425. data/test/template/number_helper_i18n_test.rb +69 -0
  426. data/test/template/number_helper_test.rb +132 -0
  427. data/test/template/prototype_helper_test.rb +639 -0
  428. data/test/template/raw_output_helper_test.rb +21 -0
  429. data/test/template/record_tag_helper_test.rb +58 -0
  430. data/test/template/render_test.rb +329 -0
  431. data/test/template/sanitize_helper_test.rb +57 -0
  432. data/test/template/scriptaculous_helper_test.rb +90 -0
  433. data/test/template/tag_helper_test.rb +98 -0
  434. data/test/template/template_test.rb +32 -0
  435. data/test/template/test_test.rb +54 -0
  436. data/test/template/text_helper_test.rb +601 -0
  437. data/test/template/translation_helper_test.rb +95 -0
  438. data/test/template/url_helper_test.rb +641 -0
  439. data/test/testing_sandbox.rb +15 -0
  440. data/test/view/test_case_test.rb +176 -0
  441. metadata +519 -0
@@ -0,0 +1,116 @@
1
+ module ActionController #:nodoc:
2
+ class InvalidAuthenticityToken < ActionControllerError #:nodoc:
3
+ end
4
+
5
+ module RequestForgeryProtection
6
+ def self.included(base)
7
+ base.class_eval do
8
+ helper_method :form_authenticity_token
9
+ helper_method :protect_against_forgery?
10
+ end
11
+ base.extend(ClassMethods)
12
+ end
13
+
14
+ # Protecting controller actions from CSRF attacks by ensuring that all forms are coming from the current web application, not a
15
+ # forged link from another site, is done by embedding a token based on a random string stored in the session (which an attacker wouldn't know) in all
16
+ # forms and Ajax requests generated by Rails and then verifying the authenticity of that token in the controller. Only
17
+ # HTML/JavaScript requests are checked, so this will not protect your XML API (presumably you'll have a different authentication
18
+ # scheme there anyway). Also, GET requests are not protected as these should be idempotent anyway.
19
+ #
20
+ # This is turned on with the <tt>protect_from_forgery</tt> method, which will check the token and raise an
21
+ # ActionController::InvalidAuthenticityToken if it doesn't match what was expected. You can customize the error message in
22
+ # production by editing public/422.html. A call to this method in ApplicationController is generated by default in post-Rails 2.0
23
+ # applications.
24
+ #
25
+ # The token parameter is named <tt>authenticity_token</tt> by default. If you are generating an HTML form manually (without the
26
+ # use of Rails' <tt>form_for</tt>, <tt>form_tag</tt> or other helpers), you have to include a hidden field named like that and
27
+ # set its value to what is returned by <tt>form_authenticity_token</tt>. Same applies to manually constructed Ajax requests. To
28
+ # make the token available through a global variable to scripts on a certain page, you could add something like this to a view:
29
+ #
30
+ # <%= javascript_tag "window._token = '#{form_authenticity_token}'" %>
31
+ #
32
+ # Request forgery protection is disabled by default in test environment. If you are upgrading from Rails 1.x, add this to
33
+ # config/environments/test.rb:
34
+ #
35
+ # # Disable request forgery protection in test environment
36
+ # config.action_controller.allow_forgery_protection = false
37
+ #
38
+ # == Learn more about CSRF (Cross-Site Request Forgery) attacks
39
+ #
40
+ # Here are some resources:
41
+ # * http://isc.sans.org/diary.html?storyid=1750
42
+ # * http://en.wikipedia.org/wiki/Cross-site_request_forgery
43
+ #
44
+ # Keep in mind, this is NOT a silver-bullet, plug 'n' play, warm security blanket for your rails application.
45
+ # There are a few guidelines you should follow:
46
+ #
47
+ # * Keep your GET requests safe and idempotent. More reading material:
48
+ # * http://www.xml.com/pub/a/2002/04/24/deviant.html
49
+ # * http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.1.1
50
+ # * Make sure the session cookies that Rails creates are non-persistent. Check in Firefox and look for "Expires: at end of session"
51
+ #
52
+ module ClassMethods
53
+ # Turn on request forgery protection. Bear in mind that only non-GET, HTML/JavaScript requests are checked.
54
+ #
55
+ # Example:
56
+ #
57
+ # class FooController < ApplicationController
58
+ # protect_from_forgery :except => :index
59
+ #
60
+ # # you can disable csrf protection on controller-by-controller basis:
61
+ # skip_before_filter :verify_authenticity_token
62
+ # end
63
+ #
64
+ # Valid Options:
65
+ #
66
+ # * <tt>:only/:except</tt> - Passed to the <tt>before_filter</tt> call. Set which actions are verified.
67
+ def protect_from_forgery(options = {})
68
+ self.request_forgery_protection_token ||= :authenticity_token
69
+ before_filter :verify_authenticity_token, :only => options.delete(:only), :except => options.delete(:except)
70
+ if options[:secret] || options[:digest]
71
+ ActiveSupport::Deprecation.warn("protect_from_forgery only takes :only and :except options now. :digest and :secret have no effect", caller)
72
+ end
73
+ end
74
+ end
75
+
76
+ protected
77
+ # The actual before_filter that is used. Modify this to change how you handle unverified requests.
78
+ def verify_authenticity_token
79
+ verified_request? || handle_unverified_request
80
+ end
81
+
82
+ def handle_unverified_request
83
+ reset_session
84
+ end
85
+
86
+ # Returns true or false if a request is verified. Checks:
87
+ #
88
+ # * is the format restricted? By default, only HTML requests are checked.
89
+ # * is it a GET request? Gets should be safe and idempotent
90
+ # * Does the form_authenticity_token match the given token value from the params?
91
+ def verified_request?
92
+ !protect_against_forgery? ||
93
+ request.get? ||
94
+ form_authenticity_token == form_authenticity_param ||
95
+ form_authenticity_token == request.headers['X-CSRF-Token']
96
+ end
97
+
98
+ def form_authenticity_param
99
+ params[request_forgery_protection_token]
100
+ end
101
+
102
+ def verifiable_request_format?
103
+ !request.content_type.nil? && request.content_type.verify_request?
104
+ end
105
+
106
+ # Sets the token value for the current session. Pass a <tt>:secret</tt> option
107
+ # in +protect_from_forgery+ to add a custom salt to the hash.
108
+ def form_authenticity_token
109
+ session[:_csrf_token] ||= ActiveSupport::SecureRandom.base64(32)
110
+ end
111
+
112
+ def protect_against_forgery?
113
+ allow_forgery_protection && request_forgery_protection_token
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,183 @@
1
+ module ActionController #:nodoc:
2
+ # Actions that fail to perform as expected throw exceptions. These
3
+ # exceptions can either be rescued for the public view (with a nice
4
+ # user-friendly explanation) or for the developers view (with tons of
5
+ # debugging information). The developers view is already implemented by
6
+ # the Action Controller, but the public view should be tailored to your
7
+ # specific application.
8
+ #
9
+ # The default behavior for public exceptions is to render a static html
10
+ # file with the name of the error code thrown. If no such file exists, an
11
+ # empty response is sent with the correct status code.
12
+ #
13
+ # You can override what constitutes a local request by overriding the
14
+ # <tt>local_request?</tt> method in your own controller. Custom rescue
15
+ # behavior is achieved by overriding the <tt>rescue_action_in_public</tt>
16
+ # and <tt>rescue_action_locally</tt> methods.
17
+ module Rescue
18
+ LOCALHOST = [/^127\.0\.0\.\d{1,3}$/, /^::1$/, /^0:0:0:0:0:0:0:1(%.*)?$/].freeze
19
+
20
+ DEFAULT_RESCUE_RESPONSE = :internal_server_error
21
+ DEFAULT_RESCUE_RESPONSES = {
22
+ 'ActionController::RoutingError' => :not_found,
23
+ 'ActionController::UnknownAction' => :not_found,
24
+ 'ActiveRecord::RecordNotFound' => :not_found,
25
+ 'ActiveRecord::StaleObjectError' => :conflict,
26
+ 'ActiveRecord::RecordInvalid' => :unprocessable_entity,
27
+ 'ActiveRecord::RecordNotSaved' => :unprocessable_entity,
28
+ 'ActionController::MethodNotAllowed' => :method_not_allowed,
29
+ 'ActionController::NotImplemented' => :not_implemented,
30
+ 'ActionController::InvalidAuthenticityToken' => :unprocessable_entity
31
+ }
32
+
33
+ DEFAULT_RESCUE_TEMPLATE = 'diagnostics'
34
+ DEFAULT_RESCUE_TEMPLATES = {
35
+ 'ActionView::MissingTemplate' => 'missing_template',
36
+ 'ActionController::RoutingError' => 'routing_error',
37
+ 'ActionController::UnknownAction' => 'unknown_action',
38
+ 'ActionView::TemplateError' => 'template_error'
39
+ }
40
+
41
+ RESCUES_TEMPLATE_PATH = ActionView::Template::EagerPath.new_and_loaded(
42
+ File.join(File.dirname(__FILE__), "templates"))
43
+
44
+ def self.included(base) #:nodoc:
45
+ base.cattr_accessor :rescue_responses
46
+ base.rescue_responses = Hash.new(DEFAULT_RESCUE_RESPONSE)
47
+ base.rescue_responses.update DEFAULT_RESCUE_RESPONSES
48
+
49
+ base.cattr_accessor :rescue_templates
50
+ base.rescue_templates = Hash.new(DEFAULT_RESCUE_TEMPLATE)
51
+ base.rescue_templates.update DEFAULT_RESCUE_TEMPLATES
52
+
53
+ base.extend(ClassMethods)
54
+ base.send :include, ActiveSupport::Rescuable
55
+
56
+ base.class_eval do
57
+ alias_method_chain :perform_action, :rescue
58
+ end
59
+ end
60
+
61
+ module ClassMethods
62
+ def call_with_exception(env, exception) #:nodoc:
63
+ request = env["action_controller.rescue.request"] ||= Request.new(env)
64
+ response = env["action_controller.rescue.response"] ||= Response.new
65
+ new.process(request, response, :rescue_action, exception)
66
+ end
67
+ end
68
+
69
+ protected
70
+ # Exception handler called when the performance of an action raises
71
+ # an exception.
72
+ def rescue_action(exception)
73
+ rescue_with_handler(exception) ||
74
+ rescue_action_without_handler(exception)
75
+ end
76
+
77
+ # Overwrite to implement custom logging of errors. By default
78
+ # logs as fatal.
79
+ def log_error(exception) #:doc:
80
+ ActiveSupport::Deprecation.silence do
81
+ if ActionView::TemplateError === exception
82
+ logger.fatal(exception.to_s)
83
+ else
84
+ logger.fatal(
85
+ "\n#{exception.class} (#{exception.message}):\n " +
86
+ clean_backtrace(exception).join("\n ") + "\n\n"
87
+ )
88
+ end
89
+ end
90
+ end
91
+
92
+ # Overwrite to implement public exception handling (for requests
93
+ # answering false to <tt>local_request?</tt>). By default will call
94
+ # render_optional_error_file. Override this method to provide more
95
+ # user friendly error messages.
96
+ def rescue_action_in_public(exception) #:doc:
97
+ render_optional_error_file response_code_for_rescue(exception)
98
+ end
99
+
100
+ # Attempts to render a static error page based on the
101
+ # <tt>status_code</tt> thrown, or just return headers if no such file
102
+ # exists. At first, it will try to render a localized static page.
103
+ # For example, if a 500 error is being handled Rails and locale is :da,
104
+ # it will first attempt to render the file at <tt>public/500.da.html</tt>
105
+ # then attempt to render <tt>public/500.html</tt>. If none of them exist,
106
+ # the body of the response will be left empty.
107
+ def render_optional_error_file(status_code)
108
+ status = interpret_status(status_code)
109
+ locale_path = "#{Rails.public_path}/#{status[0,3]}.#{I18n.locale}.html" if I18n.locale
110
+ path = "#{Rails.public_path}/#{status[0,3]}.html"
111
+
112
+ if locale_path && File.exist?(locale_path)
113
+ render :file => locale_path, :status => status, :content_type => Mime::HTML
114
+ elsif File.exist?(path)
115
+ render :file => path, :status => status, :content_type => Mime::HTML
116
+ else
117
+ head status
118
+ end
119
+ end
120
+
121
+ # True if the request came from localhost, 127.0.0.1. Override this
122
+ # method if you wish to redefine the meaning of a local request to
123
+ # include remote IP addresses or other criteria.
124
+ def local_request? #:doc:
125
+ LOCALHOST.any?{ |local_ip| request.remote_addr =~ local_ip && request.remote_ip =~ local_ip }
126
+ end
127
+
128
+ # Render detailed diagnostics for unhandled exceptions rescued from
129
+ # a controller action.
130
+ def rescue_action_locally(exception)
131
+ @template.instance_variable_set("@exception", exception)
132
+ @template.instance_variable_set("@rescues_path", RESCUES_TEMPLATE_PATH)
133
+ @template.instance_variable_set("@contents",
134
+ @template.render(:file => template_path_for_local_rescue(exception)))
135
+
136
+ response.content_type = Mime::HTML
137
+ render_for_file(rescues_path("layout"),
138
+ response_code_for_rescue(exception))
139
+ end
140
+
141
+ def rescue_action_without_handler(exception)
142
+ log_error(exception) if logger
143
+ erase_results if performed?
144
+
145
+ # Let the exception alter the response if it wants.
146
+ # For example, MethodNotAllowed sets the Allow header.
147
+ if exception.respond_to?(:handle_response!)
148
+ exception.handle_response!(response)
149
+ end
150
+
151
+ if consider_all_requests_local || local_request?
152
+ rescue_action_locally(exception)
153
+ else
154
+ rescue_action_in_public(exception)
155
+ end
156
+ end
157
+
158
+ private
159
+ def perform_action_with_rescue #:nodoc:
160
+ perform_action_without_rescue
161
+ rescue Exception => exception
162
+ rescue_action(exception)
163
+ end
164
+
165
+ def rescues_path(template_name)
166
+ RESCUES_TEMPLATE_PATH["rescues/#{template_name}.erb"]
167
+ end
168
+
169
+ def template_path_for_local_rescue(exception)
170
+ rescues_path(rescue_templates[exception.class.name])
171
+ end
172
+
173
+ def response_code_for_rescue(exception)
174
+ rescue_responses[exception.class.name]
175
+ end
176
+
177
+ def clean_backtrace(exception)
178
+ defined?(Rails) && Rails.respond_to?(:backtrace_cleaner) ?
179
+ Rails.backtrace_cleaner.clean(exception.backtrace) :
180
+ exception.backtrace
181
+ end
182
+ end
183
+ end
@@ -0,0 +1,682 @@
1
+ module ActionController
2
+ # == Overview
3
+ #
4
+ # ActionController::Resources are a way of defining RESTful \resources. A RESTful \resource, in basic terms,
5
+ # is something that can be pointed at and it will respond with a representation of the data requested.
6
+ # In real terms this could mean a user with a browser requests an HTML page, or that a desktop application
7
+ # requests XML data.
8
+ #
9
+ # RESTful design is based on the assumption that there are four generic verbs that a user of an
10
+ # application can request from a \resource (the noun).
11
+ #
12
+ # \Resources can be requested using four basic HTTP verbs (GET, POST, PUT, DELETE), the method used
13
+ # denotes the type of action that should take place.
14
+ #
15
+ # === The Different Methods and their Usage
16
+ #
17
+ # * GET - Requests for a \resource, no saving or editing of a \resource should occur in a GET request.
18
+ # * POST - Creation of \resources.
19
+ # * PUT - Editing of attributes on a \resource.
20
+ # * DELETE - Deletion of a \resource.
21
+ #
22
+ # === Examples
23
+ #
24
+ # # A GET request on the Posts resource is asking for all Posts
25
+ # GET /posts
26
+ #
27
+ # # A GET request on a single Post resource is asking for that particular Post
28
+ # GET /posts/1
29
+ #
30
+ # # A POST request on the Posts resource is asking for a Post to be created with the supplied details
31
+ # POST /posts # with => { :post => { :title => "My Whizzy New Post", :body => "I've got a brand new combine harvester" } }
32
+ #
33
+ # # A PUT request on a single Post resource is asking for a Post to be updated
34
+ # PUT /posts # with => { :id => 1, :post => { :title => "Changed Whizzy Title" } }
35
+ #
36
+ # # A DELETE request on a single Post resource is asking for it to be deleted
37
+ # DELETE /posts # with => { :id => 1 }
38
+ #
39
+ # By using the REST convention, users of our application can assume certain things about how the data
40
+ # is requested and how it is returned. Rails simplifies the routing part of RESTful design by
41
+ # supplying you with methods to create them in your routes.rb file.
42
+ #
43
+ # Read more about REST at http://en.wikipedia.org/wiki/Representational_State_Transfer
44
+ module Resources
45
+ INHERITABLE_OPTIONS = :namespace, :shallow
46
+
47
+ class Resource #:nodoc:
48
+ DEFAULT_ACTIONS = :index, :create, :new, :edit, :show, :update, :destroy
49
+
50
+ attr_reader :collection_methods, :member_methods, :new_methods
51
+ attr_reader :path_prefix, :name_prefix, :path_segment
52
+ attr_reader :plural, :singular
53
+ attr_reader :options
54
+
55
+ def initialize(entities, options)
56
+ @plural ||= entities
57
+ @singular ||= options[:singular] || plural.to_s.singularize
58
+ @path_segment = options.delete(:as) || @plural
59
+
60
+ @options = options
61
+
62
+ arrange_actions
63
+ add_default_actions
64
+ set_allowed_actions
65
+ set_prefixes
66
+ end
67
+
68
+ def controller
69
+ @controller ||= "#{options[:namespace]}#{(options[:controller] || plural).to_s}"
70
+ end
71
+
72
+ def requirements(with_id = false)
73
+ @requirements ||= @options[:requirements] || {}
74
+ @id_requirement ||= { :id => @requirements.delete(:id) || /[^#{Routing::SEPARATORS.join}]+/ }
75
+
76
+ with_id ? @requirements.merge(@id_requirement) : @requirements
77
+ end
78
+
79
+ def conditions
80
+ @conditions ||= @options[:conditions] || {}
81
+ end
82
+
83
+ def path
84
+ @path ||= "#{path_prefix}/#{path_segment}"
85
+ end
86
+
87
+ def new_path
88
+ new_action = self.options[:path_names][:new] if self.options[:path_names]
89
+ new_action ||= Base.resources_path_names[:new]
90
+ @new_path ||= "#{path}/#{new_action}"
91
+ end
92
+
93
+ def shallow_path_prefix
94
+ @shallow_path_prefix ||= @options[:shallow] ? @options[:namespace].try(:sub, /\/$/, '') : path_prefix
95
+ end
96
+
97
+ def member_path
98
+ @member_path ||= "#{shallow_path_prefix}/#{path_segment}/:id"
99
+ end
100
+
101
+ def nesting_path_prefix
102
+ @nesting_path_prefix ||= "#{shallow_path_prefix}/#{path_segment}/:#{singular}_id"
103
+ end
104
+
105
+ def shallow_name_prefix
106
+ @shallow_name_prefix ||= @options[:shallow] ? @options[:namespace].try(:gsub, /\//, '_') : name_prefix
107
+ end
108
+
109
+ def nesting_name_prefix
110
+ "#{shallow_name_prefix}#{singular}_"
111
+ end
112
+
113
+ def action_separator
114
+ @action_separator ||= Base.resource_action_separator
115
+ end
116
+
117
+ def uncountable?
118
+ @singular.to_s == @plural.to_s
119
+ end
120
+
121
+ def has_action?(action)
122
+ !DEFAULT_ACTIONS.include?(action) || action_allowed?(action)
123
+ end
124
+
125
+ protected
126
+ def arrange_actions
127
+ @collection_methods = arrange_actions_by_methods(options.delete(:collection))
128
+ @member_methods = arrange_actions_by_methods(options.delete(:member))
129
+ @new_methods = arrange_actions_by_methods(options.delete(:new))
130
+ end
131
+
132
+ def add_default_actions
133
+ add_default_action(member_methods, :get, :edit)
134
+ add_default_action(new_methods, :get, :new)
135
+ end
136
+
137
+ def set_allowed_actions
138
+ only, except = @options.values_at(:only, :except)
139
+ @allowed_actions ||= {}
140
+
141
+ if only == :all || except == :none
142
+ only = nil
143
+ except = []
144
+ elsif only == :none || except == :all
145
+ only = []
146
+ except = nil
147
+ end
148
+
149
+ if only
150
+ @allowed_actions[:only] = Array(only).map(&:to_sym)
151
+ elsif except
152
+ @allowed_actions[:except] = Array(except).map(&:to_sym)
153
+ end
154
+ end
155
+
156
+ def action_allowed?(action)
157
+ only, except = @allowed_actions.values_at(:only, :except)
158
+ (!only || only.include?(action)) && (!except || !except.include?(action))
159
+ end
160
+
161
+ def set_prefixes
162
+ @path_prefix = options.delete(:path_prefix)
163
+ @name_prefix = options.delete(:name_prefix)
164
+ end
165
+
166
+ def arrange_actions_by_methods(actions)
167
+ (actions || {}).inject({}) do |flipped_hash, (key, value)|
168
+ (flipped_hash[value] ||= []) << key
169
+ flipped_hash
170
+ end
171
+ end
172
+
173
+ def add_default_action(collection, method, action)
174
+ (collection[method] ||= []).unshift(action)
175
+ end
176
+ end
177
+
178
+ class SingletonResource < Resource #:nodoc:
179
+ def initialize(entity, options)
180
+ @singular = @plural = entity
181
+ options[:controller] ||= @singular.to_s.pluralize
182
+ super
183
+ end
184
+
185
+ alias_method :shallow_path_prefix, :path_prefix
186
+ alias_method :shallow_name_prefix, :name_prefix
187
+ alias_method :member_path, :path
188
+ alias_method :nesting_path_prefix, :path
189
+ end
190
+
191
+ # Creates named routes for implementing verb-oriented controllers
192
+ # for a collection \resource.
193
+ #
194
+ # For example:
195
+ #
196
+ # map.resources :messages
197
+ #
198
+ # will map the following actions in the corresponding controller:
199
+ #
200
+ # class MessagesController < ActionController::Base
201
+ # # GET messages_url
202
+ # def index
203
+ # # return all messages
204
+ # end
205
+ #
206
+ # # GET new_message_url
207
+ # def new
208
+ # # return an HTML form for describing a new message
209
+ # end
210
+ #
211
+ # # POST messages_url
212
+ # def create
213
+ # # create a new message
214
+ # end
215
+ #
216
+ # # GET message_url(:id => 1)
217
+ # def show
218
+ # # find and return a specific message
219
+ # end
220
+ #
221
+ # # GET edit_message_url(:id => 1)
222
+ # def edit
223
+ # # return an HTML form for editing a specific message
224
+ # end
225
+ #
226
+ # # PUT message_url(:id => 1)
227
+ # def update
228
+ # # find and update a specific message
229
+ # end
230
+ #
231
+ # # DELETE message_url(:id => 1)
232
+ # def destroy
233
+ # # delete a specific message
234
+ # end
235
+ # end
236
+ #
237
+ # Along with the routes themselves, +resources+ generates named routes for use in
238
+ # controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
239
+ #
240
+ # Named Route Helpers
241
+ # ============ =====================================================
242
+ # messages messages_url, hash_for_messages_url,
243
+ # messages_path, hash_for_messages_path
244
+ #
245
+ # message message_url(id), hash_for_message_url(id),
246
+ # message_path(id), hash_for_message_path(id)
247
+ #
248
+ # new_message new_message_url, hash_for_new_message_url,
249
+ # new_message_path, hash_for_new_message_path
250
+ #
251
+ # edit_message edit_message_url(id), hash_for_edit_message_url(id),
252
+ # edit_message_path(id), hash_for_edit_message_path(id)
253
+ #
254
+ # You can use these helpers instead of +url_for+ or methods that take +url_for+ parameters. For example:
255
+ #
256
+ # redirect_to :controller => 'messages', :action => 'index'
257
+ # # and
258
+ # <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
259
+ #
260
+ # now become:
261
+ #
262
+ # redirect_to messages_url
263
+ # # and
264
+ # <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
265
+ #
266
+ # Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
267
+ # form tags. The form helpers make this a little easier. For an update form with a <tt>@message</tt> object:
268
+ #
269
+ # <%= form_tag message_path(@message), :method => :put %>
270
+ #
271
+ # or
272
+ #
273
+ # <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
274
+ #
275
+ # or
276
+ #
277
+ # <% form_for @message do |f| %>
278
+ #
279
+ # which takes into account whether <tt>@message</tt> is a new record or not and generates the
280
+ # path and method accordingly.
281
+ #
282
+ # The +resources+ method accepts the following options to customize the resulting routes:
283
+ # * <tt>:collection</tt> - Add named routes for other actions that operate on the collection.
284
+ # Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>,
285
+ # an array of any of the previous, or <tt>:any</tt> if the method does not matter.
286
+ # These routes map to a URL like /messages/rss, with a route of +rss_messages_url+.
287
+ # * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member.
288
+ # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new \resource action.
289
+ # * <tt>:controller</tt> - Specify the controller name for the routes.
290
+ # * <tt>:singular</tt> - Specify the singular name used in the member routes.
291
+ # * <tt>:requirements</tt> - Set custom routing parameter requirements; this is a hash of either
292
+ # regular expressions (which must match for the route to match) or extra parameters. For example:
293
+ #
294
+ # map.resource :profile, :path_prefix => ':name', :requirements => { :name => /[a-zA-Z]+/, :extra => 'value' }
295
+ #
296
+ # will only match if the first part is alphabetic, and will pass the parameter :extra to the controller.
297
+ # * <tt>:conditions</tt> - Specify custom routing recognition conditions. \Resources sets the <tt>:method</tt> value for the method-specific routes.
298
+ # * <tt>:as</tt> - Specify a different \resource name to use in the URL path. For example:
299
+ # # products_path == '/productos'
300
+ # map.resources :products, :as => 'productos' do |product|
301
+ # # product_reviews_path(product) == '/productos/1234/comentarios'
302
+ # product.resources :product_reviews, :as => 'comentarios'
303
+ # end
304
+ #
305
+ # * <tt>:has_one</tt> - Specify nested \resources, this is a shorthand for mapping singleton \resources beneath the current.
306
+ # * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural \resources.
307
+ #
308
+ # You may directly specify the routing association with +has_one+ and +has_many+ like:
309
+ #
310
+ # map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]
311
+ #
312
+ # This is the same as:
313
+ #
314
+ # map.resources :notes do |notes|
315
+ # notes.resource :author
316
+ # notes.resources :comments
317
+ # notes.resources :attachments
318
+ # end
319
+ #
320
+ # * <tt>:path_names</tt> - Specify different path names for the actions. For example:
321
+ # # new_products_path == '/productos/nuevo'
322
+ # # bids_product_path(1) == '/productos/1/licitacoes'
323
+ # map.resources :products, :as => 'productos', :member => { :bids => :get }, :path_names => { :new => 'nuevo', :bids => 'licitacoes' }
324
+ #
325
+ # You can also set default action names from an environment, like this:
326
+ # config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
327
+ #
328
+ # * <tt>:path_prefix</tt> - Set a prefix to the routes with required route variables.
329
+ #
330
+ # Weblog comments usually belong to a post, so you might use +resources+ like:
331
+ #
332
+ # map.resources :articles
333
+ # map.resources :comments, :path_prefix => '/articles/:article_id'
334
+ #
335
+ # You can nest +resources+ calls to set this automatically:
336
+ #
337
+ # map.resources :articles do |article|
338
+ # article.resources :comments
339
+ # end
340
+ #
341
+ # The comment \resources work the same, but must now include a value for <tt>:article_id</tt>.
342
+ #
343
+ # article_comments_url(@article)
344
+ # article_comment_url(@article, @comment)
345
+ #
346
+ # article_comments_url(:article_id => @article)
347
+ # article_comment_url(:article_id => @article, :id => @comment)
348
+ #
349
+ # If you don't want to load all objects from the database you might want to use the <tt>article_id</tt> directly:
350
+ #
351
+ # articles_comments_url(@comment.article_id, @comment)
352
+ #
353
+ # * <tt>:name_prefix</tt> - Define a prefix for all generated routes, usually ending in an underscore.
354
+ # Use this if you have named routes that may clash.
355
+ #
356
+ # map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
357
+ # map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
358
+ #
359
+ # You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested \resource:
360
+ #
361
+ # map.resources :articles do |article|
362
+ # article.resources :comments, :name_prefix => nil
363
+ # end
364
+ #
365
+ # This will yield named \resources like so:
366
+ #
367
+ # comments_url(@article)
368
+ # comment_url(@article, @comment)
369
+ #
370
+ # * <tt>:shallow</tt> - If true, paths for nested resources which reference a specific member
371
+ # (ie. those with an :id parameter) will not use the parent path prefix or name prefix.
372
+ #
373
+ # The <tt>:shallow</tt> option is inherited by any nested resource(s).
374
+ #
375
+ # For example, 'users', 'posts' and 'comments' all use shallow paths with the following nested resources:
376
+ #
377
+ # map.resources :users, :shallow => true do |user|
378
+ # user.resources :posts do |post|
379
+ # post.resources :comments
380
+ # end
381
+ # end
382
+ # # --> GET /users/1/posts (maps to the PostsController#index action as usual)
383
+ # # also adds the usual named route called "user_posts"
384
+ # # --> GET /posts/2 (maps to the PostsController#show action as if it were not nested)
385
+ # # also adds the named route called "post"
386
+ # # --> GET /posts/2/comments (maps to the CommentsController#index action)
387
+ # # also adds the named route called "post_comments"
388
+ # # --> GET /comments/2 (maps to the CommentsController#show action as if it were not nested)
389
+ # # also adds the named route called "comment"
390
+ #
391
+ # You may also use <tt>:shallow</tt> in combination with the +has_one+ and +has_many+ shorthand notations like:
392
+ #
393
+ # map.resources :users, :has_many => { :posts => :comments }, :shallow => true
394
+ #
395
+ # * <tt>:only</tt> and <tt>:except</tt> - Specify which of the seven default actions should be routed to.
396
+ #
397
+ # <tt>:only</tt> and <tt>:except</tt> may be set to <tt>:all</tt>, <tt>:none</tt>, an action name or a
398
+ # list of action names. By default, routes are generated for all seven actions.
399
+ #
400
+ # For example:
401
+ #
402
+ # map.resources :posts, :only => [:index, :show] do |post|
403
+ # post.resources :comments, :except => [:update, :destroy]
404
+ # end
405
+ # # --> GET /posts (maps to the PostsController#index action)
406
+ # # --> POST /posts (fails)
407
+ # # --> GET /posts/1 (maps to the PostsController#show action)
408
+ # # --> DELETE /posts/1 (fails)
409
+ # # --> POST /posts/1/comments (maps to the CommentsController#create action)
410
+ # # --> PUT /posts/1/comments/1 (fails)
411
+ #
412
+ # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
413
+ #
414
+ # Examples:
415
+ #
416
+ # map.resources :messages, :path_prefix => "/thread/:thread_id"
417
+ # # --> GET /thread/7/messages/1
418
+ #
419
+ # map.resources :messages, :collection => { :rss => :get }
420
+ # # --> GET /messages/rss (maps to the #rss action)
421
+ # # also adds a named route called "rss_messages"
422
+ #
423
+ # map.resources :messages, :member => { :mark => :post }
424
+ # # --> POST /messages/1/mark (maps to the #mark action)
425
+ # # also adds a named route called "mark_message"
426
+ #
427
+ # map.resources :messages, :new => { :preview => :post }
428
+ # # --> POST /messages/new/preview (maps to the #preview action)
429
+ # # also adds a named route called "preview_new_message"
430
+ #
431
+ # map.resources :messages, :new => { :new => :any, :preview => :post }
432
+ # # --> POST /messages/new/preview (maps to the #preview action)
433
+ # # also adds a named route called "preview_new_message"
434
+ # # --> /messages/new can be invoked via any request method
435
+ #
436
+ # map.resources :messages, :controller => "categories",
437
+ # :path_prefix => "/category/:category_id",
438
+ # :name_prefix => "category_"
439
+ # # --> GET /categories/7/messages/1
440
+ # # has named route "category_message"
441
+ #
442
+ # The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an
443
+ # HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
444
+ # <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for \resource routes.
445
+ def resources(*entities, &block)
446
+ options = entities.extract_options!
447
+ entities.each { |entity| map_resource(entity, options.dup, &block) }
448
+ end
449
+
450
+ # Creates named routes for implementing verb-oriented controllers for a singleton \resource.
451
+ # A singleton \resource is global to its current context. For unnested singleton \resources,
452
+ # the \resource is global to the current user visiting the application, such as a user's
453
+ # <tt>/account</tt> profile. For nested singleton \resources, the \resource is global to its parent
454
+ # \resource, such as a <tt>projects</tt> \resource that <tt>has_one :project_manager</tt>.
455
+ # The <tt>project_manager</tt> should be mapped as a singleton \resource under <tt>projects</tt>:
456
+ #
457
+ # map.resources :projects do |project|
458
+ # project.resource :project_manager
459
+ # end
460
+ #
461
+ # See +resources+ for general conventions. These are the main differences:
462
+ # * A singular name is given to <tt>map.resource</tt>. The default controller name is still taken from the plural name.
463
+ # * To specify a custom plural name, use the <tt>:plural</tt> option. There is no <tt>:singular</tt> option.
464
+ # * No default index route is created for the singleton \resource controller.
465
+ # * When nesting singleton \resources, only the singular name is used as the path prefix (example: 'account/messages/1')
466
+ #
467
+ # For example:
468
+ #
469
+ # map.resource :account
470
+ #
471
+ # maps these actions in the Accounts controller:
472
+ #
473
+ # class AccountsController < ActionController::Base
474
+ # # GET new_account_url
475
+ # def new
476
+ # # return an HTML form for describing the new account
477
+ # end
478
+ #
479
+ # # POST account_url
480
+ # def create
481
+ # # create an account
482
+ # end
483
+ #
484
+ # # GET account_url
485
+ # def show
486
+ # # find and return the account
487
+ # end
488
+ #
489
+ # # GET edit_account_url
490
+ # def edit
491
+ # # return an HTML form for editing the account
492
+ # end
493
+ #
494
+ # # PUT account_url
495
+ # def update
496
+ # # find and update the account
497
+ # end
498
+ #
499
+ # # DELETE account_url
500
+ # def destroy
501
+ # # delete the account
502
+ # end
503
+ # end
504
+ #
505
+ # Along with the routes themselves, +resource+ generates named routes for
506
+ # use in controllers and views. <tt>map.resource :account</tt> produces
507
+ # these named routes and helpers:
508
+ #
509
+ # Named Route Helpers
510
+ # ============ =============================================
511
+ # account account_url, hash_for_account_url,
512
+ # account_path, hash_for_account_path
513
+ #
514
+ # new_account new_account_url, hash_for_new_account_url,
515
+ # new_account_path, hash_for_new_account_path
516
+ #
517
+ # edit_account edit_account_url, hash_for_edit_account_url,
518
+ # edit_account_path, hash_for_edit_account_path
519
+ def resource(*entities, &block)
520
+ options = entities.extract_options!
521
+ entities.each { |entity| map_singleton_resource(entity, options.dup, &block) }
522
+ end
523
+
524
+ private
525
+ def map_resource(entities, options = {}, &block)
526
+ resource = Resource.new(entities, options)
527
+
528
+ with_options :controller => resource.controller do |map|
529
+ map_associations(resource, options)
530
+
531
+ if block_given?
532
+ with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
533
+ end
534
+
535
+ map_collection_actions(map, resource)
536
+ map_default_collection_actions(map, resource)
537
+ map_new_actions(map, resource)
538
+ map_member_actions(map, resource)
539
+ end
540
+ end
541
+
542
+ def map_singleton_resource(entities, options = {}, &block)
543
+ resource = SingletonResource.new(entities, options)
544
+
545
+ with_options :controller => resource.controller do |map|
546
+ map_associations(resource, options)
547
+
548
+ if block_given?
549
+ with_options(options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix), &block)
550
+ end
551
+
552
+ map_collection_actions(map, resource)
553
+ map_new_actions(map, resource)
554
+ map_member_actions(map, resource)
555
+ map_default_singleton_actions(map, resource)
556
+ end
557
+ end
558
+
559
+ def map_associations(resource, options)
560
+ map_has_many_associations(resource, options.delete(:has_many), options) if options[:has_many]
561
+
562
+ path_prefix = "#{options.delete(:path_prefix)}#{resource.nesting_path_prefix}"
563
+ name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"
564
+
565
+ Array(options[:has_one]).each do |association|
566
+ resource(association, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => path_prefix, :name_prefix => name_prefix))
567
+ end
568
+ end
569
+
570
+ def map_has_many_associations(resource, associations, options)
571
+ case associations
572
+ when Hash
573
+ associations.each do |association,has_many|
574
+ map_has_many_associations(resource, association, options.merge(:has_many => has_many))
575
+ end
576
+ when Array
577
+ associations.each do |association|
578
+ map_has_many_associations(resource, association, options)
579
+ end
580
+ when Symbol, String
581
+ resources(associations, options.slice(*INHERITABLE_OPTIONS).merge(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :has_many => options[:has_many]))
582
+ else
583
+ end
584
+ end
585
+
586
+ def map_collection_actions(map, resource)
587
+ resource.collection_methods.each do |method, actions|
588
+ actions.each do |action|
589
+ [method].flatten.each do |m|
590
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
591
+ action_path ||= action
592
+
593
+ map_resource_routes(map, resource, action, "#{resource.path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.name_prefix}#{resource.plural}", m)
594
+ end
595
+ end
596
+ end
597
+ end
598
+
599
+ def map_default_collection_actions(map, resource)
600
+ index_route_name = "#{resource.name_prefix}#{resource.plural}"
601
+
602
+ if resource.uncountable?
603
+ index_route_name << "_index"
604
+ end
605
+
606
+ map_resource_routes(map, resource, :index, resource.path, index_route_name)
607
+ map_resource_routes(map, resource, :create, resource.path, index_route_name)
608
+ end
609
+
610
+ def map_default_singleton_actions(map, resource)
611
+ map_resource_routes(map, resource, :create, resource.path, "#{resource.shallow_name_prefix}#{resource.singular}")
612
+ end
613
+
614
+ def map_new_actions(map, resource)
615
+ resource.new_methods.each do |method, actions|
616
+ actions.each do |action|
617
+ route_path = resource.new_path
618
+ route_name = "new_#{resource.name_prefix}#{resource.singular}"
619
+
620
+ unless action == :new
621
+ route_path = "#{route_path}#{resource.action_separator}#{action}"
622
+ route_name = "#{action}_#{route_name}"
623
+ end
624
+
625
+ map_resource_routes(map, resource, action, route_path, route_name, method)
626
+ end
627
+ end
628
+ end
629
+
630
+ def map_member_actions(map, resource)
631
+ resource.member_methods.each do |method, actions|
632
+ actions.each do |action|
633
+ [method].flatten.each do |m|
634
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
635
+ action_path ||= Base.resources_path_names[action] || action
636
+
637
+ map_resource_routes(map, resource, action, "#{resource.member_path}#{resource.action_separator}#{action_path}", "#{action}_#{resource.shallow_name_prefix}#{resource.singular}", m, { :force_id => true })
638
+ end
639
+ end
640
+ end
641
+
642
+ route_path = "#{resource.shallow_name_prefix}#{resource.singular}"
643
+ map_resource_routes(map, resource, :show, resource.member_path, route_path)
644
+ map_resource_routes(map, resource, :update, resource.member_path, route_path)
645
+ map_resource_routes(map, resource, :destroy, resource.member_path, route_path)
646
+ end
647
+
648
+ def map_resource_routes(map, resource, action, route_path, route_name = nil, method = nil, resource_options = {} )
649
+ if resource.has_action?(action)
650
+ action_options = action_options_for(action, resource, method, resource_options)
651
+ formatted_route_path = "#{route_path}.:format"
652
+
653
+ if route_name && @set.named_routes[route_name.to_sym].nil?
654
+ map.named_route(route_name, formatted_route_path, action_options)
655
+ else
656
+ map.connect(formatted_route_path, action_options)
657
+ end
658
+ end
659
+ end
660
+
661
+ def add_conditions_for(conditions, method)
662
+ ({:conditions => conditions.dup}).tap do |options|
663
+ options[:conditions][:method] = method unless method == :any
664
+ end
665
+ end
666
+
667
+ def action_options_for(action, resource, method = nil, resource_options = {})
668
+ default_options = { :action => action.to_s }
669
+ require_id = !resource.kind_of?(SingletonResource)
670
+ force_id = resource_options[:force_id] && !resource.kind_of?(SingletonResource)
671
+
672
+ case default_options[:action]
673
+ when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
674
+ when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
675
+ when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
676
+ when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
677
+ when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
678
+ else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements(force_id))
679
+ end
680
+ end
681
+ end
682
+ end