actionview 4.1.13 → 6.1.3.1

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 +5 -5
  2. data/CHANGELOG.md +181 -359
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +12 -6
  5. data/lib/action_view/base.rb +115 -43
  6. data/lib/action_view/buffers.rb +22 -4
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -84
  11. data/lib/action_view/flows.rb +12 -13
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +311 -105
  15. data/lib/action_view/helpers/asset_url_helper.rb +197 -80
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +109 -45
  18. data/lib/action_view/helpers/capture_helper.rb +20 -22
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +245 -140
  23. data/lib/action_view/helpers/debug_helper.rb +14 -17
  24. data/lib/action_view/helpers/form_helper.rb +875 -148
  25. data/lib/action_view/helpers/form_options_helper.rb +128 -82
  26. data/lib/action_view/helpers/form_tag_helper.rb +253 -91
  27. data/lib/action_view/helpers/javascript_helper.rb +37 -15
  28. data/lib/action_view/helpers/number_helper.rb +100 -77
  29. data/lib/action_view/helpers/output_safety_helper.rb +42 -10
  30. data/lib/action_view/helpers/rendering_helper.rb +26 -15
  31. data/lib/action_view/helpers/sanitize_helper.rb +79 -164
  32. data/lib/action_view/helpers/tag_helper.rb +277 -64
  33. data/lib/action_view/helpers/tags/base.rb +143 -92
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -30
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +14 -5
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +41 -22
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +3 -0
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +7 -1
  61. data/lib/action_view/helpers/tags/text_field.rb +11 -7
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +39 -0
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +4 -1
  69. data/lib/action_view/helpers/text_helper.rb +80 -45
  70. data/lib/action_view/helpers/translation_helper.rb +148 -67
  71. data/lib/action_view/helpers/url_helper.rb +289 -147
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +80 -13
  75. data/lib/action_view/lookup_context.rb +137 -92
  76. data/lib/action_view/model_naming.rb +4 -2
  77. data/lib/action_view/path_set.rb +30 -16
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +152 -13
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +61 -261
  85. data/lib/action_view/renderer/renderer.rb +67 -6
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +83 -75
  88. data/lib/action_view/rendering.rb +73 -46
  89. data/lib/action_view/routing_url_for.rb +54 -17
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +22 -9
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +267 -181
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +109 -99
  108. data/lib/action_view/test_case.rb +73 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +74 -44
  113. data/lib/action_view.rb +14 -9
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +71 -26
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
  118. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  119. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  120. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  121. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  122. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  123. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  124. data/lib/action_view/vendor/html-scanner.rb +0 -20
@@ -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/tag_helper"
5
+ require "active_support/core_ext/string/output_safety"
6
+ require "active_support/core_ext/module/attribute_accessors"
7
+ require "active_support/core_ext/symbol/starts_ends_with"
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
@@ -67,7 +72,7 @@ module ActionView
67
72
  def form_tag(url_for_options = {}, options = {}, &block)
68
73
  html_options = html_options_for_form(url_for_options, options)
69
74
  if block_given?
70
- form_tag_in_block(html_options, &block)
75
+ form_tag_with_body(html_options, capture(&block))
71
76
  else
72
77
  form_tag_html(html_options)
73
78
  end
@@ -80,42 +85,48 @@ module ActionView
80
85
  # associated records. <tt>option_tags</tt> is a string containing the option tags for the select box.
81
86
  #
82
87
  # ==== Options
83
- # * <tt>:multiple</tt> - If set to true the selection will allow multiple choices.
88
+ # * <tt>:multiple</tt> - If set to true, the selection will allow multiple choices.
84
89
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
85
- # * <tt>:include_blank</tt> - If set to true, an empty option will be created.
86
- # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something
90
+ # * <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.
91
+ # * <tt>:prompt</tt> - Create a prompt option with blank value and the text asking user to select something.
87
92
  # * Any other key creates standard HTML attributes for the tag.
88
93
  #
89
94
  # ==== Examples
90
95
  # select_tag "people", options_from_collection_for_select(@people, "id", "name")
91
96
  # # <select id="people" name="people"><option value="1">David</option></select>
92
97
  #
93
- # select_tag "people", "<option>David</option>".html_safe
98
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name", "1")
99
+ # # <select id="people" name="people"><option value="1" selected="selected">David</option></select>
100
+ #
101
+ # select_tag "people", raw("<option>David</option>")
94
102
  # # => <select id="people" name="people"><option>David</option></select>
95
103
  #
96
- # select_tag "count", "<option>1</option><option>2</option><option>3</option><option>4</option>".html_safe
104
+ # select_tag "count", raw("<option>1</option><option>2</option><option>3</option><option>4</option>")
97
105
  # # => <select id="count" name="count"><option>1</option><option>2</option>
98
106
  # # <option>3</option><option>4</option></select>
99
107
  #
100
- # select_tag "colors", "<option>Red</option><option>Green</option><option>Blue</option>".html_safe, multiple: true
108
+ # select_tag "colors", raw("<option>Red</option><option>Green</option><option>Blue</option>"), multiple: true
101
109
  # # => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
102
110
  # # <option>Green</option><option>Blue</option></select>
103
111
  #
104
- # select_tag "locations", "<option>Home</option><option selected='selected'>Work</option><option>Out</option>".html_safe
112
+ # select_tag "locations", raw("<option>Home</option><option selected='selected'>Work</option><option>Out</option>")
105
113
  # # => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
106
114
  # # <option>Out</option></select>
107
115
  #
108
- # select_tag "access", "<option>Read</option><option>Write</option>".html_safe, multiple: true, class: 'form_input'
109
- # # => <select class="form_input" id="access" multiple="multiple" name="access[]"><option>Read</option>
116
+ # select_tag "access", raw("<option>Read</option><option>Write</option>"), multiple: true, class: 'form_input', id: 'unique_id'
117
+ # # => <select class="form_input" id="unique_id" multiple="multiple" name="access[]"><option>Read</option>
110
118
  # # <option>Write</option></select>
111
119
  #
112
120
  # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
113
- # # => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>
121
+ # # => <select id="people" name="people"><option value="" label=" "></option><option value="1">David</option></select>
122
+ #
123
+ # select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: "All"
124
+ # # => <select id="people" name="people"><option value="">All</option><option value="1">David</option></select>
114
125
  #
115
126
  # select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
116
127
  # # => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>
117
128
  #
118
- # select_tag "destination", "<option>NYC</option><option>Paris</option><option>Rome</option>".html_safe, disabled: true
129
+ # select_tag "destination", raw("<option>NYC</option><option>Paris</option><option>Rome</option>"), disabled: true
119
130
  # # => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
120
131
  # # <option>Paris</option><option>Rome</option></select>
121
132
  #
@@ -124,25 +135,28 @@ module ActionView
124
135
  # # <option selected="selected">MasterCard</option></select>
125
136
  def select_tag(name, option_tags = nil, options = {})
126
137
  option_tags ||= ""
127
- html_name = (options[:multiple] == true && !name.to_s.ends_with?("[]")) ? "#{name}[]" : name
138
+ html_name = (options[:multiple] == true && !name.end_with?("[]")) ? "#{name}[]" : name
128
139
 
129
140
  if options.include?(:include_blank)
130
- include_blank = options.delete(:include_blank)
141
+ include_blank = options[:include_blank]
142
+ options = options.except(:include_blank)
143
+ options_for_blank_options_tag = { value: "" }
131
144
 
132
145
  if include_blank == true
133
- include_blank = ''
146
+ include_blank = ""
147
+ options_for_blank_options_tag[:label] = " "
134
148
  end
135
149
 
136
150
  if include_blank
137
- option_tags = content_tag(:option, include_blank, value: '').safe_concat(option_tags)
151
+ option_tags = content_tag("option", include_blank, options_for_blank_options_tag).safe_concat(option_tags)
138
152
  end
139
153
  end
140
154
 
141
155
  if prompt = options.delete(:prompt)
142
- option_tags = content_tag(:option, prompt, value: '').safe_concat(option_tags)
156
+ option_tags = content_tag("option", prompt, value: "").safe_concat(option_tags)
143
157
  end
144
158
 
145
- content_tag :select, option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
159
+ content_tag "select", option_tags, { "name" => html_name, "id" => sanitize_to_id(name) }.update(options.stringify_keys)
146
160
  end
147
161
 
148
162
  # Creates a standard text field; use these text fields to input smaller chunks of text like a username
@@ -153,6 +167,8 @@ module ActionView
153
167
  # * <tt>:size</tt> - The number of visible characters that will fit in the input.
154
168
  # * <tt>:maxlength</tt> - The maximum number of characters that the browser will allow the user to enter.
155
169
  # * <tt>:placeholder</tt> - The text contained in the field by default which is removed when the field receives focus.
170
+ # If set to true, use a translation is found in the current I18n locale
171
+ # (through helpers.placeholders.<modelname>.<attribute>).
156
172
  # * Any other key creates standard HTML attributes for the tag.
