actionview 4.2.10 → 5.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +141 -272
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -3
  5. data/lib/action_view/base.rb +33 -21
  6. data/lib/action_view/buffers.rb +1 -1
  7. data/lib/action_view/context.rb +1 -1
  8. data/lib/action_view/dependency_tracker.rb +52 -20
  9. data/lib/action_view/digestor.rb +86 -83
  10. data/lib/action_view/flows.rb +9 -11
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +8 -8
  13. data/lib/action_view/helpers/asset_tag_helper.rb +74 -38
  14. data/lib/action_view/helpers/asset_url_helper.rb +160 -59
  15. data/lib/action_view/helpers/atom_feed_helper.rb +16 -16
  16. data/lib/action_view/helpers/cache_helper.rb +90 -35
  17. data/lib/action_view/helpers/capture_helper.rb +7 -6
  18. data/lib/action_view/helpers/controller_helper.rb +3 -2
  19. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  20. data/lib/action_view/helpers/date_helper.rb +156 -108
  21. data/lib/action_view/helpers/debug_helper.rb +3 -4
  22. data/lib/action_view/helpers/form_helper.rb +475 -94
  23. data/lib/action_view/helpers/form_options_helper.rb +87 -47
  24. data/lib/action_view/helpers/form_tag_helper.rb +88 -57
  25. data/lib/action_view/helpers/javascript_helper.rb +10 -10
  26. data/lib/action_view/helpers/number_helper.rb +76 -59
  27. data/lib/action_view/helpers/output_safety_helper.rb +34 -4
  28. data/lib/action_view/helpers/record_tag_helper.rb +12 -99
  29. data/lib/action_view/helpers/rendering_helper.rb +3 -3
  30. data/lib/action_view/helpers/sanitize_helper.rb +17 -14
  31. data/lib/action_view/helpers/tag_helper.rb +198 -73
  32. data/lib/action_view/helpers/tags/base.rb +132 -97
  33. data/lib/action_view/helpers/tags/check_box.rb +17 -17
  34. data/lib/action_view/helpers/tags/collection_check_boxes.rb +9 -33
  35. data/lib/action_view/helpers/tags/collection_helpers.rb +68 -36
  36. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +3 -11
  37. data/lib/action_view/helpers/tags/collection_select.rb +2 -2
  38. data/lib/action_view/helpers/tags/date_select.rb +36 -36
  39. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  40. data/lib/action_view/helpers/tags/grouped_collection_select.rb +2 -2
  41. data/lib/action_view/helpers/tags/label.rb +5 -1
  42. data/lib/action_view/helpers/tags/password_field.rb +1 -1
  43. data/lib/action_view/helpers/tags/placeholderable.rb +1 -1
  44. data/lib/action_view/helpers/tags/radio_button.rb +4 -4
  45. data/lib/action_view/helpers/tags/search_field.rb +12 -9
  46. data/lib/action_view/helpers/tags/select.rb +9 -9
  47. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  48. data/lib/action_view/helpers/tags/text_field.rb +5 -6
  49. data/lib/action_view/helpers/tags/translator.rb +15 -13
  50. data/lib/action_view/helpers/text_helper.rb +47 -30
  51. data/lib/action_view/helpers/translation_helper.rb +60 -30
  52. data/lib/action_view/helpers/url_helper.rb +132 -104
  53. data/lib/action_view/helpers.rb +1 -1
  54. data/lib/action_view/layouts.rb +59 -54
  55. data/lib/action_view/log_subscriber.rb +56 -7
  56. data/lib/action_view/lookup_context.rb +76 -61
  57. data/lib/action_view/model_naming.rb +1 -1
  58. data/lib/action_view/path_set.rb +28 -19
  59. data/lib/action_view/railtie.rb +30 -6
  60. data/lib/action_view/record_identifier.rb +51 -25
  61. data/lib/action_view/renderer/abstract_renderer.rb +19 -15
  62. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +55 -0
  63. data/lib/action_view/renderer/partial_renderer.rb +208 -206
  64. data/lib/action_view/renderer/renderer.rb +2 -6
  65. data/lib/action_view/renderer/streaming_template_renderer.rb +46 -48
  66. data/lib/action_view/renderer/template_renderer.rb +65 -66
  67. data/lib/action_view/rendering.rb +16 -9
  68. data/lib/action_view/routing_url_for.rb +25 -17
  69. data/lib/action_view/tasks/cache_digests.rake +23 -0
  70. data/lib/action_view/template/error.rb +14 -13
  71. data/lib/action_view/template/handlers/builder.rb +7 -7
  72. data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +9 -0
  73. data/lib/action_view/template/handlers/erb/erubi.rb +81 -0
  74. data/lib/action_view/template/handlers/erb/erubis.rb +81 -0
  75. data/lib/action_view/template/handlers/erb.rb +9 -76
  76. data/lib/action_view/template/handlers/html.rb +9 -0
  77. data/lib/action_view/template/handlers/raw.rb +1 -3
  78. data/lib/action_view/template/handlers.rb +8 -6
  79. data/lib/action_view/template/html.rb +2 -4
  80. data/lib/action_view/template/resolver.rb +133 -109
  81. data/lib/action_view/template/text.rb +5 -8
  82. data/lib/action_view/template/types.rb +15 -17
  83. data/lib/action_view/template.rb +51 -28
  84. data/lib/action_view/test_case.rb +32 -27
  85. data/lib/action_view/testing/resolvers.rb +29 -31
  86. data/lib/action_view/version.rb +1 -1
  87. data/lib/action_view/view_paths.rb +26 -32
  88. data/lib/action_view.rb +5 -5
  89. data/lib/assets/compiled/rails-ujs.js +685 -0
  90. metadata +23 -23
  91. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,9 +1,9 @@
1
- require 'cgi'
2
- require 'erb'
3
- require 'action_view/helpers/form_helper'
4
- require 'active_support/core_ext/string/output_safety'
5
- require 'active_support/core_ext/array/extract_options'
6
- require 'active_support/core_ext/array/wrap'
1
+ require "cgi"
2
+ require "erb"
3
+ require "action_view/helpers/form_helper"
4
+ require "active_support/core_ext/string/output_safety"
5
+ require "active_support/core_ext/array/extract_options"
6
+ require "active_support/core_ext/array/wrap"
7
7
 
8
8
  module ActionView
9
9
  # = Action View Form Option Helpers
@@ -18,10 +18,10 @@ module ActionView
18
18
  #
19
19
  # could become:
20
20
  #
21
- # <select name="post[category]">
22
- # <option></option>
23
- # <option>joke</option>
24
- # <option>poem</option>
21
+ # <select name="post[category]" id="post_category">
22
+ # <option value=""></option>
23
+ # <option value="joke">joke</option>
24
+ # <option value="poem">poem</option>
25
25
  # </select>
26
26
  #
27
27
  # Another common case is a select tag for a <tt>belongs_to</tt>-associated object.
@@ -32,11 +32,11 @@ module ActionView
32
32
  #
33
33
  # could become:
34
34
  #
35
- # <select name="post[person_id]">
35
+ # <select name="post[person_id]" id="post_person_id">
36
36
  # <option value="">None</option>
37
37
  # <option value="1">David</option>
38
- # <option value="2" selected="selected">Sam</option>
39
- # <option value="3">Tobias</option>
38
+ # <option value="2" selected="selected">Eileen</option>
39
+ # <option value="3">Rafael</option>
40
40
  # </select>
41
41
  #
42
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.
@@ -45,11 +45,11 @@ module ActionView
45
45
  #
46
46
  # could become:
47
47
  #
48
- # <select name="post[person_id]">
48
+ # <select name="post[person_id]" id="post_person_id">
49
49
  # <option value="">Select Person</option>
50
50
  # <option value="1">David</option>
51
- # <option value="2">Sam</option>
52
- # <option value="3">Tobias</option>
51
+ # <option value="2">Eileen</option>
52
+ # <option value="3">Rafael</option>
53
53
  # </select>
54
54
  #
55
55
  # * <tt>:index</tt> - like the other form helpers, +select+ can accept an <tt>:index</tt> option to manually set the ID used in the resulting output. Unlike other helpers, +select+ expects this
@@ -71,19 +71,19 @@ module ActionView
71
71
  #
72
72
  # could become:
73
73
  #
74
- # <select name="post[category]">
75
- # <option></option>
76
- # <option>joke</option>
77
- # <option>poem</option>
78
- # <option disabled="disabled">restricted</option>
74
+ # <select name="post[category]" id="post_category">
75
+ # <option value=""></option>
76
+ # <option value="joke">joke</option>
77
+ # <option value="poem">poem</option>
78
+ # <option disabled="disabled" value="restricted">restricted</option>
79
79
  # </select>
80
80
  #
81
81
  # When used with the <tt>collection_select</tt> helper, <tt>:disabled</tt> can also be a Proc that identifies those options that should be disabled.
82
82
  #
83
- # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: lambda{|category| category.archived? }})
83
+ # collection_select(:post, :category_id, Category.all, :id, :name, {disabled: -> (category) { category.archived? }})
84
84
  #
85
85
  # If the categories "2008 stuff" and "Christmas" return true when the method <tt>archived?</tt> is called, this would return:
86
- # <select name="post[category_id]">
86
+ # <select name="post[category_id]" id="post_category_id">
87
87
  # <option value="1" disabled="disabled">2008 stuff</option>
88
88
  # <option value="2" disabled="disabled">Christmas</option>
89
89
  # <option value="3">Jokes</option>
@@ -109,11 +109,11 @@ module ActionView
109
109
  #
110
110
  # would become:
111
111
  #
112
- # <select name="post[person_id]">
112
+ # <select name="post[person_id]" id="post_person_id">
113
113
  # <option value=""></option>
114
114
  # <option value="1" selected="selected">David</option>
115
- # <option value="2">Sam</option>
116
- # <option value="3">Tobias</option>
115
+ # <option value="2">Eileen</option>
116
+ # <option value="3">Rafael</option>
117
117
  # </select>
118
118
  #
119
119
  # assuming the associated person has ID 1.
@@ -192,7 +192,7 @@ module ActionView
192
192
  # collection_select(:post, :author_id, Author.all, :id, :name_with_initial, prompt: true)
193
193
  #
194
194
  # If <tt>@post.author_id</tt> is already <tt>1</tt>, this would return:
195
- # <select name="post[author_id]">
195
+ # <select name="post[author_id]" id="post_author_id">
196
196
  # <option value="">Please select</option>
197
197
  # <option value="1" selected="selected">D. Heinemeier Hansson</option>
198
198
  # <option value="2">D. Thomas</option>
@@ -243,7 +243,7 @@ module ActionView
243
243
  #
244
244
  # Possible output:
245
245
  #
246
- # <select name="city[country_id]">
246
+ # <select name="city[country_id]" id="city_country_id">
247
247
  # <optgroup label="Africa">
248
248
  # <option value="1">South Africa</option>
249
249
  # <option value="3">Somalia</option>
@@ -268,10 +268,11 @@ module ActionView
268
268
  # for more information.)
269
269
  #
270
270
  # You can also supply an array of ActiveSupport::TimeZone objects
271
- # as +priority_zones+, so that they will be listed above the rest of the
272
- # (long) list. (You can use ActiveSupport::TimeZone.us_zones as a convenience
273
- # for obtaining a list of the US time zones, or a Regexp to select the zones
274
- # of your choice)
271
+ # as +priority_zones+ so that they will be listed above the rest of the
272
+ # (long) list. You can use ActiveSupport::TimeZone.us_zones for a list
273
+ # of US time zones, ActiveSupport::TimeZone.country_zones(country_code)
274
+ # for another country's time zones, or a Regexp to select the zones of
275
+ # your choice.
275
276
  #
276
277
  # Finally, this method supports a <tt>:default</tt> option, which selects
277
278
  # a default ActiveSupport::TimeZone if the object's time zone is +nil+.
@@ -302,17 +303,17 @@ module ActionView
302
303
  # # => <option value="DKK">Kroner</option>
303
304
  #
304
305
  # options_for_select([ "VISA", "MasterCard" ], "MasterCard")
305
- # # => <option>VISA</option>
306
- # # => <option selected="selected">MasterCard</option>
306
+ # # => <option value="VISA">VISA</option>
307
+ # # => <option selected="selected" value="MasterCard">MasterCard</option>
307
308
  #
308
309
  # options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
309
310
  # # => <option value="$20">Basic</option>
310
311
  # # => <option value="$40" selected="selected">Plus</option>
311
312
  #
312
313
  # options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
