actionpack 2.1.2 → 2.2.2

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 (200) hide show
  1. data/CHANGELOG +223 -7
  2. data/README +6 -12
  3. data/Rakefile +11 -11
  4. data/lib/action_controller.rb +9 -9
  5. data/lib/action_controller/assertions/response_assertions.rb +29 -78
  6. data/lib/action_controller/assertions/routing_assertions.rb +33 -33
  7. data/lib/action_controller/assertions/selector_assertions.rb +9 -5
  8. data/lib/action_controller/base.rb +227 -161
  9. data/lib/action_controller/benchmarking.rb +37 -24
  10. data/lib/action_controller/caching/actions.rb +53 -21
  11. data/lib/action_controller/caching/fragments.rb +10 -36
  12. data/lib/action_controller/caching/sweeping.rb +3 -3
  13. data/lib/action_controller/cgi_ext/session.rb +2 -22
  14. data/lib/action_controller/cgi_process.rb +8 -46
  15. data/lib/action_controller/components.rb +4 -1
  16. data/lib/action_controller/cookies.rb +10 -0
  17. data/lib/action_controller/dispatcher.rb +49 -15
  18. data/lib/action_controller/filters.rb +48 -10
  19. data/lib/action_controller/headers.rb +16 -14
  20. data/lib/action_controller/helpers.rb +2 -2
  21. data/lib/action_controller/http_authentication.rb +1 -1
  22. data/lib/action_controller/integration.rb +57 -60
  23. data/lib/action_controller/layout.rb +27 -53
  24. data/lib/action_controller/mime_responds.rb +5 -1
  25. data/lib/action_controller/mime_type.rb +64 -42
  26. data/lib/action_controller/mime_types.rb +2 -1
  27. data/lib/action_controller/performance_test.rb +16 -0
  28. data/lib/action_controller/polymorphic_routes.rb +16 -9
  29. data/lib/action_controller/rack_process.rb +303 -0
  30. data/lib/action_controller/request.rb +205 -97
  31. data/lib/action_controller/request_forgery_protection.rb +2 -2
  32. data/lib/action_controller/request_profiler.rb +0 -0
  33. data/lib/action_controller/rescue.rb +20 -115
  34. data/lib/action_controller/resources.rb +186 -83
  35. data/lib/action_controller/response.rb +140 -26
  36. data/lib/action_controller/routing.rb +28 -30
  37. data/lib/action_controller/routing/builder.rb +45 -54
  38. data/lib/action_controller/routing/optimisations.rb +31 -21
  39. data/lib/action_controller/routing/recognition_optimisation.rb +33 -27
  40. data/lib/action_controller/routing/route.rb +162 -147
  41. data/lib/action_controller/routing/route_set.rb +8 -7
  42. data/lib/action_controller/routing/routing_ext.rb +4 -1
  43. data/lib/action_controller/routing/segments.rb +50 -21
  44. data/lib/action_controller/session/cookie_store.rb +3 -2
  45. data/lib/action_controller/session/drb_server.rb +7 -7
  46. data/lib/action_controller/session_management.rb +6 -2
  47. data/lib/action_controller/streaming.rb +15 -8
  48. data/lib/action_controller/templates/rescues/diagnostics.erb +2 -2
  49. data/lib/action_controller/templates/rescues/template_error.erb +2 -2
  50. data/lib/action_controller/test_case.rb +66 -2
  51. data/lib/action_controller/test_process.rb +71 -66
  52. data/lib/action_controller/translation.rb +13 -0
  53. data/lib/action_controller/url_rewriter.rb +90 -13
  54. data/lib/action_controller/vendor/html-scanner/html/node.rb +9 -2
  55. data/lib/action_controller/vendor/html-scanner/html/sanitizer.rb +1 -1
  56. data/lib/action_controller/vendor/html-scanner/html/selector.rb +2 -2
  57. data/lib/action_controller/verification.rb +2 -2
  58. data/lib/action_pack/version.rb +1 -1
  59. data/lib/action_view.rb +19 -11
  60. data/lib/action_view/base.rb +184 -150
  61. data/lib/action_view/helpers.rb +38 -0
  62. data/lib/action_view/helpers/active_record_helper.rb +56 -27
  63. data/lib/action_view/helpers/asset_tag_helper.rb +356 -153
  64. data/lib/action_view/helpers/atom_feed_helper.rb +74 -19
  65. data/lib/action_view/helpers/benchmark_helper.rb +3 -3
  66. data/lib/action_view/helpers/cache_helper.rb +1 -2
  67. data/lib/action_view/helpers/capture_helper.rb +19 -44
  68. data/lib/action_view/helpers/date_helper.rb +486 -296
  69. data/lib/action_view/helpers/debug_helper.rb +20 -13
  70. data/lib/action_view/helpers/form_helper.rb +71 -30
  71. data/lib/action_view/helpers/form_options_helper.rb +15 -85
  72. data/lib/action_view/helpers/form_tag_helper.rb +61 -38
  73. data/lib/action_view/helpers/javascript_helper.rb +80 -89
  74. data/lib/action_view/helpers/number_helper.rb +179 -74
  75. data/lib/action_view/helpers/prototype_helper.rb +216 -201
  76. data/lib/action_view/helpers/record_tag_helper.rb +4 -5
  77. data/lib/action_view/helpers/sanitize_helper.rb +65 -33
  78. data/lib/action_view/helpers/scriptaculous_helper.rb +2 -2
  79. data/lib/action_view/helpers/tag_helper.rb +39 -22
  80. data/lib/action_view/helpers/text_helper.rb +212 -118
  81. data/lib/action_view/helpers/translation_helper.rb +21 -0
  82. data/lib/action_view/helpers/url_helper.rb +100 -58
  83. data/lib/action_view/inline_template.rb +13 -14
  84. data/lib/action_view/locale/en.yml +91 -0
  85. data/lib/action_view/partials.rb +100 -55
  86. data/lib/action_view/paths.rb +125 -0
  87. data/lib/action_view/renderable.rb +102 -0
  88. data/lib/action_view/renderable_partial.rb +48 -0
  89. data/lib/action_view/template.rb +90 -101
  90. data/lib/action_view/template_error.rb +11 -21
  91. data/lib/action_view/template_handler.rb +8 -28
  92. data/lib/action_view/template_handlers.rb +45 -0
  93. data/lib/action_view/template_handlers/builder.rb +5 -15
  94. data/lib/action_view/template_handlers/erb.rb +9 -6
  95. data/lib/action_view/template_handlers/rjs.rb +2 -17
  96. data/lib/action_view/test_case.rb +7 -4
  97. data/test/abstract_unit.rb +4 -1
  98. data/test/active_record_unit.rb +28 -30
  99. data/test/activerecord/render_partial_with_record_identification_test.rb +25 -12
  100. data/test/controller/action_pack_assertions_test.rb +8 -37
  101. data/test/controller/addresses_render_test.rb +0 -3
  102. data/test/controller/assert_select_test.rb +51 -24
  103. data/test/controller/base_test.rb +4 -4
  104. data/test/controller/caching_test.rb +136 -66
  105. data/test/controller/capture_test.rb +1 -21
  106. data/test/controller/cgi_test.rb +157 -10
  107. data/test/controller/components_test.rb +41 -25
  108. data/test/controller/content_type_test.rb +49 -17
  109. data/test/controller/cookie_test.rb +1 -1
  110. data/test/controller/deprecation/deprecated_base_methods_test.rb +0 -3
  111. data/test/controller/dispatcher_test.rb +9 -1
  112. data/test/controller/filter_params_test.rb +2 -2
  113. data/test/controller/filters_test.rb +13 -13
  114. data/test/controller/html-scanner/cdata_node_test.rb +15 -0
  115. data/test/controller/html-scanner/node_test.rb +21 -0
  116. data/test/controller/html-scanner/sanitizer_test.rb +14 -0
  117. data/test/controller/integration_test.rb +167 -6
  118. data/test/controller/layout_test.rb +11 -68
  119. data/test/controller/logging_test.rb +46 -0
  120. data/test/controller/mime_responds_test.rb +61 -59
  121. data/test/controller/mime_type_test.rb +6 -6
  122. data/test/controller/polymorphic_routes_test.rb +37 -2
  123. data/test/controller/rack_test.rb +323 -0
  124. data/test/controller/redirect_test.rb +72 -71
  125. data/test/controller/render_test.rb +1120 -108
  126. data/test/controller/request_forgery_protection_test.rb +66 -52
  127. data/test/controller/request_test.rb +103 -146
  128. data/test/controller/rescue_test.rb +20 -24
  129. data/test/controller/resources_test.rb +408 -25
  130. data/test/controller/routing_test.rb +1774 -1774
  131. data/test/controller/send_file_test.rb +0 -4
  132. data/test/controller/session/cookie_store_test.rb +53 -1
  133. data/test/controller/test_test.rb +15 -37
  134. data/test/controller/translation_test.rb +26 -0
  135. data/test/controller/url_rewriter_test.rb +27 -28
  136. data/test/controller/view_paths_test.rb +48 -47
  137. data/test/fixtures/_top_level_partial.html.erb +1 -0
  138. data/test/fixtures/_top_level_partial_only.erb +1 -0
  139. data/test/fixtures/developers/_developer.erb +1 -0
  140. data/test/fixtures/fun/games/_game.erb +1 -0
  141. data/test/fixtures/fun/serious/games/_game.erb +1 -0
  142. data/test/fixtures/functional_caching/formatted_fragment_cached.html.erb +3 -0
  143. data/test/fixtures/functional_caching/formatted_fragment_cached.js.rjs +6 -0
  144. data/test/fixtures/functional_caching/formatted_fragment_cached.xml.builder +5 -0
  145. data/test/fixtures/functional_caching/inline_fragment_cached.html.erb +2 -0
  146. data/test/fixtures/layouts/_column.html.erb +2 -0
  147. data/test/fixtures/projects/_project.erb +1 -0
  148. data/test/fixtures/public/javascripts/subdir/subdir.js +1 -0
  149. data/test/fixtures/public/stylesheets/subdir/subdir.css +1 -0
  150. data/test/fixtures/replies/_reply.erb +1 -0
  151. data/test/fixtures/test/_counter.html.erb +1 -0
  152. data/test/fixtures/test/_customer.erb +1 -1
  153. data/test/fixtures/test/_customer_with_var.erb +1 -0
  154. data/test/fixtures/test/_layout_for_block_with_args.html.erb +3 -0
  155. data/test/fixtures/test/_local_inspector.html.erb +1 -0
  156. data/test/fixtures/test/_partial_with_only_html_version.html.erb +1 -0
  157. data/test/fixtures/test/hello.builder +1 -1
  158. data/test/fixtures/test/hyphen-ated.erb +1 -0
  159. data/test/fixtures/test/implicit_content_type.atom.builder +2 -0
  160. data/test/fixtures/test/nested_layout.erb +3 -0
  161. data/test/fixtures/test/non_erb_block_content_for.builder +1 -1
  162. data/test/fixtures/test/sub_template_raise.html.erb +1 -0
  163. data/test/fixtures/test/template.erb +1 -0
  164. data/test/fixtures/test/using_layout_around_block_with_args.html.erb +1 -0
  165. data/test/template/active_record_helper_i18n_test.rb +46 -0
  166. data/test/template/active_record_helper_test.rb +24 -24
  167. data/test/template/asset_tag_helper_test.rb +161 -29
  168. data/test/template/atom_feed_helper_test.rb +114 -5
  169. data/test/template/compiled_templates_test.rb +59 -0
  170. data/test/template/date_helper_i18n_test.rb +113 -0
  171. data/test/template/date_helper_test.rb +403 -109
  172. data/test/template/form_helper_test.rb +213 -154
  173. data/test/template/form_options_helper_test.rb +249 -897
  174. data/test/template/form_tag_helper_test.rb +80 -32
  175. data/test/template/javascript_helper_test.rb +17 -18
  176. data/test/template/number_helper_i18n_test.rb +54 -0
  177. data/test/template/number_helper_test.rb +43 -13
  178. data/test/template/prototype_helper_test.rb +101 -84
  179. data/test/template/record_tag_helper_test.rb +24 -20
  180. data/test/template/render_test.rb +193 -0
  181. data/test/template/sanitize_helper_test.rb +3 -3
  182. data/test/template/tag_helper_test.rb +34 -14
  183. data/test/template/text_helper_test.rb +83 -9
  184. data/test/template/translation_helper_test.rb +28 -0
  185. data/test/template/url_helper_test.rb +55 -18
  186. metadata +57 -18
  187. data/lib/action_view/helpers/javascripts/controls.js +0 -963
  188. data/lib/action_view/helpers/javascripts/dragdrop.js +0 -972
  189. data/lib/action_view/helpers/javascripts/effects.js +0 -1120
  190. data/lib/action_view/helpers/javascripts/prototype.js +0 -4225
  191. data/lib/action_view/partial_template.rb +0 -70
  192. data/lib/action_view/template_finder.rb +0 -177
  193. data/lib/action_view/template_handlers/compilable.rb +0 -128
  194. data/test/controller/custom_handler_test.rb +0 -45
  195. data/test/controller/new_render_test.rb +0 -945
  196. data/test/fixtures/test/block_content_for.erb +0 -2
  197. data/test/fixtures/test/erb_content_for.erb +0 -2
  198. data/test/template/deprecated_erb_variable_test.rb +0 -9
  199. data/test/template/template_finder_test.rb +0 -73
  200. data/test/template/template_object_test.rb +0 -95
