actionview 7.0.1 → 7.1.1

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 (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +281 -202
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/app/assets/javascripts/rails-ujs.esm.js +693 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +33 -12
  8. data/lib/action_view/buffers.rb +106 -8
  9. data/lib/action_view/cache_expiry.rb +40 -43
  10. data/lib/action_view/context.rb +1 -1
  11. data/lib/action_view/deprecator.rb +7 -0
  12. data/lib/action_view/digestor.rb +1 -1
  13. data/lib/action_view/gem_version.rb +2 -2
  14. data/lib/action_view/helpers/active_model_helper.rb +1 -1
  15. data/lib/action_view/helpers/asset_tag_helper.rb +133 -48
  16. data/lib/action_view/helpers/asset_url_helper.rb +13 -12
  17. data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
  18. data/lib/action_view/helpers/cache_helper.rb +3 -9
  19. data/lib/action_view/helpers/capture_helper.rb +26 -12
  20. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  21. data/lib/action_view/helpers/controller_helper.rb +6 -0
  22. data/lib/action_view/helpers/csp_helper.rb +2 -2
  23. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  24. data/lib/action_view/helpers/date_helper.rb +76 -64
  25. data/lib/action_view/helpers/debug_helper.rb +3 -3
  26. data/lib/action_view/helpers/form_helper.rb +62 -31
  27. data/lib/action_view/helpers/form_options_helper.rb +6 -3
  28. data/lib/action_view/helpers/form_tag_helper.rb +88 -44
  29. data/lib/action_view/helpers/javascript_helper.rb +1 -0
  30. data/lib/action_view/helpers/number_helper.rb +15 -13
  31. data/lib/action_view/helpers/output_safety_helper.rb +4 -4
  32. data/lib/action_view/helpers/rendering_helper.rb +5 -6
  33. data/lib/action_view/helpers/sanitize_helper.rb +34 -15
  34. data/lib/action_view/helpers/tag_helper.rb +27 -16
  35. data/lib/action_view/helpers/tags/base.rb +11 -52
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
  37. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
  38. data/lib/action_view/helpers/tags/collection_select.rb +3 -0
  39. data/lib/action_view/helpers/tags/date_field.rb +1 -1
  40. data/lib/action_view/helpers/tags/date_select.rb +2 -0
  41. data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
  42. data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
  43. data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
  44. data/lib/action_view/helpers/tags/month_field.rb +1 -1
  45. data/lib/action_view/helpers/tags/select.rb +4 -1
  46. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  47. data/lib/action_view/helpers/tags/time_field.rb +1 -1
  48. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
  49. data/lib/action_view/helpers/tags/week_field.rb +1 -1
  50. data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
  51. data/lib/action_view/helpers/tags.rb +2 -0
  52. data/lib/action_view/helpers/text_helper.rb +33 -17
  53. data/lib/action_view/helpers/translation_helper.rb +6 -6
  54. data/lib/action_view/helpers/url_helper.rb +90 -65
  55. data/lib/action_view/helpers.rb +2 -0
  56. data/lib/action_view/layouts.rb +13 -8
  57. data/lib/action_view/log_subscriber.rb +49 -32
  58. data/lib/action_view/lookup_context.rb +29 -13
  59. data/lib/action_view/path_registry.rb +57 -0
  60. data/lib/action_view/path_set.rb +13 -14
  61. data/lib/action_view/railtie.rb +26 -3
  62. data/lib/action_view/record_identifier.rb +16 -9
  63. data/lib/action_view/renderer/abstract_renderer.rb +1 -1
  64. data/lib/action_view/renderer/collection_renderer.rb +9 -1
  65. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
  66. data/lib/action_view/renderer/partial_renderer.rb +3 -2
  67. data/lib/action_view/renderer/renderer.rb +2 -0
  68. data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
  69. data/lib/action_view/renderer/template_renderer.rb +3 -2
  70. data/lib/action_view/rendering.rb +24 -6
  71. data/lib/action_view/ripper_ast_parser.rb +6 -6
  72. data/lib/action_view/routing_url_for.rb +7 -4
  73. data/lib/action_view/template/error.rb +14 -1
  74. data/lib/action_view/template/handlers/builder.rb +4 -4
  75. data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
  76. data/lib/action_view/template/handlers/erb.rb +73 -1
  77. data/lib/action_view/template/handlers.rb +1 -1
  78. data/lib/action_view/template/html.rb +1 -1
  79. data/lib/action_view/template/raw_file.rb +1 -1
  80. data/lib/action_view/template/renderable.rb +1 -1
  81. data/lib/action_view/template/resolver.rb +15 -5
  82. data/lib/action_view/template/text.rb +1 -1
  83. data/lib/action_view/template/types.rb +25 -34
  84. data/lib/action_view/template.rb +227 -53
  85. data/lib/action_view/template_path.rb +2 -0
  86. data/lib/action_view/test_case.rb +174 -21
  87. data/lib/action_view/unbound_template.rb +15 -5
  88. data/lib/action_view/version.rb +1 -1
  89. data/lib/action_view/view_paths.rb +19 -28
  90. data/lib/action_view.rb +4 -1
  91. data/lib/assets/compiled/rails-ujs.js +36 -5
  92. metadata +27 -27
@@ -13,8 +13,9 @@ require "active_support/core_ext/string/output_safety"
13
13
  require "active_support/core_ext/string/inflections"
14
14
 
15
15
  module ActionView
16
- # = Action View Form Helpers
17
16
  module Helpers # :nodoc:
17
+ # = Action View Form \Helpers
18
+ #
18
19
  # Form helpers are designed to make working with resources much easier
19
20
  # compared to using vanilla HTML.
20
21
  #
@@ -28,7 +29,7 @@ module ActionView
28
29
  # when the form is initially displayed, input fields corresponding to attributes
29
30
  # of the resource should show the current values of those attributes.
30
31
  #
31
- # In Rails, this is usually achieved by creating the form using +form_for+ and
32
+ # In \Rails, this is usually achieved by creating the form using +form_for+ and
32
33
  # a number of related helper methods. +form_for+ generates an appropriate <tt>form</tt>
33
34
  # tag and yields a form builder object that knows the model the form is about.
34
35
  # Input fields are created by calling methods defined on the form builder, which
@@ -122,7 +123,7 @@ module ActionView
122
123
  # of a specific model object.
123
124
  #
124
125
  # The method can be used in several slightly different ways, depending on
125
- # how much you wish to rely on Rails to infer automatically from the model
126
+ # how much you wish to rely on \Rails to infer automatically from the model
126
127
  # how the form should be constructed. For a generic model object, a form
127
128
  # can be created by passing +form_for+ a string or symbol representing
128
129
  # the object we are concerned with:
@@ -251,7 +252,7 @@ module ActionView
251
252
  # form is going to be sent. However, further simplification is possible
252
253
  # if the record passed to +form_for+ is a _resource_, i.e. it corresponds
253
254
  # to a set of RESTful routes, e.g. defined using the +resources+ method
254
- # in <tt>config/routes.rb</tt>. In this case Rails will simply infer the
255
+ # in <tt>config/routes.rb</tt>. In this case \Rails will simply infer the
255
256
  # appropriate URL from the record itself. For example,
256
257
  #
257
258
  # <%= form_for @post do |f| %>
@@ -496,7 +497,7 @@ module ActionView
496
497
  # <%= form.text_field :title %>
497
498
  # <% end %>
498
499
  # # =>
499
- # <form method="post" data-remote="true">
500
+ # <form method="post">
500
501
  # <input type="text" name="title">
501
502
  # </form>
502
503
  #
@@ -555,7 +556,7 @@ module ActionView
555
556
  # is a _resource_. It corresponds to a set of RESTful routes, most likely
556
557
  # defined via +resources+ in <tt>config/routes.rb</tt>.
557
558
  #
558
- # So when passing such a model record, Rails infers the URL and method.
559
+ # So when passing such a model record, \Rails infers the URL and method.
559
560
  #
560
561
  # <%= form_with model: @post do |form| %>
561
562
  # ...
@@ -618,12 +619,12 @@ module ActionView
618
619
  # * <tt>:local</tt> - Whether to use standard HTTP form submission.
619
620
  # When set to <tt>true</tt>, the form is submitted via standard HTTP.
620
621
  # When set to <tt>false</tt>, the form is submitted as a "remote form", which
621
- # is handled by Rails UJS as an XHR. When unspecified, the behavior is derived
622
+ # is handled by \Rails UJS as an XHR. When unspecified, the behavior is derived
622
623
  # from <tt>config.action_view.form_with_generates_remote_forms</tt> where the
623
624
  # config's value is actually the inverse of what <tt>local</tt>'s value would be.
624
- # As of Rails 6.1, that configuration option defaults to <tt>false</tt>
625
+ # As of \Rails 6.1, that configuration option defaults to <tt>false</tt>
625
626
  # (which has the equivalent effect of passing <tt>local: true</tt>).
626
- # In previous versions of Rails, that configuration option defaults to
627
+ # In previous versions of \Rails, that configuration option defaults to
627
628
  # <tt>true</tt> (the equivalent of passing <tt>local: false</tt>).
628
629
  # * <tt>:skip_enforcing_utf8</tt> - If set to true, a hidden input with name
629
630
  # utf8 is not output.
@@ -756,10 +757,14 @@ module ActionView
756
757
 
757
758
  if model
758
759
  if url != false
759
- url ||= polymorphic_path(model, format: format)
760
+ url ||= if format.nil?
761
+ polymorphic_path(model, {})
762
+ else
763
+ polymorphic_path(model, format: format)
764
+ end
760
765
  end
761
766
 
762
- model = _object_for_form_builder(model)
767
+ model = convert_to_model(_object_for_form_builder(model))
763
768
  scope ||= model_name_from_record_or_class(model).param_key
764
769
  end
765
770
 
@@ -1014,9 +1019,10 @@ module ActionView
1014
1019
  # <% end %>
1015
1020
  #
1016
1021
  # Note that fields_for will automatically generate a hidden field
1017
- # to store the ID of the record. There are circumstances where this
1018
- # hidden field is not needed and you can pass <tt>include_id: false</tt>
1019
- # to prevent fields_for from rendering it automatically.
1022
+ # to store the ID of the record if it responds to <tt>persisted?</tt>.
1023
+ # There are circumstances where this hidden field is not needed and you
1024
+ # can pass <tt>include_id: false</tt> to prevent fields_for from
1025
+ # rendering it automatically.
1020
1026
  def fields_for(record_name, record_object = nil, options = {}, &block)
1021
1027
  options = { model: record_object, allow_method_names_outside_object: false, skip_default_ids: false }.merge!(options)
1022
1028
 
@@ -1082,7 +1088,7 @@ module ActionView
1082
1088
 
1083
1089
  # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
1084
1090
  # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
1085
- # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
1091
+ # is found in the current I18n locale (through <tt>helpers.label.<modelname>.<attribute></tt>) or you specify it explicitly.
1086
1092
  # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
1087
1093
  # onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
1088
1094
  # target labels for radio_button tags (where the value is used in the ID of the input tag).
@@ -1237,7 +1243,7 @@ module ActionView
1237
1243
  def file_field(object_name, method, options = {})
1238
1244
  options = { include_hidden: multiple_file_field_include_hidden }.merge!(options)
1239
1245
 
1240
- Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(method, options)).render
1246
+ Tags::FileField.new(object_name, method, self, convert_direct_upload_option_to_url(options.dup)).render
1241
1247
  end