313
- # # => <option selected="selected">VISA</option>
314
- # # => <option>MasterCard</option>
315
- # # => <option selected="selected">Discover</option>
314
+ # # => <option selected="selected" value="VISA">VISA</option>
315
+ # # => <option value="MasterCard">MasterCard</option>
316
+ # # => <option selected="selected" value="Discover">Discover</option>
316
317
  #
317
318
  # You can optionally provide HTML attributes as the last element of the array.
318
319
  #
@@ -351,18 +352,18 @@ module ActionView
351
352
  return container if String === container
352
353
 
353
354
  selected, disabled = extract_selected_and_disabled(selected).map do |r|
354
- Array(r).map { |item| item.to_s }
355
+ Array(r).map(&:to_s)
355
356
  end
356
357
 
357
358
  container.map do |element|
358
359
  html_attributes = option_html_attributes(element)
359
- text, value = option_text_and_value(element).map { |item| item.to_s }
360
+ text, value = option_text_and_value(element).map(&:to_s)
360
361
 
361
362
  html_attributes[:selected] ||= option_value_selected?(value, selected)
362
363
  html_attributes[:disabled] ||= disabled && option_value_selected?(value, disabled)
363
364
  html_attributes[:value] = value
364
365
 
365
- content_tag_string(:option, text, html_attributes)
366
+ tag_builder.content_tag_string(:option, text, html_attributes)
366
367
  end.join("\n").html_safe
367
368
  end
368
369
 
@@ -456,7 +457,7 @@ module ActionView
456
457
  option_tags = options_from_collection_for_select(
457
458
  group.send(group_method), option_key_method, option_value_method, selected_key)
458
459
 
459
- content_tag(:optgroup, option_tags, label: group.send(group_label_method))
460
+ content_tag("optgroup".freeze, option_tags, label: group.send(group_label_method))
460
461
  end.join.html_safe
461
462
  end
462
463
 
@@ -528,7 +529,7 @@ module ActionView
528
529
  body = "".html_safe
529
530
 
530
531
  if prompt
531
- body.safe_concat content_tag(:option, prompt_text(prompt), value: "")
532
+ body.safe_concat content_tag("option".freeze, prompt_text(prompt), value: "")
532
533
  end
533
534
 
534
535
  grouped_options.each do |container|
@@ -541,14 +542,14 @@ module ActionView
541
542
  end
542
543
 
543
544
  html_attributes = { label: label }.merge!(html_attributes)
544
- body.safe_concat content_tag(:optgroup, options_for_select(container, selected_key), html_attributes)
545
+ body.safe_concat content_tag("optgroup".freeze, options_for_select(container, selected_key), html_attributes)
545
546
  end
546
547
 
547
548
  body
548
549
  end
549
550
 
550
551
  # Returns a string of option tags for pretty much any time zone in the
551
- # world. Supply a ActiveSupport::TimeZone name as +selected+ to have it
552
+ # world. Supply an ActiveSupport::TimeZone name as +selected+ to have it
552
553
  # marked as the selected option tag. You can also supply an array of
553
554
  # ActiveSupport::TimeZone objects as +priority_zones+, so that they will
554
555
  # be listed above the rest of the (long) list. (You can use
@@ -556,7 +557,7 @@ module ActionView
556
557
  # of the US time zones, or a Regexp to select the zones of your choice)
557
558
  #
558
559
  # The +selected+ parameter must be either +nil+, or a string that names
559
- # a ActiveSupport::TimeZone.
560
+ # an ActiveSupport::TimeZone.
560
561
  #
561
562
  # By default, +model+ is the ActiveSupport::TimeZone constant (which can
562
563
  # be obtained in Active Record as a value object). The only requirement
@@ -577,7 +578,7 @@ module ActionView
577
578
  end
578
579
 
579
580
  zone_options.safe_concat options_for_select(convert_zones[priority_zones], selected)
580
- zone_options.safe_concat content_tag(:option, '-------------', value: '', disabled: true)
581
+ zone_options.safe_concat content_tag("option".freeze, "-------------", value: "", disabled: true)
581
582
  zone_options.safe_concat "\n"
582
583
 
583
584
  zones = zones - priority_zones
@@ -644,6 +645,24 @@ module ActionView
644
645
  # collection_radio_buttons(:post, :author_id, Author.all, :id, :name_with_initial) do |b|
645
646
  # b.label(:"data-value" => b.value) { b.radio_button + b.text }
646
647
  # end
648
+ #
649
+ # ==== Gotcha
650
+ #
651
+ # The HTML specification says when nothing is select on a collection of radio buttons
652
+ # web browsers do not send any value to server.
653
+ # Unfortunately this introduces a gotcha:
654
+ # if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
655
+ # any strong parameters idiom like:
656
+ #
657
+ # params.require(:user).permit(...)
658
+ #
659
+ # will raise an error since no <tt>{user: ...}</tt> will be present.
660
+ #
661
+ # To prevent this the helper generates an auxiliary hidden field before
662
+ # every collection of radio buttons. The hidden field has the same name as collection radio button and blank value.
663
+ #
664
+ # In case if you don't want the helper to generate this hidden field you can specify
665
+ # <tt>include_hidden: false</tt> option.
647
666
  def collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
648
667
  Tags::CollectionRadioButtons.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
649
668
  end
@@ -707,6 +726,27 @@ module ActionView
707
726
  # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
708
727
  # b.label(:"data-value" => b.value) { b.check_box + b.text }
709
728
  # end
729
+ #
730
+ # ==== Gotcha
731
+ #
732
+ # When no selection is made for a collection of checkboxes most
733
+ # web browsers will not send any value.
734
+ #
735
+ # For example, if we have a +User+ model with +category_ids+ field and we
736
+ # have the following code in our update action:
737
+ #
738
+ # @user.update(params[:user])
739
+ #
740
+ # If no +category_ids+ are selected then we can safely assume this field
741
+ # will not be updated.
742
+ #
743
+ # This is possible thanks to a hidden field generated by the helper method
744
+ # for every collection of checkboxes.
745
+ # This hidden field is given the same field name as the checkboxes with a
746
+ # blank value.
747
+ #
748
+ # In the rare case you don't want this hidden field, you can pass the
749
+ # <tt>include_hidden: false</tt> option to the helper method.
710
750
  def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
711
751
  Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
712
752
  end
@@ -760,7 +800,7 @@ module ActionView
760
800
  end
761
801
 
762
802
  def prompt_text(prompt)
763
- prompt.kind_of?(String) ? prompt : I18n.translate('helpers.select.prompt', default: 'Please select')
803
+ prompt.kind_of?(String) ? prompt : I18n.translate("helpers.select.prompt", default: "Please select")
764
804
  end
765
805
  end
766
806
 
@@ -1,7 +1,7 @@
1
- require 'cgi'
2
- require 'action_view/helpers/tag_helper'
3
- require 'active_support/core_ext/string/output_safety'
4
- require 'active_support/core_ext/module/attribute_accessors'
1
+ require "cgi"
2
+ require "action_view/helpers/tag_helper"
3
+ require "active_support/core_ext/string/output_safety"
4
+ require "active_support/core_ext/module/attribute_accessors"
5
5
 
6
6
  module ActionView
7
7
  # = Action View Form Tag Helpers
@@ -18,9 +18,9 @@ module ActionView
18
18
  include TextHelper
19
19
 
20
20
  mattr_accessor :embed_authenticity_token_in_remote_forms
21
- self.embed_authenticity_token_in_remote_forms = false
21
+ self.embed_authenticity_token_in_remote_forms = nil
22
22
 
23
- # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
23
+ # Starts a form tag that points the action to a url configured with <tt>url_for_options</tt> just like
24
24
  # ActionController::Base#url_for. The method for the form defaults to POST.
25
25
  #
26
26
  # ==== Options
@@ -80,7 +80,7 @@ module ActionView
80
80
  # associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
81
81
  #
82
82
  # ==== Options
83
- # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
83
+ # * <tt>:multiple</tt> - If set to true, the selection will allow multiple choices.
84
84
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
85
85
  # * <tt>:include_blank</tt> - If set to true, an empty option will be created. If set to a string, the string will be used as the option's content and the value will be empty.
86
86
  # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something.
@@ -93,22 +93,22 @@ module ActionView
93
93
  # select_tag "people", options_from_collection_for_select(@people, "id", "name", "1")
94
94
  # # <select id="people" name="people"><option value="1" selected="selected">David</option></select>
95
95
  #
96
- # select_tag "people", "<option>David</option>".html_safe
96
+ # select_tag "people", raw("<option>David</option>")
97
97
  # # => <select id="people" name="people"><option>David</option></select>
98
98
  #
99
- # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
99
+ # select_tag "count", raw("<option>1</option><option>2</option><option>3</option><option>4</option>")
100
100
  # # => <select id="count" name="count"><option>1</option><option>2</option>
101
101
  # # <option>3</option><option>4</option></select>
102
102
  #
103
- # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
103
+ # select_tag "colors", raw("<option>Red</option><option>Green</option><option>Blue</option>"), multiple: true
104
104
  # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
105
105
  # # <option>Green</option><option>Blue</option></select>
106
106
  #
107
- # select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
107
+ # select_tag "locations", raw("<option>Home</option><option selected='selected'>Work</option><option>Out</option>")
108
108
  # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
109
109
  # # <option>Out</option></select>
110
110
  #
111
- # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input', id: 'unique_id'
111
+ # select_tag "access", raw("<option>Read</option><option>Write</option>"), multiple: true, class: 'form_input', id: 'unique_id'
112
112
  # # => <select class="form_input" id="unique_id" multiple="multiple" name="access[]"><option>Read</option>
113
113
  # # <option>Write</option></select>
114
114
  #
@@ -121,7 +121,7 @@ module ActionView
121
121
  # select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
122
122
  # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
123
123
  #
124
- # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, disabled: true
124
+ # select_tag "destination", raw("<option>NYC</option><option>Paris</option><option>Rome</option>"), disabled: true
125
125
  # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
126
126
  # # <option>Paris</option><option>Rome</option></select>
127
127
  #
@@ -134,21 +134,23 @@ module ActionView
134
134
 
135
135
  if options.include?(:include_blank)
136
136
  include_blank = options.delete(:include_blank)
137
+ options_for_blank_options_tag = { value: "" }
137
138
 
138
139
  if include_blank == true
139
- include_blank = ''
140
+ include_blank = ""
141
+ options_for_blank_options_tag[:label] = " "
140
142
  end
141
143
 
142
144
  if include_blank
143
- option_tags = content_tag(:option, include_blank, value: '').safe_concat(option_tags)
145
+ option_tags = content_tag("option".freeze, include_blank, options_for_blank_options_tag).safe_concat(option_tags)
144
146
  end
145
147
  end
146
148
 
147
149
  if prompt = options.delete(:prompt)
148
- option_tags = content_tag(:option, prompt, value: '').safe_concat(option_tags)
150
+ option_tags = content_tag("option".freeze, prompt, value: "").safe_concat(option_tags)
149
151
  end
150
152
 
151
- content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
153
+ content_tag "select".freeze, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
152
154
  end
153
155
 
154
156
  # Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -414,42 +416,45 @@ module ActionView
414
416
  # the form is processed normally, otherwise no action is taken.
415
417
  # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
416
418
  # disabled version of the submit button when the form is submitted. This feature is
417
- # provided by the unobtrusive JavaScript driver.
419
+ # provided by the unobtrusive JavaScript driver. To disable this feature for a single submit tag
420
+ # pass <tt>:data => { disable_with: false }</tt> Defaults to value attribute.
418
421
  #
419
422
  # ==== Examples
420
423
  # submit_tag
421
- # # => <input name="commit" type="submit" value="Save changes" />
424
+ # # => <input name="commit" data-disable-with="Save changes" type="submit" value="Save changes" />
422
425
  #
423
426
  # submit_tag "Edit this article"
424
- # # => <input name="commit" type="submit" value="Edit this article" />
427
+ # # => <input name="commit" data-disable-with="Edit this article" type="submit" value="Edit this article" />
425
428
  #
426
429
  # submit_tag "Save edits", disabled: true
427
- # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
430
+ # # => <input disabled="disabled" name="commit" data-disable-with="Save edits" type="submit" value="Save edits" />
428
431
  #
429
- # submit_tag "Complete sale", data: { disable_with: "Please wait..." }
430
- # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
432
+ # submit_tag "Complete sale", data: { disable_with: "Submitting..." }
433
+ # # => <input name="commit" data-disable-with="Submitting..." type="submit" value="Complete sale" />
431
434
  #
432
435
  # submit_tag nil, class: "form_submit"