@@ -2,21 +2,28 @@ module ActionView
2
2
  module Helpers
3
3
  # Provides a set of methods for making it easier to debug Rails objects.
4
4
  module DebugHelper
5
- # Returns a <pre>-tag that has +object+ dumped by YAML. This creates a very
6
- # readable way to inspect an object.
5
+ # Returns a YAML representation of +object+ wrapped with <pre> and </pre>.
6
+ # If the object cannot be converted to YAML using +to_yaml+, +inspect+ will be called instead.
7
+ # Useful for inspecting an object at the time of rendering.
7
8
  #
8
9
  # ==== Example
9
- # my_hash = {'first' => 1, 'second' => 'two', 'third' => [1,2,3]}
10
- # debug(my_hash)
11
10
  #
12
- # => <pre class='debug_dump'>---
13
- # first: 1
14
- # second: two
15
- # third:
16
- # - 1
17
- # - 2
18
- # - 3
19
- # </pre>
11
+ # @user = User.new({ :username => 'testing', :password => 'xyz', :age => 42}) %>
12
+ # debug(@user)
13
+ # # =>
14
+ # <pre class='debug_dump'>--- !ruby/object:User
15
+ # attributes:
16
+ # &nbsp; updated_at:
17
+ # &nbsp; username: testing
18
+ #
19
+ # &nbsp; age: 42
20
+ # &nbsp; password: xyz
21
+ # &nbsp; created_at:
22
+ # attributes_cache: {}
23
+ #
24
+ # new_record: true
25
+ # </pre>
26
+
20
27
  def debug(object)
21
28
  begin
22
29
  Marshal::dump(object)
@@ -28,4 +35,4 @@ module ActionView
28
35
  end
29
36
  end
30
37
  end
31
- end
38
+ end
@@ -76,7 +76,7 @@ module ActionView
76
76
  # Creates a form and a scope around a specific model object that is used as
77
77
  # a base for questioning about values for the fields.
78
78
  #
79
- # Rails provides succint resource-oriented form generation with +form_for+
79
+ # Rails provides succinct resource-oriented form generation with +form_for+
80
80
  # like this:
81
81
  #
82
82
  # <% form_for @offer do |f| %>
@@ -249,9 +249,9 @@ module ActionView
249
249
  args.unshift object
250
250
  end
251
251
 
252
- concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}), proc.binding)
252
+ concat(form_tag(options.delete(:url) || {}, options.delete(:html) || {}))
253
253
  fields_for(object_name, *(args << options), &proc)
254
- concat('</form>', proc.binding)
254
+ concat('</form>')
255
255
  end
256
256
 
257
257
  def apply_form_for_options!(object_or_array, options) #:nodoc:
@@ -304,10 +304,6 @@ module ActionView
304
304
  when String, Symbol
305
305
  object_name = record_or_name_or_array
306
306
  object = args.first
307
- when Array
308
- object = record_or_name_or_array.last
309
- object_name = ActionController::RecordIdentifier.singular_class_name(object)
310
- apply_form_for_options!(record_or_name_or_array, options)
311
307
  else
312
308
  object = record_or_name_or_array
313
309
  object_name = ActionController::RecordIdentifier.singular_class_name(object)
@@ -333,7 +329,7 @@ module ActionView
333
329
  # # => <label for="post_title" class="title_label">A short title</label>
334
330
  #
335
331
  def label(object_name, method, text = nil, options = {})
336
- InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_label_tag(text, options)
332
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_label_tag(text, options)
337
333
  end
338
334
 
339
335
  # Returns an input tag of the "text" type tailored for accessing a specified attribute (identified by +method+) on an object
@@ -355,7 +351,7 @@ module ActionView
355
351
  # # => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />
356
352
  #
357
353
  def text_field(object_name, method, options = {})
358
- InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("text", options)
354
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("text", options)
359
355
  end
360
356
 
361
357
  # Returns an input tag of the "password" type tailored for accessing a specified attribute (identified by +method+) on an object
@@ -377,7 +373,7 @@ module ActionView
377
373
  # # => <input type="text" id="account_pin" name="account[pin]" size="20" value="#{@account.pin}" class="form_input" />
378
374
  #
379
375
  def password_field(object_name, method, options = {})
380
- InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("password", options)
376
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("password", options)
381
377
  end
382
378
 
383
379
  # Returns a hidden input tag tailored for accessing a specified attribute (identified by +method+) on an object
@@ -395,7 +391,7 @@ module ActionView
395
391
  # hidden_field(:user, :token)
396
392
  # # => <input type="hidden" id="user_token" name="user[token]" value="#{@user.token}" />
397
393
  def hidden_field(object_name, method, options = {})
398
- InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("hidden", options)
394
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("hidden", options)
399
395
  end
400
396
 
401
397
  # Returns an file upload input tag tailored for accessing a specified attribute (identified by +method+) on an object
@@ -414,7 +410,7 @@ module ActionView
414
410
  # # => <input type="file" id="attachment_file" name="attachment[file]" class="file_input" />
415
411
  #
416
412
  def file_field(object_name, method, options = {})
417
- InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_input_field_tag("file", options)
413
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_input_field_tag("file", options)
418
414
  end
419
415
 
420
416
  # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
@@ -442,15 +438,44 @@ module ActionView
442
438
  # # #{@entry.body}
443
439
  # # </textarea>
444
440
  def text_area(object_name, method, options = {})
445
- InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_text_area_tag(options)
441
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_text_area_tag(options)
446
442
  end
447
443
 
448
444
  # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object
