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,572 @@
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
+ class Resource #:nodoc:
46
+ attr_reader :collection_methods, :member_methods, :new_methods
47
+ attr_reader :path_prefix, :name_prefix, :path_segment
48
+ attr_reader :plural, :singular
49
+ attr_reader :options
50
+
51
+ def initialize(entities, options)
52
+ @plural ||= entities
53
+ @singular ||= options[:singular] || plural.to_s.singularize
54
+ @path_segment = options.delete(:as) || @plural
55
+
56
+ @options = options
57
+
58
+ arrange_actions
59
+ add_default_actions
60
+ set_prefixes
61
+ end
62
+
63
+ def controller
64
+ @controller ||= "#{options[:namespace]}#{(options[:controller] || plural).to_s}"
65
+ end
66
+
67
+ def requirements(with_id = false)
68
+ @requirements ||= @options[:requirements] || {}
69
+ @id_requirement ||= { :id => @requirements.delete(:id) || /[^#{Routing::SEPARATORS.join}]+/ }
70
+
71
+ with_id ? @requirements.merge(@id_requirement) : @requirements
72
+ end
73
+
74
+ def conditions
75
+ @conditions = @options[:conditions] || {}
76
+ end
77
+
78
+ def path
79
+ @path ||= "#{path_prefix}/#{path_segment}"
80
+ end
81
+
82
+ def new_path
83
+ new_action = self.options[:path_names][:new] if self.options[:path_names]
84
+ new_action ||= Base.resources_path_names[:new]
85
+ @new_path ||= "#{path}/#{new_action}"
86
+ end
87
+
88
+ def member_path
89
+ @member_path ||= "#{path}/:id"
90
+ end
91
+
92
+ def nesting_path_prefix
93
+ @nesting_path_prefix ||= "#{path}/:#{singular}_id"
94
+ end
95
+
96
+ def nesting_name_prefix
97
+ "#{name_prefix}#{singular}_"
98
+ end
99
+
100
+ def action_separator
101
+ @action_separator ||= Base.resource_action_separator
102
+ end
103
+
104
+ def uncountable?
105
+ @singular.to_s == @plural.to_s
106
+ end
107
+
108
+ protected
109
+ def arrange_actions
110
+ @collection_methods = arrange_actions_by_methods(options.delete(:collection))
111
+ @member_methods = arrange_actions_by_methods(options.delete(:member))
112
+ @new_methods = arrange_actions_by_methods(options.delete(:new))
113
+ end
114
+
115
+ def add_default_actions
116
+ add_default_action(member_methods, :get, :edit)
117
+ add_default_action(new_methods, :get, :new)
118
+ end
119
+
120
+ def set_prefixes
121
+ @path_prefix = options.delete(:path_prefix)
122
+ @name_prefix = options.delete(:name_prefix)
123
+ end
124
+
125
+ def arrange_actions_by_methods(actions)
126
+ (actions || {}).inject({}) do |flipped_hash, (key, value)|
127
+ (flipped_hash[value] ||= []) << key
128
+ flipped_hash
129
+ end
130
+ end
131
+
132
+ def add_default_action(collection, method, action)
133
+ (collection[method] ||= []).unshift(action)
134
+ end
135
+ end
136
+
137
+ class SingletonResource < Resource #:nodoc:
138
+ def initialize(entity, options)
139
+ @singular = @plural = entity
140
+ options[:controller] ||= @singular.to_s.pluralize
141
+ super
142
+ end
143
+
144
+ alias_method :member_path, :path
145
+ alias_method :nesting_path_prefix, :path
146
+ end
147
+
148
+ # Creates named routes for implementing verb-oriented controllers
149
+ # for a collection resource.
150
+ #
151
+ # For example:
152
+ #
153
+ # map.resources :messages
154
+ #
155
+ # will map the following actions in the corresponding controller:
156
+ #
157
+ # class MessagesController < ActionController::Base
158
+ # # GET messages_url
159
+ # def index
160
+ # # return all messages
161
+ # end
162
+ #
163
+ # # GET new_message_url
164
+ # def new
165
+ # # return an HTML form for describing a new message
166
+ # end
167
+ #
168
+ # # POST messages_url
169
+ # def create
170
+ # # create a new message
171
+ # end
172
+ #
173
+ # # GET message_url(:id => 1)
174
+ # def show
175
+ # # find and return a specific message
176
+ # end
177
+ #
178
+ # # GET edit_message_url(:id => 1)
179
+ # def edit
180
+ # # return an HTML form for editing a specific message
181
+ # end
182
+ #
183
+ # # PUT message_url(:id => 1)
184
+ # def update
185
+ # # find and update a specific message
186
+ # end
187
+ #
188
+ # # DELETE message_url(:id => 1)
189
+ # def destroy
190
+ # # delete a specific message
191
+ # end
192
+ # end
193
+ #
194
+ # Along with the routes themselves, +resources+ generates named routes for use in
195
+ # controllers and views. <tt>map.resources :messages</tt> produces the following named routes and helpers:
196
+ #
197
+ # Named Route Helpers
198
+ # ============ =====================================================
199
+ # messages messages_url, hash_for_messages_url,
200
+ # messages_path, hash_for_messages_path
201
+ #
202
+ # message message_url(id), hash_for_message_url(id),
203
+ # message_path(id), hash_for_message_path(id)
204
+ #
205
+ # new_message new_message_url, hash_for_new_message_url,
206
+ # new_message_path, hash_for_new_message_path
207
+ #
208
+ # edit_message edit_message_url(id), hash_for_edit_message_url(id),
209
+ # edit_message_path(id), hash_for_edit_message_path(id)
210
+ #
211
+ # You can use these helpers instead of +url_for+ or methods that take +url_for+ parameters. For example:
212
+ #
213
+ # redirect_to :controller => 'messages', :action => 'index'
214
+ # # and
215
+ # <%= link_to "edit this message", :controller => 'messages', :action => 'edit', :id => @message.id %>
216
+ #
217
+ # now become:
218
+ #
219
+ # redirect_to messages_url
220
+ # # and
221
+ # <%= link_to "edit this message", edit_message_url(@message) # calls @message.id automatically
222
+ #
223
+ # Since web browsers don't support the PUT and DELETE verbs, you will need to add a parameter '_method' to your
224
+ # form tags. The form helpers make this a little easier. For an update form with a <tt>@message</tt> object:
225
+ #
226
+ # <%= form_tag message_path(@message), :method => :put %>
227
+ #
228
+ # or
229
+ #
230
+ # <% form_for :message, @message, :url => message_path(@message), :html => {:method => :put} do |f| %>
231
+ #
232
+ # or
233
+ #
234
+ # <% form_for @message do |f| %>
235
+ #
236
+ # which takes into account whether <tt>@message</tt> is a new record or not and generates the
237
+ # path and method accordingly.
238
+ #
239
+ # The +resources+ method accepts the following options to customize the resulting routes:
240
+ # * <tt>:collection</tt> - Add named routes for other actions that operate on the collection.
241
+ # Takes a hash of <tt>#{action} => #{method}</tt>, where method is <tt>:get</tt>/<tt>:post</tt>/<tt>:put</tt>/<tt>:delete</tt>
242
+ # or <tt>:any</tt> if the method does not matter. These routes map to a URL like /messages/rss, with a route of +rss_messages_url+.
243
+ # * <tt>:member</tt> - Same as <tt>:collection</tt>, but for actions that operate on a specific member.
244
+ # * <tt>:new</tt> - Same as <tt>:collection</tt>, but for actions that operate on the new resource action.
245
+ # * <tt>:controller</tt> - Specify the controller name for the routes.
246
+ # * <tt>:singular</tt> - Specify the singular name used in the member routes.
247
+ # * <tt>:requirements</tt> - Set custom routing parameter requirements.
248
+ # * <tt>:conditions</tt> - Specify custom routing recognition conditions. Resources sets the <tt>:method</tt> value for the method-specific routes.
249
+ # * <tt>:as</tt> - Specify a different resource name to use in the URL path. For example:
250
+ # # products_path == '/productos'
251
+ # map.resources :products, :as => 'productos' do |product|
252
+ # # product_reviews_path(product) == '/productos/1234/comentarios'
253
+ # product.resources :product_reviews, :as => 'comentarios'
254
+ # end
255
+ #
256
+ # * <tt>:has_one</tt> - Specify nested resources, this is a shorthand for mapping singleton resources beneath the current.
257
+ # * <tt>:has_many</tt> - Same has <tt>:has_one</tt>, but for plural resources.
258
+ #
259
+ # You may directly specify the routing association with +has_one+ and +has_many+ like:
260
+ #
261
+ # map.resources :notes, :has_one => :author, :has_many => [:comments, :attachments]
262
+ #
263
+ # This is the same as:
264
+ #
265
+ # map.resources :notes do |notes|
266
+ # notes.resource :author
267
+ # notes.resources :comments
268
+ # notes.resources :attachments
269
+ # end
270
+ #
271
+ # * <tt>:path_names</tt> - Specify different names for the 'new' and 'edit' actions. For example:
272
+ # # new_products_path == '/productos/nuevo'
273
+ # map.resources :products, :as => 'productos', :path_names => { :new => 'nuevo', :edit => 'editar' }
274
+ #
275
+ # You can also set default action names from an environment, like this:
276
+ # config.action_controller.resources_path_names = { :new => 'nuevo', :edit => 'editar' }
277
+ #
278
+ # * <tt>:path_prefix</tt> - Set a prefix to the routes with required route variables.
279
+ #
280
+ # Weblog comments usually belong to a post, so you might use resources like:
281
+ #
282
+ # map.resources :articles
283
+ # map.resources :comments, :path_prefix => '/articles/:article_id'
284
+ #
285
+ # You can nest resources calls to set this automatically:
286
+ #
287
+ # map.resources :articles do |article|
288
+ # article.resources :comments
289
+ # end
290
+ #
291
+ # The comment resources work the same, but must now include a value for <tt>:article_id</tt>.
292
+ #
293
+ # article_comments_url(@article)
294
+ # article_comment_url(@article, @comment)
295
+ #
296
+ # article_comments_url(:article_id => @article)
297
+ # article_comment_url(:article_id => @article, :id => @comment)
298
+ #
299
+ # * <tt>:name_prefix</tt> - Define a prefix for all generated routes, usually ending in an underscore.
300
+ # Use this if you have named routes that may clash.
301
+ #
302
+ # map.resources :tags, :path_prefix => '/books/:book_id', :name_prefix => 'book_'
303
+ # map.resources :tags, :path_prefix => '/toys/:toy_id', :name_prefix => 'toy_'
304
+ #
305
+ # You may also use <tt>:name_prefix</tt> to override the generic named routes in a nested resource:
306
+ #
307
+ # map.resources :articles do |article|
308
+ # article.resources :comments, :name_prefix => nil
309
+ # end
310
+ #
311
+ # This will yield named resources like so:
312
+ #
313
+ # comments_url(@article)
314
+ # comment_url(@article, @comment)
315
+ #
316
+ # If <tt>map.resources</tt> is called with multiple resources, they all get the same options applied.
317
+ #
318
+ # Examples:
319
+ #
320
+ # map.resources :messages, :path_prefix => "/thread/:thread_id"
321
+ # # --> GET /thread/7/messages/1
322
+ #
323
+ # map.resources :messages, :collection => { :rss => :get }
324
+ # # --> GET /messages/rss (maps to the #rss action)
325
+ # # also adds a named route called "rss_messages"
326
+ #
327
+ # map.resources :messages, :member => { :mark => :post }
328
+ # # --> POST /messages/1/mark (maps to the #mark action)
329
+ # # also adds a named route called "mark_message"
330
+ #
331
+ # map.resources :messages, :new => { :preview => :post }
332
+ # # --> POST /messages/new/preview (maps to the #preview action)
333
+ # # also adds a named route called "preview_new_message"
334
+ #
335
+ # map.resources :messages, :new => { :new => :any, :preview => :post }
336
+ # # --> POST /messages/new/preview (maps to the #preview action)
337
+ # # also adds a named route called "preview_new_message"
338
+ # # --> /messages/new can be invoked via any request method
339
+ #
340
+ # map.resources :messages, :controller => "categories",
341
+ # :path_prefix => "/category/:category_id",
342
+ # :name_prefix => "category_"
343
+ # # --> GET /categories/7/messages/1
344
+ # # has named route "category_message"
345
+ #
346
+ # The +resources+ method sets HTTP method restrictions on the routes it generates. For example, making an
347
+ # HTTP POST on <tt>new_message_url</tt> will raise a RoutingError exception. The default route in
348
+ # <tt>config/routes.rb</tt> overrides this and allows invalid HTTP methods for resource routes.
349
+ def resources(*entities, &block)
350
+ options = entities.extract_options!
351
+ entities.each { |entity| map_resource(entity, options.dup, &block) }
352
+ end
353
+
354
+ # Creates named routes for implementing verb-oriented controllers for a singleton resource.
355
+ # A singleton resource is global to its current context. For unnested singleton resources,
356
+ # the resource is global to the current user visiting the application, such as a user's
357
+ # /account profile. For nested singleton resources, the resource is global to its parent
358
+ # resource, such as a <tt>projects</tt> resource that <tt>has_one :project_manager</tt>.
359
+ # The <tt>project_manager</tt> should be mapped as a singleton resource under <tt>projects</tt>:
360
+ #
361
+ # map.resources :projects do |project|
362
+ # project.resource :project_manager
363
+ # end
364
+ #
365
+ # See map.resources for general conventions. These are the main differences:
366
+ # * A singular name is given to map.resource. The default controller name is still taken from the plural name.
367
+ # * To specify a custom plural name, use the <tt>:plural</tt> option. There is no <tt>:singular</tt> option.
368
+ # * No default index route is created for the singleton resource controller.
369
+ # * When nesting singleton resources, only the singular name is used as the path prefix (example: 'account/messages/1')
370
+ #
371
+ # For example:
372
+ #
373
+ # map.resource :account
374
+ #
375
+ # maps these actions in the Accounts controller:
376
+ #
377
+ # class AccountsController < ActionController::Base
378
+ # # GET new_account_url
379
+ # def new
380
+ # # return an HTML form for describing the new account
381
+ # end
382
+ #
383
+ # # POST account_url
384
+ # def create
385
+ # # create an account
386
+ # end
387
+ #
388
+ # # GET account_url
389
+ # def show
390
+ # # find and return the account
391
+ # end
392
+ #
393
+ # # GET edit_account_url
394
+ # def edit
395
+ # # return an HTML form for editing the account
396
+ # end
397
+ #
398
+ # # PUT account_url
399
+ # def update
400
+ # # find and update the account
401
+ # end
402
+ #
403
+ # # DELETE account_url
404
+ # def destroy
405
+ # # delete the account
406
+ # end
407
+ # end
408
+ #
409
+ # Along with the routes themselves, +resource+ generates named routes for
410
+ # use in controllers and views. <tt>map.resource :account</tt> produces
411
+ # these named routes and helpers:
412
+ #
413
+ # Named Route Helpers
414
+ # ============ =============================================
415
+ # account account_url, hash_for_account_url,
416
+ # account_path, hash_for_account_path
417
+ #
418
+ # new_account new_account_url, hash_for_new_account_url,
419
+ # new_account_path, hash_for_new_account_path
420
+ #
421
+ # edit_account edit_account_url, hash_for_edit_account_url,
422
+ # edit_account_path, hash_for_edit_account_path
423
+ def resource(*entities, &block)
424
+ options = entities.extract_options!
425
+ entities.each { |entity| map_singleton_resource(entity, options.dup, &block) }
426
+ end
427
+
428
+ private
429
+ def map_resource(entities, options = {}, &block)
430
+ resource = Resource.new(entities, options)
431
+
432
+ with_options :controller => resource.controller do |map|
433
+ map_collection_actions(map, resource)
434
+ map_default_collection_actions(map, resource)
435
+ map_new_actions(map, resource)
436
+ map_member_actions(map, resource)
437
+
438
+ map_associations(resource, options)
439
+
440
+ if block_given?
441
+ with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], &block)
442
+ end
443
+ end
444
+ end
445
+
446
+ def map_singleton_resource(entities, options = {}, &block)
447
+ resource = SingletonResource.new(entities, options)
448
+
449
+ with_options :controller => resource.controller do |map|
450
+ map_collection_actions(map, resource)
451
+ map_default_singleton_actions(map, resource)
452
+ map_new_actions(map, resource)
453
+ map_member_actions(map, resource)
454
+
455
+ map_associations(resource, options)
456
+
457
+ if block_given?
458
+ with_options(:path_prefix => resource.nesting_path_prefix, :name_prefix => resource.nesting_name_prefix, :namespace => options[:namespace], &block)
459
+ end
460
+ end
461
+ end
462
+
463
+ def map_associations(resource, options)
464
+ path_prefix = "#{options.delete(:path_prefix)}#{resource.nesting_path_prefix}"
465
+ name_prefix = "#{options.delete(:name_prefix)}#{resource.nesting_name_prefix}"
466
+
467
+ Array(options[:has_many]).each do |association|
468
+ resources(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace])
469
+ end
470
+
471
+ Array(options[:has_one]).each do |association|
472
+ resource(association, :path_prefix => path_prefix, :name_prefix => name_prefix, :namespace => options[:namespace])
473
+ end
474
+ end
475
+
476
+ def map_collection_actions(map, resource)
477
+ resource.collection_methods.each do |method, actions|
478
+ actions.each do |action|
479
+ action_options = action_options_for(action, resource, method)
480
+ map.named_route("#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}", action_options)
481
+ map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.plural}", "#{resource.path}#{resource.action_separator}#{action}.:format", action_options)
482
+ end
483
+ end
484
+ end
485
+
486
+ def map_default_collection_actions(map, resource)
487
+ index_action_options = action_options_for("index", resource)
488
+ index_route_name = "#{resource.name_prefix}#{resource.plural}"
489
+
490
+ if resource.uncountable?
491
+ index_route_name << "_index"
492
+ end
493
+
494
+ map.named_route(index_route_name, resource.path, index_action_options)
495
+ map.named_route("formatted_#{index_route_name}", "#{resource.path}.:format", index_action_options)
496
+
497
+ create_action_options = action_options_for("create", resource)
498
+ map.connect(resource.path, create_action_options)
499
+ map.connect("#{resource.path}.:format", create_action_options)
500
+ end
501
+
502
+ def map_default_singleton_actions(map, resource)
503
+ create_action_options = action_options_for("create", resource)
504
+ map.connect(resource.path, create_action_options)
505
+ map.connect("#{resource.path}.:format", create_action_options)
506
+ end
507
+
508
+ def map_new_actions(map, resource)
509
+ resource.new_methods.each do |method, actions|
510
+ actions.each do |action|
511
+ action_options = action_options_for(action, resource, method)
512
+ if action == :new
513
+ map.named_route("new_#{resource.name_prefix}#{resource.singular}", resource.new_path, action_options)
514
+ map.named_route("formatted_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}.:format", action_options)
515
+ else
516
+ map.named_route("#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}", action_options)
517
+ map.named_route("formatted_#{action}_new_#{resource.name_prefix}#{resource.singular}", "#{resource.new_path}#{resource.action_separator}#{action}.:format", action_options)
518
+ end
519
+ end
520
+ end
521
+ end
522
+
523
+ def map_member_actions(map, resource)
524
+ resource.member_methods.each do |method, actions|
525
+ actions.each do |action|
526
+ action_options = action_options_for(action, resource, method)
527
+
528
+ action_path = resource.options[:path_names][action] if resource.options[:path_names].is_a?(Hash)
529
+ action_path ||= Base.resources_path_names[action] || action
530
+
531
+ map.named_route("#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}", action_options)
532
+ map.named_route("formatted_#{action}_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}#{resource.action_separator}#{action_path}.:format",action_options)
533
+ end
534
+ end
535
+
536
+ show_action_options = action_options_for("show", resource)
537
+ map.named_route("#{resource.name_prefix}#{resource.singular}", resource.member_path, show_action_options)
538
+ map.named_route("formatted_#{resource.name_prefix}#{resource.singular}", "#{resource.member_path}.:format", show_action_options)
539
+
540
+ update_action_options = action_options_for("update", resource)
541
+ map.connect(resource.member_path, update_action_options)
542
+ map.connect("#{resource.member_path}.:format", update_action_options)
543
+
544
+ destroy_action_options = action_options_for("destroy", resource)
545
+ map.connect(resource.member_path, destroy_action_options)
546
+ map.connect("#{resource.member_path}.:format", destroy_action_options)
547
+ end
548
+
549
+ def add_conditions_for(conditions, method)
550
+ returning({:conditions => conditions.dup}) do |options|
551
+ options[:conditions][:method] = method unless method == :any
552
+ end
553
+ end
554
+
555
+ def action_options_for(action, resource, method = nil)
556
+ default_options = { :action => action.to_s }
557
+ require_id = !resource.kind_of?(SingletonResource)
558
+ case default_options[:action]
559
+ when "index", "new"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements)
560
+ when "create"; default_options.merge(add_conditions_for(resource.conditions, method || :post)).merge(resource.requirements)
561
+ when "show", "edit"; default_options.merge(add_conditions_for(resource.conditions, method || :get)).merge(resource.requirements(require_id))
562
+ when "update"; default_options.merge(add_conditions_for(resource.conditions, method || :put)).merge(resource.requirements(require_id))
563
+ when "destroy"; default_options.merge(add_conditions_for(resource.conditions, method || :delete)).merge(resource.requirements(require_id))
564
+ else default_options.merge(add_conditions_for(resource.conditions, method)).merge(resource.requirements)
565
+ end
566
+ end
567
+ end
568
+ end
569
+
570
+ class ActionController::Routing::RouteSet::Mapper
571
+ include ActionController::Resources
572
+ end