actionview 7.0.8 → 7.1.3.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +295 -317
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +1 -1
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -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 +134 -50
  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 +30 -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 +54 -25
  27. data/lib/action_view/helpers/form_options_helper.rb +2 -1
  28. data/lib/action_view/helpers/form_tag_helper.rb +49 -15
  29. data/lib/action_view/helpers/javascript_helper.rb +1 -0
  30. data/lib/action_view/helpers/number_helper.rb +37 -330
  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 +51 -21
  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 +156 -84
  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 +8 -6
  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 +10 -2
  65. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +2 -1
  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 +242 -54
  84. data/lib/action_view/template_path.rb +2 -0
  85. data/lib/action_view/test_case.rb +176 -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 -25
  91. data/lib/assets/compiled/rails-ujs.js +0 -777
@@ -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
@@ -40,21 +41,25 @@ module ActionView
40
41
  include OutputSafetyHelper
41
42
 
42
43
  # The preferred method of outputting text in your views is to use the
43
- # <%= "text" %> eRuby syntax. The regular _puts_ and _print_ methods
44
+ # <tt><%= "text" %></tt> eRuby syntax. The regular +puts+ and +print+ methods
44
45
  # do not operate as expected in an eRuby code block. If you absolutely must
45
- # output text within a non-output code block (i.e., <% %>), you can use the concat method.
46
+ # output text within a non-output code block (i.e., <tt><% %></tt>), you
47
+ # can use the +concat+ method.
48
+ #
49
+ # <% concat "hello" %> is equivalent to <%= "hello" %>
46
50
  #
47
51
  # <%
48
- # concat "hello"
49
- # # is the equivalent of <%= "hello" %>
50
- #
51
- # if logged_in
52
- # concat "Logged in!"
53
- # else
54
- # concat link_to('login', action: :login)
55
- # end
56
- # # will either display "Logged in!" or a login link
52
+ # unless signed_in?
53
+ # concat link_to("Sign In", action: :sign_in)
54
+ # end
57
55
  # %>
56
+ #
57
+ # is equivalent to
58
+ #
59
+ # <% unless signed_in? %>
60
+ # <%= link_to "Sign In", action: :sign_in %>
61
+ # <% end %>
62
+ #
58
63
  def concat(string)
59
64
  output_buffer << string
60
65
  end
@@ -63,17 +68,36 @@ module ActionView
63
68
  output_buffer.respond_to?(:safe_concat) ? output_buffer.safe_concat(string) : concat(string)
64
69
  end
65
70
 
66
- # Truncates a given +text+ after a given <tt>:length</tt> if +text+ is longer than <tt>:length</tt>
67
- # (defaults to 30). The last characters will be replaced with the <tt>:omission</tt> (defaults to "...")
68
- # for a total length not exceeding <tt>:length</tt>.
71
+ # Truncates +text+ if it is longer than a specified +:length+. If +text+
72
+ # is truncated, an omission marker will be appended to the result for a
73
+ # total length not exceeding +:length+.
74
+ #
75
+ # You can also pass a block to render and append extra content after the
76
+ # omission marker when +text+ is truncated. However, this content _can_
77
+ # cause the total length to exceed +:length+ characters.
78
+ #
79
+ # The result will be escaped unless <tt>escape: false</tt> is specified.
80
+ # In any case, the result will be marked HTML-safe. Care should be taken
81
+ # if +text+ might contain HTML tags or entities, because truncation could
82
+ # produce invalid HTML, such as unbalanced or incomplete tags.
69
83
  #
70
- # Pass a <tt>:separator</tt> to truncate +text+ at a natural break.
84
+ # ==== Options
85
+ #
86
+ # [+:length+]
87
+ # The maximum number of characters that should be returned, excluding
88
+ # any extra content from the block. Defaults to 30.
89
+ #
90
+ # [+:omission+]
91
+ # The string to append after truncating. Defaults to <tt>"..."</tt>.
92
+ #
93
+ # [+:separator+]
94
+ # A string or regexp used to find a breaking point at which to truncate.
95
+ # By default, truncation can occur at any character in +text+.
71
96
  #
72
- # Pass a block if you want to show extra content when the text is truncated.
97
+ # [+:escape+]
98
+ # Whether to escape the result. Defaults to true.
73
99
  #
