actionpack 1.13.6 → 2.0.0

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

Potentially problematic release.


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

Files changed (317) hide show
  1. data/CHANGELOG +1400 -20
  2. data/MIT-LICENSE +1 -1
  3. data/README +5 -5
  4. data/RUNNING_UNIT_TESTS +4 -5
  5. data/Rakefile +5 -6
  6. data/install.rb +2 -2
  7. data/lib/action_controller.rb +11 -15
  8. data/lib/action_controller/assertions.rb +12 -25
  9. data/lib/action_controller/assertions/dom_assertions.rb +18 -4
  10. data/lib/action_controller/assertions/model_assertions.rb +8 -1
  11. data/lib/action_controller/assertions/response_assertions.rb +35 -12
  12. data/lib/action_controller/assertions/routing_assertions.rb +56 -12
  13. data/lib/action_controller/assertions/selector_assertions.rb +105 -38
  14. data/lib/action_controller/assertions/tag_assertions.rb +28 -15
  15. data/lib/action_controller/base.rb +318 -250
  16. data/lib/action_controller/benchmarking.rb +33 -29
  17. data/lib/action_controller/caching.rb +130 -64
  18. data/lib/action_controller/cgi_ext.rb +16 -0
  19. data/lib/action_controller/cgi_ext/{cookie_performance_fix.rb → cookie.rb} +25 -40
  20. data/lib/action_controller/cgi_ext/query_extension.rb +22 -0
  21. data/lib/action_controller/cgi_ext/session.rb +73 -0
  22. data/lib/action_controller/cgi_ext/stdinput.rb +23 -0
  23. data/lib/action_controller/cgi_process.rb +34 -57
  24. data/lib/action_controller/components.rb +19 -36
  25. data/lib/action_controller/cookies.rb +10 -9
  26. data/lib/action_controller/dispatcher.rb +195 -0
  27. data/lib/action_controller/filters.rb +35 -34
  28. data/lib/action_controller/flash.rb +30 -35
  29. data/lib/action_controller/helpers.rb +121 -47
  30. data/lib/action_controller/http_authentication.rb +126 -0
  31. data/lib/action_controller/integration.rb +105 -101
  32. data/lib/action_controller/layout.rb +59 -47
  33. data/lib/action_controller/mime_responds.rb +57 -68
  34. data/lib/action_controller/mime_type.rb +43 -80
  35. data/lib/action_controller/mime_types.rb +20 -0
  36. data/lib/action_controller/polymorphic_routes.rb +88 -0
  37. data/lib/action_controller/record_identifier.rb +91 -0
  38. data/lib/action_controller/request.rb +553 -88
  39. data/lib/action_controller/request_forgery_protection.rb +126 -0
  40. data/lib/action_controller/request_profiler.rb +138 -0
  41. data/lib/action_controller/rescue.rb +185 -69
  42. data/lib/action_controller/resources.rb +211 -172
  43. data/lib/action_controller/response.rb +49 -8
  44. data/lib/action_controller/routing.rb +359 -236
  45. data/lib/action_controller/routing_optimisation.rb +119 -0
  46. data/lib/action_controller/session/active_record_store.rb +3 -2
  47. data/lib/action_controller/session/cookie_store.rb +161 -0
  48. data/lib/action_controller/session/mem_cache_store.rb +9 -16
  49. data/lib/action_controller/session_management.rb +17 -8
  50. data/lib/action_controller/streaming.rb +6 -3
  51. data/lib/action_controller/templates/rescues/_request_and_response.erb +24 -0
  52. data/lib/action_controller/templates/rescues/{_trace.rhtml → _trace.erb} +0 -0
  53. data/lib/action_controller/templates/rescues/{diagnostics.rhtml → diagnostics.erb} +2 -2
  54. data/lib/action_controller/templates/rescues/{layout.rhtml → layout.erb} +0 -0
  55. data/lib/action_controller/templates/rescues/{missing_template.rhtml → missing_template.erb} +0 -0
  56. data/lib/action_controller/templates/rescues/{routing_error.rhtml → routing_error.erb} +0 -0
  57. data/lib/action_controller/templates/rescues/{template_error.rhtml → template_error.erb} +2 -2
  58. data/lib/action_controller/templates/rescues/{unknown_action.rhtml → unknown_action.erb} +0 -0
  59. data/lib/action_controller/test_case.rb +53 -0
  60. data/lib/action_controller/test_process.rb +59 -46
  61. data/lib/action_controller/url_rewriter.rb +48 -24
  62. data/lib/action_controller/vendor/html-scanner/html/document.rb +7 -4
  63. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +173 -0
  64. data/lib/action_controller/vendor/html-scanner/html/selector.rb +11 -6
  65. data/lib/action_controller/verification.rb +27 -21
  66. data/lib/action_pack.rb +1 -1
  67. data/lib/action_pack/version.rb +4 -4
  68. data/lib/action_view.rb +2 -3
  69. data/lib/action_view/base.rb +218 -63
  70. data/lib/action_view/compiled_templates.rb +1 -2
  71. data/lib/action_view/helpers/active_record_helper.rb +35 -17
  72. data/lib/action_view/helpers/asset_tag_helper.rb +395 -87
  73. data/lib/action_view/helpers/atom_feed_helper.rb +111 -0
  74. data/lib/action_view/helpers/benchmark_helper.rb +12 -5
  75. data/lib/action_view/helpers/cache_helper.rb +29 -0
  76. data/lib/action_view/helpers/capture_helper.rb +97 -63
  77. data/lib/action_view/helpers/date_helper.rb +295 -35
  78. data/lib/action_view/helpers/debug_helper.rb +6 -2
  79. data/lib/action_view/helpers/form_helper.rb +354 -111
  80. data/lib/action_view/helpers/form_options_helper.rb +171 -109
  81. data/lib/action_view/helpers/form_tag_helper.rb +332 -76
  82. data/lib/action_view/helpers/javascript_helper.rb +35 -11
  83. data/lib/action_view/helpers/javascripts/controls.js +484 -354
  84. data/lib/action_view/helpers/javascripts/dragdrop.js +88 -58
  85. data/lib/action_view/helpers/javascripts/effects.js +396 -364
  86. data/lib/action_view/helpers/javascripts/prototype.js +2817 -1107
  87. data/lib/action_view/helpers/number_helper.rb +84 -60
  88. data/lib/action_view/helpers/prototype_helper.rb +419 -43
  89. data/lib/action_view/helpers/record_identification_helper.rb +20 -0
  90. data/lib/action_view/helpers/record_tag_helper.rb +59 -0
  91. data/lib/action_view/helpers/sanitize_helper.rb +223 -0
  92. data/lib/action_view/helpers/scriptaculous_helper.rb +63 -4
  93. data/lib/action_view/helpers/tag_helper.rb +69 -39
  94. data/lib/action_view/helpers/text_helper.rb +221 -148
  95. data/lib/action_view/helpers/url_helper.rb +283 -165
  96. data/lib/action_view/partials.rb +134 -62
  97. data/lib/action_view/template_error.rb +4 -12
  98. data/lib/actionpack.rb +1 -0
  99. data/test/abstract_unit.rb +21 -1
  100. data/test/action_view_test.rb +26 -0
  101. data/test/active_record_unit.rb +12 -20
  102. data/test/activerecord/active_record_store_test.rb +2 -2
  103. data/test/activerecord/render_partial_with_record_identification_test.rb +74 -0
  104. data/test/controller/action_pack_assertions_test.rb +21 -152
  105. data/test/controller/addresses_render_test.rb +2 -7
  106. data/test/controller/assert_select_test.rb +120 -14
  107. data/test/controller/base_test.rb +11 -13
  108. data/test/controller/caching_test.rb +125 -5
  109. data/test/controller/capture_test.rb +23 -16
  110. data/test/controller/cgi_test.rb +66 -391
  111. data/test/controller/components_test.rb +31 -42
  112. data/test/controller/content_type_test.rb +1 -1
  113. data/test/controller/cookie_test.rb +42 -14
  114. data/test/controller/deprecation/deprecated_base_methods_test.rb +1 -42
  115. data/test/controller/dispatcher_test.rb +123 -0
  116. data/test/controller/fake_models.rb +5 -0
  117. data/test/controller/filters_test.rb +44 -7
  118. data/test/controller/flash_test.rb +46 -2
  119. data/test/controller/fragment_store_setting_test.rb +10 -8
  120. data/test/controller/helper_test.rb +19 -2
  121. data/test/controller/html-scanner/document_test.rb +124 -0
  122. data/test/controller/html-scanner/node_test.rb +69 -0
  123. data/test/controller/html-scanner/sanitizer_test.rb +250 -0
  124. data/test/controller/html-scanner/tag_node_test.rb +239 -0
  125. data/test/controller/html-scanner/text_node_test.rb +51 -0
  126. data/test/controller/html-scanner/tokenizer_test.rb +125 -0
  127. data/test/controller/http_authentication_test.rb +54 -0
  128. data/test/controller/integration_test.rb +12 -26
  129. data/test/controller/layout_test.rb +64 -12
  130. data/test/controller/mime_responds_test.rb +193 -38
  131. data/test/controller/mime_type_test.rb +30 -8
  132. data/test/controller/new_render_test.rb +104 -22
  133. data/test/controller/polymorphic_routes_test.rb +98 -0
  134. data/test/controller/record_identifier_test.rb +103 -0
  135. data/test/controller/redirect_test.rb +120 -18
  136. data/test/controller/render_test.rb +195 -45
  137. data/test/controller/request_forgery_protection_test.rb +217 -0
  138. data/test/controller/request_test.rb +545 -27
  139. data/test/controller/rescue_test.rb +501 -0
  140. data/test/controller/resources_test.rb +258 -132
  141. data/test/controller/routing_test.rb +502 -106
  142. data/test/controller/selector_test.rb +5 -5
  143. data/test/controller/send_file_test.rb +17 -7
  144. data/test/controller/session/cookie_store_test.rb +246 -0
  145. data/test/controller/session/mem_cache_store_test.rb +182 -0
  146. data/test/controller/session_fixation_test.rb +8 -11
  147. data/test/controller/session_management_test.rb +7 -7
  148. data/test/controller/test_test.rb +150 -38
  149. data/test/controller/url_rewriter_test.rb +87 -12
  150. data/test/controller/verification_test.rb +11 -0
  151. data/test/controller/view_paths_test.rb +137 -0
  152. data/test/controller/webservice_test.rb +11 -75
  153. data/test/fixtures/addresses/{list.rhtml → list.erb} +0 -0
  154. data/test/fixtures/db_definitions/sqlite.sql +2 -1
  155. data/test/fixtures/developer.rb +2 -0
  156. data/test/fixtures/fun/games/{hello_world.rhtml → hello_world.erb} +0 -0
  157. data/test/fixtures/helpers/fun/pdf_helper.rb +1 -1
  158. data/test/fixtures/layout_tests/alt/hello.rhtml +1 -0
  159. data/test/fixtures/layout_tests/layouts/multiple_extensions.html.erb +1 -0
  160. data/test/fixtures/layouts/{builder.rxml → builder.builder} +0 -0
  161. data/test/fixtures/layouts/{standard.rhtml → standard.erb} +0 -0
  162. data/test/fixtures/layouts/{talk_from_action.rhtml → talk_from_action.erb} +0 -0
  163. data/test/fixtures/layouts/{yield.rhtml → yield.erb} +0 -0
  164. data/test/fixtures/multipart/binary_file +0 -0
  165. data/test/fixtures/multipart/bracketed_param +5 -0
  166. data/test/fixtures/override/test/hello_world.erb +1 -0
  167. data/test/fixtures/override2/layouts/test/sub.erb +1 -0
  168. data/test/fixtures/post_test/layouts/post.html.erb +1 -0
  169. data/test/fixtures/post_test/layouts/super_post.iphone.erb +1 -0
  170. data/test/fixtures/post_test/post/index.html.erb +1 -0
  171. data/test/fixtures/post_test/post/index.iphone.erb +1 -0
  172. data/test/fixtures/post_test/super_post/index.html.erb +1 -0
  173. data/test/fixtures/post_test/super_post/index.iphone.erb +1 -0
  174. data/test/fixtures/public/404.html +1 -0
  175. data/test/fixtures/public/500.html +1 -0
  176. data/test/fixtures/public/javascripts/application.js +0 -1
  177. data/test/fixtures/public/javascripts/bank.js +1 -0
  178. data/test/fixtures/public/javascripts/robber.js +1 -0
  179. data/test/fixtures/public/stylesheets/bank.css +1 -0
  180. data/test/fixtures/public/stylesheets/robber.css +1 -0
  181. data/test/fixtures/replies.yml +2 -0
  182. data/test/fixtures/reply.rb +2 -1
  183. data/test/fixtures/respond_to/{all_types_with_layout.rhtml → all_types_with_layout.html.erb} +0 -0
  184. data/test/fixtures/respond_to/{all_types_with_layout.rjs → all_types_with_layout.js.rjs} +0 -0
  185. data/test/fixtures/respond_to/custom_constant_handling_without_block.mobile.erb +1 -0
  186. data/test/fixtures/respond_to/iphone_with_html_response_type.html.erb +1 -0
  187. data/test/fixtures/respond_to/iphone_with_html_response_type.iphone.erb +1 -0
  188. data/test/fixtures/respond_to/layouts/missing.html.erb +1 -0
  189. data/test/fixtures/respond_to/layouts/standard.html.erb +1 -0
  190. data/test/fixtures/respond_to/layouts/standard.iphone.erb +1 -0
  191. data/test/fixtures/respond_to/{using_defaults.rhtml → using_defaults.html.erb} +0 -0
  192. data/test/fixtures/respond_to/{using_defaults.rjs → using_defaults.js.rjs} +0 -0
  193. data/test/fixtures/respond_to/{using_defaults.rxml → using_defaults.xml.builder} +0 -0
  194. data/test/fixtures/respond_to/{using_defaults_with_type_list.rhtml → using_defaults_with_type_list.html.erb} +0 -0
  195. data/test/fixtures/respond_to/{using_defaults_with_type_list.rjs → using_defaults_with_type_list.js.rjs} +0 -0
  196. data/test/fixtures/respond_to/{using_defaults_with_type_list.rxml → using_defaults_with_type_list.xml.builder} +0 -0
  197. data/test/fixtures/scope/test/{modgreet.rhtml → modgreet.erb} +0 -0
  198. data/test/fixtures/test/{_customer.rhtml → _customer.erb} +0 -0
  199. data/test/fixtures/test/{_customer_greeting.rhtml → _customer_greeting.erb} +0 -0
  200. data/test/fixtures/test/_hash_greeting.erb +1 -0
  201. data/test/fixtures/test/_hash_object.erb +2 -0
  202. data/test/fixtures/test/{_hello.rxml → _hello.builder} +0 -0
  203. data/test/fixtures/test/_layout_for_partial.html.erb +3 -0
  204. data/test/fixtures/test/_partial.erb +1 -0
  205. data/test/fixtures/test/_partial.html.erb +1 -0
  206. data/test/fixtures/test/_partial.js.erb +1 -0
  207. data/test/fixtures/test/_partial_for_use_in_layout.html.erb +1 -0
  208. data/test/fixtures/test/{_partial_only.rhtml → _partial_only.erb} +0 -0
  209. data/test/fixtures/test/{_person.rhtml → _person.erb} +0 -0
  210. data/test/fixtures/test/{action_talk_to_layout.rhtml → action_talk_to_layout.erb} +0 -0
  211. data/test/fixtures/test/{block_content_for.rhtml → block_content_for.erb} +0 -0
  212. data/test/fixtures/test/calling_partial_with_layout.html.erb +1 -0
  213. data/test/fixtures/test/{capturing.rhtml → capturing.erb} +0 -0
  214. data/test/fixtures/test/{content_for.rhtml → content_for.erb} +0 -0
  215. data/test/fixtures/test/content_for_concatenated.erb +3 -0
  216. data/test/fixtures/test/content_for_with_parameter.erb +2 -0
  217. data/test/fixtures/test/dot.directory/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
  218. data/test/fixtures/test/{erb_content_for.rhtml → erb_content_for.erb} +0 -0
  219. data/test/fixtures/test/formatted_html_erb.html.erb +1 -0
  220. data/test/fixtures/test/formatted_xml_erb.builder +1 -0
  221. data/test/fixtures/test/formatted_xml_erb.html.erb +1 -0
  222. data/test/fixtures/test/formatted_xml_erb.xml.erb +1 -0
  223. data/test/fixtures/test/{greeting.rhtml → greeting.erb} +0 -0
  224. data/test/fixtures/test/{hello.rxml → hello.builder} +0 -0
  225. data/test/fixtures/test/{hello_world.rxml → hello_world.builder} +0 -0
  226. data/test/fixtures/test/{hello_world.rhtml → hello_world.erb} +0 -0
  227. data/test/fixtures/test/{hello_world_container.rxml → hello_world_container.builder} +0 -0
  228. data/test/fixtures/test/{hello_world_with_layout_false.rhtml → hello_world_with_layout_false.erb} +0 -0
  229. data/test/fixtures/test/{hello_xml_world.rxml → hello_xml_world.builder} +0 -0
  230. data/test/fixtures/test/list.erb +1 -0
  231. data/test/fixtures/test/{non_erb_block_content_for.rxml → non_erb_block_content_for.builder} +0 -0
  232. data/test/fixtures/test/{potential_conflicts.rhtml → potential_conflicts.erb} +0 -0
  233. data/test/fixtures/test/{render_file_with_ivar.rhtml → render_file_with_ivar.erb} +0 -0
  234. data/test/fixtures/test/{render_file_with_locals.rhtml → render_file_with_locals.erb} +0 -0
  235. data/test/fixtures/test/{render_to_string_test.rhtml → render_to_string_test.erb} +0 -0
  236. data/test/fixtures/test/{update_element_with_capture.rhtml → update_element_with_capture.erb} +0 -0
  237. data/test/fixtures/test/using_layout_around_block.html.erb +1 -0
  238. data/test/fixtures/topic.rb +1 -1
  239. data/test/template/active_record_helper_test.rb +67 -20
  240. data/test/template/asset_tag_helper_test.rb +222 -54
  241. data/test/template/atom_feed_helper_test.rb +101 -0
  242. data/test/template/benchmark_helper_test.rb +2 -2
  243. data/test/template/compiled_templates_test.rb +76 -32
  244. data/test/template/date_helper_test.rb +125 -9
  245. data/test/template/form_helper_test.rb +326 -33
  246. data/test/template/form_options_helper_test.rb +822 -15
  247. data/test/template/form_tag_helper_test.rb +96 -30
  248. data/test/template/javascript_helper_test.rb +61 -13
  249. data/test/template/number_helper_test.rb +12 -11
  250. data/test/template/prototype_helper_test.rb +185 -24
  251. data/test/template/sanitize_helper_test.rb +49 -0
  252. data/test/template/scriptaculous_helper_test.rb +9 -3
  253. data/test/template/tag_helper_test.rb +13 -2
  254. data/test/template/text_helper_test.rb +38 -52
  255. data/test/template/url_helper_test.rb +216 -46
  256. metadata +144 -116
  257. data/examples/.htaccess +0 -24
  258. data/examples/address_book/index.rhtml +0 -33
  259. data/examples/address_book/layout.rhtml +0 -8
  260. data/examples/address_book_controller.cgi +0 -9
  261. data/examples/address_book_controller.fcgi +0 -6
  262. data/examples/address_book_controller.rb +0 -52
  263. data/examples/address_book_controller.rbx +0 -4
  264. data/examples/benchmark.rb +0 -52
  265. data/examples/benchmark_with_ar.fcgi +0 -89
  266. data/examples/blog_controller.cgi +0 -53
  267. data/examples/debate/index.rhtml +0 -14
  268. data/examples/debate/new_topic.rhtml +0 -22
  269. data/examples/debate/topic.rhtml +0 -32
  270. data/examples/debate_controller.cgi +0 -57
  271. data/lib/action_controller/assertions/deprecated_assertions.rb +0 -228
  272. data/lib/action_controller/cgi_ext/cgi_ext.rb +0 -36
  273. data/lib/action_controller/cgi_ext/cgi_methods.rb +0 -211
  274. data/lib/action_controller/cgi_ext/pstore_performance_fix.rb +0 -30
  275. data/lib/action_controller/cgi_ext/raw_post_data_fix.rb +0 -95
  276. data/lib/action_controller/cgi_ext/session_performance_fix.rb +0 -30
  277. data/lib/action_controller/deprecated_dependencies.rb +0 -65
  278. data/lib/action_controller/deprecated_redirects.rb +0 -17
  279. data/lib/action_controller/deprecated_request_methods.rb +0 -34
  280. data/lib/action_controller/macros/auto_complete.rb +0 -53
  281. data/lib/action_controller/macros/in_place_editing.rb +0 -33
  282. data/lib/action_controller/pagination.rb +0 -408
  283. data/lib/action_controller/scaffolding.rb +0 -189
  284. data/lib/action_controller/templates/rescues/_request_and_response.rhtml +0 -44
  285. data/lib/action_controller/templates/scaffolds/edit.rhtml +0 -7
  286. data/lib/action_controller/templates/scaffolds/layout.rhtml +0 -69
  287. data/lib/action_controller/templates/scaffolds/list.rhtml +0 -27
  288. data/lib/action_controller/templates/scaffolds/new.rhtml +0 -6
  289. data/lib/action_controller/templates/scaffolds/show.rhtml +0 -9
  290. data/lib/action_controller/vendor/xml_node.rb +0 -97
  291. data/lib/action_view/helpers/deprecated_helper.rb +0 -37
  292. data/lib/action_view/helpers/java_script_macros_helper.rb +0 -233
  293. data/lib/action_view/helpers/pagination_helper.rb +0 -86
  294. data/test/activerecord/active_record_assertions_test.rb +0 -92
  295. data/test/activerecord/pagination_test.rb +0 -165
  296. data/test/controller/deprecated_instance_variables_test.rb +0 -48
  297. data/test/controller/raw_post_test.rb +0 -68
  298. data/test/fixtures/deprecated_instance_variables/_cookies_ivar.rhtml +0 -1
  299. data/test/fixtures/deprecated_instance_variables/_cookies_method.rhtml +0 -1
  300. data/test/fixtures/deprecated_instance_variables/_flash_ivar.rhtml +0 -1
  301. data/test/fixtures/deprecated_instance_variables/_flash_method.rhtml +0 -1
  302. data/test/fixtures/deprecated_instance_variables/_headers_ivar.rhtml +0 -1
  303. data/test/fixtures/deprecated_instance_variables/_headers_method.rhtml +0 -1
  304. data/test/fixtures/deprecated_instance_variables/_params_ivar.rhtml +0 -1
  305. data/test/fixtures/deprecated_instance_variables/_params_method.rhtml +0 -1
  306. data/test/fixtures/deprecated_instance_variables/_request_ivar.rhtml +0 -1
  307. data/test/fixtures/deprecated_instance_variables/_request_method.rhtml +0 -1
  308. data/test/fixtures/deprecated_instance_variables/_response_ivar.rhtml +0 -1
  309. data/test/fixtures/deprecated_instance_variables/_response_method.rhtml +0 -1
  310. data/test/fixtures/deprecated_instance_variables/_session_ivar.rhtml +0 -1
  311. data/test/fixtures/deprecated_instance_variables/_session_method.rhtml +0 -1
  312. data/test/fixtures/respond_to/layouts/standard.rhtml +0 -1
  313. data/test/fixtures/test/_hash_object.rhtml +0 -1
  314. data/test/fixtures/test/list.rhtml +0 -1
  315. data/test/template/deprecated_helper_test.rb +0 -36
  316. data/test/template/deprecated_instance_variables_test.rb +0 -43
  317. data/test/template/java_script_macros_helper_test.rb +0 -109
