actionview 4.2.11.1 → 7.0.2.4

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

Potentially problematic release.


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

Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +229 -215
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +116 -43
  6. data/lib/action_view/buffers.rb +20 -3
  7. data/lib/action_view/cache_expiry.rb +66 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
  10. data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
  11. data/lib/action_view/dependency_tracker.rb +21 -122
  12. data/lib/action_view/digestor.rb +92 -85
  13. data/lib/action_view/flows.rb +15 -16
  14. data/lib/action_view/gem_version.rb +6 -4
  15. data/lib/action_view/helpers/active_model_helper.rb +17 -12
  16. data/lib/action_view/helpers/asset_tag_helper.rb +356 -101
  17. data/lib/action_view/helpers/asset_url_helper.rb +180 -74
  18. data/lib/action_view/helpers/atom_feed_helper.rb +21 -19
  19. data/lib/action_view/helpers/cache_helper.rb +156 -43
  20. data/lib/action_view/helpers/capture_helper.rb +21 -14
  21. data/lib/action_view/helpers/controller_helper.rb +16 -5
  22. data/lib/action_view/helpers/csp_helper.rb +26 -0
  23. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  24. data/lib/action_view/helpers/date_helper.rb +288 -132
  25. data/lib/action_view/helpers/debug_helper.rb +9 -6
  26. data/lib/action_view/helpers/form_helper.rb +956 -173
  27. data/lib/action_view/helpers/form_options_helper.rb +178 -97
  28. data/lib/action_view/helpers/form_tag_helper.rb +220 -101
  29. data/lib/action_view/helpers/javascript_helper.rb +33 -19
  30. data/lib/action_view/helpers/number_helper.rb +88 -63
  31. data/lib/action_view/helpers/output_safety_helper.rb +38 -6
  32. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  33. data/lib/action_view/helpers/sanitize_helper.rb +31 -32
  34. data/lib/action_view/helpers/tag_helper.rb +332 -71
  35. data/lib/action_view/helpers/tags/base.rb +123 -99
  36. data/lib/action_view/helpers/tags/check_box.rb +21 -20
  37. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  38. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  39. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  40. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  41. data/lib/action_view/helpers/tags/collection_select.rb +5 -3
  42. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  43. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  44. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  45. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  46. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  47. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  48. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  49. data/lib/action_view/helpers/tags/file_field.rb +18 -0
  50. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  51. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  52. data/lib/action_view/helpers/tags/label.rb +7 -2
  53. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  54. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  55. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  56. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  57. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  58. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  59. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  60. data/lib/action_view/helpers/tags/select.rb +11 -10
  61. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  62. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  63. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  64. data/lib/action_view/helpers/tags/time_field.rb +12 -2
  65. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  66. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  67. data/lib/action_view/helpers/tags/translator.rb +15 -16
  68. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  69. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  70. data/lib/action_view/helpers/tags/weekday_select.rb +28 -0
  71. data/lib/action_view/helpers/tags.rb +5 -2
  72. data/lib/action_view/helpers/text_helper.rb +80 -51
  73. data/lib/action_view/helpers/translation_helper.rb +120 -69
  74. data/lib/action_view/helpers/url_helper.rb +398 -171
  75. data/lib/action_view/helpers.rb +29 -27
  76. data/lib/action_view/layouts.rb +68 -63
  77. data/lib/action_view/log_subscriber.rb +77 -10
  78. data/lib/action_view/lookup_context.rb +137 -113
  79. data/lib/action_view/model_naming.rb +4 -2
  80. data/lib/action_view/path_set.rb +28 -32
  81. data/lib/action_view/railtie.rb +74 -13
  82. data/lib/action_view/record_identifier.rb +53 -26
  83. data/lib/action_view/render_parser.rb +188 -0
  84. data/lib/action_view/renderer/abstract_renderer.rb +152 -15
  85. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  86. data/lib/action_view/renderer/object_renderer.rb +34 -0
  87. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  88. data/lib/action_view/renderer/partial_renderer.rb +51 -333
  89. data/lib/action_view/renderer/renderer.rb +68 -11
  90. data/lib/action_view/renderer/streaming_template_renderer.rb +60 -56
  91. data/lib/action_view/renderer/template_renderer.rb +87 -74
  92. data/lib/action_view/rendering.rb +73 -47
  93. data/lib/action_view/ripper_ast_parser.rb +198 -0
  94. data/lib/action_view/routing_url_for.rb +35 -24
  95. data/lib/action_view/tasks/cache_digests.rake +25 -0
  96. data/lib/action_view/template/error.rb +151 -41
  97. data/lib/action_view/template/handlers/builder.rb +12 -13
  98. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  99. data/lib/action_view/template/handlers/erb.rb +29 -89
  100. data/lib/action_view/template/handlers/html.rb +11 -0
  101. data/lib/action_view/template/handlers/raw.rb +4 -4
  102. data/lib/action_view/template/handlers.rb +14 -10
  103. data/lib/action_view/template/html.rb +12 -13
  104. data/lib/action_view/template/inline.rb +22 -0
  105. data/lib/action_view/template/raw_file.rb +25 -0
  106. data/lib/action_view/template/renderable.rb +24 -0
  107. data/lib/action_view/template/resolver.rb +139 -300
  108. data/lib/action_view/template/sources/file.rb +17 -0
  109. data/lib/action_view/template/sources.rb +13 -0
  110. data/lib/action_view/template/text.rb +10 -12
  111. data/lib/action_view/template/types.rb +28 -26
  112. data/lib/action_view/template.rb +123 -91
  113. data/lib/action_view/template_details.rb +66 -0
  114. data/lib/action_view/template_path.rb +64 -0
  115. data/lib/action_view/test_case.rb +70 -53
  116. data/lib/action_view/testing/resolvers.rb +25 -35
  117. data/lib/action_view/unbound_template.rb +57 -0
  118. data/lib/action_view/version.rb +3 -1
  119. data/lib/action_view/view_paths.rb +73 -58
  120. data/lib/action_view.rb +16 -11
  121. data/lib/assets/compiled/rails-ujs.js +746 -0
  122. metadata +52 -32
  123. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  124. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,11 +1,14 @@
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
+ # frozen_string_literal: true
2
+
3
+ require "cgi"
4
+ require "action_view/helpers/url_helper"
5
+ require "action_view/helpers/text_helper"
6
+ require "active_support/core_ext/string/output_safety"
7
+ require "active_support/core_ext/module/attribute_accessors"
5
8
 