74
- # The result is marked as HTML-safe, but it is escaped by default, unless <tt>:escape</tt> is
75
- # +false+. Care should be taken if +text+ contains HTML tags or entities, because truncation
76
- # may produce invalid HTML (such as unbalanced or incomplete tags).
100
+ # ==== Examples
77
101
  #
78
102
  # truncate("Once upon a time in a world far far away")
79
103
  # # => "Once upon a time in a world..."
@@ -94,7 +118,7 @@ module ActionView
94
118
  # # => "<p>Once upon a time in a wo..."
95
119
  #
96
120
  # truncate("Once upon a time in a world far far away") { link_to "Continue", "#" }
97
- # # => "Once upon a time in a wo...<a href="#">Continue</a>"
121
+ # # => "Once upon a time in a world...<a href=\"#\">Continue</a>"
98
122
  def truncate(text, options = {}, &block)
99
123
  if text
100
124
  length = options.fetch(:length, 30)
@@ -106,76 +130,108 @@ module ActionView
106
130
  end
107
131
  end
108
132
 
109
- # Highlights one or more +phrases+ everywhere in +text+ by inserting it into
110
- # a <tt>:highlighter</tt> string. The highlighter can be specialized by passing <tt>:highlighter</tt>
111
- # as a single-quoted string with <tt>\1</tt> where the phrase is to be inserted (defaults to
112
- # <tt><mark>\1</mark></tt>) or passing a block that receives each matched term. By default +text+
113
- # is sanitized to prevent possible XSS attacks. If the input is trustworthy, passing false
114
- # for <tt>:sanitize</tt> will turn sanitizing off.
133
+ # Highlights occurrences of +phrases+ in +text+ by formatting them with a
134
+ # highlighter string. +phrases+ can be one or more strings or regular
135
+ # expressions. The result will be marked HTML safe. By default, +text+ is
136
+ # sanitized before highlighting to prevent possible XSS attacks.
137
+ #
138
+ # If a block is specified, it will be used instead of the highlighter
139
+ # string. Each occurrence of a phrase will be passed to the block, and its
140
+ # return value will be inserted into the final result.
141
+ #
142
+ # ==== Options
143
+ #
144
+ # [+:highlighter+]
145
+ # The highlighter string. Uses <tt>\1</tt> as the placeholder for a
146
+ # phrase, similar to +String#sub+. Defaults to <tt>"<mark>\1</mark>"</tt>.
147
+ # This option is ignored if a block is specified.
148
+ #
149
+ # [+:sanitize+]
150
+ # Whether to sanitize +text+ before highlighting. Defaults to true.
151
+ #
152
+ # ==== Examples
115
153
  #
116
154
  # highlight('You searched for: rails', 'rails')
117
- # # => You searched for: <mark>rails</mark>
155
+ # # => "You searched for: <mark>rails</mark>"
118
156
  #
119
157
  # highlight('You searched for: rails', /for|rails/)
120
- # # => You searched <mark>for</mark>: <mark>rails</mark>
158
+ # # => "You searched <mark>for</mark>: <mark>rails</mark>"
121
159
  #
122
160
  # highlight('You searched for: ruby, rails, dhh', 'actionpack')
123
- # # => You searched for: ruby, rails, dhh
161
+ # # => "You searched for: ruby, rails, dhh"
124
162
  #
125
163
  # highlight('You searched for: rails', ['for', 'rails'], highlighter: '<em>\1</em>')
126
- # # => You searched <em>for</em>: <em>rails</em>
164
+ # # => "You searched <em>for</em>: <em>rails</em>"
127
165
  #
128
166
  # highlight('You searched for: rails', 'rails', highlighter: '<a href="search?q=\1">\1</a>')
129
- # # => You searched for: <a href="search?q=rails">rails</a>
167
+ # # => "You searched for: <a href=\"search?q=rails\">rails</a>"
130
168
  #
131
169
  # highlight('You searched for: rails', 'rails') { |match| link_to(search_path(q: match, match)) }
132
- # # => You searched for: <a href="search?q=rails">rails</a>
170
+ # # => "You searched for: <a href=\"search?q=rails\">rails</a>"
133
171
  #
134
172
  # highlight('<a href="javascript:alert(\'no!\')">ruby</a> on rails', 'rails', sanitize: false)
135
- # # => <a href="javascript:alert('no!')">ruby</a> on <mark>rails</mark>
173
+ # # => "<a href=\"javascript:alert('no!')\">ruby</a> on <mark>rails</mark>"
136
174
  def highlight(text, phrases, options = {}, &block)