433
436
  # # => <input class="form_submit" name="commit" type="submit" />
434
437
  #
435
438
  # submit_tag "Edit", class: "edit_button"
436
- # # => <input class="edit_button" name="commit" type="submit" value="Edit" />
439
+ # # => <input class="edit_button" data-disable-with="Edit" name="commit" type="submit" value="Edit" />
437
440
  #
438
441
  # submit_tag "Save", data: { confirm: "Are you sure?" }
439
- # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
442
+ # # => <input name='commit' type='submit' value='Save' data-disable-with="Save" data-confirm="Are you sure?" />
440
443
  #
441
444
  def submit_tag(value = "Save changes", options = {})
442
- options = options.stringify_keys
443
-
444
- tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
445
+ options = options.deep_stringify_keys
446
+ tag_options = { "type" => "submit", "name" => "commit", "value" => value }.update(options)
447
+ set_default_disable_with value, tag_options
448
+ tag :input, tag_options
445
449
  end
446
450
 
447
451
  # Creates a button element that defines a <tt>submit</tt> button,
448
- # <tt>reset</tt>button or a generic button which can be used in
452
+ # <tt>reset</tt> button or a generic button which can be used in
449
453
  # JavaScript, for example. You can use the button tag as a regular
450
454
  # submit tag but it isn't supported in legacy browsers. However,
451
- # the button tag allows richer labels such as images and emphasis,
452
- # so this helper will also accept a block.
455
+ # the button tag does allow for richer labels such as images and emphasis,
456
+ # so this helper will also accept a block. By default, it will create
457
+ # a button tag with type `submit`, if type is not given.
453
458
  #
454
459
  # ==== Options
455
460
  # * <tt>:data</tt> - This option can be used to add custom data attributes.
@@ -472,6 +477,15 @@ module ActionView
472
477
  # button_tag
473
478
  # # => <button name="button" type="submit">Button</button>
474
479
  #
480
+ # button_tag 'Reset', type: 'reset'
481
+ # # => <button name="button" type="reset">Reset</button>
482
+ #
483
+ # button_tag 'Button', type: 'button'
484
+ # # => <button name="button" type="button">Button</button>
485
+ #
486
+ # button_tag 'Reset', type: 'reset', disabled: true
487
+ # # => <button name="button" type="reset" disabled="disabled">Reset</button>
488
+ #
475
489
  # button_tag(type: 'button') do
476
490
  # content_tag(:strong, 'Ask me!')
477
491
  # end
@@ -479,6 +493,9 @@ module ActionView
479
493
  # # <strong>Ask me!</strong>
480
494
  # # </button>
481
495
  #
496
+ # button_tag "Save", data: { confirm: "Are you sure?" }
497
+ # # => <button name="button" type="submit" data-confirm="Are you sure?">Save</button>
498
+ #
482
499
  # button_tag "Checkout", data: { disable_with: "Please wait..." }
483
500
  # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
484
501
  #
@@ -489,12 +506,12 @@ module ActionView
489
506
  options ||= {}
490
507
  end
491
508
 
492
- options = { 'name' => 'button', 'type' => 'submit' }.merge!(options.stringify_keys)
509
+ options = { "name" => "button", "type" => "submit" }.merge!(options.stringify_keys)
493
510
 
494
511
  if block_given?
495
512
  content_tag :button, options, &block
496
513
  else
497
- content_tag :button, content_or_options || 'Button', options
514
+ content_tag :button, content_or_options || "Button", options
498
515
  end
499
516
  end
500
517
 
@@ -555,7 +572,7 @@ module ActionView
555
572
  # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
556
573
  def field_set_tag(legend = nil, options = nil, &block)
557
574
  output = tag(:fieldset, options, true)
558
- output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
575
+ output.safe_concat(content_tag("legend".freeze, legend)) unless legend.blank?
559
576
  output.concat(capture(&block)) if block_given?
560
577
  output.safe_concat("</fieldset>")
561
578
  end
@@ -656,7 +673,7 @@ module ActionView
656
673
  text_field_tag(name, value, options.merge(type: :time))
657
674
  end
658
675
 
659
- # Creates a text field of type "datetime".
676
+ # Creates a text field of type "datetime-local".
660
677
  #
661
678
  # === Options
