actionview 7.0.7.2 → 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 (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +262 -329
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  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 +4 -4
  14. data/lib/action_view/helpers/active_model_helper.rb +1 -1
  15. data/lib/action_view/helpers/asset_tag_helper.rb +130 -46
  16. data/lib/action_view/helpers/asset_url_helper.rb +6 -5
  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 +24 -10
  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 +2 -2
  24. data/lib/action_view/helpers/date_helper.rb +17 -19
  25. data/lib/action_view/helpers/debug_helper.rb +3 -3
  26. data/lib/action_view/helpers/form_helper.rb +43 -17
  27. data/lib/action_view/helpers/form_options_helper.rb +2 -1
  28. data/lib/action_view/helpers/form_tag_helper.rb +43 -9
  29. data/lib/action_view/helpers/javascript_helper.rb +1 -0
  30. data/lib/action_view/helpers/number_helper.rb +2 -1
  31. data/lib/action_view/helpers/output_safety_helper.rb +2 -2
  32. data/lib/action_view/helpers/rendering_helper.rb +1 -1
  33. data/lib/action_view/helpers/sanitize_helper.rb +33 -14
  34. data/lib/action_view/helpers/tag_helper.rb +5 -27
  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 +3 -0
  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 +32 -16
  53. data/lib/action_view/helpers/translation_helper.rb +3 -3
  54. data/lib/action_view/helpers/url_helper.rb +41 -14
  55. data/lib/action_view/helpers.rb +2 -0
  56. data/lib/action_view/layouts.rb +6 -4
  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 +15 -8
  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 +10 -2
  66. data/lib/action_view/renderer/partial_renderer.rb +2 -1
  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 +22 -4
  71. data/lib/action_view/ripper_ast_parser.rb +6 -6
  72. data/lib/action_view/template/error.rb +14 -1
  73. data/lib/action_view/template/handlers/builder.rb +4 -4
  74. data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
  75. data/lib/action_view/template/handlers/erb.rb +73 -1
  76. data/lib/action_view/template/handlers.rb +1 -1
  77. data/lib/action_view/template/html.rb +1 -1
  78. data/lib/action_view/template/raw_file.rb +1 -1
  79. data/lib/action_view/template/renderable.rb +1 -1
  80. data/lib/action_view/template/resolver.rb +10 -2
  81. data/lib/action_view/template/text.rb +1 -1
  82. data/lib/action_view/template/types.rb +25 -34
  83. data/lib/action_view/template.rb +227 -53
  84. data/lib/action_view/template_path.rb +2 -0
  85. data/lib/action_view/test_case.rb +174 -21
  86. data/lib/action_view/unbound_template.rb +15 -5
  87. data/lib/action_view/version.rb +1 -1
  88. data/lib/action_view/view_paths.rb +15 -24
  89. data/lib/action_view.rb +4 -1
  90. metadata +24 -24
@@ -5,7 +5,6 @@ module ActionView
5
5
  module Tags # :nodoc:
6
6
  class Base # :nodoc:
7
7
  include Helpers::ActiveModelInstanceTag, Helpers::TagHelper, Helpers::FormTagHelper
8
- include FormOptionsHelper
9
8
 
10
9
  attr_reader :object
11
10
 
@@ -35,22 +34,24 @@ module ActionView
35
34
 
36
35
  private
37
36
  def value
37
+ return unless object
38
+
38
39
  if @allow_method_names_outside_object
39
- object.public_send @method_name if object && object.respond_to?(@method_name)
40
+ object.public_send @method_name if object.respond_to?(@method_name)
40
41
  else
41
- object.public_send @method_name if object
42
+ object.public_send @method_name
42
43
  end
43
44
  end
44
45
 
45
46
  def value_before_type_cast
46
- unless object.nil?
47
- method_before_type_cast = @method_name + "_before_type_cast"
47
+ return unless object
48
48
 
49
- if value_came_from_user? && object.respond_to?(method_before_type_cast)
50
- object.public_send(method_before_type_cast)
51
- else
52
- value
53
- end
49
+ method_before_type_cast = @method_name + "_before_type_cast"
50
+
51
+ if value_came_from_user? && object.respond_to?(method_before_type_cast)
52
+ object.public_send(method_before_type_cast)
53
+ else
54
+ value
54
55
  end
55
56
  end
56
57
 
@@ -120,48 +121,6 @@ module ActionView
120
121
  value.to_s.gsub(/[\s.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
121
122
  end
122
123
 
123
- def select_content_tag(option_tags, options, html_options)
124
- html_options = html_options.stringify_keys
125
- add_default_name_and_id(html_options)
126
-
127
- if placeholder_required?(html_options)
128
- raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
129
- options[:include_blank] ||= true unless options[:prompt]
130
- end
131
-
132
- value = options.fetch(:selected) { value() }
133
- select = content_tag("select", add_options(option_tags, options, value), html_options)
134
-
135
- if html_options["multiple"] && options.fetch(:include_hidden, true)
136
- tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
137
- else
138
- select
139
- end
140
- end
141
-
142
- def placeholder_required?(html_options)
143
- # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
144
- html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
145
- end
146
-
147
- def add_options(option_tags, options, value = nil)
148
- if options[:include_blank]
149
- content = (options[:include_blank] if options[:include_blank].is_a?(String))
150
- label = (" " unless content)
151
- option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
152
- end
153
-
154
- if value.blank? && options[:prompt]
155
- tag_options = { value: "" }.tap do |prompt_opts|
156
- prompt_opts[:disabled] = true if options[:disabled] == ""
157
- prompt_opts[:selected] = true if options[:selected] == ""
158
- end
159
- option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
160
- end
161
-
162
- option_tags
163
- end
164
-
165
124
  def name_and_id_index(options)
166
125
  if options.key?("index")
167
126
  options.delete("index") || ""
@@ -7,6 +7,7 @@ module ActionView
7
7
  module Tags # :nodoc:
8
8
  class CollectionCheckBoxes < Base # :nodoc:
9
9
  include CollectionHelpers
10
+ include FormOptionsHelper
10
11
 
11
12
  class CheckBoxBuilder < Builder # :nodoc:
12
13
  def check_box(extra_html_options = {})
@@ -7,6 +7,7 @@ module ActionView
7
7
  module Tags # :nodoc:
8
8
  class CollectionRadioButtons < Base # :nodoc:
9
9
  include CollectionHelpers
10
+ include FormOptionsHelper
10
11
 
11
12
  class RadioButtonBuilder < Builder # :nodoc:
12
13
  def radio_button(extra_html_options = {})
@@ -4,6 +4,9 @@ module ActionView
4
4
  module Helpers
5
5
  module Tags # :nodoc:
6
6
  class CollectionSelect < Base # :nodoc:
7
+ include SelectRenderer
8
+ include FormOptionsHelper
9
+
7
10
  def initialize(object_name, method_name, template_object, collection, value_method, text_method, options, html_options)
8
11
  @collection = collection
9
12
  @value_method = value_method
@@ -5,7 +5,7 @@ module ActionView
5
5
  module Tags # :nodoc:
6
6
  class DateField < DatetimeField # :nodoc:
7
7
  private
8
- def format_date(value)
8
+ def format_datetime(value)
9
9
  value&.strftime("%Y-%m-%d")
10
10
  end
11
11
  end
@@ -6,6 +6,8 @@ module ActionView
6
6
  module Helpers
7
7
  module Tags # :nodoc:
8
8
  class DateSelect < Base # :nodoc:
9
+ include SelectRenderer
10
+
9
11
  def initialize(object_name, method_name, template_object, options, html_options)
10
12
  @html_options = html_options
11
13
 
@@ -6,20 +6,28 @@ module ActionView
6
6
  class DatetimeField < TextField # :nodoc:
7
7
  def render
8
8
  options = @options.stringify_keys
9
- options["value"] ||= format_date(value)
10
- options["min"] = format_date(datetime_value(options["min"]))
11
- options["max"] = format_date(datetime_value(options["max"]))
9
+ options["value"] = datetime_value(options["value"] || value)
10
+ options["min"] = format_datetime(parse_datetime(options["min"]))
11
+ options["max"] = format_datetime(parse_datetime(options["max"]))
12
12
  @options = options
13
13
  super
14
14
  end
15
15
 
16
16
  private
17
- def format_date(value)
17
+ def datetime_value(value)
18
+ if value.is_a?(String)
19
+ value
20
+ else
21
+ format_datetime(value)
22
+ end
23
+ end
24
+
25
+ def format_datetime(value)
18
26
  raise NotImplementedError
19
27
  end
20
28
 
21
- def datetime_value(value)
22
- if value.is_a? String
29
+ def parse_datetime(value)
30
+ if value.is_a?(String)
23
31
  DateTime.parse(value) rescue nil
24
32
  else
25
33
  value
@@ -4,6 +4,11 @@ module ActionView
4
4
  module Helpers
5
5
  module Tags # :nodoc:
6
6
  class DatetimeLocalField < DatetimeField # :nodoc:
7
+ def initialize(object_name, method_name, template_object, options = {})
8
+ @include_seconds = options.delete(:include_seconds) { true }
9
+ super
10
+ end
11
+
7
12
  class << self
8
13
  def field_type
9
14
  @field_type ||= "datetime-local"
@@ -11,8 +16,12 @@ module ActionView
11
16
  end
12
17
 
13
18
  private
14
- def format_date(value)
15
- value&.strftime("%Y-%m-%dT%T")
19
+ def format_datetime(value)
20
+ if @include_seconds
21
+ value&.strftime("%Y-%m-%dT%T")
22
+ else
23
+ value&.strftime("%Y-%m-%dT%H:%M")
24
+ end
16
25
  end
17
26
  end
18
27
  end
@@ -4,6 +4,9 @@ module ActionView
4
4
  module Helpers
5
5
  module Tags # :nodoc:
6
6
  class GroupedCollectionSelect < Base # :nodoc:
7
+ include SelectRenderer
8
+ include FormOptionsHelper
9
+
7
10
  def initialize(object_name, method_name, template_object, collection, group_method, group_label_method, option_key_method, option_value_method, options, html_options)
8
11
  @collection = collection
9
12
  @group_method = group_method
@@ -5,7 +5,7 @@ module ActionView
5
5
  module Tags # :nodoc:
6
6
  class MonthField < DatetimeField # :nodoc:
7
7
  private
8
- def format_date(value)
8
+ def format_datetime(value)
9
9
  value&.strftime("%Y-%m")
10
10
  end
11
11
  end
@@ -4,6 +4,9 @@ module ActionView
4
4
  module Helpers
5
5
  module Tags # :nodoc:
6
6
  class Select < Base # :nodoc:
7
+ include SelectRenderer
8
+ include FormOptionsHelper
9
+
7
10
  def initialize(object_name, method_name, template_object, choices, options, html_options)
8
11
  @choices = block_given? ? template_object.capture { yield || "" } : choices
9
12
  @choices = @choices.to_a if @choices.is_a?(Range)
@@ -0,0 +1,56 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers
5
+ module Tags # :nodoc:
6
+ module SelectRenderer # :nodoc:
7
+ private
8
+ def select_content_tag(option_tags, options, html_options)
9
+ html_options = html_options.stringify_keys
10
+ [:required, :multiple, :size].each do |prop|
11
+ html_options[prop.to_s] = options.delete(prop) if options.key?(prop) && !html_options.key?(prop.to_s)
12
+ end
13
+
14
+ add_default_name_and_id(html_options)
15
+
16
+ if placeholder_required?(html_options)
17
+ raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
18
+ options[:include_blank] ||= true unless options[:prompt]
19
+ end
20
+
21
+ value = options.fetch(:selected) { value() }
22
+ select = content_tag("select", add_options(option_tags, options, value), html_options)
23
+
24
+ if html_options["multiple"] && options.fetch(:include_hidden, true)
25
+ tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
26
+ else
27
+ select
28
+ end
29
+ end
30
+
31
+ def placeholder_required?(html_options)
32
+ # See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
33
+ html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
34
+ end
35
+
36
+ def add_options(option_tags, options, value = nil)
37
+ if options[:include_blank]
38
+ content = (options[:include_blank] if options[:include_blank].is_a?(String))
39
+ label = (" " unless content)
40
+ option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
41
+ end
42
+
43
+ if value.blank? && options[:prompt]
44
+ tag_options = { value: "" }.tap do |prompt_opts|
45
+ prompt_opts[:disabled] = true if options[:disabled] == ""
46
+ prompt_opts[:selected] = true if options[:selected] == ""
47
+ end
48
+ option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
49
+ end
50
+
51
+ option_tags
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -10,7 +10,7 @@ module ActionView
10
10
  end
11
11
 
12
12
  private
13
- def format_date(value)
13
+ def format_datetime(value)
14
14
  if @include_seconds
15
15
  value&.strftime("%T.%L")
16
16
  else
@@ -4,6 +4,9 @@ module ActionView
4
4
  module Helpers
5
5
  module Tags # :nodoc:
6
6
  class TimeZoneSelect < Base # :nodoc:
7
+ include SelectRenderer
8
+ include FormOptionsHelper
9
+
7
10
  def initialize(object_name, method_name, template_object, priority_zones, options, html_options)
8
11
  @priority_zones = priority_zones
9
12
  @html_options = html_options
@@ -5,7 +5,7 @@ module ActionView
5
5
  module Tags # :nodoc:
6
6
  class WeekField < DatetimeField # :nodoc:
7
7
  private
8
- def format_date(value)
8
+ def format_datetime(value)
9
9
  value&.strftime("%Y-W%V")
10
10
  end
11
11
  end
@@ -4,6 +4,9 @@ module ActionView
4
4
  module Helpers
5
5
  module Tags # :nodoc:
6
6
  class WeekdaySelect < Base # :nodoc:
7
+ include SelectRenderer
8
+ include FormOptionsHelper
9
+
7
10
  def initialize(object_name, method_name, template_object, options, html_options)
8
11
  @html_options = html_options
9
12
 
@@ -5,6 +5,8 @@ module ActionView
5
5
  module Tags # :nodoc:
6
6
  extend ActiveSupport::Autoload
7
7
 
8
+ autoload :SelectRenderer
9
+
8
10
  eager_autoload do
9
11
  autoload :Base
10
12
  autoload :Translator
@@ -8,8 +8,9 @@ require "action_view/helpers/tag_helper"
8
8
  require "action_view/helpers/output_safety_helper"
9
9
 
10
10
  module ActionView
11
- # = Action View Text Helpers
12
11
  module Helpers # :nodoc:
12
+ # = Action View Text \Helpers
13
+ #
13
14
  # The TextHelper module provides a set of methods for filtering, formatting
14
15
  # and transforming strings, which can reduce the amount of inline Ruby code in
15
16
  # your views. These helper methods extend Action View making them callable
@@ -139,16 +140,19 @@ module ActionView
139
140
  if text.blank? || phrases.blank?
140
141
  text || ""
141
142
  else
142
- match = Array(phrases).map do |p|
143
- Regexp === p ? p.to_s : Regexp.escape(p)
144
- end.join("|")
145
-
146
- if block_given?
147
- text.gsub(/(#{match})(?![^<]*?>)/i, &block)
148
- else
149
- highlighter = options.fetch(:highlighter, '<mark>\1</mark>')
150
- text.gsub(/(#{match})(?![^<]*?>)/i, highlighter)
151
- end
143
+ patterns = Array(phrases).map { |phrase| Regexp === phrase ? phrase : Regexp.escape(phrase) }
144
+ pattern = /(#{patterns.join("|")})/i
145
+ highlighter = options.fetch(:highlighter, '<mark>\1</mark>') unless block
146
+
147
+ text.scan(/<[^>]*|[^<]+/).each do |segment|
148
+ if !segment.start_with?("<")
149
+ if block
150
+ segment.gsub!(pattern, &block)
151
+ else
152
+ segment.gsub!(pattern, highlighter)
153
+ end
154
+ end
155
+ end.join
152
156
  end.html_safe
153
157
  end
154
158
 
@@ -262,9 +266,17 @@ module ActionView
262
266
  # word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
263
267
  # # => Once\r\nupon\r\na\r\ntime
264
268
  def word_wrap(text, line_width: 80, break_sequence: "\n")
265
- text.split("\n").collect! do |line|
266
- line.length > line_width ? line.gsub(/(.{1,#{line_width}})(\s+|$)/, "\\1#{break_sequence}").rstrip : line
267
- end * break_sequence
269
+ # Match up to `line_width` characters, followed by one of
270
+ # (1) non-newline whitespace plus an optional newline
271
+ # (2) the end of the string, ignoring any trailing newlines
272
+ # (3) a newline
273
+ #
274
+ # -OR-
275
+ #
276
+ # Match an empty line
277
+ pattern = /(.{1,#{line_width}})(?:[^\S\n]+\n?|\n*\Z|\n)|\n/
278
+
279
+ text.gsub(pattern, "\\1#{break_sequence}").chomp!(break_sequence)
268
280
  end
269
281
 
270
282
  # Returns +text+ transformed into HTML using simple formatting rules.
@@ -279,6 +291,7 @@ module ActionView
279
291
  #
280
292
  # ==== Options
281
293
  # * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
294
+ # * <tt>:sanitize_options</tt> - Any extra options you want appended to the sanitize.
282
295
  # * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
283
296
  #
284
297
  # ==== Examples
@@ -303,10 +316,13 @@ module ActionView
303
316
  #
304
317
  # simple_format("<blink>Blinkable!</blink> It's true.", {}, sanitize: false)
305
318
  # # => "<p><blink>Blinkable!</blink> It's true.</p>"
319
+ #
320
+ # simple_format("<a target=\"_blank\" href=\"http://example.com\">Continue</a>", {}, { sanitize_options: { attributes: %w[target href] } })
321
+ # # => "<p><a target=\"_blank\" href=\"http://example.com\">Continue</a></p>"
306
322
  def simple_format(text, html_options = {}, options = {})
307
- wrapper_tag = options.fetch(:wrapper_tag, :p)
323
+ wrapper_tag = options[:wrapper_tag] || "p"
308
324
 
309
- text = sanitize(text) if options.fetch(:sanitize, true)
325
+ text = sanitize(text, options.fetch(:sanitize_options, {})) if options.fetch(:sanitize, true)
310
326
  paragraphs = split_paragraphs(text)
311
327
 
312
328
  if paragraphs.empty?
@@ -4,14 +4,14 @@ require "action_view/helpers/tag_helper"
4
4
  require "active_support/html_safe_translation"
5
5
 
6
6
  module ActionView
7
- # = Action View Translation Helpers
8
7
  module Helpers # :nodoc:
8
+ # = Action View Translation \Helpers
9
9
  module TranslationHelper
10
10
  extend ActiveSupport::Concern
11
11
 
12
12
  include TagHelper
13
13
 
14
- # Specify whether an error should be raised for missing translations
14
+ # Specify whether an error should be raised for missing translations.
15
15
  singleton_class.attr_accessor :raise_on_missing_translations
16
16
 
17
17
  included do
@@ -93,7 +93,7 @@ module ActionView
93
93
  break translated unless translated == MISSING_TRANSLATION
94
94
 
95
95
  if alternatives.present? && !alternatives.first.is_a?(Symbol)
96
- break alternatives.first && I18n.translate(**options, default: alternatives)
96
+ break alternatives.first && I18n.translate(nil, **options, default: alternatives)
97
97
  end
98
98
 
99
99
  first_key ||= key
@@ -3,11 +3,13 @@
3
3
  require "active_support/core_ext/array/access"
4
4
  require "active_support/core_ext/hash/keys"
5
5
  require "active_support/core_ext/string/output_safety"
6
+ require "action_view/helpers/content_exfiltration_prevention_helper"
6
7
  require "action_view/helpers/tag_helper"
7
8
 
8
9
  module ActionView
9
- # = Action View URL Helpers
10
10
  module Helpers # :nodoc:
11
+ # = Action View URL \Helpers
12
+ #
11
13
  # Provides a set of methods for making links and getting URLs that
12
14
  # depend on the routing subsystem (see ActionDispatch::Routing).
13
15
  # This allows you to use the same format for links in views
@@ -22,6 +24,7 @@ module ActionView
22
24
  extend ActiveSupport::Concern
23
25
 
24
26
  include TagHelper
27
+ include ContentExfiltrationPreventionHelper
25
28
 
26
29
  module ClassMethods
27
30
  def _url_for_modules
@@ -93,7 +96,7 @@ module ActionView
93
96
  # ==== Examples
94
97
  #
95
98
  # Because it relies on +url_for+, +link_to+ supports both older-style controller/action/id arguments
96
- # and newer RESTful routes. Current Rails style favors RESTful routes whenever possible, so base
99
+ # and newer RESTful routes. Current \Rails style favors RESTful routes whenever possible, so base
97
100
  # your application on resources and use
98
101
  #
99
102
  # link_to "Profile", profile_path(@profile)
@@ -170,9 +173,31 @@ module ActionView
170
173
  # link_to "External link", "http://www.rubyonrails.org/", target: "_blank", rel: "nofollow"
171
174
  # # => <a href="http://www.rubyonrails.org/" target="_blank" rel="nofollow">External link</a>
172
175
  #
173
- # ==== Deprecated: Rails UJS Attributes
176
+ # ==== Turbo
174
177
  #
175
- # Prior to Rails 7, Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following Rails 7,
178
+ # Rails 7 ships with Turbo enabled by default. Turbo provides the following +:data+ options:
179
+ #
180
+ # * <tt>turbo_method: symbol of HTTP verb</tt> - Performs a Turbo link visit
181
+ # with the given HTTP verb. Forms are recommended when performing non-+GET+ requests.
182
+ # Only use <tt>data-turbo-method</tt> where a form is not possible.
183
+ #
184
+ # * <tt>turbo_confirm: "question?"</tt> - Adds a confirmation dialog to the link with the
185
+ # given value.
186
+ #
187
+ # {Consult the Turbo Handbook for more information on the options
188
+ # above.}[https://turbo.hotwired.dev/handbook/drive#performing-visits-with-a-different-method]
189
+ #
190
+ # ===== \Examples
191
+ #
192
+ # link_to "Delete profile", @profile, data: { turbo_method: :delete }
193
+ # # => <a href="/profiles/1" data-turbo-method="delete">Delete profile</a>
194
+ #
195
+ # link_to "Visit Other Site", "https://rubyonrails.org/", data: { turbo_confirm: "Are you sure?" }
196
+ # # => <a href="https://rubyonrails.org/" data-turbo-confirm="Are you sure?">Visit Other Site</a>
197
+ #
198
+ # ==== Deprecated: \Rails UJS Attributes
199
+ #
200
+ # Prior to \Rails 7, \Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following \Rails 7,
176
201
  # this library is no longer on by default. This library integrated with the following options:
177
202
  #
178
203
  # * <tt>method: symbol of HTTP verb</tt> - This modifier will dynamically
@@ -198,7 +223,7 @@ module ActionView
198
223
  # * <tt>:disable_with</tt> - Value of this parameter will be used as the
199
224
  # name for a disabled version of the link.
200
225
  #
201
- # ===== Rails UJS Examples
226
+ # ===== \Rails UJS Examples
202
227
  #
203
228
  # link_to "Remove Profile", profile_path(@profile), method: :delete
204
229
  # # => <a href="/profiles/1" rel="nofollow" data-method="delete">Remove Profile</a>
@@ -221,9 +246,6 @@ module ActionView
221
246
  # Generates a form containing a single button that submits to the URL created
222
247
  # by the set of +options+. This is the safest method to ensure links that
223
248
  # cause changes to your data are not triggered by search bots or accelerators.
224
- # If the HTML button does not work with your layout, you can also consider
225
- # using the +link_to+ method with the <tt>:method</tt> modifier as described in
226
- # the +link_to+ documentation.
227
249
  #
228
250
  # You can control the form and button behavior with +html_options+. Most
229
251
  # values in +html_options+ are passed through to the button element. For
@@ -237,6 +259,10 @@ module ActionView
237
259
  # The form submits a POST request by default. You can specify a different
238
260
  # HTTP verb via the +:method+ option within +html_options+.
239
261
  #
262
+ # If the HTML button generated from +button_to+ does not work with your layout, you can
263
+ # consider using the +link_to+ method with the +data-turbo-method+
264
+ # attribute as described in the +link_to+ documentation.
265
+ #
240
266
  # ==== Options
241
267
  # The +options+ hash accepts the same options as +url_for+. To generate a
242
268
  # <tt><form></tt> element without an <tt>[action]</tt> attribute, pass
@@ -302,9 +328,9 @@ module ActionView
302
328
  # # <input name="authenticity_token" type="hidden" value="10f2163b45388899ad4d5ae948988266befcb6c3d1b2451cf657a0c293d605a6" autocomplete="off"/>
303
329
  # # </form>"
304
330
  #
305
- # ==== Deprecated: Rails UJS Attributes
331
+ # ==== Deprecated: \Rails UJS Attributes
306
332
  #
307
- # Prior to Rails 7, Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following Rails 7,
333
+ # Prior to \Rails 7, \Rails shipped with a JavaScript library called <tt>@rails/ujs</tt> on by default. Following \Rails 7,
308
334
  # this library is no longer on by default. This library integrated with the following options:
309
335
  #
310
336
  # * <tt>:remote</tt> - If set to true, will allow <tt>@rails/ujs</tt> to control the
@@ -320,7 +346,7 @@ module ActionView
320
346
  # used as the value for a disabled version of the submit
321
347
  # button when the form is submitted.
322
348
  #
323
- # ===== Rails UJS Examples
349
+ # ===== \Rails UJS Examples
324
350
  #
325
351
  # <%= button_to "Create", { action: "create" }, remote: true, form: { "data-type" => "json" } %>
326
352
  # # => "<form method="post" action="/images/create" class="button_to" data-remote="true" data-type="json">
@@ -380,7 +406,8 @@ module ActionView
380
406
  autocomplete: "off")
381
407
  end
382
408
  end
383
- content_tag("form", inner_tags, form_options)
409
+ html = content_tag("form", inner_tags, form_options)
410
+ prevent_content_exfiltration(html)
384
411
  end
385
412
 
386
413
  # Creates a link tag of the given +name+ using a URL created by the set of
@@ -495,7 +522,7 @@ module ActionView
495
522
  # * <tt>:reply_to</tt> - Preset the +Reply-To+ field of the email.
496
523
  #
497
524
  # ==== Obfuscation
498
- # Prior to Rails 4.0, +mail_to+ provided options for encoding the address
525
+ # Prior to \Rails 4.0, +mail_to+ provided options for encoding the address
499
526
  # in order to hinder email harvesters. To take advantage of these options,
500
527
  # install the +actionview-encoded_mail_to+ gem.
501
528
  #
@@ -595,7 +622,7 @@ module ActionView
595
622
  # We ignore any extra parameters in the request_uri if the
596
623
  # submitted URL doesn't have any either. This lets the function
597
624
  # work with things like ?order=asc
598
- # the behaviour can be disabled with check_parameters: true
625
+ # the behavior can be disabled with check_parameters: true
599
626
  request_uri = url_string.index("?") || check_parameters ? request.fullpath : request.path
600
627
  request_uri = URI::DEFAULT_PARSER.unescape(request_uri).force_encoding(Encoding::BINARY)
601
628
 
@@ -12,6 +12,7 @@ require "action_view/helpers/asset_tag_helper"
12
12
  require "action_view/helpers/asset_url_helper"
13
13
  require "action_view/helpers/atom_feed_helper"
14
14
  require "action_view/helpers/cache_helper"
15
+ require "action_view/helpers/content_exfiltration_prevention_helper"
15
16
  require "action_view/helpers/controller_helper"
16
17
  require "action_view/helpers/csp_helper"
17
18
  require "action_view/helpers/csrf_helper"
@@ -45,6 +46,7 @@ module ActionView # :nodoc:
45
46
  include AtomFeedHelper
46
47
  include CacheHelper
47
48
  include CaptureHelper
49
+ include ContentExfiltrationPreventionHelper
48
50
  include ControllerHelper
49
51
  include CspHelper
50
52
  include CsrfHelper
@@ -4,12 +4,14 @@ require "action_view/rendering"
4
4
  require "active_support/core_ext/module/redefine_method"
5
5
 
6
6
  module ActionView
7
+ # = Action View \Layouts
8
+ #
7
9
  # Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
8
10
  # repeated setups. The inclusion pattern has pages that look like this:
9
11
  #
10
- # <%= render "shared/header" %>
12
+ # <%= render "application/header" %>
11
13
  # Hello World
12
- # <%= render "shared/footer" %>
14
+ # <%= render "application/footer" %>
13
15
  #
14
16
  # This approach is a decent way of keeping common structures isolated from the changing content, but it's verbose
15
17
  # and if you ever want to change the structure of these two includes, you'll have to change all the templates.
@@ -210,9 +212,9 @@ module ActionView
210
212
  class_attribute :_layout_conditions, instance_accessor: false, default: {}
211
213
 
212
214
  _write_layout_method
213
- end
214
215
 
215
- delegate :_layout_conditions, to: :class
216
+ delegate :_layout_conditions, to: :class
217
+ end
216
218
 
217
219
  module ClassMethods
218
220
  def inherited(klass) # :nodoc: