actionview 4.2.11.1 → 6.1.5

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

Potentially problematic release.


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

Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +232 -186
  3. data/MIT-LICENSE +1 -2
  4. data/README.rdoc +9 -8
  5. data/lib/action_view/base.rb +115 -39
  6. data/lib/action_view/buffers.rb +18 -1
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -85
  11. data/lib/action_view/flows.rb +11 -12
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +282 -83
  15. data/lib/action_view/helpers/asset_url_helper.rb +175 -69
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +107 -43
  18. data/lib/action_view/helpers/capture_helper.rb +20 -13
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +232 -130
  23. data/lib/action_view/helpers/debug_helper.rb +7 -6
  24. data/lib/action_view/helpers/form_helper.rb +808 -146
  25. data/lib/action_view/helpers/form_options_helper.rb +124 -78
  26. data/lib/action_view/helpers/form_tag_helper.rb +120 -74
  27. data/lib/action_view/helpers/javascript_helper.rb +33 -17
  28. data/lib/action_view/helpers/number_helper.rb +87 -62
  29. data/lib/action_view/helpers/output_safety_helper.rb +36 -4
  30. data/lib/action_view/helpers/rendering_helper.rb +21 -10
  31. data/lib/action_view/helpers/sanitize_helper.rb +30 -31
  32. data/lib/action_view/helpers/tag_helper.rb +269 -68
  33. data/lib/action_view/helpers/tags/base.rb +141 -97
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -34
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +4 -3
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +6 -0
  50. data/lib/action_view/helpers/tags/label.rb +7 -2
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +3 -1
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +14 -9
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +4 -2
  61. data/lib/action_view/helpers/tags/text_field.rb +8 -8
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +15 -16
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +3 -1
  69. data/lib/action_view/helpers/text_helper.rb +56 -38
  70. data/lib/action_view/helpers/translation_helper.rb +150 -68
  71. data/lib/action_view/helpers/url_helper.rb +284 -117
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +77 -10
  75. data/lib/action_view/lookup_context.rb +134 -91
  76. data/lib/action_view/model_naming.rb +3 -1
  77. data/lib/action_view/path_set.rb +26 -24
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +151 -14
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +55 -303
  85. data/lib/action_view/renderer/renderer.rb +66 -9
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +82 -73
  88. data/lib/action_view/rendering.rb +71 -45
  89. data/lib/action_view/routing_url_for.rb +34 -23
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +12 -8
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +263 -197
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +108 -92
  108. data/lib/action_view/test_case.rb +66 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +73 -58
  113. data/lib/action_view.rb +14 -8
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +42 -29
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
@@ -1,13 +1,12 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
- require 'active_support/core_ext/hash/keys'
4
- require 'active_support/core_ext/string/output_safety'
5
- require 'active_support/number_helper'
3
+ require "active_support/core_ext/hash/keys"
4
+ require "active_support/core_ext/string/output_safety"
5
+ require "active_support/number_helper"
6
6
 
7
7
  module ActionView
8
8
  # = Action View Number Helpers
9
9
  module Helpers #:nodoc:
10
-
11
10
  # Provides methods for converting numbers into formatted strings.
12
11
  # Methods are provided for phone numbers, currency, percentage,
13
12
  # precision, positional notation, file size and pretty printing.
@@ -15,7 +14,6 @@ module ActionView
15
14
  # Most methods expect a +number+ argument, and will return it
16
15
  # unchanged if can't be converted into a valid number.
17
16
  module NumberHelper
18
-
19
17
  # Raised when argument +number+ param given to the helpers is invalid and
20
18
  # the option :raise is set to +true+.
21
19
  class InvalidNumberError < StandardError
@@ -25,7 +23,7 @@ module ActionView
25
23
  end
26
24
  end
27
25
 
28
- # Formats a +number+ into a US phone number (e.g., (555)
26
+ # Formats a +number+ into a phone number (US by default e.g., (555)
29
27
  # 123-9876). You can customize the format in the +options+ hash.
30
28
  #
31
29
  # ==== Options
@@ -37,6 +35,8 @@ module ActionView
37
35
  # end of the generated number.