157
173
  #
158
174
  # ==== Examples
@@ -225,7 +241,7 @@ module ActionView
225
241
  # # => <input id="collected_input" name="collected_input" onchange="alert('Input collected!')"
226
242
  # # type="hidden" value="" />
227
243
  def hidden_field_tag(name, value = nil, options = {})
228
- text_field_tag(name, value, options.stringify_keys.update("type" => "hidden"))
244
+ text_field_tag(name, value, options.merge(type: :hidden))
229
245
  end
230
246
 
231
247
  # Creates a file upload field. If you are using file uploads then you will also need
@@ -264,7 +280,7 @@ module ActionView
264
280
  # file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
265
281
  # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
266
282
  def file_field_tag(name, options = {})
267
- text_field_tag(name, nil, options.update("type" => "file"))
283
+ text_field_tag(name, nil, convert_direct_upload_option_to_url(options.merge(type: :file)))
268
284
  end
269
285
 
270
286
  # Creates a password field, a masked text field that will hide the users input behind a mask character.
@@ -297,7 +313,7 @@ module ActionView
297
313
  # password_field_tag 'pin', '1234', maxlength: 4, size: 6, class: "pin_input"
298
314
  # # => <input class="pin_input" id="pin" maxlength="4" name="pin" size="6" type="password" value="1234" />
299
315
  def password_field_tag(name = "password", value = nil, options = {})
300
- text_field_tag(name, value, options.update("type" => "password"))
316
+ text_field_tag(name, value, options.merge(type: :password))
301
317
  end
302
318
 
303
319
  # Creates a text input area; use a textarea for longer text inputs such as blog posts or descriptions.
@@ -377,14 +393,14 @@ module ActionView
377
393
  # * Any other key creates standard HTML options for the tag.
378
394
  #
379
395
  # ==== Examples
380
- # radio_button_tag 'gender', 'male'
381
- # # => <input id="gender_male" name="gender" type="radio" value="male" />
396
+ # radio_button_tag 'favorite_color', 'maroon'
397
+ # # => <input id="favorite_color_maroon" name="favorite_color" type="radio" value="maroon" />
382
398
  #
383
399
  # radio_button_tag 'receive_updates', 'no', true
384
400
  # # => <input checked="checked" id="receive_updates_no" name="receive_updates" type="radio" value="no" />
385
401
  #
386
402
  # radio_button_tag 'time_slot', "3:00 p.m.", false, disabled: true
387
- # # => <input disabled="disabled" id="time_slot_300_pm" name="time_slot" type="radio" value="3:00 p.m." />
403
+ # # => <input disabled="disabled" id="time_slot_3:00_p.m." name="time_slot" type="radio" value="3:00 p.m." />
388
404
  #
389
405
  # radio_button_tag 'color', "green", true, class: "color_input"
390
406
  # # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
@@ -408,42 +424,45 @@ module ActionView
408
424
  # the form is processed normally, otherwise no action is taken.
409
425
  # * <tt>:disable_with</tt> - Value of this parameter will be used as the value for a
410
426
  # disabled version of the submit button when the form is submitted. This feature is
411
- # provided by the unobtrusive JavaScript driver.
427
+ # provided by the unobtrusive JavaScript driver. To disable this feature for a single submit tag
428
+ # pass <tt>:data => { disable_with: false }</tt> Defaults to value attribute.
412
429
  #
413
430
  # ==== Examples
414
431
  # submit_tag
415
- # # => <input name="commit" type="submit" value="Save changes" />
432
+ # # => <input name="commit" data-disable-with="Save changes" type="submit" value="Save changes" />
416
433
  #
417
434
  # submit_tag "Edit this article"
418
- # # => <input name="commit" type="submit" value="Edit this article" />
435
+ # # => <input name="commit" data-disable-with="Edit this article" type="submit" value="Edit this article" />
419
436
  #
420
437
  # submit_tag "Save edits", disabled: true
421
- # # => <input disabled="disabled" name="commit" type="submit" value="Save edits" />
438
+ # # => <input disabled="disabled" name="commit" data-disable-with="Save edits" type="submit" value="Save edits" />
422
439
  #
423
- # submit_tag "Complete sale", data: { disable_with: "Please wait..." }
424
- # # => <input name="commit" data-disable-with="Please wait..." type="submit" value="Complete sale" />
440
+ # submit_tag "Complete sale", data: { disable_with: "Submitting..." }
441
+ # # => <input name="commit" data-disable-with="Submitting..." type="submit" value="Complete sale" />
425
442
  #