6
9
  module ActionView
7
10
  # = Action View Form Tag Helpers
8
- module Helpers
11
+ module Helpers # :nodoc:
9
12
  # Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like
10
13
  # FormHelper does. Instead, you provide the names and values manually.
11
14
  #
@@ -18,9 +21,11 @@ module ActionView
18
21
  include TextHelper
19
22
 
20
23
  mattr_accessor :embed_authenticity_token_in_remote_forms
21
- self.embed_authenticity_token_in_remote_forms = false
24
+ self.embed_authenticity_token_in_remote_forms = nil
25
+
26
+ mattr_accessor :default_enforce_utf8, default: true
22
27
 
23
- # Starts a form tag that points the action to an url configured with <tt>url_for_options</tt> just like
28
+ # Starts a form tag that points the action to a URL configured with <tt>url_for_options</tt> just like
24
29
  # ActionController::Base#url_for. The method for the form defaults to POST.
25
30
  #
26
31
  # ==== Options
@@ -58,6 +63,9 @@ module ActionView
58
63
  # <%= form_tag('/posts', remote: true) %>
59
64
  # # => <form action="/posts" method="post" data-remote="true">
60
65
  #
66
+ # form_tag(false, method: :get)
67
+ # # => <form method="get">
68
+ #
61
69
  # form_tag('http://far.away.com/form', authenticity_token: false)
62
70
  # # form without authenticity token
63
71
  #
@@ -73,6 +81,65 @@ module ActionView
73
81
  end
74
82
  end
75
83
 