1242
1248
 
1243
1249
  # Returns a textarea opening and closing tag set tailored for accessing a specified attribute (identified by +method+)
@@ -1310,7 +1316,7 @@ module ActionView
1310
1316
  # ...
1311
1317
  # <% end %>
1312
1318
  #
1313
- # because parameter name repetition is precisely what Rails seeks to distinguish
1319
+ # because parameter name repetition is precisely what \Rails seeks to distinguish
1314
1320
  # the elements of the array. For each item with a checked check box you
1315
1321
  # get an extra ghost item with only that attribute, assigned to "0".
1316
1322
  #
@@ -1437,10 +1443,12 @@ module ActionView
1437
1443
  # formatted by trying to call +strftime+ with "%H:%M" on the object's value.
1438
1444
  # It is also possible to override this by passing the "value" option.
1439
1445
  #
1440
- # === Options
1441
- # * Accepts same options as time_field_tag
1446
+ # ==== Options
1447
+ #
1448
+ # Supports the same options as FormTagHelper#time_field_tag.
1449
+ #
1450
+ # ==== Examples
1442
1451
  #
1443
- # === Example
1444
1452
  # time_field("task", "started_at")
1445
1453
  # # => <input id="task_started_at" name="task[started_at]" type="time" />
1446
1454
  #