662
679
  # * <tt>:min</tt> - The minimum acceptable value.
@@ -664,19 +681,10 @@ module ActionView
664
681
  # * <tt>:step</tt> - The acceptable value granularity.
665
682
  # * Otherwise accepts the same options as text_field_tag.
666
683
  def datetime_field_tag(name, value = nil, options = {})
667
- text_field_tag(name, value, options.merge(type: :datetime))
684
+ text_field_tag(name, value, options.merge(type: "datetime-local"))
668
685
  end
669
686
 
670
- # Creates a text field of type "datetime-local".
671
- #
672
- # === Options
673
- # * <tt>:min</tt> - The minimum acceptable value.
674
- # * <tt>:max</tt> - The maximum acceptable value.
675
- # * <tt>:step</tt> - The acceptable value granularity.
676
- # * Otherwise accepts the same options as text_field_tag.
677
- def datetime_local_field_tag(name, value = nil, options = {})
678
- text_field_tag(name, value, options.merge(type: 'datetime-local'))
679
- end
687
+ alias datetime_local_field_tag datetime_field_tag
680
688
 
681
689
  # Creates a text field of type "month".
682
690
  #
@@ -776,10 +784,10 @@ module ActionView
776
784
  # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
777
785
  #
778
786
  # number_field_tag 'quantity', nil, min: 1, max: 10
779
- # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
787
+ # # => <input id="quantity" name="quantity" min="1" max="10" type="number" />
780
788
  #
781
789
  # number_field_tag 'quantity', nil, min: 1, max: 10, step: 2
782
- # # => <input id="quantity" name="quantity" min="1" max="9" step="2" type="number" />
790
+ # # => <input id="quantity" name="quantity" min="1" max="10" step="2" type="number" />
783
791
  #
784
792
  # number_field_tag 'quantity', '1', class: 'special_input', disabled: true
785
793
  # # => <input disabled="disabled" class="special_input" id="quantity" name="quantity" type="number" value="1" />
@@ -835,19 +843,26 @@ module ActionView
835
843
 
836
844
  def extra_tags_for_form(html_options)
837
845
  authenticity_token = html_options.delete("authenticity_token")
838
- method = html_options.delete("method").to_s
846
+ method = html_options.delete("method").to_s.downcase
839
847
 
840
- method_tag = case method
841
- when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
848
+ method_tag = \
849
+ case method
850
+ when "get"
842
851
  html_options["method"] = "get"
843
- ''
844
- when /^post$/i, "", nil
852
+ ""
853
+ when "post", ""
845
854
  html_options["method"] = "post"
846
- token_tag(authenticity_token)
855
+ token_tag(authenticity_token, form_options: {
856
+ action: html_options["action"],
857
+ method: "post"
858
+ })
847
859
  else
848
860
  html_options["method"] = "post"
849
- method_tag(method) + token_tag(authenticity_token)
850
- end
861
+ method_tag(method) + token_tag(authenticity_token, form_options: {
862
+ action: html_options["action"],
863
+ method: method
864
+ })
865
+ end
851
866
 
852
867
  if html_options.delete("enforce_utf8") { true }
853
868
  utf8_enforcer_tag + method_tag
@@ -869,7 +884,23 @@ module ActionView
869
884
 
870
885
  # see http://www.w3.org/TR/html4/types.html#type-name
871
886
  def sanitize_to_id(name)
872
- name.to_s.delete(']').tr('^-a-zA-Z0-9:.', "_")
887
+ name.to_s.delete("]").tr("^-a-zA-Z0-9:.", "_")
888
+ end
889
+
890
+ def set_default_disable_with(value, tag_options)
891
+ return unless ActionView::Base.automatically_disable_submit_tag
892
+ data = tag_options["data"]
893
+
894
+ unless tag_options["data-disable-with"] == false || (data && data["disable_with"] == false)
895
+ disable_with_text = tag_options["data-disable-with"]
896
+ disable_with_text ||= data["disable_with"] if data
897
+ disable_with_text ||= value.to_s.clone
898
+ tag_options.deep_merge!("data" => { "disable_with" => disable_with_text })
899
+ else
900
+ data.delete("disable_with") if data
901
+ end
902
+
903
+ tag_options.delete("data-disable-with")
873
904
  end
874
905
  end
875
906
  end