84
+ # Generate an HTML <tt>id</tt> attribute value for the given name and
85
+ # field combination
86
+ #
87
+ # Return the value generated by the <tt>FormBuilder</tt> for the given
88
+ # attribute name.
89
+ #
90
+ # <%= label_tag :post, :title %>
91
+ # <%= text_field_tag :post, :title, aria: { describedby: field_id(:post, :title, :error) } %>
92
+ # <%= tag.span("is blank", id: field_id(:post, :title, :error) %>
93
+ #
94
+ # In the example above, the <tt><input type="text"></tt> element built by
95
+ # the call to <tt>text_field_tag</tt> declares an
96
+ # <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
97
+ # element, sharing a common <tt>id</tt> root (<tt>post_title</tt>, in this
98
+ # case).
99
+ def field_id(object_name, method_name, *suffixes, index: nil, namespace: nil)
100
+ if object_name.respond_to?(:model_name)
101
+ object_name = object_name.model_name.singular
102
+ end
103
+
104
+ sanitized_object_name = object_name.to_s.gsub(/\]\[|[^-a-zA-Z0-9:.]/, "_").delete_suffix("_")
105
+
106
+ sanitized_method_name = method_name.to_s.delete_suffix("?")
107
+
108
+ [
109
+ namespace,
110
+ sanitized_object_name.presence,
111
+ (index unless sanitized_object_name.empty?),
112
+ sanitized_method_name,
113
+ *suffixes,
114
+ ].tap(&:compact!).join("_")
115
+ end
116
+
117
+ # Generate an HTML <tt>name</tt> attribute value for the given name and
118
+ # field combination
119
+ #
120
+ # Return the value generated by the <tt>FormBuilder</tt> for the given
121
+ # attribute name.
122
+ #
123
+ # <%= text_field_tag :post, :title, name: field_name(:post, :title, :subtitle) %>
124
+ # <%# => <input type="text" name="post[title][subtitle]">
125
+ #
126
+ # <%= text_field_tag :post, :tag, name: field_name(:post, :tag, multiple: true) %>
127
+ # <%# => <input type="text" name="post[tag][]">
128
+ #
129
+ def field_name(object_name, method_name, *method_names, multiple: false, index: nil)
130
+ names = method_names.map! { |name| "[#{name}]" }.join
131
+
132
+ # a little duplication to construct fewer strings
133
+ case
134
+ when object_name.empty?
135
+ "#{method_name}#{names}#{multiple ? "[]" : ""}"
136
+ when index
137
+ "#{object_name}[#{index}][#{method_name}]#{names}#{multiple ? "[]" : ""}"
138
+ else
139
+ "#{object_name}[#{method_name}]#{names}#{multiple ? "[]" : ""}"
140
+ end
141
+ end
142
+
76
143
  # Creates a dropdown selection box, or if the <tt>:multiple</tt> option is set to true, a multiple
77
144
  # choice selection box.
78
145
  #
@@ -80,7 +147,7 @@ module ActionView
80
147
  # associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
81
148
  #
82
149
  # ==== Options
83
- # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
150
+ # * <tt>:multiple</tt> - If set to true, the selection will allow multiple choices.
84
151
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
85
152
  # * <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
153
  # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something.
@@ -93,27 +160,27 @@ module ActionView
93
160
  # select_tag "people", options_from_collection_for_select(@people, "id", "name", "1")
94
161
  # # <select id="people" name="people"><option value="1" selected="selected">David</option></select>
95
162
  #
96
- # select_tag "people", "<option>David</option>".html_safe
163
+ # select_tag "people", raw("<option>David</option>")
97
164
  # # => <select id="people" name="people"><option>David</option></select>
98
165
  #
99
- # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
166
+ # select_tag "count", raw("<option>1</option><option>2</option><option>3</option><option>4</option>")
100
167
  # # => <select id="count" name="count"><option>1</option><option>2</option>
101
168
  # # <option>3</option><option>4</option></select>
102
169
  #
103
- # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
170
+ # select_tag "colors", raw("<option>Red</option><option>Green</option><option>Blue</option>"), multiple: true
104
171
  # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
105
172
  # # <option>Green</option><option>Blue</option></select>
106
173
  #
107
- # select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
174
+ # select_tag "locations", raw("<option>Home</option><option selected='selected'>Work</option><option>Out</option>")
108
175
  # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
109
176
  # # <option>Out</option></select>
110
177
  #
111
- # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input', id: 'unique_id'
178
+ # select_tag "access", raw("<option>Read</option><option>Write</option>"), multiple: true, class: 'form_input', id: 'unique_id'
112
179
  # # => <select class="form_input" id="unique_id" multiple="multiple" name="access[]"><option>Read</option>
113
180
  # # <option>Write</option></select>
114
181
  #
115
182
  # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