@@ -1491,6 +1499,12 @@ module ActionView
1491
1499
  # datetime_field("user", "born_on", min: "2014-05-20T00:00:00")
1492
1500
  # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" min="2014-05-20T00:00:00.000" />
1493
1501
  #
1502
+ # By default, provided datetimes will be formatted including seconds. You can render just the date, hour,
1503
+ # and minute by passing <tt>include_seconds: false</tt>.
1504
+ #
1505
+ # @user.born_on = Time.current
1506
+ # datetime_field("user", "born_on", include_seconds: false)
1507
+ # # => <input id="user_born_on" name="user[born_on]" type="datetime-local" value="2014-05-20T14:35" />
1494
1508
  def datetime_field(object_name, method, options = {})
1495
1509
  Tags::DatetimeLocalField.new(object_name, method, self, options).render
1496
1510
  end
@@ -1552,7 +1566,8 @@ module ActionView
1552
1566
  # Returns an input tag of type "number".
1553
1567
  #
1554
1568
  # ==== Options
1555
- # * Accepts same options as number_field_tag
1569
+ #
1570
+ # Supports the same options as FormTagHelper#number_field_tag.
1556
1571
  def number_field(object_name, method, options = {})
1557
1572
  Tags::NumberField.new(object_name, method, self, options).render
1558
1573
  end
@@ -1560,7 +1575,8 @@ module ActionView
1560
1575
  # Returns an input tag of type "range".
1561
1576
  #
1562
1577
  # ==== Options
1563
- # * Accepts same options as range_field_tag
1578
+ #
1579
+ # Supports the same options as FormTagHelper#range_field_tag.
1564
1580
  def range_field(object_name, method, options = {})
1565
1581
  Tags::RangeField.new(object_name, method, self, options).render
1566
1582
  end
@@ -1605,6 +1621,8 @@ module ActionView
1605
1621
  end
1606
1622
  end
1607
1623
 
1624
+ # = Action View Form Builder
1625
+ #
1608
1626
  # A +FormBuilder+ object is associated with a particular model object and
1609
1627
  # allows you to generate fields associated with the model object. The
1610
1628
  # +FormBuilder+ object is yielded when using +form_for+ or +fields_for+.
@@ -1730,7 +1748,7 @@ module ActionView
1730
1748
  # <tt><button></tt> element should be treated as the <tt><form></tt>
1731
1749
  # element's submit button, regardless of where it exists in the DOM.
1732
1750
  def id
1733
- options.dig(:html, :id)
1751
+ options.dig(:html, :id) || options[:id]
1734
1752
  end
1735
1753
 
1736
1754
  # Generate an HTML <tt>id</tt> attribute value for the given field
@@ -1749,7 +1767,7 @@ module ActionView
1749
1767
  # <tt>aria-describedby</tt> attribute referencing the <tt><span></tt>
1750
1768
  # element, sharing a common <tt>id</tt> root (<tt>post_title</tt>, in this
1751
1769
  # case).
1752
- def field_id(method, *suffixes, namespace: @options[:namespace], index: @index)
1770
+ def field_id(method, *suffixes, namespace: @options[:namespace], index: @options[:index])
1753
1771
  @template.field_id(@object_name, method, *suffixes, namespace: namespace, index: index)
1754
1772
  end
1755
1773
 
@@ -1769,7 +1787,7 @@ module ActionView
1769
1787
  # <%# => <input type="text" name="post[tag][]">
1770
1788
  # <% end %>
1771
1789
  #
1772
- def field_name(method, *methods, multiple: false, index: @index)
1790
+ def field_name(method, *methods, multiple: false, index: @options[:index])
1773
1791
  object_name = @options.fetch(:as) { @object_name }
1774
1792
 
1775
1793
  @template.field_name(object_name, method, *methods, index: index, multiple: multiple)
@@ -2069,6 +2087,18 @@ module ActionView
2069
2087
  # DateHelper that are designed to work with an object as base, like
2070
2088
  # FormOptionsHelper#collection_select and DateHelper#datetime_select.
2071
2089
  #
2090
+ # +fields_for+ tries to be smart about parameters, but it can be confused if both
2091
+ # name and value parameters are provided and the provided value has the shape of an
2092
+ # option Hash. To remove the ambiguity, explicitly pass an option Hash, even if empty.
2093
+ #
2094
+ # <%= form_for @person do |person_form| %>
2095
+ # ...
2096
+ # <%= fields_for :permission, @person.permission, {} do |permission_fields| %>
2097
+ # Admin?: <%= check_box_tag permission_fields.field_name(:admin), @person.permission[:admin] %>
2098
+ # <% end %>
2099
+ # ...
2100
+ # <% end %>
2101
+ #
2072
2102
  # === Nested Attributes Examples
2073
2103
  #
2074
2104
  # When the object belonging to the current scope has a nested attribute
@@ -2249,8 +2279,9 @@ module ActionView
2249
2279
  # to store the ID of the record. There are circumstances where this
2250
2280
  # hidden field is not needed and you can pass <tt>include_id: false</tt>
2251
2281
  # to prevent fields_for from rendering it automatically.
2252
- def fields_for(record_name, record_object = nil, fields_options = {}, &block)
2253
- fields_options, record_object = record_object, nil if record_object.is_a?(Hash) && record_object.extractable_options?
2282
+ def fields_for(record_name, record_object = nil, fields_options = nil, &block)
2283
+ fields_options, record_object = record_object, nil if fields_options.nil? && record_object.is_a?(Hash) && record_object.extractable_options?
2284
+ fields_options ||= {}
2254
2285
  fields_options[:builder] ||= options[:builder]
2255
2286
  fields_options[:namespace] = options[:namespace]
2256
2287
  fields_options[:parent_builder] = self
@@ -2285,7 +2316,7 @@ module ActionView
2285
2316
  @template.fields_for(record_name, record_object, fields_options, &block)
2286
2317
  end
2287
2318
 
2288
- # See the docs for the <tt>ActionView::FormHelper.fields</tt> helper method.
2319
+ # See the docs for the ActionView::Helpers::FormHelper#fields helper method.
2289
2320
  def fields(scope = nil, model: nil, **options, &block)
2290
2321
  options[:allow_method_names_outside_object] = true
2291
2322
  options[:skip_default_ids] = !FormHelper.form_with_generates_ids
@@ -2297,7 +2328,7 @@ module ActionView
2297
2328
 
2298
2329
  # Returns a label tag tailored for labelling an input field for a specified attribute (identified by +method+) on an object
2299
2330
  # assigned to the template (identified by +object+). The text of label will default to the attribute name unless a translation
2300
- # is found in the current I18n locale (through helpers.label.<modelname>.<attribute>) or you specify it explicitly.
2331
+ # is found in the current I18n locale (through <tt>helpers.label.<modelname>.<attribute></tt>) or you specify it explicitly.
2301
2332
  # Additional options on the label tag can be passed as a hash with +options+. These options will be tagged
2302
2333
  # onto the HTML as an HTML element attribute as in the example shown, except for the <tt>:value</tt> option, which is designed to
2303
2334
  # target labels for radio_button tags (where the value is used in the ID of the input tag).
@@ -2407,7 +2438,7 @@ module ActionView
2407
2438
  # ...
2408
2439
  # <% end %>
2409
2440
  #
2410
- # because parameter name repetition is precisely what Rails seeks to distinguish
2441
+ # because parameter name repetition is precisely what \Rails seeks to distinguish
2411
2442
  # the elements of the array. For each item with a checked check box you