426
443
  # submit_tag nil, class: "form_submit"
427
444
  # # => <input class="form_submit" name="commit" type="submit" />
428
445
  #
429
446
  # submit_tag "Edit", class: "edit_button"
430
- # # => <input class="edit_button" name="commit" type="submit" value="Edit" />
447
+ # # => <input class="edit_button" data-disable-with="Edit" name="commit" type="submit" value="Edit" />
431
448
  #
432
449
  # submit_tag "Save", data: { confirm: "Are you sure?" }
433
- # # => <input name='commit' type='submit' value='Save' data-confirm="Are you sure?" />
450
+ # # => <input name='commit' type='submit' value='Save' data-disable-with="Save" data-confirm="Are you sure?" />
434
451
  #
435
452
  def submit_tag(value = "Save changes", options = {})
436
- options = options.stringify_keys
437
-
438
- tag :input, { "type" => "submit", "name" => "commit", "value" => value }.update(options)
453
+ options = options.deep_stringify_keys
454
+ tag_options = { "type" => "submit", "name" => "commit", "value" => value }.update(options)
455
+ set_default_disable_with value, tag_options
456
+ tag :input, tag_options
439
457
  end
440
458
 
441
459
  # Creates a button element that defines a <tt>submit</tt> button,
442
- # <tt>reset</tt>button or a generic button which can be used in
460
+ # <tt>reset</tt> button or a generic button which can be used in
443
461
  # JavaScript, for example. You can use the button tag as a regular
444
462
  # submit tag but it isn't supported in legacy browsers. However,
445
- # the button tag allows richer labels such as images and emphasis,
446
- # so this helper will also accept a block.
463
+ # the button tag does allow for richer labels such as images and emphasis,
464
+ # so this helper will also accept a block. By default, it will create
465
+ # a button tag with type <tt>submit</tt>, if type is not given.
447
466
  #
448
467
  # ==== Options
449
468
  # * <tt>:data</tt> - This option can be used to add custom data attributes.
@@ -466,6 +485,15 @@ module ActionView
466
485
  # button_tag
467
486
  # # => <button name="button" type="submit">Button</button>
468
487
  #
488
+ # button_tag 'Reset', type: 'reset'
489
+ # # => <button name="button" type="reset">Reset</button>
490
+ #
491
+ # button_tag 'Button', type: 'button'
492
+ # # => <button name="button" type="button">Button</button>
493
+ #
494
+ # button_tag 'Reset', type: 'reset', disabled: true
495
+ # # => <button name="button" type="reset" disabled="disabled">Reset</button>
496
+ #
469
497
  # button_tag(type: 'button') do
470
498
  # content_tag(:strong, 'Ask me!')
471
499
  # end
@@ -473,6 +501,9 @@ module ActionView
473
501
  # # <strong>Ask me!</strong>
474
502
  # # </button>
475
503
  #
504
+ # button_tag "Save", data: { confirm: "Are you sure?" }
505
+ # # => <button name="button" type="submit" data-confirm="Are you sure?">Save</button>
506
+ #
476
507
  # button_tag "Checkout", data: { disable_with: "Please wait..." }
477
508
  # # => <button data-disable-with="Please wait..." name="button" type="submit">Checkout</button>
478
509
  #
@@ -483,12 +514,12 @@ module ActionView
483
514
  options ||= {}
484
515
  end
485
516
 
486
- options = { 'name' => 'button', 'type' => 'submit' }.merge!(options.stringify_keys)
517
+ options = { "name" => "button", "type" => "submit" }.merge!(options.stringify_keys)
487
518
 
488
519
  if block_given?
489
520
  content_tag :button, options, &block
490
521
  else
491
- content_tag :button, content_or_options || 'Button', options
522
+ content_tag :button, content_or_options || "Button", options
492
523
  end
493
524
  end
494
525
 
@@ -509,22 +540,23 @@ module ActionView
509
540
  #
510
541
  # ==== Examples
511
542
  # image_submit_tag("login.png")
512
- # # => <input alt="Login" src="/images/login.png" type="image" />
543
+ # # => <input src="/assets/login.png" type="image" />
513
544
  #
514
545
  # image_submit_tag("purchase.png", disabled: true)
515
- # # => <input alt="Purchase" disabled="disabled" src="/images/purchase.png" type="image" />
546
+ # # => <input disabled="disabled" src="/assets/purchase.png" type="image" />
516
547
  #
517
548
  # image_submit_tag("search.png", class: 'search_button', alt: 'Find')
518
- # # => <input alt="Find" class="search_button" src="/images/search.png" type="image" />
549
+ # # => <input class="search_button" src="/assets/search.png" type="image" />
519
550
  #
520
551
  # image_submit_tag("agree.png", disabled: true, class: "agree_disagree_button")
521
- # # => <input alt="Agree" class="agree_disagree_button" disabled="disabled" src="/images/agree.png" type="image" />
552
+ # # => <input class="agree_disagree_button" disabled="disabled" src="/assets/agree.png" type="image" />
522
553
  #
523
554
  # image_submit_tag("save.png", data: { confirm: "Are you sure?" })
524
- # # => <input alt="Save" src="/images/save.png" data-confirm="Are you sure?" type="image" />
555
+ # # => <input src="/assets/save.png" data-confirm="Are you sure?" type="image" />
525
556
  def image_submit_tag(source, options = {})
526
557
  options = options.stringify_keys
527
- tag :input, { "alt" => image_alt(source), "type" => "image", "src" => path_to_image(source) }.update(options)
558
+ src = path_to_image(source, skip_pipeline: options.delete("skip_pipeline"))
559
+ tag :input, { "type" => "image", "src" => src }.update(options)
528
560
  end
529
561
 
530
562
  # Creates a field set for grouping HTML form elements.
@@ -549,7 +581,7 @@ module ActionView
549
581
  # # => <fieldset class="format"><p><input id="name" name="name" type="text" /></p></fieldset>
550
582
  def field_set_tag(legend = nil, options = nil, &block)
551
583
  output = tag(:fieldset, options, true)
552
- output.safe_concat(content_tag(:legend, legend)) unless legend.blank?
584
+ output.safe_concat(content_tag("legend", legend)) unless legend.blank?
553
585
  output.concat(capture(&block)) if block_given?
554
586
  output.safe_concat("</fieldset>")
555
587
  end
@@ -558,24 +590,63 @@ module ActionView
558
590
  #
559
591
  # ==== Options
560
592
  # * Accepts the same options as text_field_tag.
593
+ #
594
+ # ==== Examples
595
+ # color_field_tag 'name'
596
+ # # => <input id="name" name="name" type="color" />
597
+ #
598
+ # color_field_tag 'color', '#DEF726'
599
+ # # => <input id="color" name="color" type="color" value="#DEF726" />
600
+ #
601
+ # color_field_tag 'color', nil, class: 'special_input'
602
+ # # => <input class="special_input" id="color" name="color" type="color" />
603
+ #
604
+ # color_field_tag 'color', '#DEF726', class: 'special_input', disabled: true
605
+ # # => <input disabled="disabled" class="special_input" id="color" name="color" type="color" value="#DEF726" />
561
606
  def color_field_tag(name, value = nil, options = {})
562
- text_field_tag(name, value, options.stringify_keys.update("type" => "color"))
607
+ text_field_tag(name, value, options.merge(type: :color))
563
608
  end
564
609
 
565
610
  # Creates a text field of type "search".
566
611
  #
567
612
  # ==== Options
568
613
  # * Accepts the same options as text_field_tag.
614
+ #
615
+ # ==== Examples
616
+ # search_field_tag 'name'
617
+ # # => <input id="name" name="name" type="search" />
618
+ #
619
+ # search_field_tag 'search', 'Enter your search query here'
620
+ # # => <input id="search" name="search" type="search" value="Enter your search query here" />
621
+ #
622
+ # search_field_tag 'search', nil, class: 'special_input'
623
+ # # => <input class="special_input" id="search" name="search" type="search" />
624
+ #
625
+ # search_field_tag 'search', 'Enter your search query here', class: 'special_input', disabled: true
626
+ # # => <input disabled="disabled" class="special_input" id="search" name="search" type="search" value="Enter your search query here" />
569
627
  def search_field_tag(name, value = nil, options = {})
570
- text_field_tag(name, value, options.stringify_keys.update("type" => "search"))
628
+ text_field_tag(name, value, options.merge(type: :search))
571
629
  end
572
630
 
573
631
  # Creates a text field of type "tel".
574
632
  #
575
633
  # ==== Options
576
634
  # * Accepts the same options as text_field_tag.
635
+ #
636
+ # ==== Examples
637
+ # telephone_field_tag 'name'
638
+ # # => <input id="name" name="name" type="tel" />
639
+ #
640
+ # telephone_field_tag 'tel', '0123456789'
641
+ # # => <input id="tel" name="tel" type="tel" value="0123456789" />
642
+ #
643
+ # telephone_field_tag 'tel', nil, class: 'special_input'
644
+ # # => <input class="special_input" id="tel" name="tel" type="tel" />
645
+ #
646
+ # telephone_field_tag 'tel', '0123456789', class: 'special_input', disabled: true
647
+ # # => <input disabled="disabled" class="special_input" id="tel" name="tel" type="tel" value="0123456789" />
577
648
  def telephone_field_tag(name, value = nil, options = {})
578
- text_field_tag(name, value, options.stringify_keys.update("type" => "tel"))
649
+ text_field_tag(name, value, options.merge(type: :tel))
579
650
  end
580
651
  alias phone_field_tag telephone_field_tag
581
652
 
@@ -583,8 +654,21 @@ module ActionView
583
654
  #
584
655
  # ==== Options
585
656
  # * Accepts the same options as text_field_tag.
657
+ #
658
+ # ==== Examples
659
+ # date_field_tag 'name'
660
+ # # => <input id="name" name="name" type="date" />
661
+ #
662
+ # date_field_tag 'date', '01/01/2014'
663
+ # # => <input id="date" name="date" type="date" value="01/01/2014" />
664
+ #
665
+ # date_field_tag 'date', nil, class: 'special_input'
666
+ # # => <input class="special_input" id="date" name="date" type="date" />
667
+ #
668
+ # date_field_tag 'date', '01/01/2014', class: 'special_input', disabled: true
669
+ # # => <input disabled="disabled" class="special_input" id="date" name="date" type="date" value="01/01/2014" />
586
670
  def date_field_tag(name, value = nil, options = {})
587
- text_field_tag(name, value, options.stringify_keys.update("type" => "date"))
671
+ text_field_tag(name, value, options.merge(type: :date))
588
672
  end
589
673
 
590
674
  # Creates a text field of type "time".
@@ -595,10 +679,10 @@ module ActionView
595
679
  # * <tt>:step</tt> - The acceptable value granularity.
596
680
  # * Otherwise accepts the same options as text_field_tag.
597
681
  def time_field_tag(name, value = nil, options = {})
598
- text_field_tag(name, value, options.stringify_keys.update("type" => "time"))
682
+ text_field_tag(name, value, options.merge(type: :time))
599
683
  end
600
684
 
601
- # Creates a text field of type "datetime".
685
+ # Creates a text field of type "datetime-local".
602
686
  #
603
687
  # === Options
604
688
  # * <tt>:min</tt> - The minimum acceptable value.
@@ -606,19 +690,10 @@ module ActionView
606
690
  # * <tt>:step</tt> - The acceptable value granularity.
607
691
  # * Otherwise accepts the same options as text_field_tag.
608
692
  def datetime_field_tag(name, value = nil, options = {})
609
- text_field_tag(name, value, options.stringify_keys.update("type" => "datetime"))
693
+ text_field_tag(name, value, options.merge(type: "datetime-local"))
610
694
  end
611
695
 
612
- # Creates a text field of type "datetime-local".
613
- #
614
- # === Options
615
- # * <tt>:min</tt> - The minimum acceptable value.
616
- # * <tt>:max</tt> - The maximum acceptable value.
617
- # * <tt>:step</tt> - The acceptable value granularity.
618
- # * Otherwise accepts the same options as text_field_tag.
619
- def datetime_local_field_tag(name, value = nil, options = {})
620
- text_field_tag(name, value, options.stringify_keys.update("type" => "datetime-local"))
621
- end
696
+ alias datetime_local_field_tag datetime_field_tag
622
697
 
623
698
  # Creates a text field of type "month".
624
699
  #
@@ -628,7 +703,7 @@ module ActionView
628
703
  # * <tt>:step</tt> - The acceptable value granularity.
629
704
  # * Otherwise accepts the same options as text_field_tag.
630
705
  def month_field_tag(name, value = nil, options = {})
631
- text_field_tag(name, value, options.stringify_keys.update("type" => "month"))
706
+ text_field_tag(name, value, options.merge(type: :month))
632
707
  end
633
708
 
634
709
  # Creates a text field of type "week".
@@ -639,23 +714,49 @@ module ActionView
639
714
  # * <tt>:step</tt> - The acceptable value granularity.
640
715
  # * Otherwise accepts the same options as text_field_tag.
641
716
  def week_field_tag(name, value = nil, options = {})
642
- text_field_tag(name, value, options.stringify_keys.update("type" => "week"))
717
+ text_field_tag(name, value, options.merge(type: :week))
643
718
  end
644
719
 
645
720
  # Creates a text field of type "url".
646
721
  #
647
722
  # ==== Options
648
723
  # * Accepts the same options as text_field_tag.
724
+ #
725
+ # ==== Examples
726
+ # url_field_tag 'name'
727
+ # # => <input id="name" name="name" type="url" />
728
+ #
729
+ # url_field_tag 'url', 'http://rubyonrails.org'
730
+ # # => <input id="url" name="url" type="url" value="http://rubyonrails.org" />
731
+ #
732
+ # url_field_tag 'url', nil, class: 'special_input'
733
+ # # => <input class="special_input" id="url" name="url" type="url" />
734
+ #
735
+ # url_field_tag 'url', 'http://rubyonrails.org', class: 'special_input', disabled: true
736
+ # # => <input disabled="disabled" class="special_input" id="url" name="url" type="url" value="http://rubyonrails.org" />
649
737
  def url_field_tag(name, value = nil, options = {})
650
- text_field_tag(name, value, options.stringify_keys.update("type" => "url"))
738
+ text_field_tag(name, value, options.merge(type: :url))
651
739
  end
652
740
 
653
741
  # Creates a text field of type "email".
654
742
  #
655
743
  # ==== Options
656
744
  # * Accepts the same options as text_field_tag.
745
+ #
746
+ # ==== Examples
747
+ # email_field_tag 'name'
748
+ # # => <input id="name" name="name" type="email" />
749
+ #
750
+ # email_field_tag 'email', 'email@example.com'
751
+ # # => <input id="email" name="email" type="email" value="email@example.com" />
752
+ #
753
+ # email_field_tag 'email', nil, class: 'special_input'
754
+ # # => <input class="special_input" id="email" name="email" type="email" />
755
+ #
756
+ # email_field_tag 'email', 'email@example.com', class: 'special_input', disabled: true
757
+ # # => <input disabled="disabled" class="special_input" id="email" name="email" type="email" value="email@example.com" />
657
758
  def email_field_tag(name, value = nil, options = {})
658
- text_field_tag(name, value, options.stringify_keys.update("type" => "email"))
759
+ text_field_tag(name, value, options.merge(type: :email))
659
760
  end
660
761
 
661
762
  # Creates a number field.
@@ -665,12 +766,40 @@ module ActionView
665
766
  # * <tt>:max</tt> - The maximum acceptable value.
666
767
  # * <tt>:in</tt> - A range specifying the <tt>:min</tt> and
667
768
  # <tt>:max</tt> values.
769
+ # * <tt>:within</tt> - Same as <tt>:in</tt>.
668
770
  # * <tt>:step</tt> - The acceptable value granularity.
669
771
  # * Otherwise accepts the same options as text_field_tag.
670
772
  #
671
773
  # ==== Examples
774
+ # number_field_tag 'quantity'
775
+ # # => <input id="quantity" name="quantity" type="number" />
776
+ #
777
+ # number_field_tag 'quantity', '1'
778
+ # # => <input id="quantity" name="quantity" type="number" value="1" />
779
+ #
780
+ # number_field_tag 'quantity', nil, class: 'special_input'
781
+ # # => <input class="special_input" id="quantity" name="quantity" type="number" />
782
+ #
783
+ # number_field_tag 'quantity', nil, min: 1
784
+ # # => <input id="quantity" name="quantity" min="1" type="number" />
785
+ #
786
+ # number_field_tag 'quantity', nil, max: 9
787
+ # # => <input id="quantity" name="quantity" max="9" type="number" />
788
+ #
672
789
  # number_field_tag 'quantity', nil, in: 1...10
673
790
  # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
791
+ #
792
+ # number_field_tag 'quantity', nil, within: 1...10
793
+ # # => <input id="quantity" name="quantity" min="1" max="9" type="number" />
794
+ #
795
+ # number_field_tag 'quantity', nil, min: 1, max: 10
796
+ # # => <input id="quantity" name="quantity" min="1" max="10" type="number" />
797
+ #
798
+ # number_field_tag 'quantity', nil, min: 1, max: 10, step: 2
799
+ # # => <input id="quantity" name="quantity" min="1" max="10" step="2" type="number" />
800
+ #
801
+ # number_field_tag 'quantity', '1', class: 'special_input', disabled: true
802
+ # # => <input disabled="disabled" class="special_input" id="quantity" name="quantity" type="number" value="1" />
674
803
  def number_field_tag(name, value = nil, options = {})
675
804
  options = options.stringify_keys
676
805
  options["type"] ||= "number"
@@ -685,13 +814,16 @@ module ActionView
685
814
  # ==== Options
686
815
  # * Accepts the same options as number_field_tag.
687
816
  def range_field_tag(name, value = nil, options = {})
688
- number_field_tag(name, value, options.stringify_keys.update("type" => "range"))
817
+ number_field_tag(name, value, options.merge(type: :range))
689
818
  end
690
819
 
691
820
  # Creates the hidden UTF8 enforcer tag. Override this method in a helper
692
821
  # to customize the tag.
693
822
  def utf8_enforcer_tag
694
- tag(:input, :type => "hidden", :name => "utf8", :value => "&#x2713;".html_safe)
823
+ # Use raw HTML to ensure the value is written as an HTML entity; it
824
+ # needs to be the right character regardless of which encoding the
825
+ # browser infers.
826
+ '<input name="utf8" type="hidden" value="&#x2713;" />'.html_safe
695
827
  end
696
828
 
697
829
  private
@@ -720,23 +852,32 @@ module ActionView
720
852
 
721
853
  def extra_tags_for_form(html_options)
722
854
  authenticity_token = html_options.delete("authenticity_token")
723
- method = html_options.delete("method").to_s
855
+ method = html_options.delete("method").to_s.downcase
724
856
 
725
- method_tag = case method
726
- when /^get$/i # must be case-insensitive, but can't use downcase as might be nil
857
+ method_tag = \
858
+ case method
859
+ when "get"
727
860
  html_options["method"] = "get"
728
- ''
729
- when /^post$/i, "", nil
861
+ ""
862
+ when "post", ""
730
863
  html_options["method"] = "post"
731
- token_tag(authenticity_token)
864
+ token_tag(authenticity_token, form_options: {
865
+ action: html_options["action"],
866
+ method: "post"
867
+ })
732
868
  else
733
869
  html_options["method"] = "post"
734
- method_tag(method) + token_tag(authenticity_token)
735
- end
870
+ method_tag(method) + token_tag(authenticity_token, form_options: {
871
+ action: html_options["action"],
872
+ method: method
873
+ })
874
+ end
736
875
 
737
- enforce_utf8 = html_options.delete("enforce_utf8") { true }
738
- tags = (enforce_utf8 ? utf8_enforcer_tag : ''.html_safe) << method_tag
739
- content_tag(:div, tags, :style => 'display:none')
876
+ if html_options.delete("enforce_utf8") { default_enforce_utf8 }
877
+ utf8_enforcer_tag + method_tag
878
+ else
879
+ method_tag
880
+ end
740
881
  end
741
882
 
742
883
  def form_tag_html(html_options)
@@ -744,8 +885,7 @@ module ActionView
744
885
  tag(:form, html_options, true) + extra_tags
745
886
  end
746
887
 
747
- def form_tag_in_block(html_options, &block)
748
- content = capture(&block)
888
+ def form_tag_with_body(html_options, content)
749
889
  output = form_tag_html(html_options)
750
890
  output << content
751
891
  output.safe_concat("</form>")
@@ -753,7 +893,29 @@ module ActionView
753
893
 
754
894
  # see http://www.w3.org/TR/html4/types.html#type-name
755
895
  def sanitize_to_id(name)
756
- name.to_s.delete(']').gsub(/[^-a-zA-Z0-9:.]/, "_")
896
+ name.to_s.delete("]").tr("^-a-zA-Z0-9:.", "_")
897
+ end
898
+
899
+ def set_default_disable_with(value, tag_options)
900
+ data = tag_options.fetch("data", {})
901
+
902
+ if tag_options["data-disable-with"] == false || data["disable_with"] == false
903
+ data.delete("disable_with")
904
+ elsif ActionView::Base.automatically_disable_submit_tag
905
+ disable_with_text = tag_options["data-disable-with"]
906
+ disable_with_text ||= data["disable_with"]
907
+ disable_with_text ||= value.to_s.clone
908
+ tag_options.deep_merge!("data" => { "disable_with" => disable_with_text })
909
+ end
910
+
911
+ tag_options.delete("data-disable-with")
912
+ end
913
+
914
+ def convert_direct_upload_option_to_url(options)
915
+ if options.delete(:direct_upload) && respond_to?(:rails_direct_uploads_url)
916
+ options["data-direct-upload-url"] = rails_direct_uploads_url
917
+ end
918
+ options
757
919
  end
758
920
  end
759
921
  end