116
- # # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
183
+ # # => <select id="people" name="people"><option value="" label=" "></option><option value="1">David</option></select>
117
184
  #
118
185
  # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: "All"
119
186
  # # => <select id="people" name="people"><option value="">All</option><option value="1">David</option></select>
@@ -121,7 +188,7 @@ module ActionView
121
188
  # select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
122
189
  # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
123
190
  #
124
- # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, disabled: true
191
+ # select_tag "destination", raw("<option>NYC</option><option>Paris</option><option>Rome</option>"), disabled: true
125
192
  # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
126
193
  # # <option>Paris</option><option>Rome</option></select>
127
194
  #
@@ -130,25 +197,28 @@ module ActionView
130
197
  # # <option selected="selected">MasterCard</option></select>
131
198
  def select_tag(name, option_tags = nil, options = {})
132
199
  option_tags ||= ""
133
- html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
200
+ html_name = (options[:multiple] == true && !name.end_with?("[]")) ? "#{name}[]" : name
134
201
 
135
202
  if options.include?(:include_blank)
136
- include_blank = options.delete(:include_blank)
203
+ include_blank = options[:include_blank]
204
+ options = options.except(:include_blank)
205
+ options_for_blank_options_tag = { value: "" }
137
206
 
138
207
  if include_blank == true
139
- include_blank = ''
208
+ include_blank = ""
209
+ options_for_blank_options_tag[:label] = " "
140
210
  end
141
211
 
142
212
  if include_blank
143
- option_tags = content_tag(:option, include_blank, value: '').safe_concat(option_tags)
213
+ option_tags = content_tag("option", include_blank, options_for_blank_options_tag).safe_concat(option_tags)
144
214
  end
145
215
  end
146
216
 
147
217
  if prompt = options.delete(:prompt)
148
- option_tags = content_tag(:option, prompt, value: '').safe_concat(option_tags)
218
+ option_tags = content_tag("option", prompt, value: "").safe_concat(option_tags)
149
219
  end
150
220
 
151
- content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
221
+ content_tag "select", option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
152
222
  end
153
223
 
154
224
  # Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -159,6 +229,8 @@ module ActionView
159
229
  # * <tt>:size</tt> - The number of visible characters that will fit in the input.
160
230
  # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
161
231
  # * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
232
+ # If set to true, use the translation found in the current I18n locale
233
+ # (through helpers.placeholder.<modelname>.<attribute>).
162
234
  # * Any other key creates standard HTML attributes for the tag.
163
235
  #
164
236
  # ==== Examples
@@ -222,16 +294,16 @@ module ActionView
222
294
  #
223
295
  # ==== Examples
224
296
  # hidden_field_tag 'tags_list'
225
- # # => <input id="tags_list" name="tags_list" type="hidden" />
297
+ # # => <input type="hidden" name="tags_list" id="tags_list" autocomplete="off" />
226
298
  #
227
299
  # hidden_field_tag 'token', 'VUBJKB23UIVI1UU1VOBVI@'
228
- # # => <input id="token" name="token" type="hidden" value="VUBJKB23UIVI1UU1VOBVI@" />
300
+ # # => <input type="hidden" name="token" id="token" value="VUBJKB23UIVI1UU1VOBVI@" autocomplete="off" />
229
301
  #
230
302
  # hidden_field_tag 'collected_input', '', onchange: "alert('Input collected!')"
231
- # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
232
- # # type="hidden" value="" />
303
+ # # => <input type="hidden" name="collected_input" id="collected_input"
304
+ # value="" onchange="alert(&#39;Input collected!&#39;)" autocomplete="off" />
233
305
  def hidden_field_tag(name, value = nil, options = {})
234
- text_field_tag(name, value, options.merge(type: :hidden))
306
+ text_field_tag(name, value, options.merge(type: :hidden, autocomplete: "off"))
235
307
  end
236
308
 
237
309
  # Creates a file upload field. If you are using file uploads then you will also need
@@ -270,7 +342,7 @@ module ActionView
270
342
  # file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
271
343
  # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
272
344
  def file_field_tag(name, options = {})
273
- text_field_tag(name, nil, options.merge(type: :file))
345
+ text_field_tag(name, nil, convert_direct_upload_option_to_url(options.merge(type: :file)))
274
346
  end
275
347
 
276
348
  # Creates a password field, a masked text field that will hide the users input behind a mask character.
@@ -383,14 +455,14 @@ module ActionView
383
455
  # * Any other key creates standard HTML options for the tag.
384
456
  #
385
457
  # ==== Examples
386
- # radio_button_tag 'gender', 'male'
387
- # # => <input id="gender_male" name="gender" type="radio" value="male" />
458
+ # radio_button_tag 'favorite_color', 'maroon'
459
+ # # => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" />
388
460
  #
389
461
  # radio_button_tag 'receive_updates', 'no', true
390
462
  # # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
391
463
  #
392
464
  # radio_button_tag 'time_slot', "3:00 p.m.", false, disabled: true
393
- # # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
465
+ # # => <input disabled="disabled" id="time_slot_3:00_p.m." name="time_slot" type="radio" value="3:00 p.m." />
394
466
  #
395
467
  # radio_button_tag 'color', "green", true, class: "color_input"
396
468
  # # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
@@ -407,49 +479,55 @@ module ActionView
407
479
  # * <tt>:disabled</tt> - If true, the user will not be able to use this input.
408
480
  # * Any other key creates standard HTML options for the tag.
409
481
  #
410
- # ==== Data attributes
411
- #
412
- # * <tt>confirm: 'question?'</tt> - If present the unobtrusive JavaScript
413
- # drivers will provide a prompt with the question specified. If the user accepts,
414
- # the form is processed normally, otherwise no action is taken.
415
- # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
416
- # disabled version of the submit button when the form is submitted. This feature is
417
- # provided by the unobtrusive JavaScript driver.
418
- #
419
482
  # ==== Examples
420
483
  # submit_tag
421
- # # => <input name="commit" type="submit" value="Save changes" />
484
+ # # => <input name="commit" data-disable-with="Save changes" type="submit" value="Save changes" />
422
485
  #
423
486
  # submit_tag "Edit this article"
424
- # # => <input name="commit" type="submit" value="Edit this article" />
487
+ # # => <input name="commit" data-disable-with="Edit this article" type="submit" value="Edit this article" />
425
488
  #
426
489
  # submit_tag "Save edits", disabled: true
427
- # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
428
- #
429
- # submit_tag "Complete sale", data: { disable_with: "Please wait..." }
430
- # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
490
+ # # => <input disabled="disabled" name="commit" data-disable-with="Save edits" type="submit" value="Save edits" />
431
491
  #
432
492
  # submit_tag nil, class: "form_submit"
433
493
  # # => <input class="form_submit" name="commit" type="submit" />
434
494
  #
435
495
  # submit_tag "Edit", class: "edit_button"
436
- # # => <input class="edit_button" name="commit" type="submit" value="Edit" />
496
+ # # => <input class="edit_button" data-disable-with="Edit" name="commit" type="submit" value="Edit" />
497
+ #
498
+ # ==== Deprecated: Rails UJS attributes
499
+ #
500
+ # Prior to Rails 7, Rails shipped with the JavaScript library called @rails/ujs on by default. Following Rails 7,
501
+ # this library is no longer on by default. This library integrated with the following options:
502
+ #
503
+ # * <tt>confirm: 'question?'</tt> - If present the unobtrusive JavaScript
504
+ # drivers will provide a prompt with the question specified. If the user accepts,
505
+ # the form is processed normally, otherwise no action is taken.
506
+ # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
507
+ # disabled version of the submit button when the form is submitted. This feature is
508
+ # provided by the unobtrusive JavaScript driver. To disable this feature for a single submit tag
509
+ # pass <tt>:data => { disable_with: false }</tt> Defaults to value attribute.
510
+ #
511
+ # submit_tag "Complete sale", data: { disable_with: "Submitting..." }
512
+ # # => <input name="commit" data-disable-with="Submitting..." type="submit" value="Complete sale" />
437
513
  #
438
514
  # submit_tag "Save", data: { confirm: "Are you sure?" }
439
- # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
515
+ # # => <input name='commit' type='submit' value='Save' data-disable-with="Save" data-confirm="Are you sure?" />
440
516
  #
441
517
  def submit_tag(value = "Save changes", options = {})
442
- options = options.stringify_keys
443
-
444
- tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
518
+ options = options.deep_stringify_keys
519
+ tag_options = { "type" => "submit", "name" => "commit", "value" => value }.update(options)
520
+ set_default_disable_with value, tag_options
521
+ tag :input, tag_options
445
522
  end
446
523
 
447
524
  # 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
525
+ # <tt>reset</tt> button or a generic button which can be used in
449
526
  # JavaScript, for example. You can use the button tag as a regular
450
527
  # 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.
528
+ # the button tag does allow for richer labels such as images and emphasis,
529
+ # so this helper will also accept a block. By default, it will create
530
+ # a button tag with type <tt>submit</tt>, if type is not given.
453
531
  #
454
532
  # ==== Options
455
533
  # * <tt>:data</tt> - This option can be used to add custom data attributes.
@@ -457,21 +535,19 @@ module ActionView
457
535
  # use this input.
458
536
  # * Any other key creates standard HTML options for the tag.
459
537
  #
460
- # ==== Data attributes
461
- #
462
- # * <tt>confirm: 'question?'</tt> - If present, the
463
- # unobtrusive JavaScript drivers will provide a prompt with
464
- # the question specified. If the user accepts, the form is
465
- # processed normally, otherwise no action is taken.
466
- # * <tt>:disable_with</tt> - Value of this parameter will be
467
- # used as the value for a disabled version of the submit
468
- # button when the form is submitted. This feature is provided
469
- # by the unobtrusive JavaScript driver.
470
- #
471
538
  # ==== Examples
472
539
  # button_tag
473
540
  # # => <button name="button" type="submit">Button</button>
474
541
  #
542
+ # button_tag 'Reset', type: 'reset'
543
+ # # => <button name="button" type="reset">Reset</button>
544
+ #
545
+ # button_tag 'Button', type: 'button'
546
+ # # => <button name="button" type="button">Button</button>
547
+ #
548
+ # button_tag 'Reset', type: 'reset', disabled: true
549
+ # # => <button name="button" type="reset" disabled="disabled">Reset</button>
550
+ #
475
551
  # button_tag(type: 'button') do
476
552
  # content_tag(:strong, 'Ask me!')
477
553
  # end
@@ -479,6 +555,23 @@ module ActionView
479
555
  # # <strong>Ask me!</strong>
480
556
  # # </button>
481
557
  #
558
+ # ==== Deprecated: Rails UJS attributes
559
+ #
560
+ # Prior to Rails 7, Rails shipped with a JavaScript library called @rails/ujs on by default. Following Rails 7,
561
+ # this library is no longer on by default. This library integrated with the following options:
562
+ #
563
+ # * <tt>confirm: 'question?'</tt> - If present, the
564
+ # unobtrusive JavaScript drivers will provide a prompt with
565
+ # the question specified. If the user accepts, the form is
566
+ # processed normally, otherwise no action is taken.
567
+ # * <tt>:disable_with</tt> - Value of this parameter will be
568
+ # used as the value for a disabled version of the submit
569
+ # button when the form is submitted. This feature is provided
570
+ # by the unobtrusive JavaScript driver.
571
+ #
572
+ # button_tag "Save", data: { confirm: "Are you sure?" }
573
+ # # => <button name="button" type="submit" data-confirm="Are you sure?">Save</button>
574
+ #
482
575
  # button_tag "Checkout", data: { disable_with: "Please wait..." }
483
576
  # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
484
577
  #
@@ -489,12 +582,12 @@ module ActionView
489
582
  options ||= {}
490
583
  end
491
584
 
492
- options = { 'name' => 'button', 'type' => 'submit' }.merge!(options.stringify_keys)
585
+ options = { "name" => "button", "type" => "submit" }.merge!(options.stringify_keys)
493
586
 
494
587
  if block_given?
495
588
  content_tag :button, options, &block
496
589
  else
497
- content_tag :button, content_or_options || 'Button', options
590
+ content_tag :button, content_or_options || "Button", options
498
591
  end
499
592
  end
500
593
 
@@ -515,22 +608,23 @@ module ActionView
515
608
  #
516
609
  # ==== Examples
517
610
  # image_submit_tag("login.png")
518
- # # => <input alt="Login" src="/assets/login.png" type="image" />
611
+ # # => <input src="/assets/login.png" type="image" />
519
612
  #
520
613
  # image_submit_tag("purchase.png", disabled: true)
521
- # # => <input alt="Purchase" disabled="disabled" src="/assets/purchase.png" type="image" />
614
+ # # => <input disabled="disabled" src="/assets/purchase.png" type="image" />
522
615
  #
523
616
  # image_submit_tag("search.png", class: 'search_button', alt: 'Find')
524
- # # => <input alt="Find" class="search_button" src="/assets/search.png" type="image" />
617
+ # # => <input class="search_button" src="/assets/search.png" type="image" />
525
618
  #
526
619
  # image_submit_tag("agree.png", disabled: true, class: "agree_disagree_button")
527
- # # => <input alt="Agree" class="agree_disagree_button" disabled="disabled" src="/assets/agree.png" type="image" />
620
+ # # => <input class="agree_disagree_button" disabled="disabled" src="/assets/agree.png" type="image" />
528
621
  #
529
622
  # image_submit_tag("save.png", data: { confirm: "Are you sure?" })
530
- # # => <input alt="Save" src="/assets/save.png" data-confirm="Are you sure?" type="image" />
623
+ # # => <input src="/assets/save.png" data-confirm="Are you sure?" type="image" />
531
624
  def image_submit_tag(source, options = {})
532
625
  options = options.stringify_keys
533
- tag :input, { "alt" => image_alt(source), "type" => "image", "src" => path_to_image(source) }.update(options)
626
+ src = path_to_image(source, skip_pipeline: options.delete("skip_pipeline"))
627
+ tag :input, { "type" => "image", "src" => src }.update(options)
534
628
  end
535
629
 
536
630
  # Creates a field set for grouping HTML form elements.
@@ -555,7 +649,7 @@ module ActionView
555
649
  # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
556
650
  def field_set_tag(legend = nil, options = nil, &block)
557
651
  output = tag(:fieldset, options, true)
558
- output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
652
+ output.safe_concat(content_tag("legend", legend)) unless legend.blank?
559
653
  output.concat(capture(&block)) if block_given?
560
654
  output.safe_concat("</fieldset>")
561
655
  end
@@ -651,12 +745,13 @@ module ActionView
651
745
  # * <tt>:min</tt> - The minimum acceptable value.
652
746
  # * <tt>:max</tt> - The maximum acceptable value.
653
747
  # * <tt>:step</tt> - The acceptable value granularity.
748
+ # * <tt>:include_seconds</tt> - Include seconds and ms in the output timestamp format (true by default).
654
749
  # * Otherwise accepts the same options as text_field_tag.
655
750
  def time_field_tag(name, value = nil, options = {})
656
751
  text_field_tag(name, value, options.merge(type: :time))
657
752
  end
658
753
 
659
- # Creates a text field of type "datetime".
754
+ # Creates a text field of type "datetime-local".
660
755
  #
661
756
  # === Options
662
757
  # * <tt>:min</tt> - The minimum acceptable value.
@@ -664,19 +759,10 @@ module ActionView
664
759
  # * <tt>:step</tt> - The acceptable value granularity.
665
760
  # * Otherwise accepts the same options as text_field_tag.
666
761
  def datetime_field_tag(name, value = nil, options = {})
667
- text_field_tag(name, value, options.merge(type: :datetime))
762
+ text_field_tag(name, value, options.merge(type: "datetime-local"))
668
763
  end
669
764
 
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
765
+ alias datetime_local_field_tag datetime_field_tag
680
766
 
681
767
  # Creates a text field of type "month".
682
768
  #
@@ -776,10 +862,10 @@ module ActionView
776
862
  # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
777
863
  #
778
864
  # number_field_tag 'quantity', nil, min: 1, max: 10
779
- # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
865
+ # # => <input id="quantity" name="quantity" min="1" max="10" type="number" />
780
866
  #
781
867
  # 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" />
868
+ # # => <input id="quantity" name="quantity" min="1" max="10" step="2" type="number" />
783
869
  #
784
870
  # number_field_tag 'quantity', '1', class: 'special_input', disabled: true
785
871
  # # => <input disabled="disabled" class="special_input" id="quantity" name="quantity" type="number" value="1" />
@@ -806,7 +892,7 @@ module ActionView
806
892
  # Use raw HTML to ensure the value is written as an HTML entity; it
807
893
  # needs to be the right character regardless of which encoding the
808
894
  # browser infers.
809
- '<input name="utf8" type="hidden" value="&#x2713;" />'.html_safe
895
+ '<input name="utf8" type="hidden" value="&#x2713;" autocomplete="off" />'.html_safe
810
896
  end
811
897
 
812
898
  private
@@ -815,13 +901,17 @@ module ActionView
815
901
  html_options["enctype"] = "multipart/form-data" if html_options.delete("multipart")
816
902
  # The following URL is unescaped, this is just a hash of options, and it is the
817
903
  # responsibility of the caller to escape all the values.
818
- html_options["action"] = url_for(url_for_options)
904
+ if url_for_options == false || html_options["action"] == false
905
+ html_options.delete("action")
906
+ else
907
+ html_options["action"] = url_for(url_for_options)
908
+ end
819
909
  html_options["accept-charset"] = "UTF-8"
820
910
 
821
911
  html_options["data-remote"] = true if html_options.delete("remote")
822
912
 
823
913
  if html_options["data-remote"] &&
824
- !embed_authenticity_token_in_remote_forms &&
914
+ embed_authenticity_token_in_remote_forms == false &&
825
915
  html_options["authenticity_token"].blank?
826
916
  # The authenticity token is taken from the meta tag in this case
827
917
  html_options["authenticity_token"] = false
@@ -835,21 +925,28 @@ module ActionView
835
925
 
836
926
  def extra_tags_for_form(html_options)
837
927
  authenticity_token = html_options.delete("authenticity_token")
838
- method = html_options.delete("method").to_s
928
+ method = html_options.delete("method").to_s.downcase
839
929
 
840
- method_tag = case method
841
- when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
930
+ method_tag = \
931
+ case method
932
+ when "get"
842
933
  html_options["method"] = "get"
843
- ''
844
- when /^post$/i, "", nil
934
+ ""
935
+ when "post", ""
845
936
  html_options["method"] = "post"
846
- token_tag(authenticity_token)
937
+ token_tag(authenticity_token, form_options: {
938
+ action: html_options["action"],
939
+ method: "post"
940
+ })
847
941
  else
848
942
  html_options["method"] = "post"
849
- method_tag(method) + token_tag(authenticity_token)
850
- end
943
+ method_tag(method) + token_tag(authenticity_token, form_options: {
944
+ action: html_options["action"],
945
+ method: method
946
+ })
947
+ end
851
948
 
852
- if html_options.delete("enforce_utf8") { true }
949
+ if html_options.delete("enforce_utf8") { default_enforce_utf8 }
853
950
  utf8_enforcer_tag + method_tag
854
951
  else
855
952
  method_tag
@@ -863,13 +960,35 @@ module ActionView
863
960
 
864
961
  def form_tag_with_body(html_options, content)
865
962
  output = form_tag_html(html_options)
866
- output << content
963
+ output << content.to_s if content
867
964
  output.safe_concat("</form>")
868
965
  end
869
966
 
870
967
  # see http://www.w3.org/TR/html4/types.html#type-name
871
968
  def sanitize_to_id(name)
872
- name.to_s.delete(']').tr('^-a-zA-Z0-9:.', "_")
969
+ name.to_s.delete("]").tr("^-a-zA-Z0-9:.", "_")
970
+ end
971
+
972
+ def set_default_disable_with(value, tag_options)
973
+ data = tag_options.fetch("data", {})
974
+
975
+ if tag_options["data-disable-with"] == false || data["disable_with"] == false
976
+ data.delete("disable_with")
977
+ elsif ActionView::Base.automatically_disable_submit_tag
978
+ disable_with_text = tag_options["data-disable-with"]
979
+ disable_with_text ||= data["disable_with"]
980
+ disable_with_text ||= value.to_s.clone
981
+ tag_options.deep_merge!("data" => { "disable_with" => disable_with_text })
982
+ end
983
+
984
+ tag_options.delete("data-disable-with")
985
+ end
986
+
987
+ def convert_direct_upload_option_to_url(options)
988
+ if options.delete(:direct_upload) && respond_to?(:rails_direct_uploads_url)
989
+ options["data-direct-upload-url"] = rails_direct_uploads_url
990
+ end
991
+ options
873
992
  end
874
993
  end
875
994
  end