2412
2443
  # get an extra ghost item with only that attribute, assigned to "0".
2413
2444
  #
@@ -2490,7 +2521,7 @@ module ActionView
2490
2521
  # * Creates standard HTML attributes for the tag.
2491
2522
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
2492
2523
  # * <tt>:multiple</tt> - If set to true, *in most updated browsers* the user will be allowed to select multiple files.
2493
- # * <tt>:include_hidden</tt> - When <tt>multiple: true</tt> and <tt>include_hidden: true</tt>, the field will be prefixed with an <tt><input type="hidden"></tt> field with an empty value to support submitting an empty collection of files.
2524
+ # * <tt>:include_hidden</tt> - When <tt>multiple: true</tt> and <tt>include_hidden: true</tt>, the field will be prefixed with an <tt><input type="hidden"></tt> field with an empty value to support submitting an empty collection of files. Since <tt>include_hidden</tt> will default to <tt>config.active_storage.multiple_file_field_include_hidden</tt> if you don't specify <tt>include_hidden</tt>, you will need to pass <tt>include_hidden: false</tt> to prevent submitting an empty collection of files when passing <tt>multiple: true</tt>.
2494
2525
  # * <tt>:accept</tt> - If set to one or multiple mime-types, the user will be suggested a filter when choosing a file. You still need to set up model validations.
2495
2526
  #
2496
2527
  # ==== Examples
@@ -8,8 +8,9 @@ require "active_support/core_ext/array/wrap"
8
8
  require "action_view/helpers/text_helper"
9
9
 
10
10
  module ActionView
11
- # = Action View Form Option Helpers
12
11
  module Helpers # :nodoc:
12
+ # = Action View Form Option \Helpers
13
+ #
13
14
  # Provides a number of methods for turning different kinds of containers into a set of option tags.
14
15
  #
15
16
  # The <tt>collection_select</tt>, <tt>select</tt> and <tt>time_zone_select</tt> methods take an <tt>options</tt> parameter, a hash:
@@ -500,6 +501,8 @@ module ActionView
500
501
  # <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a
501
502
  # nested array of text-value pairs. See <tt>options_for_select</tt> for more info.
502
503
  # Ex. ["North America",[["United States","US"],["Canada","CA"]]]
504
+ # An optional third value can be provided as HTML attributes for the <tt>optgroup</tt>.
505
+ # Ex. ["North America",[["United States","US"],["Canada","CA"]], { disabled: "disabled" }]
503
506
  # * +selected_key+ - A value equal to the +value+ attribute for one of the <tt><option></tt> tags,
504
507
  # which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
505
508
  # as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
@@ -598,9 +601,9 @@ module ActionView
598
601
  #
599
602
  # Options:
600
603
  # * <tt>:index_as_value</tt> - Defaults to false, set to true to use the indexes from
601
- # `I18n.translate("date.day_names")` as the values. By default, Sunday is always 0.
604
+ # <tt>I18n.translate("date.day_names")</tt> as the values. By default, Sunday is always 0.
602
605
  # * <tt>:day_format</tt> - The I18n key of the array to use for the weekday options.
603
- # Defaults to :day_names, set to :abbr_day_names for abbreviations.
606
+ # Defaults to +:day_names+, set to +:abbr_day_names+ for abbreviations.
604
607
  # * <tt>:beginning_of_week</tt> - Defaults to Date.beginning_of_week.
605
608
  #
606
609
  # NOTE: Only the option tags are returned, you have to wrap this call in
@@ -1,14 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "cgi"
4
+ require "action_view/helpers/content_exfiltration_prevention_helper"
4
5
  require "action_view/helpers/url_helper"
5
6
  require "action_view/helpers/text_helper"
6
7
  require "active_support/core_ext/string/output_safety"
7
8
  require "active_support/core_ext/module/attribute_accessors"
8
9
 
9
10
  module ActionView
10
- # = Action View Form Tag Helpers
11
11
  module Helpers # :nodoc:
12
+ # = Action View Form Tag \Helpers
13
+ #
12
14
  # Provides a number of methods for creating form tags that don't rely on an Active Record object assigned to the template like
13
15
  # FormHelper does. Instead, you provide the names and values manually.
14
16
  #
@@ -19,6 +21,7 @@ module ActionView
19
21
 
20
22
  include UrlHelper
21
23
  include TextHelper
24
+ include ContentExfiltrationPreventionHelper
22
25
 
23
26
  mattr_accessor :embed_authenticity_token_in_remote_forms
24
27
  self.embed_authenticity_token_in_remote_forms = nil
@@ -62,7 +65,7 @@ module ActionView
62
65
  #
63
66
  # <%= form_tag('/posts', remote: true) %>
64
67
  # # => <form action="/posts" method="post" data-remote="true">
65
-
68
+ #
66
69
  # form_tag(false, method: :get)
67
70
  # # => <form method="get">
68
71
  #
@@ -131,7 +134,7 @@ module ActionView
131
134
 
132
135
  # a little duplication to construct fewer strings
133
136
  case
134
- when object_name.empty?
137
+ when object_name.blank?
135
138
  "#{method_name}#{names}#{multiple ? "[]" : ""}"
136
139
  when index
137
140
  "#{object_name}[#{index}][#{method_name}]#{names}#{multiple ? "[]" : ""}"
@@ -342,7 +345,7 @@ module ActionView
342
345
  # file_field_tag 'file', accept: 'text/html', class: 'upload', value: 'index.html'
343
346
  # # => <input accept="text/html" class="upload" id="file" name="file" type="file" value="index.html" />
344
347
  def file_field_tag(name, options = {})
345
- text_field_tag(name, nil, convert_direct_upload_option_to_url(name, options.merge(type: :file)))
348
+ text_field_tag(name, nil, convert_direct_upload_option_to_url(options.merge(type: :file)))
346
349
  end
347
350
 
348
351
  # Creates a password field, a masked text field that will hide the users input behind a mask character.
@@ -420,9 +423,17 @@ module ActionView
420
423
  content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
421
424
  end
422
425
 
426
+ ##
427
+ # :call-seq:
428
+ # check_box_tag(name, options = {})
429
+ # check_box_tag(name, value, options = {})
430
+ # check_box_tag(name, value, checked, options = {})
431
+ #
423
432
  # Creates a check box form input tag.
424
433
  #
425
434
  # ==== Options
435
+ # * <tt>:value</tt> - The value of the input. Defaults to <tt>"1"</tt>.
436
+ # * <tt>:checked</tt> - If set to true, the checkbox will be checked by default.
426
437
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
427
438
  # * Any other key creates standard HTML options for the tag.
428
439
  #
@@ -441,16 +452,27 @@ module ActionView
441
452
  #
442
453
  # check_box_tag 'eula', 'accepted', false, disabled: true
443
454
  # # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
444
- def check_box_tag(name, value = "1", checked = false, options = {})
455
+ def check_box_tag(name, *args)
456
+ if args.length >= 4
457
+ raise ArgumentError, "wrong number of arguments (given #{args.length + 1}, expected 1..4)"
458
+ end
459
+ options = args.extract_options!
460
+ value, checked = args.empty? ? ["1", false] : [*args, false]
445
461
  html_options = { "type" => "checkbox", "name" => name, "id" => sanitize_to_id(name), "value" => value }.update(options.stringify_keys)
446
462
  html_options["checked"] = "checked" if checked
447
463
  tag :input, html_options
448
464
  end
449
465
 
466
+ ##
467
+ # :call-seq:
468
+ # radio_button_tag(name, value, options = {})
469
+ # radio_button_tag(name, value, checked, options = {})
470
+ #
450
471
  # Creates a radio button; use groups of radio buttons named the same to allow users to
451
472
  # select from a group of options.
452
473
  #
453
474
  # ==== Options
475
+ # * <tt>:checked</tt> - If set to true, the radio button will be selected by default.
454
476
  # * <tt>:disabled</tt> - If set to true, the user will not be able to use this input.
455
477
  # * Any other key creates standard HTML options for the tag.
456
478
  #
@@ -466,7 +488,12 @@ module ActionView
466
488
  #
467
489
  # radio_button_tag 'color', "green", true, class: "color_input"
468
490
  # # => <input checked="checked" class="color_input" id="color_green" name="color" type="radio" value="green" />
469
- def radio_button_tag(name, value, checked = false, options = {})
491
+ def radio_button_tag(name, value, *args)
492
+ if args.length >= 3
493
+ raise ArgumentError, "wrong number of arguments (given #{args.length + 2}, expected 2..4)"
494
+ end
495
+ options = args.extract_options!
496
+ checked = args.empty? ? false : args.first
470
497
  html_options = { "type" => "radio", "name" => name, "id" => "#{sanitize_to_id(name)}_#{sanitize_to_id(value)}", "value" => value }.update(options.stringify_keys)
471
498
  html_options["checked"] = "checked" if checked
472
499
  tag :input, html_options
@@ -495,9 +522,9 @@ module ActionView
495
522
  # submit_tag "Edit", class: "edit_button"
496
523
  # # => <input class="edit_button" data-disable-with="Edit" name="commit" type="submit" value="Edit" />
497
524
  #
498
- # ==== Deprecated: Rails UJS attributes
525
+ # ==== Deprecated: \Rails UJS attributes
499
526
  #
500
- # Prior to Rails 7, Rails shipped with the JavaScript library called @rails/ujs on by default. Following Rails 7,
527
+ # Prior to \Rails 7, \Rails shipped with the JavaScript library called @rails/ujs on by default. Following \Rails 7,
501
528
  # this library is no longer on by default. This library integrated with the following options:
502
529
  #
503
530
  # * <tt>confirm: 'question?'</tt> - If present the unobtrusive JavaScript
@@ -555,9 +582,9 @@ module ActionView
555
582
  # # <strong>Ask me!</strong>
556
583
  # # </button>
557
584
  #
558
- # ==== Deprecated: Rails UJS attributes
585
+ # ==== Deprecated: \Rails UJS attributes
559
586
  #