38
36
  # * <tt>:country_code</tt> - Sets the country code for the phone
39
37
  # number.
38
+ # * <tt>:pattern</tt> - Specifies how the number is divided into three
39
+ # groups with the custom regexp to override the default format.
40
40
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
41
41
  # the argument is invalid.
42
42
  #
@@ -54,6 +54,11 @@ module ActionView
54
54
  #
55
55
  # number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
56
56
  # # => +1.123.555.1234 x 1343
57
+ #
58
+ # number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
59
+ # # => "(755) 6123-4567"
60
+ # number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/)
61
+ # # => "133-1234-5678"
57
62
  def number_to_phone(number, options = {})
58
63
  return unless number
59
64
  options = options.symbolize_keys
@@ -65,6 +70,14 @@ module ActionView
65
70
  # Formats a +number+ into a currency string (e.g., $13.65). You
66
71
  # can customize the format in the +options+ hash.
67
72
  #
73
+ # The currency unit and number formatting of the current locale will be used
74
+ # unless otherwise specified in the provided options. No currency conversion
75
+ # is performed. If the user is given a way to change their locale, they will
76
+ # also be able to change the relative value of the currency displayed with
77
+ # this helper. If your application will ever support multiple locales, you
78
+ # may want to specify a constant <tt>:locale</tt> option or consider
79
+ # using a library capable of currency conversion.
80
+ #
68
81
  # ==== Options
69
82
  #
70
83
  # * <tt>:locale</tt> - Sets the locale to be used for formatting
@@ -81,12 +94,15 @@ module ActionView
81
94
  # (defaults to "%u%n"). Fields are <tt>%u</tt> for the
82
95
  # currency, and <tt>%n</tt> for the number.
83
96
  # * <tt>:negative_format</tt> - Sets the format for negative
84
- # numbers (defaults to prepending an hyphen to the formatted
97
+ # numbers (defaults to prepending a hyphen to the formatted
85
98
  # number given by <tt>:format</tt>). Accepts the same fields
86
99
  # than <tt>:format</tt>, except <tt>%n</tt> is here the
87
100
  # absolute value of the number.
88
101
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
89
102
  # the argument is invalid.
103
+ # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
104
+ # insignificant zeros after the decimal separator (defaults to
105
+ # +false+).
90
106
  #
91
107
  # ==== Examples
92
108
  #
@@ -98,12 +114,16 @@ module ActionView
98
114
  #
99
115
  # number_to_currency("123a456", raise: true) # => InvalidNumberError
100
116
  #
117
+ # number_to_currency(-0.456789, precision: 0)
118
+ # # => "$0"
101
119
  # number_to_currency(-1234567890.50, negative_format: "(%u%n)")
102
120
  # # => ($1,234,567,890.50)
103
121
  # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
104
122
  # # => R$1234567890,50
105
123
  # number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
106
124
  # # => 1234567890,50 R$
125
+ # number_to_currency(1234567890.50, strip_insignificant_zeros: true)
126
+ # # => "$1,234,567,890.5"
107
127
  def number_to_currency(number, options = {})
108
128
  delegate_number_helper_method(:number_to_currency, number, options)
109
129
  end
@@ -117,8 +137,8 @@ module ActionView
117
137
  # (defaults to current locale).
118
138
  # * <tt>:precision</tt> - Sets the precision of the number
119
139
  # (defaults to 3).
120
- # * <tt>:significant</tt> - If +true+, precision will be the #
121
- # of significant_digits. If +false+, the # of fractional
140
+ # * <tt>:significant</tt> - If +true+, precision will be the number
141
+ # of significant_digits. If +false+, the number of fractional
122
142
  # digits (defaults to +false+).
123
143
  # * <tt>:separator</tt> - Sets the separator between the
124
144
  # fractional and integer digits (defaults to ".").
@@ -141,7 +161,7 @@ module ActionView
141
161
  # number_to_percentage(302.24398923423, precision: 5) # => 302.24399%
142
162
  # number_to_percentage(1000, locale: :fr) # => 1 000,000%
143
163
  # number_to_percentage("98a") # => 98a%
144
- # number_to_percentage(100, format: "%n %") # => 100 %
164
+ # number_to_percentage(100, format: "%n %") # => 100.000 %
145
165
  #
146
166
  # number_to_percentage("98a", raise: true) # => InvalidNumberError
147
167
  def number_to_percentage(number, options = {})
@@ -160,6 +180,9 @@ module ActionView
160
180
  # to ",").
161
181
  # * <tt>:separator</tt> - Sets the separator between the
162
182
  # fractional and integer digits (defaults to ".").
183
+ # * <tt>:delimiter_pattern</tt> - Sets a custom regular expression used for
184
+ # deriving the placement of delimiter. Helpful when using currency formats
185
+ # like INR.
163
186
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
164
187
  # the argument is invalid.
165
188
  #
@@ -176,6 +199,9 @@ module ActionView
176
199
  # number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
177
200
  # # => 98 765 432,98
178
201
  #
202
+ # number_with_delimiter("123456.78",
203
+ # delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) # => "1,23,456.78"
204
+ #
179
205
  # number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
180
206
  def number_with_delimiter(number, options = {})
181
207
  delegate_number_helper_method(:number_to_delimited, number, options)
@@ -192,8 +218,8 @@ module ActionView
192
218
  # (defaults to current locale).
193
219
  # * <tt>:precision</tt> - Sets the precision of the number
194
220
  # (defaults to 3).
195
- # * <tt>:significant</tt> - If +true+, precision will be the #
196
- # of significant_digits. If +false+, the # of fractional
221
+ # * <tt>:significant</tt> - If +true+, precision will be the number
222
+ # of significant_digits. If +false+, the number of fractional
197
223
  # digits (defaults to +false+).
198
224
  # * <tt>:separator</tt> - Sets the separator between the
199
225
  # fractional and integer digits (defaults to ".").
@@ -227,7 +253,7 @@ module ActionView
227
253
  end
228
254
 
229
255
  # Formats the bytes in +number+ into a more understandable
230
- # representation (e.g., giving it 1500 yields 1.5 KB). This
256
+ # representation (e.g., giving it 1500 yields 1.46 KB). This
231
257
  # method is useful for reporting file sizes to users. You can
232
258
  # customize the format in the +options+ hash.
233
259
  #
@@ -240,8 +266,8 @@ module ActionView
240
266
  # (defaults to current locale).
241
267
  # * <tt>:precision</tt> - Sets the precision of the number
242
268
  # (defaults to 3).
243
- # * <tt>:significant</tt> - If +true+, precision will be the #
244
- # of significant_digits. If +false+, the # of fractional
269
+ # * <tt>:significant</tt> - If +true+, precision will be the number
270
+ # of significant_digits. If +false+, the number of fractional
245
271
  # digits (defaults to +true+)
246
272
  # * <tt>:separator</tt> - Sets the separator between the
247
273
  # fractional and integer digits (defaults to ".").
@@ -250,8 +276,6 @@ module ActionView
250
276
  # * <tt>:strip_insignificant_zeros</tt> - If +true+ removes
251
277
  # insignificant zeros after the decimal separator (defaults to
252
278
  # +true+)
253
- # * <tt>:prefix</tt> - If +:si+ formats the number using the SI
254
- # prefix (defaults to :binary)
255
279
  # * <tt>:raise</tt> - If true, raises +InvalidNumberError+ when
256
280
  # the argument is invalid.
257
281
  #
@@ -263,6 +287,8 @@ module ActionView
263
287
  # number_to_human_size(1234567) # => 1.18 MB
264
288
  # number_to_human_size(1234567890) # => 1.15 GB
265
289
  # number_to_human_size(1234567890123) # => 1.12 TB
290
+ # number_to_human_size(1234567890123456) # => 1.1 PB
291
+ # number_to_human_size(1234567890123456789) # => 1.07 EB
266
292
  # number_to_human_size(1234567, precision: 2) # => 1.2 MB
267
293
  # number_to_human_size(483989, precision: 2) # => 470 KB
268
294
  # number_to_human_size(1234567, precision: 2, separator: ',') # => 1,2 MB
@@ -273,15 +299,15 @@ module ActionView
273
299
  end
274
300
 
275
301
  # Pretty prints (formats and approximates) a number in a way it
276
- # is more readable by humans (eg.: 1200000000 becomes "1.2
302
+ # is more readable by humans (e.g.: 1200000000 becomes "1.2
277
303
  # Billion"). This is useful for numbers that can get very large
278
304
  # (and too hard to read).
279
305
  #
280
306
  # See <tt>number_to_human_size</tt> if you want to print a file
281
307
  # size.
282
308
  #
283
- # You can also define you own unit-quantifier names if you want
284
- # to use other decimal units (eg.: 1500 becomes "1.5
309
+ # You can also define your own unit-quantifier names if you want
310
+ # to use other decimal units (e.g.: 1500 becomes "1.5
285
311
  # kilometers", 0.150 becomes "150 milliliters", etc). You may
286
312
  # define a wide range of unit quantifiers, even fractional ones
287
313
  # (centi, deci, mili, etc).
@@ -292,8 +318,8 @@ module ActionView
292
318
  # (defaults to current locale).
293
319
  # * <tt>:precision</tt> - Sets the precision of the number
294
320
  # (defaults to 3).
295
- # * <tt>:significant</tt> - If +true+, precision will be the #
296
- # of significant_digits. If +false+, the # of fractional
321
+ # * <tt>:significant</tt> - If +true+, precision will be the number
322
+ # of significant_digits. If +false+, the number of fractional
297
323
  # digits (defaults to +true+)
298
324
  # * <tt>:separator</tt> - Sets the separator between the
299
325
  # fractional and integer digits (defaults to ".").
@@ -379,54 +405,53 @@ module ActionView
379
405
  end
380
406
 
381
407
  private
408
+ def delegate_number_helper_method(method, number, options)
409
+ return unless number
410
+ options = escape_unsafe_options(options.symbolize_keys)
382
411
 
383
- def delegate_number_helper_method(method, number, options)
384
- return unless number
385
- options = escape_unsafe_options(options.symbolize_keys)
386
-
387
- wrap_with_output_safety_handling(number, options.delete(:raise)) {
388
- ActiveSupport::NumberHelper.public_send(method, number, options)
389
- }
390
- end
412
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
413
+ ActiveSupport::NumberHelper.public_send(method, number, options)
414
+ }
415
+ end
391
416
 
392
- def escape_unsafe_options(options)
393
- options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
394
- options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
395
- options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
396
- options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
397
- options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
398
- options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
399
- options
400
- end
417
+ def escape_unsafe_options(options)
418
+ options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
419
+ options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
420
+ options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
421
+ options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
422
+ options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
423
+ options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
424
+ options
425
+ end
401
426
 
402
- def escape_units(units)
403
- Hash[units.map do |k, v|
404
- [k, ERB::Util.html_escape(v)]
405
- end]
406
- end
427
+ def escape_units(units)
428
+ units.transform_values do |v|
429
+ ERB::Util.html_escape(v)
430
+ end
431
+ end
407
432
 
408
- def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
409
- valid_float = valid_float?(number)
410
- raise InvalidNumberError, number if raise_on_invalid && !valid_float
433
+ def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
434
+ valid_float = valid_float?(number)
435
+ raise InvalidNumberError, number if raise_on_invalid && !valid_float
411
436
 
412
- formatted_number = yield
437
+ formatted_number = yield
413
438
 
414
- if valid_float || number.html_safe?
415
- formatted_number.html_safe
416
- else
417
- formatted_number
439
+ if valid_float || number.html_safe?
440
+ formatted_number.html_safe
441
+ else
442
+ formatted_number
443
+ end
418
444
  end
419
- end
420
445
 
421
- def valid_float?(number)
422
- !parse_float(number, false).nil?
423
- end
446
+ def valid_float?(number)
447
+ !parse_float(number, false).nil?
448
+ end
424
449
 
425
- def parse_float(number, raise_error)
426
- Float(number)
427
- rescue ArgumentError, TypeError
428
- raise InvalidNumberError, number if raise_error
429
- end
450
+ def parse_float(number, raise_error)
451
+ Float(number)
452
+ rescue ArgumentError, TypeError
453
+ raise InvalidNumberError, number if raise_error
454
+ end
430
455
  end
431
456
  end
432
457
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/string/output_safety'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
2
4
 
3
5
  module ActionView #:nodoc:
4
6
  # = Action View Raw Output Helper
@@ -22,17 +24,47 @@ module ActionView #:nodoc:
22
24
  # the supplied separator, are HTML escaped unless they are HTML
23
25
  # safe, and the returned string is marked as HTML safe.
24
26
  #
25
- # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>"], "<br />")
27
+ # safe_join([raw("<p>foo</p>"), "<p>bar</p>"], "<br />")
26
28
  # # => "<p>foo</p>&lt;br /&gt;&lt;p&gt;bar&lt;/p&gt;"
27
29
  #
28
- # safe_join(["<p>foo</p>".html_safe, "<p>bar</p>".html_safe], "<br />".html_safe)
30
+ # safe_join([raw("<p>foo</p>"), raw("<p>bar</p>")], raw("<br />"))
29
31
  # # => "<p>foo</p><br /><p>bar</p>"
30
32
  #
31
- def safe_join(array, sep=$,)
33
+ def safe_join(array, sep = $,)
32
34
  sep = ERB::Util.unwrapped_html_escape(sep)
33
35
 
34
36
  array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
35
37
  end
38
+
39
+ # Converts the array to a comma-separated sentence where the last element is
40
+ # joined by the connector word. This is the html_safe-aware version of
41
+ # ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
42
+ #
43
+ def to_sentence(array, options = {})
44
+ options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
45
+
46
+ default_connectors = {
47
+ words_connector: ", ",
48
+ two_words_connector: " and ",
49
+ last_word_connector: ", and "
50
+ }
51
+ if defined?(I18n)
52
+ i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
53
+ default_connectors.merge!(i18n_connectors)
54
+ end
55
+ options = default_connectors.merge!(options)
56
+
57
+ case array.length
58
+ when 0
59
+ "".html_safe
60
+ when 1
61
+ ERB::Util.html_escape(array[0])
62
+ when 2
63
+ safe_join([array[0], array[1]], options[:two_words_connector])
64
+ else
65
+ safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]], nil)
66
+ end
67
+ end
36
68
  end
37
69
  end
38
70
  end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
- module Helpers
4
+ module Helpers #:nodoc:
3
5
  # = Action View Rendering
4
6
  #
5
7
  # Implements methods that allow rendering from a view context.
@@ -11,28 +13,37 @@ module ActionView
11
13
  # * <tt>:partial</tt> - See <tt>ActionView::PartialRenderer</tt>.
12
14
  # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add :locals to pass in those.
13
15
  # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
14
- # * <tt>:text</tt> - Renders the text passed in out.
15
16
  # * <tt>:plain</tt> - Renders the text passed in out. Setting the content
16
17
  # type as <tt>text/plain</tt>.
17
18
  # * <tt>:html</tt> - Renders the HTML safe string passed in out, otherwise
18
19
  # performs HTML escape on the string first. Setting the content type as
19
20
  # <tt>text/html</tt>.
20
21
  # * <tt>:body</tt> - Renders the text passed in, and inherits the content
21
- # type of <tt>text/html</tt> from <tt>ActionDispatch::Response</tt>
22
+ # type of <tt>text/plain</tt> from <tt>ActionDispatch::Response</tt>
22
23
  # object.
23
24
  #
24
- # If no options hash is passed or :update specified, the default is to render a partial and use the second parameter
25
- # as the locals hash.
25
+ # If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, then:
26
+ #
27
+ # If an object responding to +render_in+ is passed, +render_in+ is called on the object,
28
+ # passing in the current view context.
29
+ #
30
+ # Otherwise, a partial is rendered using the second parameter as the locals hash.
26
31
  def render(options = {}, locals = {}, &block)