449
- # assigned to the template (identified by +object+). It's intended that +method+ returns an integer and if that
450
- # integer is above zero, then the checkbox is checked. Additional options on the input tag can be passed as a
451
- # hash with +options+. The +checked_value+ defaults to 1 while the default +unchecked_value+
452
- # is set to 0 which is convenient for boolean values. Since HTTP standards say that unchecked checkboxes don't post anything,
453
- # we add a hidden value with the same name as the checkbox as a work around.
445
+ # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object.
446
+ # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked.
447
+ # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1
448
+ # while the default +unchecked_value+ is set to 0 which is convenient for boolean values.
449
+ #
450
+ # ==== Gotcha
451
+ #
452
+ # The HTML specification says unchecked check boxes are not successful, and
453
+ # thus web browsers do not send them. Unfortunately this introduces a gotcha:
454
+ # if an Invoice model has a +paid+ flag, and in the form that edits a paid
455
+ # invoice the user unchecks its check box, no +paid+ parameter is sent. So,
456
+ # any mass-assignment idiom like
457
+ #
458
+ # @invoice.update_attributes(params[:invoice])
459
+ #
460
+ # wouldn't update the flag.
461
+ #
462
+ # To prevent this the helper generates a hidden field with the same name as
463
+ # the checkbox after the very check box. So, the client either sends only the
464
+ # hidden field (representing the check box is unchecked), or both fields.
465
+ # Since the HTML specification says key/value pairs have to be sent in the
466
+ # same order they appear in the form and Rails parameters extraction always
467
+ # gets the first occurrence of any given key, that works in ordinary forms.
468
+ #
469
+ # Unfortunately that workaround does not work when the check box goes
470
+ # within an array-like parameter, as in
471
+ #
472
+ # <% fields_for "project[invoice_attributes][]", invoice, :index => nil do |form| %>
473
+ # <%= form.check_box :paid %>
474
+ # ...
475
+ # <% end %>
476
+ #
477
+ # because parameter name repetition is precisely what Rails seeks to distinguish
478
+ # the elements of the array.
454
479
  #
455
480
  # ==== Examples
456
481
  # # Let's say that @post.validated? is 1:
@@ -468,7 +493,7 @@ module ActionView
468
493
  # # <input name="eula[accepted]" type="hidden" value="no" />
469
494
  #
470
495
  def check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
471
- InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
496
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_check_box_tag(options, checked_value, unchecked_value)
472
497
  end
473
498
 
474
499
  # Returns a radio button tag for accessing a specified attribute (identified by +method+) on an object
@@ -488,7 +513,7 @@ module ActionView
488
513
  # # => <input type="radio" id="user_receive_newsletter_yes" name="user[receive_newsletter]" value="yes" />
489
514
  # # <input type="radio" id="user_receive_newsletter_no" name="user[receive_newsletter]" value="no" checked="checked" />
490
515
  def radio_button(object_name, method, tag_value, options = {})
491
- InstanceTag.new(object_name, method, self, nil, options.delete(:object)).to_radio_button_tag(tag_value, options)
516
+ InstanceTag.new(object_name, method, self, options.delete(:object)).to_radio_button_tag(tag_value, options)
492
517
  end
493
518
  end
494
519
 
@@ -501,12 +526,12 @@ module ActionView
501
526
  DEFAULT_RADIO_OPTIONS = { }.freeze unless const_defined?(:DEFAULT_RADIO_OPTIONS)
502
527
  DEFAULT_TEXT_AREA_OPTIONS = { "cols" => 40, "rows" => 20 }.freeze unless const_defined?(:DEFAULT_TEXT_AREA_OPTIONS)
503
528
 
504
- def initialize(object_name, method_name, template_object, local_binding = nil, object = nil)
529
+ def initialize(object_name, method_name, template_object, object = nil)
505
530
  @object_name, @method_name = object_name.to_s.dup, method_name.to_s.dup
506
- @template_object, @local_binding = template_object, local_binding
531
+ @template_object = template_object
507
532
  @object = object
508
- if @object_name.sub!(/\[\]$/,"")
509
- if object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
533
+ if @object_name.sub!(/\[\]$/,"") || @object_name.sub!(/\[\]\]$/,"]")
534
+ if (object ||= @template_object.instance_variable_get("@#{Regexp.last_match.pre_match}")) && object.respond_to?(:to_param)
510
535
  @auto_index = object.to_param
511
536
  else
512
537
  raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
@@ -683,7 +708,7 @@ module ActionView
683
708
  end
684
709
 
685
710
  def sanitized_object_name
686
- @sanitized_object_name ||= @object_name.gsub(/[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
711
+ @sanitized_object_name ||= @object_name.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").sub(/_$/, "")
687
712
  end
688
713
 
689
714
  def sanitized_method_name
@@ -701,6 +726,13 @@ module ActionView
701
726
  def initialize(object_name, object, template, options, proc)
702
727
  @object_name, @object, @template, @options, @proc = object_name, object, template, options, proc
703
728
  @default_options = @options ? @options.slice(:index) : {}
729
+ if @object_name.to_s.match(/\[\]$/)
730
+ if object ||= @template.instance_variable_get("@#{Regexp.last_match.pre_match}") and object.respond_to?(:to_param)
731
+ @auto_index = object.to_param
732
+ else
733
+ raise ArgumentError, "object[] naming but object param and @object var don't exist or don't respond to to_param: #{object.inspect}"
734
+ end
735
+ end
704
736
  end
705
737
 
706
738
  (field_helpers - %w(label check_box radio_button fields_for)).each do |selector|
@@ -713,16 +745,25 @@ module ActionView
713
745
  end
714
746
 
715
747
  def fields_for(record_or_name_or_array, *args, &block)
748
+ if options.has_key?(:index)
749
+ index = "[#{options[:index]}]"
750
+ elsif defined?(@auto_index)
751
+ self.object_name = @object_name.to_s.sub(/\[\]$/,"")
752
+ index = "[#{@auto_index}]"
753
+ else
754
+ index = ""
755
+ end
756
+
716
757
  case record_or_name_or_array
717
758
  when String, Symbol
718
- name = "#{object_name}[#{record_or_name_or_array}]"
759
+ name = "#{object_name}#{index}[#{record_or_name_or_array}]"
719
760
  when Array
720
761
  object = record_or_name_or_array.last
721
- name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
762
+ name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
722
763
  args.unshift(object)
723
764
  else
724
765
  object = record_or_name_or_array
725
- name = "#{object_name}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
766
+ name = "#{object_name}#{index}[#{ActionController::RecordIdentifier.singular_class_name(object)}]"
726
767
  args.unshift(object)
727
768
  end
728
769
 
@@ -741,8 +782,8 @@ module ActionView
741
782
  @template.radio_button(@object_name, method, tag_value, objectify_options(options))
742
783
  end
743
784
 
744
- def error_message_on(method, prepend_text = "", append_text = "", css_class = "formError")
745
- @template.error_message_on(@object, method, prepend_text, append_text, css_class)
785
+ def error_message_on(method, *args)
786
+ @template.error_message_on(@object, method, *args)
746
787
  end
747
788
 
748
789
  def error_messages(options = {})
@@ -96,7 +96,7 @@ module ActionView
96
96
  # By default, <tt>post.person_id</tt> is the selected option. Specify <tt>:selected => value</tt> to use a different selection
97
97
  # or <tt>:selected => nil</tt> to leave all options unselected.
98
98
  def select(object, method, choices, options = {}, html_options = {})
99
- InstanceTag.new(object, method, self, nil, options.delete(:object)).to_select_tag(choices, options, html_options)
99
+ InstanceTag.new(object, method, self, options.delete(:object)).to_select_tag(choices, options, html_options)
100
100
  end
101
101
 
102
102
  # Returns <tt><select></tt> and <tt><option></tt> tags for the collection of existing return values of
@@ -130,12 +130,7 @@ module ActionView
130
130
  # <option value="3">M. Clark</option>
131
131
  # </select>
132
132
  def collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})