560
- # Prior to Rails 7, Rails shipped with a JavaScript library called @rails/ujs on by default. Following Rails 7,
587
+ # Prior to \Rails 7, \Rails shipped with a JavaScript library called @rails/ujs on by default. Following \Rails 7,
561
588
  # this library is no longer on by default. This library integrated with the following options:
562
589
  #
563
590
  # * <tt>confirm: 'question?'</tt> - If present, the
@@ -657,9 +684,11 @@ module ActionView
657
684
  # Creates a text field of type "color".
658
685
  #
659
686
  # ==== Options
660
- # * Accepts the same options as text_field_tag.
687
+ #
688
+ # Supports the same options as #text_field_tag.
661
689
  #
662
690
  # ==== Examples
691
+ #
663
692
  # color_field_tag 'name'
664
693
  # # => <input id="name" name="name" type="color" />
665
694
  #
@@ -678,9 +707,11 @@ module ActionView
678
707
  # Creates a text field of type "search".
679
708
  #
680
709
  # ==== Options
681
- # * Accepts the same options as text_field_tag.
710
+ #
711
+ # Supports the same options as #text_field_tag.
682
712
  #
683
713
  # ==== Examples
714
+ #
684
715
  # search_field_tag 'name'
685
716
  # # => <input id="name" name="name" type="search" />
686
717
  #
@@ -699,9 +730,11 @@ module ActionView
699
730
  # Creates a text field of type "tel".
700
731
  #
701
732
  # ==== Options
702
- # * Accepts the same options as text_field_tag.
733
+ #
734
+ # Supports the same options as #text_field_tag.
703
735
  #
704
736
  # ==== Examples
737
+ #
705
738
  # telephone_field_tag 'name'
706
739
  # # => <input id="name" name="name" type="tel" />
707
740
  #
@@ -721,9 +754,11 @@ module ActionView
721
754
  # Creates a text field of type "date".
722
755
  #
723
756
  # ==== Options
724
- # * Accepts the same options as text_field_tag.
757
+ #
758
+ # Supports the same options as #text_field_tag.
725
759
  #
726
760
  # ==== Examples
761
+ #
727
762
  # date_field_tag 'name'
728
763
  # # => <input id="name" name="name" type="date" />
729
764
  #
@@ -741,23 +776,28 @@ module ActionView
741
776
 
742
777
  # Creates a text field of type "time".
743
778
  #
744
- # === Options
779
+ # ==== Options
780
+ #
781
+ # Supports the same options as #text_field_tag. Additionally, supports:
782
+ #
745
783
  # * <tt>:min</tt> - The minimum acceptable value.
746
784
  # * <tt>:max</tt> - The maximum acceptable value.
747
785
  # * <tt>:step</tt> - The acceptable value granularity.
748
786
  # * <tt>:include_seconds</tt> - Include seconds and ms in the output timestamp format (true by default).
749
- # * Otherwise accepts the same options as text_field_tag.
750
787
  def time_field_tag(name, value = nil, options = {})
751
788
  text_field_tag(name, value, options.merge(type: :time))
752
789
  end
753
790
 
754
791
  # Creates a text field of type "datetime-local".
755
792
  #
756
- # === Options
793
+ # ==== Options
794
+ #
795
+ # Supports the same options as #text_field_tag. Additionally, supports:
796
+ #
757
797
  # * <tt>:min</tt> - The minimum acceptable value.
758
798
  # * <tt>:max</tt> - The maximum acceptable value.
759
799
  # * <tt>:step</tt> - The acceptable value granularity.
760
- # * Otherwise accepts the same options as text_field_tag.
800
+ # * <tt>:include_seconds</tt> - Include seconds in the output timestamp format (true by default).
761
801
  def datetime_field_tag(name, value = nil, options = {})
762
802
  text_field_tag(name, value, options.merge(type: "datetime-local"))
763
803
  end
@@ -766,22 +806,26 @@ module ActionView
766
806
 
767
807
  # Creates a text field of type "month".
768
808
  #
769
- # === Options
809
+ # ==== Options
810
+ #
811
+ # Supports the same options as #text_field_tag. Additionally, supports:
812
+ #
770
813
  # * <tt>:min</tt> - The minimum acceptable value.
771
814
  # * <tt>:max</tt> - The maximum acceptable value.
772
815
  # * <tt>:step</tt> - The acceptable value granularity.
773
- # * Otherwise accepts the same options as text_field_tag.
774
816
  def month_field_tag(name, value = nil, options = {})
775
817
  text_field_tag(name, value, options.merge(type: :month))
776
818
  end
777
819
 
778
820
  # Creates a text field of type "week".
779
821
  #
780
- # === Options
822
+ # ==== Options
823
+ #
824
+ # Supports the same options as #text_field_tag. Additionally, supports:
825
+ #
781
826
  # * <tt>:min</tt> - The minimum acceptable value.
782
827
  # * <tt>:max</tt> - The maximum acceptable value.
783
828
  # * <tt>:step</tt> - The acceptable value granularity.
784
- # * Otherwise accepts the same options as text_field_tag.
785
829
  def week_field_tag(name, value = nil, options = {})
786
830
  text_field_tag(name, value, options.merge(type: :week))
787
831
  end
@@ -789,9 +833,11 @@ module ActionView
789
833
  # Creates a text field of type "url".
790
834
  #
791
835
  # ==== Options
792
- # * Accepts the same options as text_field_tag.
836
+ #
837
+ # Supports the same options as #text_field_tag.
793
838
  #
794
839
  # ==== Examples
840
+ #
795
841
  # url_field_tag 'name'
796
842
  # # => <input id="name" name="name" type="url" />
797
843
  #
@@ -810,9 +856,11 @@ module ActionView
810
856
  # Creates a text field of type "email".
811
857
  #
812
858
  # ==== Options
813
- # * Accepts the same options as text_field_tag.
859
+ #
860
+ # Supports the same options as #text_field_tag.
814
861
  #
815
862
  # ==== Examples
863
+ #
816
864
  # email_field_tag 'name'
817
865
  # # => <input id="name" name="name" type="email" />
818
866
  #
@@ -831,15 +879,18 @@ module ActionView
831
879
  # Creates a number field.
832
880
  #
833
881
  # ==== Options
882
+ #
883
+ # Supports the same options as #text_field_tag. Additionally, supports:
884
+ #
834
885
  # * <tt>:min</tt> - The minimum acceptable value.
835
886
  # * <tt>:max</tt> - The maximum acceptable value.
836
887
  # * <tt>:in</tt> - A range specifying the <tt>:min</tt> and
837
888
  # <tt>:max</tt> values.
838
889
  # * <tt>:within</tt> - Same as <tt>:in</tt>.
839
890
  # * <tt>:step</tt> - The acceptable value granularity.
840
- # * Otherwise accepts the same options as text_field_tag.
841
891
  #
842
892
  # ==== Examples
893
+ #
843
894
  # number_field_tag 'quantity'
844
895
  # # => <input id="quantity" name="quantity" type="number" />
845
896
  #
@@ -881,12 +932,13 @@ module ActionView
881
932
  # Creates a range form element.
882
933
  #
883
934
  # ==== Options
884
- # * Accepts the same options as number_field_tag.
935
+ #
936
+ # Supports the same options as #number_field_tag.
885
937
  def range_field_tag(name, value = nil, options = {})
886
938
  number_field_tag(name, value, options.merge(type: :range))
887
939
  end
888
940
 
889
- # Creates the hidden UTF8 enforcer tag. Override this method in a helper
941
+ # Creates the hidden UTF-8 enforcer tag. Override this method in a helper
890
942
  # to customize the tag.
891
943
  def utf8_enforcer_tag
892
944
  # Use raw HTML to ensure the value is written as an HTML entity; it
@@ -955,7 +1007,8 @@ module ActionView
955
1007
 
956
1008
  def form_tag_html(html_options)
957
1009
  extra_tags = extra_tags_for_form(html_options)
958
- tag(:form, html_options, true) + extra_tags
1010
+ html = tag(:form, html_options, true) + extra_tags
1011
+ prevent_content_exfiltration(html)
959
1012
  end
960
1013
 
961
1014
  def form_tag_with_body(html_options, content)
@@ -984,24 +1037,15 @@ module ActionView
984
1037
  tag_options.delete("data-disable-with")
985
1038
  end
986
1039
 
987
- def convert_direct_upload_option_to_url(name, options)
988
- if options.delete(:direct_upload) && respond_to?(:rails_direct_uploads_url)
989
- options["data-direct-upload-url"] = rails_direct_uploads_url
990
-
991
- if options[:object] && options[:object].class.respond_to?(:reflect_on_attachment)
992
- attachment_reflection = options[:object].class.reflect_on_attachment(name)
993
-
994
- class_with_attachment = "#{options[:object].class.name.underscore}##{name}"
995
- options["data-direct-upload-attachment-name"] = class_with_attachment
1040
+ def convert_direct_upload_option_to_url(options)
1041
+ return options unless options.delete(:direct_upload)
996
1042
 
997
- service_name = attachment_reflection.options[:service_name] || ActiveStorage::Blob.service.name
998
- options["data-direct-upload-token"] = ActiveStorage::DirectUploadToken.generate_direct_upload_token(
999
- class_with_attachment,
1000
- service_name,
1001
- session
1002
- )
1003
- end
1043
+ if respond_to?(:rails_direct_uploads_url)
1044
+ options["data-direct-upload-url"] = rails_direct_uploads_url
1045
+ elsif respond_to?(:main_app) && main_app.respond_to?(:rails_direct_uploads_url)
1046
+ options["data-direct-upload-url"] = main_app.rails_direct_uploads_url
1004
1047
  end
1048
+
1005
1049
  options
1006
1050
  end
1007
1051
  end
@@ -2,6 +2,7 @@
2
2
 
3
3
  module ActionView
4
4
  module Helpers # :nodoc:
5
+ # = Action View JavaScript \Helpers
5
6
  module JavaScriptHelper
6
7
  JS_ESCAPE_MAP = {
7
8
  "\\" => "\\\\",