27
32
  case options
28
33
  when Hash
29
- if block_given?
30
- view_renderer.render_partial(self, options.merge(:partial => options[:layout]), &block)
31
- else
32
- view_renderer.render(self, options)
34
+ in_rendering_context(options) do |renderer|
35
+ if block_given?
36
+ view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
37
+ else
38
+ view_renderer.render(self, options)
39
+ end
33
40
  end
34
41
  else
35
- view_renderer.render_partial(self, :partial => options, :locals => locals)
42
+ if options.respond_to?(:render_in)
43
+ options.render_in(self, &block)
44
+ else
45
+ view_renderer.render_partial(self, partial: options, locals: locals, &block)
46
+ end
36
47
  end
37
48
  end
38
49
 
@@ -1,28 +1,28 @@
1
- require 'active_support/core_ext/object/try'
2
- require 'active_support/deprecation'
3
- require 'rails-html-sanitizer'
1
+ # frozen_string_literal: true
2
+
3
+ require "rails-html-sanitizer"
4
4
 
5
5
  module ActionView
6
6
  # = Action View Sanitize Helpers
7
- module Helpers
7
+ module Helpers #:nodoc:
8
8
  # The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
9
9
  # These helper methods extend Action View making them callable within your template files.
10
10
  module SanitizeHelper
11
11
  extend ActiveSupport::Concern
12
- # Sanitizes HTML input, stripping all tags and attributes that aren't whitelisted.
12
+ # Sanitizes HTML input, stripping all but known-safe tags and attributes.
13
13
  #
14
14
  # It also strips href/src attributes with unsafe protocols like
15
15
  # <tt>javascript:</tt>, while also protecting against attempts to use Unicode,
16
16
  # ASCII, and hex character references to work around these protocol filters.
17
+ # All special characters will be escaped.
17
18
  #
18
- # The default sanitizer is Rails::Html::WhiteListSanitizer. See {Rails HTML
19
+ # The default sanitizer is Rails::Html::SafeListSanitizer. See {Rails HTML
19
20
  # Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information.
20
21
  #
21
22
  # Custom sanitization rules can also be provided.
22
23
  #
23
24
  # Please note that sanitizing user-provided text does not guarantee that the
24
- # resulting markup is valid or even well-formed. For example, the output may still
25
- # contain unescaped characters like <tt><</tt>, <tt>></tt>, or <tt>&</tt>.
25
+ # resulting markup is valid or even well-formed.
26
26
  #
27
27
  # ==== Options
28
28
  #
@@ -39,24 +39,22 @@ module ActionView
39
39
  #
40
40
  # <%= sanitize @comment.body %>
41
41
  #
42
- # Providing custom whitelisted tags and attributes:
42
+ # Providing custom lists of permitted tags and attributes:
43
43
  #
44
44
  # <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
45
45
  #
46
46
  # Providing a custom Rails::Html scrubber:
47
47
  #
48
48
  # class CommentScrubber < Rails::Html::PermitScrubber
49
- # def allowed_node?(node)
50
- # !%w(form script comment blockquote).include?(node.name)
49
+ # def initialize
50
+ # super
51
+ # self.tags = %w( form script comment blockquote )
52
+ # self.attributes = %w( style )
51
53
  # end
52
54
  #
53
55
  # def skip_node?(node)
54
56
  # node.text?
55
57
  # end
56
- #
57
- # def scrub_attribute?(name)
58
- # name == 'style'
59
- # end
60
58
  # end
61
59
  #
62
60
  # <%= sanitize @comment.body, scrubber: CommentScrubber.new %>
@@ -81,15 +79,15 @@ module ActionView
81
79
  # config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
82
80
  # config.action_view.sanitized_allowed_attributes = ['href', 'title']
83
81
  def sanitize(html, options = {})
84
- self.class.white_list_sanitizer.sanitize(html, options).try(:html_safe)
82
+ self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
85
83
  end
86
84
 
87
85
  # Sanitizes a block of CSS code. Used by +sanitize+ when it comes across a style attribute.
88
86
  def sanitize_css(style)
89
- self.class.white_list_sanitizer.sanitize_css(style)
87
+ self.class.safe_list_sanitizer.sanitize_css(style)
90
88
  end
91
89
 
92
- # Strips all HTML tags from +html+, including comments.
90
+ # Strips all HTML tags from +html+, including comments and special characters.
93
91
  #
94
92
  # strip_tags("Strip <i>these</i> tags!")
95
93
  # # => Strip these tags!
@@ -99,8 +97,11 @@ module ActionView
99
97
  #
100
98
  # strip_tags("<div id='top-bar'>Welcome to my website!</div>")
101
99
  # # => Welcome to my website!
100
+ #
101
+ # strip_tags("> A quote from Smith & Wesson")
102
+ # # => &gt; A quote from Smith &amp; Wesson
102
103
  def strip_tags(html)
103
- self.class.full_sanitizer.sanitize(html, encode_special_chars: false)
104
+ self.class.full_sanitizer.sanitize(html)
104
105
  end
105
106
 
106
107
  # Strips all link tags from +html+ leaving just the link text.
@@ -113,25 +114,26 @@ module ActionView
113
114
  #
114
115
  # strip_links('Blog: <a href="http://www.myblog.com/" class="nav" target=\"_blank\">Visit</a>.')
115
116
  # # => Blog: Visit.
117
+ #
118
+ # strip_links('<<a href="https://example.org">malformed & link</a>')
119
+ # # => &lt;malformed &amp; link
116
120
  def strip_links(html)
117
121
  self.class.link_sanitizer.sanitize(html)
118
122
  end
119
123
 
120
124
  module ClassMethods #:nodoc:
121
- attr_writer :full_sanitizer, :link_sanitizer, :white_list_sanitizer
125
+ attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
122
126
 
123
- # Vendors the full, link and white list sanitizers.
124
- # Provided strictly for compabitility and can be removed in Rails 5.
125
127
  def sanitizer_vendor
126
128
  Rails::Html::Sanitizer
127
129
  end
128
130
 
129
131
  def sanitized_allowed_tags
130
- sanitizer_vendor.white_list_sanitizer.allowed_tags
132
+ sanitizer_vendor.safe_list_sanitizer.allowed_tags
131
133
  end
132
134
 
133
135
  def sanitized_allowed_attributes
134
- sanitizer_vendor.white_list_sanitizer.allowed_attributes
136
+ sanitizer_vendor.safe_list_sanitizer.allowed_attributes
135
137
  end
136
138
 
137
139
  # Gets the Rails::Html::FullSanitizer instance used by +strip_tags+. Replace with
@@ -140,7 +142,6 @@ module ActionView
140
142
  # class Application < Rails::Application
141
143
  # config.action_view.full_sanitizer = MySpecialSanitizer.new
142
144
  # end
143
- #
144
145
  def full_sanitizer
145
146
  @full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
146
147
  end
@@ -151,20 +152,18 @@ module ActionView
151
152
  # class Application < Rails::Application
152
153
  # config.action_view.link_sanitizer = MySpecialSanitizer.new
153
154
  # end
154
- #
155
155
  def link_sanitizer
156
156
  @link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
157
157
  end
158
158
 
159
- # Gets the Rails::Html::WhiteListSanitizer instance used by sanitize and +sanitize_css+.
159
+ # Gets the Rails::Html::SafeListSanitizer instance used by sanitize and +sanitize_css+.
160
160
  # Replace with any object that responds to +sanitize+.
161
161
  #
162
162
  # class Application < Rails::Application
163
- # config.action_view.white_list_sanitizer = MySpecialSanitizer.new
163
+ # config.action_view.safe_list_sanitizer = MySpecialSanitizer.new
164
164
  # end
165
- #
166
- def white_list_sanitizer
167
- @white_list_sanitizer ||= sanitizer_vendor.white_list_sanitizer.new
165
+ def safe_list_sanitizer
166
+ @safe_list_sanitizer ||= sanitizer_vendor.safe_list_sanitizer.new
168
167
  end
169
168
  end
170
169
  end