@@ -1,6 +1,6 @@
1
1
  require 'cgi'
2
2
  require 'erb'
3
- require File.dirname(__FILE__) + '/form_helper'
3
+ require 'action_view/helpers/form_helper'
4
4
 
5
5
  module ActionView
6
6
  module Helpers
@@ -10,7 +10,9 @@ module ActionView
10
10
  # and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter,
11
11
  # a hash.
12
12
  #
13
- # * <tt>:include_blank</tt> - set to true if the first option element of the select element is a blank. Useful if there is not a default value required for the select element. For example,
13
+ # * <tt>:include_blank</tt> - set to true or a prompt string if the first option element of the select element is a blank. Useful if there is not a default value required for the select element.
14
+ #
15
+ # For example,
14
16
  #
15
17
  # select("post", "category", Post::CATEGORIES, {:include_blank => true})
16
18
  #
@@ -22,15 +24,31 @@ module ActionView
22
24
  # <option>poem</option>
23
25
  # </select>
24
26
  #
27
+ # Another common case is a select tag for an <tt>belongs_to</tt>-associated object.
28
+ #
29
+ # Example with @post.person_id => 2:
30
+ #
31
+ # select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, {:include_blank => 'None'})
32
+ #
33
+ # could become:
34
+ #
35
+ # <select name="post[person_id]">
36
+ # <option value="">None</option>
37
+ # <option value="1">David</option>
38
+ # <option value="2" selected="selected">Sam</option>
39
+ # <option value="3">Tobias</option>
40
+ # </select>
41
+ #
25
42
  # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this prepends an option with a generic prompt -- "Please select" -- or the given prompt string.
26
43
  #
27
- # Another common case is a select tag for an <tt>belongs_to</tt>-associated object. For example,
44
+ # Example:
28
45
  #
29
- # select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] })
46
+ # select("post", "person_id", Person.find(:all).collect {|p| [ p.name, p.id ] }, {:prompt => 'Select Person'})
30
47
  #
31
48
  # could become:
32
49
  #
33
50
  # <select name="post[person_id]">
51
+ # <option value="">Select Person</option>
34
52
  # <option value="1">David</option>
35
53
  # <option value="2">Sam</option>
36
54
  # <option value="3">Tobias</option>
@@ -48,7 +66,7 @@ module ActionView
48
66
  # could become:
49
67
  #
50
68
  # <select name="post[person_id]">
51
- # <option></option>
69
+ # <option value=""></option>
52
70
  # <option value="1" selected="selected">David</option>
53
71
  # <option value="2">Sam</option>
54
72
  # <option value="3">Tobias</option>
@@ -66,7 +84,36 @@ module ActionView
66
84
  InstanceTag.new(object, method, self, nil, options.delete(:object)).to_select_tag(choices, options, html_options)
67
85
  end
68
86
 
69
- # Return select and option tags for the given object and method using options_from_collection_for_select to generate the list of option tags.
87
+ # Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
88
+ # +method+ for +object+'s class. The value returned from calling +method+ on the instance +object+ will
89
+ # be selected. If calling +method+ returns +nil+, no selection is made without including <tt>:prompt</tt>
90
+ # or <tt>:include_blank</tt> in the +options+ hash.
91
+ #
92
+ # The <tt>:value_method</tt> and <tt>:text_method</tt> parameters are methods to be called on each member
93
+ # of +collection+. The return values are used as the +value+ attribute and contents of each
94
+ # <tt><option></tt> tag, respectively.
95
+ #
96
+ # Example object structure for use with this method:
97
+ # class Post < ActiveRecord::Base
98
+ # belongs_to :author
99
+ # end
100
+ # class Author < ActiveRecord::Base
101
+ # has_many :posts
102
+ # def name_with_initial
103
+ # "#{first_name.first}. #{last_name}"
104
+ # end
105
+ # end
106
+ #
107
+ # Sample usage (selecting the associated +Author+ for an instance of +Post+, <tt>@post</tt>):
108
+ # collection_select(:post, :author_id, Author.find(:all), :id, :name_with_initial, {:prompt => true})
109
+ #
110
+ # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
111
+ # <select name="post[author_id]">
112
+ # <option value="">Please select</option>
113
+ # <option value="1" selected="selected">D. Heinemeier Hansson</option>
114
+ # <option value="2">D. Thomas</option>
115
+ # <option value="3">M. Clark</option>
116
+ # </select>
70
117
  def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
71
118
  InstanceTag.new(object, method, self, nil, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
72
119
  end
@@ -112,17 +159,9 @@ module ActionView
112
159
  container = container.to_a if Hash === container
113
160
 
114
161
  options_for_select = container.inject([]) do |options, element|
115
- if !element.is_a?(String) and element.respond_to?(:first) and element.respond_to?(:last)
116
- is_selected = ( (selected.respond_to?(:include?) && !selected.is_a?(String) ? selected.include?(element.last) : element.last == selected) )
117
- if is_selected
118
- options << "<option value=\"#{html_escape(element.last.to_s)}\" selected=\"selected\">#{html_escape(element.first.to_s)}</option>"
119
- else
120
- options << "<option value=\"#{html_escape(element.last.to_s)}\">#{html_escape(element.first.to_s)}</option>"
121
- end
122
- else
123
- is_selected = ( (selected.respond_to?(:include?) && !selected.is_a?(String) ? selected.include?(element) : element == selected) )
124
- options << ((is_selected) ? "<option value=\"#{html_escape(element.to_s)}\" selected=\"selected\">#{html_escape(element.to_s)}</option>" : "<option value=\"#{html_escape(element.to_s)}\">#{html_escape(element.to_s)}</option>")
125
- end
162
+ text, value = option_text_and_value(element)
163
+ selected_attribute = ' selected="selected"' if option_value_selected?(value, selected)
164
+ options << %(<option value="#{html_escape(value.to_s)}"#{selected_attribute}>#{html_escape(text.to_s)}</option>)
126
165
  end
127
166
 
128
167
  options_for_select.join("\n")
@@ -130,55 +169,68 @@ module ActionView
130
169
 
131
170
  # Returns a string of option tags that have been compiled by iterating over the +collection+ and assigning the
132
171
  # the result of a call to the +value_method+ as the option value and the +text_method+ as the option text.
133
- # If +selected_value+ is specified, the element returning a match on +value_method+ will get the selected option tag.
172
+ # If +selected+ is specified, the element returning a match on +value_method+ will get the selected option tag.
134
173
  #
135
174
  # Example (call, result). Imagine a loop iterating over each +person+ in <tt>@project.people</tt> to generate an input tag:
136
175
  # options_from_collection_for_select(@project.people, "id", "name")
137
176
  # <option value="#{person.id}">#{person.name}</option>
138
177
  #
139
178
  # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
140
- def options_from_collection_for_select(collection, value_method, text_method, selected_value = nil)
141
- options_for_select(
142
- collection.inject([]) { |options, object| options << [ object.send(text_method), object.send(value_method) ] },
143
- selected_value
144
- )
179
+ def options_from_collection_for_select(collection, value_method, text_method, selected = nil)
180
+ options = collection.map do |element|
181
+ [element.send(text_method), element.send(value_method)]
182
+ end
183
+ options_for_select(options, selected)
145
184
  end
146
185
 
147
- # Returns a string of option tags, like options_from_collection_for_select, but surrounds them with <optgroup> tags.
186
+ # Returns a string of <tt><option></tt> tags, like <tt>#options_from_collection_for_select</tt>, but
187
+ # groups them by <tt><optgroup></tt> tags based on the object relationships of the arguments.
148
188
  #
149
- # An array of group objects are passed. Each group should return an array of options when calling group_method
150
- # Each group should return its name when calling group_label_method.
189
+ # Parameters:
190
+ # +collection+:: An array of objects representing the <tt><optgroup></tt> tags
191
+ # +group_method+:: The name of a method which, when called on a member of +collection+, returns an
192
+ # array of child objects representing the <tt><option></tt> tags
193
+ # +group_label_method+:: The name of a method which, when called on a member of +collection+, returns a
194
+ # string to be used as the +label+ attribute for its <tt><optgroup></tt> tag
195
+ # +option_key_method+:: The name of a method which, when called on a child object of a member of
196
+ # +collection+, returns a value to be used as the +value+ attribute for its
197
+ # <tt><option></tt> tag
198
+ # +option_value_method+:: The name of a method which, when called on a child object of a member of
199
+ # +collection+, returns a value to be used as the contents of its
200
+ # <tt><option></tt> tag
201
+ # +selected_key+:: A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
202
+ # which will have the +selected+ attribute set. Corresponds to the return value
203
+ # of one of the calls to +option_key_method+. If +nil+, no selection is made.
151
204
  #
152
- # html_option_groups_from_collection(@continents, "countries", "continent_name", "country_id", "country_name", @selected_country.id)
205
+ # Example object structure for use with this method:
206
+ # class Continent < ActiveRecord::Base
207
+ # has_many :countries
208
+ # # attribs: id, name
209
+ # end
210
+ # class Country < ActiveRecord::Base
211
+ # belongs_to :continent
212
+ # # attribs: id, name, continent_id
213
+ # end
153
214
  #
154
- # Could become:
155
- # <optgroup label="Africa">
156
- # <select>Egypt</select>
157
- # <select>Rwanda</select>
158
- # ...
159
- # </optgroup>
160
- # <optgroup label="Asia">
161
- # <select>China</select>
162
- # <select>India</select>
163
- # <select>Japan</select>
164
- # ...
165
- # </optgroup>
215
+ # Sample usage:
216
+ # option_groups_from_collection_for_select(@continents, :countries, :name, :id, :name, 3)
166
217
  #
167
- # with objects of the following classes:
168
- # class Continent
169
- # def initialize(p_name, p_countries) @continent_name = p_name; @countries = p_countries; end
170
- # def continent_name() @continent_name; end
171
- # def countries() @countries; end
172
- # end
173
- # class Country
174
- # def initialize(id, name) @id = id; @name = name end
175
- # def country_id() @id; end
176
- # def country_name() @name; end
177
- # end
218
+ # Possible output:
219
+ # <optgroup label="Africa">
220
+ # <option value="1">Egypt</option>
221
+ # <option value="4">Rwanda</option>
222
+ # ...
223
+ # </optgroup>
224
+ # <optgroup label="Asia">
225
+ # <option value="3" selected="selected">China</option>
226
+ # <option value="12">India</option>
227
+ # <option value="5">Japan</option>
228
+ # ...
229
+ # </optgroup>
178
230
  #
179
- # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
180
- def option_groups_from_collection_for_select(collection, group_method, group_label_method,
181
- option_key_method, option_value_method, selected_key = nil)
231
+ # <b>Note:</b> Only the <tt><optgroup></tt> and <tt><option></tt> tags are returned, so you still have to
232
+ # wrap the output in an appropriate <tt><select></tt> tag.
233
+ def option_groups_from_collection_for_select(collection, group_method, group_label_method, option_key_method, option_value_method, selected_key = nil)
182
234
  collection.inject("") do |options_for_select, group|
183
235
  group_label_string = eval("group.#{group_label_method}")
184
236
  options_for_select += "<optgroup label=\"#{html_escape(group_label_string)}\">"
@@ -197,16 +249,10 @@ module ActionView
197
249
 
198
250
  if priority_countries
199
251
  country_options += options_for_select(priority_countries, selected)
200
- country_options += "<option value=\"\">-------------</option>\n"
201
- end
202
-
203
- if priority_countries && priority_countries.include?(selected)
204
- country_options += options_for_select(COUNTRIES - priority_countries, selected)
205
- else
206
- country_options += options_for_select(COUNTRIES, selected)
252
+ country_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
207
253
  end
208
254
 
209
- return country_options
255
+ return country_options + options_for_select(COUNTRIES, selected)
210
256
  end
211
257
 
212
258
  # Returns a string of option tags for pretty much any time zone in the
@@ -234,7 +280,7 @@ module ActionView
234
280
 
235
281
  if priority_zones
236
282
  zone_options += options_for_select(convert_zones[priority_zones], selected)
237
- zone_options += "<option value=\"\">-------------</option>\n"
283
+ zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
238
284
 
239
285
  zones = zones.reject { |z| priority_zones.include?( z ) }
240
286
  end
@@ -244,51 +290,61 @@ module ActionView
244
290
  end
245
291
 
246
292
  private
293
+ def option_text_and_value(option)
294
+ # Options are [text, value] pairs or strings used for both.
295
+ if !option.is_a?(String) and option.respond_to?(:first) and option.respond_to?(:last)
296
+ [option.first, option.last]
297
+ else
298
+ [option, option]
299
+ end
300
+ end
301
+
302
+ def option_value_selected?(value, selected)
303
+ if selected.respond_to?(:include?) && !selected.is_a?(String)
304
+ selected.include? value
305
+ else
306
+ value == selected
307
+ end
308
+ end
309
+
247
310
  # All the countries included in the country_options output.
248
- COUNTRIES = [ "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra", "Angola", "Anguilla",
249
- "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia",
250
- "Austria", "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus",
251
- "Belgium", "Belize", "Benin", "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina",
252
- "Botswana", "Bouvet Island", "Brazil", "British Indian Ocean Territory",
253
- "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burma", "Burundi", "Cambodia",
254
- "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic",
255
- "Chad", "Chile", "China", "Christmas Island", "Cocos (Keeling) Islands", "Colombia",
256
- "Comoros", "Congo", "Congo, the Democratic Republic of the", "Cook Islands",
257
- "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba", "Cyprus", "Czech Republic", "Denmark",
258
- "Djibouti", "Dominica", "Dominican Republic", "East Timor", "Ecuador", "Egypt",
259
- "El Salvador", "England", "Equatorial Guinea", "Eritrea", "Espana", "Estonia",
260
- "Ethiopia", "Falkland Islands", "Faroe Islands", "Fiji", "Finland", "France",
261
- "French Guiana", "French Polynesia", "French Southern Territories", "Gabon", "Gambia",
262
- "Georgia", "Germany", "Ghana", "Gibraltar", "Great Britain", "Greece", "Greenland",
263
- "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guinea", "Guinea-Bissau", "Guyana",
264
- "Haiti", "Heard and Mc Donald Islands", "Honduras", "Hong Kong", "Hungary", "Iceland",
265
- "India", "Indonesia", "Ireland", "Israel", "Italy", "Iran", "Iraq", "Jamaica", "Japan", "Jordan",
266
- "Kazakhstan", "Kenya", "Kiribati", "Korea, Republic of", "Korea (South)", "Kuwait",
267
- "Kyrgyzstan", "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho",
268
- "Liberia", "Liechtenstein", "Lithuania", "Luxembourg", "Macau", "Macedonia",
269
- "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands",
270
- "Martinique", "Mauritania", "Mauritius", "Mayotte", "Mexico",
271
- "Micronesia, Federated States of", "Moldova, Republic of", "Monaco", "Mongolia",
272
- "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru", "Nepal",
273
- "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua",
274
- "Niger", "Nigeria", "Niue", "Norfolk Island", "Northern Ireland",
275
- "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau", "Panama",
276
- "Papua New Guinea", "Paraguay", "Peru", "Philippines", "Pitcairn", "Poland",
277
- "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russia", "Rwanda",
278
- "Saint Kitts and Nevis", "Saint Lucia", "Saint Vincent and the Grenadines",
279
- "Samoa (Independent)", "San Marino", "Sao Tome and Principe", "Saudi Arabia",
280
- "Scotland", "Senegal", "Serbia and Montenegro", "Seychelles", "Sierra Leone", "Singapore",
281
- "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa",
282
- "South Georgia and the South Sandwich Islands", "South Korea", "Spain", "Sri Lanka",
283
- "St. Helena", "St. Pierre and Miquelon", "Suriname", "Svalbard and Jan Mayen Islands",
284
- "Swaziland", "Sweden", "Switzerland", "Taiwan", "Tajikistan", "Tanzania", "Thailand",
285
- "Togo", "Tokelau", "Tonga", "Trinidad", "Trinidad and Tobago", "Tunisia", "Turkey",
286
- "Turkmenistan", "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine",
287
- "United Arab Emirates", "United Kingdom", "United States",
288
- "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu",
289
- "Vatican City State (Holy See)", "Venezuela", "Viet Nam", "Virgin Islands (British)",
290
- "Virgin Islands (U.S.)", "Wales", "Wallis and Futuna Islands", "Western Sahara",
291
- "Yemen", "Zambia", "Zimbabwe" ] unless const_defined?("COUNTRIES")
311
+ COUNTRIES = ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola",
312
+ "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria",
313
+ "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin",
314
+ "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil",
315
+ "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia",
316
+ "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
317
+ "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
318
+ "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba",
319
+ "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt",
320
+ "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)",
321
+ "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia",
322
+ "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea",
323
+ "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)",
324
+ "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq",
325
+ "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya",
326
+ "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan",
327
+ "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya",
328
+ "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of",
329
+ "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique",
330
+ "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of",
331
+ "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru",
332
+ "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger",
333
+ "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau",
334
+ "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines",
335
+ "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation",
336
+ "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia",
337
+ "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino",
338
+ "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore",
339
+ "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa",
340
+ "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname",
341
+ "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic",
342
+ "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste",
343
+ "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan",
344
+ "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom",
345
+ "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela",
346
+ "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara",
347
+ "Yemen", "Zambia", "Zimbabwe"] unless const_defined?("COUNTRIES")
292
348
  end
293
349
 
294
350
  class InstanceTag #:nodoc:
@@ -315,7 +371,12 @@ module ActionView
315
371
  html_options = html_options.stringify_keys
316
372
  add_default_name_and_id(html_options)
317
373
  value = value(object)
318
- content_tag("select", add_options(country_options_for_select(value, priority_countries), options, value), html_options)
374
+ content_tag("select",
375
+ add_options(
376
+ country_options_for_select(value, priority_countries),
377
+ options, value
378
+ ), html_options
379
+ )
319
380
  end
320
381
 
321
382
  def to_time_zone_select_tag(priority_zones, options, html_options)
@@ -332,8 +393,9 @@ module ActionView
332
393
 
333
394
  private
334
395
  def add_options(option_tags, options, value = nil)
335
- option_tags = "<option value=\"\"></option>\n" + option_tags if options[:include_blank]
336
-
396
+ if options[:include_blank]
397
+ option_tags = "<option value=\"\">#{options[:include_blank] if options[:include_blank].kind_of?(String)}</option>\n" + option_tags
398
+ end
337
399
  if value.blank? && options[:prompt]
338
400
  ("<option value=\"\">#{options[:prompt].kind_of?(String) ? options[:prompt] : 'Please select'}</option>\n") + option_tags
339
401
  else
@@ -1,109 +1,142 @@
1
1
  require 'cgi'
2
- require File.dirname(__FILE__) + '/tag_helper'
2
+ require 'action_view/helpers/tag_helper'
3
3
 
4
4
  module ActionView
5
5
  module Helpers
6
- # Provides a number of methods for creating form tags that doesn't rely on conventions with an object assigned to the template like
7
- # FormHelper does. With the FormTagHelper, you provide the names and values yourself.
6
+ # Provides a number of methods for creating form tags that doesn't rely on an ActiveRecord object assigned to the template like
7
+ # FormHelper does. Instead, you provide the names and values manually.
8
8
  #
9
- # NOTE: The html options disabled, readonly, and multiple can all be treated as booleans. So specifying <tt>:disabled => true</tt>
10
- # will give <tt>disabled="disabled"</tt>.
9
+ # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
10
+ # <tt>:disabled => true</tt> will give <tt>disabled="disabled"</tt>.
11
11
  module FormTagHelper
12
12
  # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
13
13
  # ActionController::Base#url_for. The method for the form defaults to POST.
14
14
  #
15
- # Examples:
16
- # * <tt>form_tag('/posts') => <form action="/posts" method="post"></tt>
17
- # * <tt>form_tag('/posts/1', :method => :put) => <form action="/posts/1" method="put"></tt>
18
- # * <tt>form_tag('/upload', :multipart => true) => <form action="/upload" method="post" enctype="multipart/form-data"></tt>
19
- #
20
- # ERb example:
21
- # <% form_tag '/posts' do -%>
22
- # <div><%= submit_tag 'Save' %></div>
23
- # <% end -%>
24
- #
25
- # Will output:
26
- # <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
27
- #
28
- # Options:
15
+ # ==== Options
29
16
  # * <tt>:multipart</tt> - If set to true, the enctype is set to "multipart/form-data".
30
17
  # * <tt>:method</tt> - The method to use when submitting the form, usually either "get" or "post".
31
18
  # If "put", "delete", or another verb is used, a hidden input with name _method
32
19
  # is added to simulate the verb over post.
20
+ # * A list of parameters to feed to the URL the form will be posted to.
21
+ #
22
+ # ==== Examples
23
+ # form_tag('/posts')
24
+ # # => <form action="/posts" method="post">
25
+ #
26
+ # form_tag('/posts/1', :method => :put)
27
+ # # => <form action="/posts/1" method="put">
28
+ #
29
+ # form_tag('/upload', :multipart => true)
30
+ # # => <form action="/upload" method="post" enctype="multipart/form-data">
31
+ #
32
+ # <% form_tag '/posts' do -%>
33
+ # <div><%= submit_tag 'Save' %></div>
34
+ # <% end -%>
35
+ # # => <form action="/posts" method="post"><div><input type="submit" name="submit" value="Save" /></div></form>
33
36
  def form_tag(url_for_options = {}, options = {}, *parameters_for_url, &block)
34
- html_options = options.stringify_keys
35
- html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
36
- html_options["action"] = url_for(url_for_options, *parameters_for_url)
37
-
38
- method_tag = ""
39
-
40
- case method = html_options.delete("method").to_s
41
- when /^get$/i # must be case-insentive, but can't use downcase as might be nil
42
- html_options["method"] = "get"
43
- when /^post$/i, "", nil
44
- html_options["method"] = "post"
45
- else
46
- html_options["method"] = "post"
47
- method_tag = content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method), :style => 'margin:0;padding:0')
48
- end
49
-
37
+ html_options = html_options_for_form(url_for_options, options, *parameters_for_url)
50
38
  if block_given?
51
- content = capture(&block)
52
- concat(tag(:form, html_options, true) + method_tag, block.binding)
53
- concat(content, block.binding)
54
- concat("</form>", block.binding)
39
+ form_tag_in_block(html_options, &block)
55
40
  else
56
- tag(:form, html_options, true) + method_tag
41
+ form_tag_html(html_options)
57
42
  end
58
43
  end
59
44
 
60
- alias_method :start_form_tag, :form_tag
61
-
62
- # Outputs "</form>"
63
- def end_form_tag
64
- "</form>"
65
- end
66
-
67
- deprecate :end_form_tag, :start_form_tag => :form_tag
68
-
69
45
  # Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
70
46
  # choice selection box.
71
47
  #
72
48
  # Helpers::FormOptions can be used to create common select boxes such as countries, time zones, or
73
- # associated records.
49
+ # associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
74
50
  #
75
- # <tt>option_tags</tt> is a string containing the option tags for the select box:
76
- # # Outputs <select id="people" name="people"><option>David</option></select>
51
+ # ==== Options
52
+ # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
53
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
54
+ # * Any other key creates standard HTML attributes for the tag.
55
+ #
56
+ # ==== Examples
77
57
  # select_tag "people", "<option>David</option>"
58
+ # # => <select id="people" name="people"><option>David</option></select>
78
59
  #
79
- # Options:
80
- # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
60
+ # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>"
61
+ # # => <select id="count" name="count"><option>1</option><option>2</option>
62
+ # # <option>3</option><option>4</option></select>
63
+ #
64
+ # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>", :multiple => true
65
+ # # => <select id="colors" multiple="multiple" name="colors"><option>Red</option>
66
+ # # <option>Green</option><option>Blue</option></select>
67
+ #
68
+ # select_tag "locations", "<option>Home</option><option selected="selected">Work</option><option>Out</option>"
69
+ # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
70
+ # # <option>Out</option></select>
71
+ #
72
+ # select_tag "access", "<option>Read</option><option>Write</option>", :multiple => true, :class => 'form_input'
73
+ # # => <select class="form_input" id="access" multiple="multiple" name="access"><option>Read</option>
74
+ # # <option>Write</option></select>
75
+ #
76
+ # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>", :disabled => true
77
+ # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
78
+ # # <option>Paris</option><option>Rome</option></select>
81
79
  def select_tag(name, option_tags = nil, options = {})
82
80
  content_tag :select, option_tags, { "name" => name, "id" => name }.update(options.stringify_keys)
83
81
  end
84
82
 
85
- # Creates a standard text field.
83
+ # Creates a standard text field; use these text fields to input smaller chunks of text like a username
84
+ # or a search query.
86
85
  #
87
- # Options:
86
+ # ==== Options
88
87
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
89
88
  # * <tt>:size</tt> - The number of visible characters that will fit in the input.
90
89
  # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
90
+ # * Any other key creates standard HTML attributes for the tag.
91
91
  #
92
- # A hash of standard HTML options for the tag.
92
+ # ==== Examples
93
+ # text_field_tag 'name'
94
+ # # => <input id="name" name="name" type="text" />
95
+ #
96
+ # text_field_tag 'query', 'Enter your search query here'
97
+ # # => <input id="query" name="query" type="text" value="Enter your search query here" />
98
+ #
99
+ # text_field_tag 'request', nil, :class => 'special_input'
100
+ # # => <input class="special_input" id="request" name="request" type="text" />
101
+ #
102
+ # text_field_tag 'address', '', :size => 75
103
+ # # => <input id="address" name="address" size="75" type="text" value="" />
104
+ #
105
+ # text_field_tag 'zip', nil, :maxlength => 5
106
+ # # => <input id="zip" maxlength="5" name="zip" type="text" />
107
+ #
108
+ # text_field_tag 'payment_amount', '$0.00', :disabled => true
109
+ # # => <input disabled="disabled" id="payment_amount" name="payment_amount" type="text" value="$0.00" />
110
+ #
111
+ # text_field_tag 'ip', '0.0.0.0', :maxlength => 15, :size => 20, :class => "ip-input"
112
+ # # => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
93
113
  def text_field_tag(name, value = nil, options = {})
94
114
  tag :input, { "type" => "text", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
95
115
  end
96
116
 
97
- # Creates a hidden field.
117
+ # Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
118
+ # data that should be hidden from the user.
119
+ #
120
+ # ==== Options
121
+ # * Creates standard HTML attributes for the tag.
98
122
  #
99
- # Takes the same options as text_field_tag
123
+ # ==== Examples
124
+ # hidden_field_tag 'tags_list'
125
+ # # => <input id="tags_list" name="tags_list" type="hidden" />
126
+ #
127
+ # hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
128
+ # # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
129
+ #
130
+ # hidden_field_tag 'collected_input', '', :onchange => "alert('Input collected!')"
131
+ # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
132
+ # # type="hidden" value="" />
100
133
  def hidden_field_tag(name, value = nil, options = {})
101
134
  text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
102
135
  end
103
136
 
104
- # Creates a file upload field.
137
+ # Creates a file upload field. If you are using file uploads then you will also need
138
+ # to set the multipart option for the form tag:
105
139
  #
106
- # If you are using file uploads then you will also need to set the multipart option for the form:
107
140
  # <%= form_tag { :action => "post" }, { :multipart => true } %>
108
141
  # <label for="file">File to Upload</label> <%= file_field_tag "file" %>
109
142
  # <%= submit_tag %>
@@ -111,55 +144,197 @@ module ActionView
111
144
  #
112
145
  # The specified URL will then be passed a File object containing the selected file, or if the field
113
146
  # was left blank, a StringIO object.
147
+ #
148
+ # ==== Options
149
+ # * Creates standard HTML attributes for the tag.
150
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
151
+ #
152
+ # ==== Examples
153
+ # file_field_tag 'attachment'
154
+ # # => <input id="attachment" name="attachment" type="file" />
155
+ #
156
+ # file_field_tag 'avatar', :class => 'profile-input'
157
+ # # => <input class="profile-input" id="avatar" name="avatar" type="file" />
158
+ #
159
+ # file_field_tag 'picture', :disabled => true
160
+ # # => <input disabled="disabled" id="picture" name="picture" type="file" />
161
+ #
162
+ # file_field_tag 'resume', :value => '~/resume.doc'
163
+ # # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
164
+ #
165
+ # file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg'
166
+ # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
167
+ #
168
+ # file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html'
169
+ # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
114
170
  def file_field_tag(name, options = {})
115
171
  text_field_tag(name, nil, options.update("type" => "file"))
116
172
  end
117
173
 
118
- # Creates a password field.
174
+ # Creates a password field, a masked text field that will hide the users input behind a mask character.
175
+ #
176
+ # ==== Options
177
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
178
+ # * <tt>:size</tt> - The number of visible characters that will fit in the input.
179
+ # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
180
+ # * Any other key creates standard HTML attributes for the tag.
181
+ #
182
+ # ==== Examples
183
+ # password_field_tag 'pass'
184
+ # # => <input id="pass" name="pass" type="password" />
185
+ #
186
+ # password_field_tag 'secret', 'Your secret here'
187
+ # # => <input id="secret" name="secret" type="password" value="Your secret here" />
188
+ #
189
+ # password_field_tag 'masked', nil, :class => 'masked_input_field'
190
+ # # => <input class="masked_input_field" id="masked" name="masked" type="password" />
119
191
  #
120
- # Takes the same options as text_field_tag
192
+ # password_field_tag 'token', '', :size => 15
193
+ # # => <input id="token" name="token" size="15" type="password" value="" />
194
+ #
195
+ # password_field_tag 'key', nil, :maxlength => 16
196
+ # # => <input id="key" maxlength="16" name="key" type="password" />
197
+ #
198
+ # password_field_tag 'confirm_pass', nil, :disabled => true
199
+ # # => <input disabled="disabled" id="confirm_pass" name="confirm_pass" type="password" />
200
+ #
201
+ # password_field_tag 'pin', '1234', :maxlength => 4, :size => 6, :class => "pin-input"
202
+ # # => <input class="pin-input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
121
203
  def password_field_tag(name = "password", value = nil, options = {})
122
204
  text_field_tag(name, value, options.update("type" => "password"))
123
205
  end
124
206
 
125
- # Creates a text input area.
207
+ # Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
208
+ #
209
+ # ==== Options
210
+ # * <tt>:size</tt> - A string specifying the dimensions (columns by rows) of the textarea (e.g., "25x10").
211
+ # * <tt>:rows</tt> - Specify the number of rows in the textarea
212
+ # * <tt>:cols</tt> - Specify the number of columns in the textarea
213
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
214
+ # * Any other key creates standard HTML attributes for the tag.
215
+ #
216
+ # ==== Examples
217
+ # text_area_tag 'post'
218
+ # # => <textarea id="post" name="post"></textarea>
219
+ #
220
+ # text_area_tag 'bio', @user.bio
221
+ # # => <textarea id="bio" name="bio">This is my biography.</textarea>
126
222
  #
127
- # Options:
128
- # * <tt>:size</tt> - A string specifying the dimensions of the textarea.
129
- # # Outputs <textarea name="body" id="body" cols="25" rows="10"></textarea>
130
- # <%= text_area_tag "body", nil, :size => "25x10" %>
223
+ # text_area_tag 'body', nil, :rows => 10, :cols => 25
224
+ # # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
225
+ #
226
+ # text_area_tag 'body', nil, :size => "25x10"
227
+ # # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
228
+ #
229
+ # text_area_tag 'description', "Description goes here.", :disabled => true
230
+ # # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
231
+ #
232
+ # text_area_tag 'comment', nil, :class => 'comment_input'
233
+ # # => <textarea class="comment_input" id="comment" name="comment"></textarea>
131
234
  def text_area_tag(name, content = nil, options = {})
132
235
  options.stringify_keys!
133
236
 
134
237
  if size = options.delete("size")
135
- options["cols"], options["rows"] = size.split("x")
238
+ options["cols"], options["rows"] = size.split("x") if size.respond_to?(:split)
136
239
  end
137
240
 
138
241
  content_tag :textarea, content, { "name" => name, "id" => name }.update(options.stringify_keys)
139
242
  end
140
243
 
141
- # Creates a check box.
244
+ # Creates a check box form input tag.
245
+ #
246
+ # ==== Options
247
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
248
+ # * Any other key creates standard HTML options for the tag.
249
+ #
250
+ # ==== Examples
251
+ # check_box_tag 'accept'
252
+ # # => <input id="accept" name="accept" type="checkbox" value="1" />
253
+ #
254
+ # check_box_tag 'rock', 'rock music'
255
+ # # => <input id="rock" name="rock" type="checkbox" value="rock music" />
256
+ #
257
+ # check_box_tag 'receive_email', 'yes', true
258
+ # # => <input checked="checked" id="receive_email" name="receive_email" type="checkbox" value="yes" />
259
+ #
260
+ # check_box_tag 'tos', 'yes', false, :class => 'accept_tos'
261
+ # # => <input class="accept_tos" id="tos" name="tos" type="checkbox" value="yes" />
262
+ #
263
+ # check_box_tag 'eula', 'accepted', false, :disabled => true
264
+ # # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
142
265
  def check_box_tag(name, value = "1", checked = false, options = {})
143
266
  html_options = { "type" => "checkbox", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
144
267
  html_options["checked"] = "checked" if checked
145
268
  tag :input, html_options
146
269
  end
147
270
 
148
- # Creates a radio button.
271
+ # Creates a radio button; use groups of radio buttons named the same to allow users to
272
+ # select from a group of options.
273
+ #
274
+ # ==== Options
275
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
276
+ # * Any other key creates standard HTML options for the tag.
277
+ #
278
+ # ==== Examples
279
+ # radio_button_tag 'gender', 'male'
280
+ # # => <input id="gender_male" name="gender" type="radio" value="male" />
281
+ #
282
+ # radio_button_tag 'receive_updates', 'no', true
283
+ # # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
284
+ #
285
+ # radio_button_tag 'time_slot', "3:00 p.m.", false, :disabled => true
286
+ # # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
287
+ #
288
+ # radio_button_tag 'color', "green", true, :class => "color_input"
289
+ # # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
149
290
  def radio_button_tag(name, value, checked = false, options = {})
150
291
  pretty_tag_value = value.to_s.gsub(/\s/, "_").gsub(/(?!-)\W/, "").downcase
151
- html_options = { "type" => "radio", "name" => name, "id" => "#{name}_#{pretty_tag_value}", "value" => value }.update(options.stringify_keys)
292
+ pretty_name = name.to_s.gsub(/\[/, "_").gsub(/\]/, "")
293
+ html_options = { "type" => "radio", "name" => name, "id" => "#{pretty_name}_#{pretty_tag_value}", "value" => value }.update(options.stringify_keys)
152
294
  html_options["checked"] = "checked" if checked
153
295
  tag :input, html_options
154
296
  end
155
297
 
156
- # Creates a submit button with the text <tt>value</tt> as the caption. If options contains a pair with the key of "disable_with",
157
- # then the value will be used to rename a disabled version of the submit button.
298
+ # Creates a submit button with the text <tt>value</tt> as the caption.
299
+ #
300
+ # ==== Options
301
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
302
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version
303
+ # of the submit button when the form is submitted.
304
+ # * Any other key creates standard HTML options for the tag.
305
+ #
306
+ # ==== Examples
307
+ # submit_tag
308
+ # # => <input name="commit" type="submit" value="Save changes" />
309
+ #
310
+ # submit_tag "Edit this article"
311
+ # # => <input name="commit" type="submit" value="Edit this article" />
312
+ #
313
+ # submit_tag "Save edits", :disabled => true
314
+ # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
315
+ #
316
+ # submit_tag "Complete sale", :disable_with => "Please wait..."
317
+ # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();"
318
+ # # type="submit" value="Complete sale" />
319
+ #
320
+ # submit_tag nil, :class => "form_submit"
321
+ # # => <input class="form_submit" name="commit" type="submit" />
322
+ #
323
+ # submit_tag "Edit", :disable_with => "Editing...", :class => 'edit-button'
324
+ # # => <input class="edit-button" disable_with="Editing..." name="commit" type="submit" value="Edit" />
158
325
  def submit_tag(value = "Save changes", options = {})
159
326
  options.stringify_keys!
160
327
 
161
328
  if disable_with = options.delete("disable_with")
162
- options["onclick"] = "this.disabled=true;this.value='#{disable_with}';this.form.submit();#{options["onclick"]}"
329
+ options["onclick"] = [
330
+ "this.setAttribute('originalValue', this.value)",
331
+ "this.disabled=true",
332
+ "this.value='#{disable_with}'",
333
+ "#{options["onclick"]}",
334
+ "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit())",
335
+ "if (result == false) { this.value = this.getAttribute('originalValue'); this.disabled = false }",
336
+ "return result",
337
+ ].join(";")
163
338
  end
164
339
 
165
340
  tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
@@ -168,9 +343,90 @@ module ActionView
168
343
  # Displays an image which when clicked will submit the form.
169
344
  #
170
345
  # <tt>source</tt> is passed to AssetTagHelper#image_path
346
+ #
347
+ # ==== Options
348
+ # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
349
+ # * Any other key creates standard HTML options for the tag.
350
+ #
351
+ # ==== Examples
352
+ # image_submit_tag("login.png")
353
+ # # => <input src="/images/login.png" type="image" />
354
+ #
355
+ # image_submit_tag("purchase.png"), :disabled => true
356
+ # # => <input disabled="disabled" src="/images/purchase.png" type="image" />
357
+ #
358
+ # image_submit_tag("search.png"), :class => 'search-button'
359
+ # # => <input class="search-button" src="/images/search.png" type="image" />
360
+ #
361
+ # image_submit_tag("agree.png"), :disabled => true, :class => "agree-disagree-button"
362
+ # # => <input class="agree-disagree-button" disabled="disabled" src="/images/agree.png" type="image" />
171
363
  def image_submit_tag(source, options = {})
172
- tag :input, { "type" => "image", "src" => image_path(source) }.update(options.stringify_keys)
364
+ tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys)
365
+ end
366
+
367
+ # Creates a field set for grouping HTML form elements.
368
+ #
369
+ # <tt>legend</tt> will become the fieldset's title (optional as per W3C).
370
+ #
371
+ # === Examples
372
+ # <% field_set_tag do %>
373
+ # <p><%= text_field_tag 'name' %></p>
374
+ # <% end %>
375
+ # # => <fieldset><p><input id="name" name="name" type="text" /></p></fieldset>
376
+ #
377
+ # <% field_set_tag 'Your details' do %>
378
+ # <p><%= text_field_tag 'name' %></p>
379
+ # <% end %>
380
+ # # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
381
+ def field_set_tag(legend = nil, &block)
382
+ content = capture(&block)
383
+ concat(tag(:fieldset, {}, true), block.binding)
384
+ concat(content_tag(:legend, legend), block.binding) unless legend.blank?
385
+ concat(content, block.binding)
386
+ concat("</fieldset>", block.binding)
173
387
  end
388
+
389
+ private
390
+ def html_options_for_form(url_for_options, options, *parameters_for_url)
391
+ returning options.stringify_keys do |html_options|
392
+ html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
393
+ html_options["action"] = url_for(url_for_options, *parameters_for_url)
394
+ end
395
+ end
396
+
397
+ def extra_tags_for_form(html_options)
398
+ case method = html_options.delete("method").to_s
399
+ when /^get$/i # must be case-insentive, but can't use downcase as might be nil
400
+ html_options["method"] = "get"
401
+ ''
402
+ when /^post$/i, "", nil
403
+ html_options["method"] = "post"
404
+ protect_against_forgery? ? content_tag(:div, token_tag, :style => 'margin:0;padding:0') : ''
405
+ else
406
+ html_options["method"] = "post"
407
+ content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0')
408
+ end
409
+ end
410
+
411
+ def form_tag_html(html_options)
412
+ extra_tags = extra_tags_for_form(html_options)
413
+ tag(:form, html_options, true) + extra_tags
414
+ end
415
+
416
+ def form_tag_in_block(html_options, &block)
417
+ content = capture(&block)
418
+ concat(form_tag_html(html_options), block.binding)
419
+ concat(content, block.binding)
420
+ concat("</form>", block.binding)
421
+ end
422
+
423
+ def token_tag
424
+ unless protect_against_forgery?
425
+ ''
426
+ else
427
+ tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
428
+ end
429
+ end
174
430
  end
175
431
  end
176
432
  end