133
- InstanceTag.new(object, method, self, nil, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
134
- end
135
-
136
- # Return select and option tags for the given object and method, using country_options_for_select to generate the list of option tags.
137
- def country_select(object, method, priority_countries = nil, options = {}, html_options = {})
138
- InstanceTag.new(object, method, self, nil, options.delete(:object)).to_country_select_tag(priority_countries, options, html_options)
133
+ InstanceTag.new(object, method, self, options.delete(:object)).to_collection_select_tag(collection, value_method, text_method, options, html_options)
139
134
  end
140
135
 
141
136
  # Return select and option tags for the given object and method, using
@@ -150,7 +145,8 @@ module ActionView
150
145
  # You can also supply an array of TimeZone objects
151
146
  # as +priority_zones+, so that they will be listed above the rest of the
152
147
  # (long) list. (You can use TimeZone.us_zones as a convenience for
153
- # obtaining a list of the US time zones.)
148
+ # obtaining a list of the US time zones, or a Regexp to select the zones
149
+ # of your choice)
154
150
  #
155
151
  # Finally, this method supports a <tt>:default</tt> option, which selects
156
152
  # a default TimeZone if the object's time zone is +nil+.
@@ -164,9 +160,11 @@ module ActionView
164
160
  #
165
161
  # time_zone_select( "user", 'time_zone', [ TimeZone['Alaska'], TimeZone['Hawaii'] ])
166
162
  #
163
+ # time_zone_select( "user", 'time_zone', /Australia/)
164
+ #
167
165
  # time_zone_select( "user", "time_zone", TZInfo::Timezone.all.sort, :model => TZInfo::Timezone)
168
166
  def time_zone_select(object, method, priority_zones = nil, options = {}, html_options = {})
169
- InstanceTag.new(object, method, self, nil, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
167
+ InstanceTag.new(object, method, self, options.delete(:object)).to_time_zone_select_tag(priority_zones, options, html_options)
170
168
  end
171
169
 
172
170
  # Accepts a container (hash, array, enumerable, your type) and returns a string of option tags. Given a container
@@ -271,28 +269,13 @@ module ActionView
271
269
  end
272
270
  end
273
271
 
274
- # Returns a string of option tags for pretty much any country in the world. Supply a country name as +selected+ to
275
- # have it marked as the selected option tag. You can also supply an array of countries as +priority_countries+, so
276
- # that they will be listed above the rest of the (long) list.
277
- #
278
- # NOTE: Only the option tags are returned, you have to wrap this call in a regular HTML select tag.
279
- def country_options_for_select(selected = nil, priority_countries = nil)
280
- country_options = ""
281
-
282
- if priority_countries
283
- country_options += options_for_select(priority_countries, selected)
284
- country_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
285
- end
286
-
287
- return country_options + options_for_select(COUNTRIES, selected)
288
- end
289
-
290
272
  # Returns a string of option tags for pretty much any time zone in the
291
273
  # world. Supply a TimeZone name as +selected+ to have it marked as the
292
274
  # selected option tag. You can also supply an array of TimeZone objects
293
275
  # as +priority_zones+, so that they will be listed above the rest of the
294
276
  # (long) list. (You can use TimeZone.us_zones as a convenience for
295
- # obtaining a list of the US time zones.)
277
+ # obtaining a list of the US time zones, or a Regexp to select the zones
278
+ # of your choice)
296
279
  #
297
280
  # The +selected+ parameter must be either +nil+, or a string that names
298
281
  # a TimeZone.
@@ -311,6 +294,9 @@ module ActionView
311
294
  convert_zones = lambda { |list| list.map { |z| [ z.to_s, z.name ] } }
312
295
 
313
296
  if priority_zones
297
+ if priority_zones.is_a?(Regexp)
298
+ priority_zones = model.all.find_all {|z| z =~ priority_zones}
299
+ end
314
300
  zone_options += options_for_select(convert_zones[priority_zones], selected)
315
301
  zone_options += "<option value=\"\" disabled=\"disabled\">-------------</option>\n"
316
302
 
@@ -338,45 +324,6 @@ module ActionView
338
324
  value == selected
339
325
  end
340
326
  end
341
-
342
- # All the countries included in the country_options output.
343
- COUNTRIES = ["Afghanistan", "Aland Islands", "Albania", "Algeria", "American Samoa", "Andorra", "Angola",
344
- "Anguilla", "Antarctica", "Antigua And Barbuda", "Argentina", "Armenia", "Aruba", "Australia", "Austria",
345
- "Azerbaijan", "Bahamas", "Bahrain", "Bangladesh", "Barbados", "Belarus", "Belgium", "Belize", "Benin",
346
- "Bermuda", "Bhutan", "Bolivia", "Bosnia and Herzegowina", "Botswana", "Bouvet Island", "Brazil",
347
- "British Indian Ocean Territory", "Brunei Darussalam", "Bulgaria", "Burkina Faso", "Burundi", "Cambodia",
348
- "Cameroon", "Canada", "Cape Verde", "Cayman Islands", "Central African Republic", "Chad", "Chile", "China",
349
- "Christmas Island", "Cocos (Keeling) Islands", "Colombia", "Comoros", "Congo",
350
- "Congo, the Democratic Republic of the", "Cook Islands", "Costa Rica", "Cote d'Ivoire", "Croatia", "Cuba",
351
- "Cyprus", "Czech Republic", "Denmark", "Djibouti", "Dominica", "Dominican Republic", "Ecuador", "Egypt",
352
- "El Salvador", "Equatorial Guinea", "Eritrea", "Estonia", "Ethiopia", "Falkland Islands (Malvinas)",
353
- "Faroe Islands", "Fiji", "Finland", "France", "French Guiana", "French Polynesia",
354
- "French Southern Territories", "Gabon", "Gambia", "Georgia", "Germany", "Ghana", "Gibraltar", "Greece", "Greenland", "Grenada", "Guadeloupe", "Guam", "Guatemala", "Guernsey", "Guinea",
355
- "Guinea-Bissau", "Guyana", "Haiti", "Heard and McDonald Islands", "Holy See (Vatican City State)",
356
- "Honduras", "Hong Kong", "Hungary", "Iceland", "India", "Indonesia", "Iran, Islamic Republic of", "Iraq",
357
- "Ireland", "Isle of Man", "Israel", "Italy", "Jamaica", "Japan", "Jersey", "Jordan", "Kazakhstan", "Kenya",
358
- "Kiribati", "Korea, Democratic People's Republic of", "Korea, Republic of", "Kuwait", "Kyrgyzstan",
359
- "Lao People's Democratic Republic", "Latvia", "Lebanon", "Lesotho", "Liberia", "Libyan Arab Jamahiriya",
360
- "Liechtenstein", "Lithuania", "Luxembourg", "Macao", "Macedonia, The Former Yugoslav Republic Of",
361
- "Madagascar", "Malawi", "Malaysia", "Maldives", "Mali", "Malta", "Marshall Islands", "Martinique",
362
- "Mauritania", "Mauritius", "Mayotte", "Mexico", "Micronesia, Federated States of", "Moldova, Republic of",
363
- "Monaco", "Mongolia", "Montenegro", "Montserrat", "Morocco", "Mozambique", "Myanmar", "Namibia", "Nauru",
364
- "Nepal", "Netherlands", "Netherlands Antilles", "New Caledonia", "New Zealand", "Nicaragua", "Niger",
365
- "Nigeria", "Niue", "Norfolk Island", "Northern Mariana Islands", "Norway", "Oman", "Pakistan", "Palau",
366
- "Palestinian Territory, Occupied", "Panama", "Papua New Guinea", "Paraguay", "Peru", "Philippines",
367
- "Pitcairn", "Poland", "Portugal", "Puerto Rico", "Qatar", "Reunion", "Romania", "Russian Federation",
368
- "Rwanda", "Saint Barthelemy", "Saint Helena", "Saint Kitts and Nevis", "Saint Lucia",
369
- "Saint Pierre and Miquelon", "Saint Vincent and the Grenadines", "Samoa", "San Marino",
370
- "Sao Tome and Principe", "Saudi Arabia", "Senegal", "Serbia", "Seychelles", "Sierra Leone", "Singapore",
371
- "Slovakia", "Slovenia", "Solomon Islands", "Somalia", "South Africa",
372
- "South Georgia and the South Sandwich Islands", "Spain", "Sri Lanka", "Sudan", "Suriname",
373
- "Svalbard and Jan Mayen", "Swaziland", "Sweden", "Switzerland", "Syrian Arab Republic",
374
- "Taiwan, Province of China", "Tajikistan", "Tanzania, United Republic of", "Thailand", "Timor-Leste",
375
- "Togo", "Tokelau", "Tonga", "Trinidad and Tobago", "Tunisia", "Turkey", "Turkmenistan",
376
- "Turks and Caicos Islands", "Tuvalu", "Uganda", "Ukraine", "United Arab Emirates", "United Kingdom",
377
- "United States", "United States Minor Outlying Islands", "Uruguay", "Uzbekistan", "Vanuatu", "Venezuela",
378
- "Viet Nam", "Virgin Islands, British", "Virgin Islands, U.S.", "Wallis and Futuna", "Western Sahara",
379
- "Yemen", "Zambia", "Zimbabwe"] unless const_defined?("COUNTRIES")
380
327
  end
381
328
 
382
329
  class InstanceTag #:nodoc:
@@ -399,19 +346,6 @@ module ActionView
399
346
  )
400
347
  end
401
348
 
402
- def to_country_select_tag(priority_countries, options, html_options)
403
- ActiveSupport::Deprecation.warn("country_select will be removed from 2.2.0. http://www.rubyonrails.org/deprecation/list-of-countries has more information.", caller)
404
- html_options = html_options.stringify_keys
405
- add_default_name_and_id(html_options)
406
- value = value(object)
407
- content_tag("select",
408
- add_options(
409
- country_options_for_select(value, priority_countries),
410
- options, value
411
- ), html_options
412
- )
413
- end
414
-
415
349
  def to_time_zone_select_tag(priority_zones, options, html_options)
416
350
  html_options = html_options.stringify_keys
417
351
  add_default_name_and_id(html_options)
@@ -439,19 +373,15 @@ module ActionView
439
373
 
440
374
  class FormBuilder
441
375
  def select(method, choices, options = {}, html_options = {})
442
- @template.select(@object_name, method, choices, options.merge(:object => @object), html_options)
376
+ @template.select(@object_name, method, choices, objectify_options(options), @default_options.merge(html_options))
443
377
  end
444
378
 
445
379
  def collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
446
- @template.collection_select(@object_name, method, collection, value_method, text_method, options.merge(:object => @object), html_options)
447
- end
448
-
449
- def country_select(method, priority_countries = nil, options = {}, html_options = {})
450
- @template.country_select(@object_name, method, priority_countries, options.merge(:object => @object), html_options)
380
+ @template.collection_select(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_options.merge(html_options))
451
381
  end
452
382
 
453
383
  def time_zone_select(method, priority_zones = nil, options = {}, html_options = {})
454
- @template.time_zone_select(@object_name, method, priority_zones, options.merge(:object => @object), html_options)
384
+ @template.time_zone_select(@object_name, method, priority_zones, objectify_options(options), @default_options.merge(html_options))
455
385
  end
456
386
  end
457
387
  end
@@ -6,7 +6,7 @@ module ActionView
6
6
  # Provides a number of methods for creating form tags that doesn't rely on an Active Record object assigned to the template like
7
7
  # FormHelper does. Instead, you provide the names and values manually.
8
8
  #
9
- # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
9
+ # NOTE: The HTML options <tt>disabled</tt>, <tt>readonly</tt>, and <tt>multiple</tt> can all be treated as booleans. So specifying
10
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
@@ -20,15 +20,15 @@ module ActionView
20
20
  # * A list of parameters to feed to the URL the form will be posted to.
21
21
  #
22
22
  # ==== Examples
23
- # form_tag('/posts')
23
+ # form_tag('/posts')
24
24
  # # => <form action="/posts" method="post">
25
25
  #
26
- # form_tag('/posts/1', :method => :put)
26
+ # form_tag('/posts/1', :method => :put)
27
27
  # # => <form action="/posts/1" method="put">
28
28
  #
29
- # form_tag('/upload', :multipart => true)
29
+ # form_tag('/upload', :multipart => true)
30
30
  # # => <form action="/upload" method="post" enctype="multipart/form-data">
31
- #
31
+ #
32
32
  # <% form_tag '/posts' do -%>
33
33
  # <div><%= submit_tag 'Save' %></div>
34
34
  # <% end -%>
@@ -62,7 +62,7 @@ module ActionView
62
62
  # # <option>3</option><option>4</option></select>
63
63
  #
64
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>
65
+ # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
66
66
  # # <option>Green</option><option>Blue</option></select>
67
67
  #
68
68
  # select_tag "locations", "<option>Home</option><option selected="selected">Work</option><option>Out</option>"
@@ -70,14 +70,15 @@ module ActionView
70
70
  # # <option>Out</option></select>
71
71
  #
72
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>
73
+ # # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
74
74
  # # <option>Write</option></select>
75
75
  #
76
76
  # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>", :disabled => true
77
77
  # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
78
78
  # # <option>Paris</option><option>Rome</option></select>
79
79
  def select_tag(name, option_tags = nil, options = {})
80
- content_tag :select, option_tags, { "name" => name, "id" => name }.update(options.stringify_keys)
80
+ html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
81
+ content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
81
82
  end
82
83
 
83
84
  # Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -88,7 +89,7 @@ module ActionView
88
89
  # * <tt>:size</tt> - The number of visible characters that will fit in the input.
89
90
  # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
90
91
  # * Any other key creates standard HTML attributes for the tag.
91
- #
92
+ #
92
93
  # ==== Examples
93
94
  # text_field_tag 'name'
94
95
  # # => <input id="name" name="name" type="text" />
@@ -111,7 +112,7 @@ module ActionView
111
112
  # text_field_tag 'ip', '0.0.0.0', :maxlength => 15, :size => 20, :class => "ip-input"
112
113
  # # => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
113
114
  def text_field_tag(name, value = nil, options = {})
114
- tag :input, { "type" => "text", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
115
+ tag :input, { "type" => "text", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
115
116
  end
116
117
 
117
118
  # Creates a label field
@@ -129,7 +130,7 @@ module ActionView
129
130
  # label_tag 'name', nil, :class => 'small_label'
130
131
  # # => <label for="name" class="small_label">Name</label>
131
132
  def label_tag(name, text = nil, options = {})
132
- content_tag :label, text || name.to_s.humanize, { "for" => name }.update(options.stringify_keys)
133
+ content_tag :label, text || name.to_s.humanize, { "for" => sanitize_to_id(name) }.update(options.stringify_keys)
133
134
  end
134
135
 
135
136
  # Creates a hidden form input field used to transmit data that would be lost due to HTTP's statelessness or
@@ -146,21 +147,21 @@ module ActionView
146
147
  # # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
147
148
  #
148
149
  # hidden_field_tag 'collected_input', '', :onchange => "alert('Input collected!')"
149
- # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
150
+ # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
150
151
  # # type="hidden" value="" />
151
152
  def hidden_field_tag(name, value = nil, options = {})
152
153
  text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
153
154
  end
154
155
 
155
- # Creates a file upload field. If you are using file uploads then you will also need
156
+ # Creates a file upload field. If you are using file uploads then you will also need
156
157
  # to set the multipart option for the form tag:
157
158
  #
158
- # <%= form_tag { :action => "post" }, { :multipart => true } %>
159
+ # <% form_tag '/upload', :multipart => true do %>
159
160
  # <label for="file">File to Upload</label> <%= file_field_tag "file" %>
160
161
  # <%= submit_tag %>
161
- # <%= end_form_tag %>
162
+ # <% end %>
162
163
  #
163
- # The specified URL will then be passed a File object containing the selected file, or if the field
164
+ # The specified URL will then be passed a File object containing the selected file, or if the field
164
165
  # was left blank, a StringIO object.
165
166
  #
166
167
  # ==== Options
@@ -181,7 +182,7 @@ module ActionView
181
182
  # # => <input id="resume" name="resume" type="file" value="~/resume.doc" />
182
183
  #
183
184
  # file_field_tag 'user_pic', :accept => 'image/png,image/gif,image/jpeg'
184
- # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
185
+ # # => <input accept="image/png,image/gif,image/jpeg" id="user_pic" name="user_pic" type="file" />
185
186
  #
186
187
  # file_field_tag 'file', :accept => 'text/html', :class => 'upload', :value => 'index.html'
187
188
  # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
@@ -281,12 +282,12 @@ module ActionView
281
282
  # check_box_tag 'eula', 'accepted', false, :disabled => true
282
283
  # # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
283
284
  def check_box_tag(name, value = "1", checked = false, options = {})
284
- html_options = { "type" => "checkbox", "name" => name, "id" => name, "value" => value }.update(options.stringify_keys)
285
+ html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
285
286
  html_options["checked"] = "checked" if checked
286
287
  tag :input, html_options
287
288
  end
288
289
 
289
- # Creates a radio button; use groups of radio buttons named the same to allow users to
290
+ # Creates a radio button; use groups of radio buttons named the same to allow users to
290
291
  # select from a group of options.
291
292
  #
292
293
  # ==== Options
@@ -313,14 +314,14 @@ module ActionView
313
314
  tag :input, html_options
314
315
  end
315
316
 
316
- # Creates a submit button with the text <tt>value</tt> as the caption.
317
+ # Creates a submit button with the text <tt>value</tt> as the caption.
317
318
  #
318
319
  # ==== Options
319
320
  # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
320
321
  # prompt with the question specified. If the user accepts, the form is
321
322
  # processed normally, otherwise no action is taken.
322
323
  # * <tt>:disabled</tt> - If true, the user will not be able to use this input.
323
- # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version
324
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a disabled version
324
325
  # of the submit button when the form is submitted.
325
326
  # * Any other key creates standard HTML options for the tag.
326
327
  #
@@ -335,7 +336,7 @@ module ActionView
335
336
  # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
336
337
  #
337
338
  # submit_tag "Complete sale", :disable_with => "Please wait..."
338
- # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();"
339
+ # # => <input name="commit" onclick="this.disabled=true;this.value='Please wait...';this.form.submit();"
339
340
  # # type="submit" value="Complete sale" />
340
341
  #
341
342
  # submit_tag nil, :class => "form_submit"
@@ -346,7 +347,7 @@ module ActionView
346
347
  # # name="commit" type="submit" value="Edit" />
347
348
  def submit_tag(value = "Save changes", options = {})
348
349
  options.stringify_keys!
349
-
350
+
350
351
  if disable_with = options.delete("disable_with")
351
352
  disable_with = "this.value='#{disable_with}'"
352
353
  disable_with << ";#{options.delete('onclick')}" if options['onclick']
@@ -357,20 +358,23 @@ module ActionView
357
358
  options["onclick"] << "result = (this.form.onsubmit ? (this.form.onsubmit() ? this.form.submit() : false) : this.form.submit());"
358
359
  options["onclick"] << "if (result == false) { this.value = this.getAttribute('originalValue');this.disabled = false; }return result;"
359
360
  end
360
-
361
+
361
362
  if confirm = options.delete("confirm")
362
363
  options["onclick"] ||= ''
363
364
  options["onclick"] << "return #{confirm_javascript_function(confirm)};"
364
365
  end
365
-
366
+
366
367
  tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options.stringify_keys)
367
368
  end
368
-
369
+
369
370
  # Displays an image which when clicked will submit the form.
370
371
  #
371
372
  # <tt>source</tt> is passed to AssetTagHelper#image_path
372
373
  #
373
374
  # ==== Options
375
+ # * <tt>:confirm => 'question?'</tt> - This will add a JavaScript confirm
376
+ # prompt with the question specified. If the user accepts, the form is
377
+ # processed normally, otherwise no action is taken.
374
378
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
375
379
  # * Any other key creates standard HTML options for the tag.
376
380
  #
@@ -387,12 +391,20 @@ module ActionView
387
391
  # image_submit_tag("agree.png", :disabled => true, :class => "agree-disagree-button")
388
392
  # # => <input class="agree-disagree-button" disabled="disabled" src="/images/agree.png" type="image" />
389
393
  def image_submit_tag(source, options = {})
394
+ options.stringify_keys!
395
+
396
+ if confirm = options.delete("confirm")
397
+ options["onclick"] ||= ''
398
+ options["onclick"] += "return #{confirm_javascript_function(confirm)};"
399
+ end
400
+
390
401
  tag :input, { "type" => "image", "src" => path_to_image(source) }.update(options.stringify_keys)
391
402
  end
392
403
 
393
404
  # Creates a field set for grouping HTML form elements.
394
405
  #
395
406
  # <tt>legend</tt> will become the fieldset's title (optional as per W3C).
407
+ # <tt>options</tt> accept the same values as tag.
396
408
  #
397
409
  # === Examples
398
410
  # <% field_set_tag do %>
@@ -404,14 +416,19 @@ module ActionView
404
416
  # <p><%= text_field_tag 'name' %></p>
405
417
  # <% end %>
406
418
  # # => <fieldset><legend>Your details</legend><p><input id="name" name="name" type="text" /></p></fieldset>
407
- def field_set_tag(legend = nil, &block)
419
+ #
420
+ # <% field_set_tag nil, :class => 'format' do %>
421
+ # <p><%= text_field_tag 'name' %></p>
422
+ # <% end %>
423
+ # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
424
+ def field_set_tag(legend = nil, options = nil, &block)
408
425
  content = capture(&block)
409
- concat(tag(:fieldset, {}, true), block.binding)
410
- concat(content_tag(:legend, legend), block.binding) unless legend.blank?
411
- concat(content, block.binding)
412
- concat("</fieldset>", block.binding)
426
+ concat(tag(:fieldset, options, true))
427
+ concat(content_tag(:legend, legend)) unless legend.blank?
428
+ concat(content)
429
+ concat("</fieldset>")
413
430
  end
414
-
431
+
415
432
  private
416
433
  def html_options_for_form(url_for_options, options, *parameters_for_url)
417
434
  returning options.stringify_keys do |html_options|
@@ -419,7 +436,7 @@ module ActionView
419
436
  html_options["action"] = url_for(url_for_options, *parameters_for_url)
420
437
  end
421
438
  end
422
-
439
+
423
440
  def extra_tags_for_form(html_options)
424
441
  case method = html_options.delete("method").to_s
425
442
  when /^get$/i # must be case-insentive, but can't use downcase as might be nil
@@ -433,17 +450,17 @@ module ActionView
433
450
  content_tag(:div, tag(:input, :type => "hidden", :name => "_method", :value => method) + token_tag, :style => 'margin:0;padding:0')
434
451
  end
435
452
  end
436
-
453
+
437
454
  def form_tag_html(html_options)
438
455
  extra_tags = extra_tags_for_form(html_options)
439
456
  tag(:form, html_options, true) + extra_tags
440
457
  end
441
-
458
+
442
459
  def form_tag_in_block(html_options, &block)
443
460
  content = capture(&block)
444
- concat(form_tag_html(html_options), block.binding)
445
- concat(content, block.binding)
446
- concat("</form>", block.binding)
461
+ concat(form_tag_html(html_options))
462
+ concat(content)
463
+ concat("</form>")
447
464
  end
448
465
 
449
466
  def token_tag
@@ -453,6 +470,12 @@ module ActionView
453
470
  tag(:input, :type => "hidden", :name => request_forgery_protection_token.to_s, :value => form_authenticity_token)
454
471
  end
455
472
  end
473
+
474
+ # see http://www.w3.org/TR/html4/types.html#type-name
475
+ def sanitize_to_id(name)
476
+ name.to_s.gsub(']','').gsub(/[^-a-zA-Z0-9:.]/, "_")
477
+ end
478
+
456
479
  end
457
480
  end
458
481
  end