137
175
  text = sanitize(text) if options.fetch(:sanitize, true)
138
176
 
139
177
  if text.blank? || phrases.blank?
140
178
  text || ""
141
179
  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
180
+ patterns = Array(phrases).map { |phrase| Regexp === phrase ? phrase : Regexp.escape(phrase) }
181
+ pattern = /(#{patterns.join("|")})/i
182
+ highlighter = options.fetch(:highlighter, '<mark>\1</mark>') unless block
183
+
184
+ text.scan(/<[^>]*|[^<]+/).each do |segment|
185
+ if !segment.start_with?("<")
186
+ if block
187
+ segment.gsub!(pattern, &block)
188
+ else
189
+ segment.gsub!(pattern, highlighter)
190
+ end
191
+ end
192
+ end.join
152
193
  end.html_safe
153
194
  end
154
195
 
155
- # Extracts an excerpt from +text+ that matches the first instance of +phrase+.
156
- # The <tt>:radius</tt> option expands the excerpt on each side of the first occurrence of +phrase+ by the number of characters
157
- # defined in <tt>:radius</tt> (which defaults to 100). If the excerpt radius overflows the beginning or end of the +text+,
158
- # then the <tt>:omission</tt> option (which defaults to "...") will be prepended/appended accordingly. Use the
159
- # <tt>:separator</tt> option to choose the delimitation. The resulting string will be stripped in any case. If the +phrase+
160
- # isn't found, +nil+ is returned.
196
+ # Extracts the first occurrence of +phrase+ plus surrounding text from
197
+ # +text+. An omission marker is prepended / appended if the start / end of
198
+ # the result does not coincide with the start / end of +text+. The result
199
+ # is always stripped in any case. Returns +nil+ if +phrase+ isn't found.
200
+ #
201
+ # ==== Options
202
+ #
203
+ # [+:radius+]
204
+ # The number of characters (or tokens — see +:separator+ option) around
205
+ # +phrase+ to include in the result. Defaults to 100.
206
+ #
207
+ # [+:omission+]
208
+ # The marker to prepend / append when the start / end of the excerpt
209
+ # does not coincide with the start / end of +text+. Defaults to
210
+ # <tt>"..."</tt>.
211
+ #
212
+ # [+:separator+]
213
+ # The separator between tokens to count for +:radius+. Defaults to
214
+ # <tt>""</tt>, which treats each character as a token.
215
+ #
216
+ # ==== Examples
161
217
  #
162
218
  # excerpt('This is an example', 'an', radius: 5)
163
- # # => ...s is an exam...
219
+ # # => "...s is an exam..."
164
220
  #
165
221
  # excerpt('This is an example', 'is', radius: 5)
166
- # # => This is a...
222
+ # # => "This is a..."
167
223
  #
168
224
  # excerpt('This is an example', 'is')
169
- # # => This is an example
225
+ # # => "This is an example"
170
226
  #
171
227
  # excerpt('This next thing is an example', 'ex', radius: 2)
172
- # # => ...next...
228
+ # # => "...next..."
173
229
  #
174
230
  # excerpt('This is also an example', 'an', radius: 8, omission: '<chop> ')
175
- # # => <chop> is also an example
231
+ # # => "<chop> is also an example"
176
232
  #
177
233
  # excerpt('This is a very beautiful morning', 'very', separator: ' ', radius: 1)
178
- # # => ...a very beautiful...
234
+ # # => "...a very beautiful..."
179
235
  def excerpt(text, phrase, options = {})
180
236
  return unless text && phrase
181
237
 
@@ -211,26 +267,26 @@ module ActionView
211
267
  # Attempts to pluralize the +singular+ word unless +count+ is 1. If
212
268
  # +plural+ is supplied, it will use that when count is > 1, otherwise
213
269
  # it will use the Inflector to determine the plural form for the given locale,
214
- # which defaults to I18n.locale
270
+ # which defaults to +I18n.locale+.
215
271
  #
216
272
  # The word will be pluralized using rules defined for the locale
217
273
  # (you must define your own inflection rules for languages other than English).
218
274
  # See ActiveSupport::Inflector.pluralize
219
275
  #
220
276
  # pluralize(1, 'person')
221
- # # => 1 person
277
+ # # => "1 person"
222
278
  #
223
279
  # pluralize(2, 'person')
224
- # # => 2 people
280
+ # # => "2 people"
225
281
  #
226
282
  # pluralize(3, 'person', plural: 'users')
227
- # # => 3 users
283
+ # # => "3 users"
228
284
  #
229
285
  # pluralize(0, 'person')
230
- # # => 0 people
286
+ # # => "0 people"
231
287
  #
232
288
  # pluralize(2, 'Person', locale: :de)
233
- # # => 2 Personen
289
+ # # => "2 Personen"
234
290
  def pluralize(count, singular, plural_arg = nil, plural: plural_arg, locale: I18n.locale)
235
291
  word = if count == 1 || count.to_s.match?(/^1(\.0+)?$/)
236
292
  singular
@@ -246,25 +302,35 @@ module ActionView
246
302
  # (which is 80 by default).
247
303
  #
248
304
  # word_wrap('Once upon a time')
249
- # # => Once upon a time
305
+ # # => "Once upon a time"
250
306
  #
251
307
  # word_wrap('Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding a successor to the throne turned out to be more trouble than anyone could have imagined...')
252
- # # => Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined...
308
+ # # => "Once upon a time, in a kingdom called Far Far Away, a king fell ill, and finding\na successor to the throne turned out to be more trouble than anyone could have\nimagined..."
253
309
  #
254
310
  # word_wrap('Once upon a time', line_width: 8)
255
- # # => Once\nupon a\ntime
311
+ # # => "Once\nupon a\ntime"
256
312
  #
257
313
  # word_wrap('Once upon a time', line_width: 1)
258
- # # => Once\nupon\na\ntime
314
+ # # => "Once\nupon\na\ntime"
259
315
  #
260
- # You can also specify a custom +break_sequence+ ("\n" by default)
316
+ # You can also specify a custom +break_sequence+ ("\n" by default):
261
317
  #
262
318
  # word_wrap('Once upon a time', line_width: 1, break_sequence: "\r\n")
263
- # # => Once\r\nupon\r\na\r\ntime
319
+ # # => "Once\r\nupon\r\na\r\ntime"
264
320
  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
321
+ return +"" if text.empty?
322
+
323
+ # Match up to `line_width` characters, followed by one of
324
+ # (1) non-newline whitespace plus an optional newline
325
+ # (2) the end of the string, ignoring any trailing newlines
326
+ # (3) a newline
327
+ #
328
+ # -OR-
329
+ #
330
+ # Match an empty line
331
+ pattern = /(.{1,#{line_width}})(?:[^\S\n]+\n?|\n*\Z|\n)|\n/
332
+
333
+ text.gsub(pattern, "\\1#{break_sequence}").chomp!(break_sequence)
268
334
  end
269
335
 
270
336
  # Returns +text+ transformed into HTML using simple formatting rules.
@@ -279,6 +345,7 @@ module ActionView
279
345
  #
280
346
  # ==== Options
281
347
  # * <tt>:sanitize</tt> - If +false+, does not sanitize +text+.
348
+ # * <tt>:sanitize_options</tt> - Any extra options you want appended to the sanitize.
282
349
  # * <tt>:wrapper_tag</tt> - String representing the wrapper tag, defaults to <tt>"p"</tt>
283
350
  #
284
351
  # ==== Examples
@@ -303,10 +370,13 @@ module ActionView
303
370
  #
304
371
  # simple_format("<blink>Blinkable!</blink> It's true.", {}, sanitize: false)
305
372
  # # => "<p><blink>Blinkable!</blink> It's true.</p>"
373
+ #
374
+ # simple_format("<a target=\"_blank\" href=\"http://example.com\">Continue</a>", {}, { sanitize_options: { attributes: %w[target href] } })
375
+ # # => "<p><a target=\"_blank\" href=\"http://example.com\">Continue</a></p>"
306
376
  def simple_format(text, html_options = {}, options = {})
307
- wrapper_tag = options.fetch(:wrapper_tag, :p)
377
+ wrapper_tag = options[:wrapper_tag] || "p"
308
378
 
309
- text = sanitize(text) if options.fetch(:sanitize, true)
379
+ text = sanitize(text, options.fetch(:sanitize_options, {})) if options.fetch(:sanitize, true)
310
380
  paragraphs = split_paragraphs(text)
311
381
 
312
382
  if paragraphs.empty?
@@ -318,7 +388,7 @@ module ActionView
318
388
  end
319
389
  end
320
390
 
321
- # Creates a Cycle object whose _to_s_ method cycles through elements of an
391
+ # Creates a Cycle object whose +to_s+ method cycles through elements of an
322
392
  # array every time it is called. This can be used for example, to alternate
323
393
  # classes for table rows. You can use named cycles to allow nesting in loops.
324
394
  # Passing a Hash as the last parameter with a <tt>:name</tt> key will create a
@@ -327,8 +397,8 @@ module ActionView
327
397
  # and passing the name of the cycle. The current cycle string can be obtained
328
398
  # anytime using the current_cycle method.
329
399
  #
330
- # # Alternate CSS classes for even and odd numbers...
331
- # @items = [1,2,3,4]
400
+ # <%# Alternate CSS classes for even and odd numbers... %>
401
+ # <% @items = [1,2,3,4] %>
332
402
  # <table>
333
403
  # <% @items.each do |item| %>
334
404
  # <tr class="<%= cycle("odd", "even") -%>">
@@ -338,10 +408,12 @@ module ActionView
338
408
  # </table>
339
409
  #
340
410
  #
341
- # # Cycle CSS classes for rows, and text colors for values within each row
342
- # @items = x = [{first: 'Robert', middle: 'Daniel', last: 'James'},
343
- # {first: 'Emily', middle: 'Shannon', maiden: 'Pike', last: 'Hicks'},
344
- # {first: 'June', middle: 'Dae', last: 'Jones'}]
411
+ # <%# Cycle CSS classes for rows, and text colors for values within each row %>
412
+ # <% @items = [
413
+ # { first: "Robert", middle: "Daniel", last: "James" },
414
+ # { first: "Emily", middle: "Shannon", maiden: "Pike", last: "Hicks" },
415
+ # { first: "June", middle: "Dae", last: "Jones" },
416
+ # ] %>
345
417
  # <% @items.each do |item| %>
346
418
  # <tr class="<%= cycle("odd", "even", name: "row_class") -%>">
347
419
  # <td>
@@ -372,8 +444,8 @@ module ActionView
372
444
  # for complex table highlighting or any other design need which requires
373
445
  # the current cycle string in more than one place.
374
446
  #
375
- # # Alternate background colors
376
- # @items = [1,2,3,4]
447
+ # <%# Alternate background colors %>
448
+ # <% @items = [1,2,3,4] %>
377
449
  # <% @items.each do |item| %>
378
450
  # <div style="background-color:<%= cycle("red","white","blue") %>">
379
451
  # <span style="background-color:<%= current_cycle %>"><%= item %></span>
@@ -387,8 +459,8 @@ module ActionView
387
459
  # Resets a cycle so that it starts from the first element the next time
388
460
  # it is called. Pass in +name+ to reset a named cycle.
389
461
  #
390
- # # Alternate CSS classes for even and odd numbers...
391
- # @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]]
462
+ # <%# Alternate CSS classes for even and odd numbers... %>
463
+ # <% @items = [[1,2,3,4], [5,6,3], [3,4,5,6,7,4]] %>
392
464
  # <table>
393
465
  # <% @items.each do |item| %>
394
466
  # <tr class="<%= cycle("even", "odd") -%>">
@@ -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.
@@ -150,7 +152,7 @@ module ActionView
150
152
  # The template will be looked always in <tt>app/views/layouts/</tt> folder. But you can point
151
153
  # <tt>layouts</tt> folder direct also. <tt>layout "layouts/demo"</tt> is the same as <tt>layout "demo"</tt>.
152
154
  #
153
- # Setting the layout to +nil+ forces it to be looked up in the filesystem and fallbacks to the parent behavior if none exists.
155
+ # Setting the layout to +nil+ forces it to be looked up in the filesystem and falls back to the parent behavior if none exists.
154
156
  # Setting it to +nil+ is useful to re-enable template lookup overriding a previous configuration set in the parent:
155
157
  #
156
158
  # class ApplicationController < ActionController::Base
@@ -162,7 +164,7 @@ module ActionView
162
164
  # end
163
165
  #
164
166
  # class CommentsController < ApplicationController
165
- # # Will search for "comments" layout and fallback "application" layout
167
+ # # Will search for "comments" layout and fall back to "application" layout
166
168
  # layout nil
167
169
  # end
168
170
  #
@@ -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: