actionview 7.0.1 → 7.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +281 -202
- data/MIT-LICENSE +1 -1
- data/README.rdoc +3 -3
- data/app/assets/javascripts/rails-ujs.esm.js +693 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +33 -12
- data/lib/action_view/buffers.rb +106 -8
- data/lib/action_view/cache_expiry.rb +40 -43
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +1 -1
- data/lib/action_view/gem_version.rb +2 -2
- data/lib/action_view/helpers/active_model_helper.rb +1 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +133 -48
- data/lib/action_view/helpers/asset_url_helper.rb +13 -12
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
- data/lib/action_view/helpers/cache_helper.rb +3 -9
- data/lib/action_view/helpers/capture_helper.rb +26 -12
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +6 -0
- data/lib/action_view/helpers/csp_helper.rb +2 -2
- data/lib/action_view/helpers/csrf_helper.rb +3 -3
- data/lib/action_view/helpers/date_helper.rb +76 -64
- data/lib/action_view/helpers/debug_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +62 -31
- data/lib/action_view/helpers/form_options_helper.rb +6 -3
- data/lib/action_view/helpers/form_tag_helper.rb +88 -44
- data/lib/action_view/helpers/javascript_helper.rb +1 -0
- data/lib/action_view/helpers/number_helper.rb +15 -13
- data/lib/action_view/helpers/output_safety_helper.rb +4 -4
- data/lib/action_view/helpers/rendering_helper.rb +5 -6
- data/lib/action_view/helpers/sanitize_helper.rb +34 -15
- data/lib/action_view/helpers/tag_helper.rb +27 -16
- data/lib/action_view/helpers/tags/base.rb +11 -52
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
- data/lib/action_view/helpers/tags/collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
- data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +4 -1
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/time_field.rb +1 -1
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
- data/lib/action_view/helpers/tags.rb +2 -0
- data/lib/action_view/helpers/text_helper.rb +33 -17
- data/lib/action_view/helpers/translation_helper.rb +6 -6
- data/lib/action_view/helpers/url_helper.rb +90 -65
- data/lib/action_view/helpers.rb +2 -0
- data/lib/action_view/layouts.rb +13 -8
- data/lib/action_view/log_subscriber.rb +49 -32
- data/lib/action_view/lookup_context.rb +29 -13
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +13 -14
- data/lib/action_view/railtie.rb +26 -3
- data/lib/action_view/record_identifier.rb +16 -9
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/collection_renderer.rb +9 -1
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
- data/lib/action_view/renderer/partial_renderer.rb +3 -2
- data/lib/action_view/renderer/renderer.rb +2 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -2
- data/lib/action_view/rendering.rb +24 -6
- data/lib/action_view/ripper_ast_parser.rb +6 -6
- data/lib/action_view/routing_url_for.rb +7 -4
- data/lib/action_view/template/error.rb +14 -1
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
- data/lib/action_view/template/handlers/erb.rb +73 -1
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +1 -1
- data/lib/action_view/template/raw_file.rb +1 -1
- data/lib/action_view/template/renderable.rb +1 -1
- data/lib/action_view/template/resolver.rb +15 -5
- data/lib/action_view/template/text.rb +1 -1
- data/lib/action_view/template/types.rb +25 -34
- data/lib/action_view/template.rb +227 -53
- data/lib/action_view/template_path.rb +2 -0
- data/lib/action_view/test_case.rb +174 -21
- data/lib/action_view/unbound_template.rb +15 -5
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +19 -28
- data/lib/action_view.rb +4 -1
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +27 -27
|
@@ -5,17 +5,18 @@ require "active_support/core_ext/string/output_safety"
|
|
|
5
5
|
require "active_support/number_helper"
|
|
6
6
|
|
|
7
7
|
module ActionView
|
|
8
|
-
# = Action View Number Helpers
|
|
9
8
|
module Helpers # :nodoc:
|
|
9
|
+
# = Action View Number \Helpers
|
|
10
|
+
#
|
|
10
11
|
# Provides methods for converting numbers into formatted strings.
|
|
11
12
|
# Methods are provided for phone numbers, currency, percentage,
|
|
12
|
-
# precision, positional notation, file size and pretty printing.
|
|
13
|
+
# precision, positional notation, file size, and pretty printing.
|
|
13
14
|
#
|
|
14
15
|
# Most methods expect a +number+ argument, and will return it
|
|
15
16
|
# unchanged if can't be converted into a valid number.
|
|
16
17
|
module NumberHelper
|
|
17
18
|
# Raised when argument +number+ param given to the helpers is invalid and
|
|
18
|
-
# the option
|
|
19
|
+
# the option +:raise+ is set to +true+.
|
|
19
20
|
class InvalidNumberError < StandardError
|
|
20
21
|
attr_accessor :number
|
|
21
22
|
def initialize(number)
|
|
@@ -202,7 +203,7 @@ module ActionView
|
|
|
202
203
|
# number_with_delimiter("123456.78",
|
|
203
204
|
# delimiter_pattern: /(\d+?)(?=(\d\d)+(\d)(?!\d))/) # => "1,23,456.78"
|
|
204
205
|
#
|
|
205
|
-
#
|
|
206
|
+
# number_with_delimiter("112a", raise: true) # => raise InvalidNumberError
|
|
206
207
|
def number_with_delimiter(number, options = {})
|
|
207
208
|
delegate_number_helper_method(:number_to_delimited, number, options)
|
|
208
209
|
end
|
|
@@ -370,13 +371,14 @@ module ActionView
|
|
|
370
371
|
# out by default (set <tt>:strip_insignificant_zeros</tt> to
|
|
371
372
|
# +false+ to change that):
|
|
372
373
|
#
|
|
373
|
-
#
|
|
374
|
-
#
|
|
374
|
+
# number_to_human(12.00001) # => "12"
|
|
375
|
+
# number_to_human(12.00001, strip_insignificant_zeros: false) # => "12.0"
|
|
375
376
|
#
|
|
376
377
|
# ==== Custom Unit Quantifiers
|
|
377
378
|
#
|
|
378
379
|
# You can also use your own custom unit quantifiers:
|
|
379
|
-
#
|
|
380
|
+
#
|
|
381
|
+
# number_to_human(500000, units: {unit: "ml", thousand: "lt"}) # => "500 lt"
|
|
380
382
|
#
|
|
381
383
|
# If in your I18n locale you have:
|
|
382
384
|
# distance:
|
|
@@ -393,12 +395,12 @@ module ActionView
|
|
|
393
395
|
#
|
|
394
396
|
# Then you could do:
|
|
395
397
|
#
|
|
396
|
-
#
|
|
397
|
-
#
|
|
398
|
-
#
|
|
399
|
-
#
|
|
400
|
-
#
|
|
401
|
-
#
|
|
398
|
+
# number_to_human(543934, units: :distance) # => "544 kilometers"
|
|
399
|
+
# number_to_human(54393498, units: :distance) # => "54400 kilometers"
|
|
400
|
+
# number_to_human(54393498000, units: :distance) # => "54.4 gazillion-distance"
|
|
401
|
+
# number_to_human(343, units: :distance, precision: 1) # => "300 meters"
|
|
402
|
+
# number_to_human(1, units: :distance) # => "1 meter"
|
|
403
|
+
# number_to_human(0.34, units: :distance) # => "34 centimeters"
|
|
402
404
|
#
|
|
403
405
|
def number_to_human(number, options = {})
|
|
404
406
|
delegate_number_helper_method(:number_to_human, number, options)
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
require "active_support/core_ext/string/output_safety"
|
|
4
4
|
|
|
5
5
|
module ActionView # :nodoc:
|
|
6
|
-
# = Action View Raw Output Helper
|
|
7
6
|
module Helpers # :nodoc:
|
|
7
|
+
# = Action View Raw Output \Helpers
|
|
8
8
|
module OutputSafetyHelper
|
|
9
9
|
# This method outputs without escaping a string. Since escaping tags is
|
|
10
|
-
# now default, this can be used when you don't want Rails to automatically
|
|
10
|
+
# now default, this can be used when you don't want \Rails to automatically
|
|
11
11
|
# escape tags. This is not recommended if the data is coming from the user's
|
|
12
12
|
# input.
|
|
13
13
|
#
|
|
14
14
|
# For example:
|
|
15
15
|
#
|
|
16
|
-
#
|
|
17
|
-
#
|
|
16
|
+
# raw @user.name
|
|
17
|
+
# # => 'Jimmy <alert>Tables</alert>'
|
|
18
18
|
def raw(stringish)
|
|
19
19
|
stringish.to_s.html_safe
|
|
20
20
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
module ActionView
|
|
4
4
|
module Helpers # :nodoc:
|
|
5
|
-
# = Action View Rendering
|
|
5
|
+
# = Action View \Rendering \Helpers
|
|
6
6
|
#
|
|
7
7
|
# Implements methods that allow rendering from a view context.
|
|
8
8
|
# In order to use this module, all you need is to implement
|
|
@@ -10,8 +10,8 @@ module ActionView
|
|
|
10
10
|
module RenderingHelper
|
|
11
11
|
# Returns the result of a render that's dictated by the options hash. The primary options are:
|
|
12
12
|
#
|
|
13
|
-
# * <tt>:partial</tt> - See
|
|
14
|
-
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add
|
|
13
|
+
# * <tt>:partial</tt> - See ActionView::PartialRenderer.
|
|
14
|
+
# * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add +:locals+ to pass in those.
|
|
15
15
|
# * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
|
|
16
16
|
# * <tt>:plain</tt> - Renders the text passed in out. Setting the content
|
|
17
17
|
# type as <tt>text/plain</tt>.
|
|
@@ -19,8 +19,7 @@ module ActionView
|
|
|
19
19
|
# performs HTML escape on the string first. Setting the content type as
|
|
20
20
|
# <tt>text/html</tt>.
|
|
21
21
|
# * <tt>:body</tt> - Renders the text passed in, and inherits the content
|
|
22
|
-
# type of <tt>text/plain</tt> from
|
|
23
|
-
# object.
|
|
22
|
+
# type of <tt>text/plain</tt> from ActionDispatch::Response object.
|
|
24
23
|
#
|
|
25
24
|
# If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, then:
|
|
26
25
|
#
|
|
@@ -47,7 +46,7 @@ module ActionView
|
|
|
47
46
|
end
|
|
48
47
|
end
|
|
49
48
|
|
|
50
|
-
#
|
|
49
|
+
# Overrides _layout_for in the context object so it supports the case a block is
|
|
51
50
|
# passed to a partial. Returns the contents that are yielded to a layout, given a
|
|
52
51
|
# name or a block.
|
|
53
52
|
#
|
|
@@ -3,20 +3,23 @@
|
|
|
3
3
|
require "rails-html-sanitizer"
|
|
4
4
|
|
|
5
5
|
module ActionView
|
|
6
|
-
# = Action View Sanitize Helpers
|
|
7
6
|
module Helpers # :nodoc:
|
|
7
|
+
# = Action View Sanitize \Helpers
|
|
8
|
+
#
|
|
8
9
|
# The SanitizeHelper module provides a set of methods for scrubbing text of undesired HTML elements.
|
|
9
10
|
# These helper methods extend Action View making them callable within your template files.
|
|
10
11
|
module SanitizeHelper
|
|
12
|
+
mattr_accessor :sanitizer_vendor, default: Rails::HTML4::Sanitizer
|
|
13
|
+
|
|
11
14
|
extend ActiveSupport::Concern
|
|
15
|
+
|
|
12
16
|
# Sanitizes HTML input, stripping all but known-safe tags and attributes.
|
|
13
17
|
#
|
|
14
|
-
# It also strips href/src attributes with unsafe protocols like
|
|
15
|
-
#
|
|
16
|
-
#
|
|
17
|
-
# All special characters will be escaped.
|
|
18
|
+
# It also strips href/src attributes with unsafe protocols like <tt>javascript:</tt>, while
|
|
19
|
+
# also protecting against attempts to use Unicode, ASCII, and hex character references to work
|
|
20
|
+
# around these protocol filters.
|
|
18
21
|
#
|
|
19
|
-
# The default sanitizer is Rails::
|
|
22
|
+
# The default sanitizer is Rails::HTML5::SafeListSanitizer. See {Rails HTML
|
|
20
23
|
# Sanitizers}[https://github.com/rails/rails-html-sanitizer] for more information.
|
|
21
24
|
#
|
|
22
25
|
# Custom sanitization rules can also be provided.
|
|
@@ -28,7 +31,7 @@ module ActionView
|
|
|
28
31
|
#
|
|
29
32
|
# * <tt>:tags</tt> - An array of allowed tags.
|
|
30
33
|
# * <tt>:attributes</tt> - An array of allowed attributes.
|
|
31
|
-
# * <tt>:scrubber</tt> - A {Rails::
|
|
34
|
+
# * <tt>:scrubber</tt> - A {Rails::HTML scrubber}[https://github.com/rails/rails-html-sanitizer]
|
|
32
35
|
# or {Loofah::Scrubber}[https://github.com/flavorjones/loofah] object that
|
|
33
36
|
# defines custom sanitization rules. A custom scrubber takes precedence over
|
|
34
37
|
# custom tags and attributes.
|
|
@@ -43,9 +46,9 @@ module ActionView
|
|
|
43
46
|
#
|
|
44
47
|
# <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
|
|
45
48
|
#
|
|
46
|
-
# Providing a custom Rails::
|
|
49
|
+
# Providing a custom Rails::HTML scrubber:
|
|
47
50
|
#
|
|
48
|
-
# class CommentScrubber < Rails::
|
|
51
|
+
# class CommentScrubber < Rails::HTML::PermitScrubber
|
|
49
52
|
# def initialize
|
|
50
53
|
# super
|
|
51
54
|
# self.tags = %w( form script comment blockquote )
|
|
@@ -60,7 +63,7 @@ module ActionView
|
|
|
60
63
|
# <%= sanitize @comment.body, scrubber: CommentScrubber.new %>
|
|
61
64
|
#
|
|
62
65
|
# See {Rails HTML Sanitizer}[https://github.com/rails/rails-html-sanitizer] for
|
|
63
|
-
# documentation about Rails::
|
|
66
|
+
# documentation about Rails::HTML scrubbers.
|
|
64
67
|
#
|
|
65
68
|
# Providing a custom Loofah::Scrubber:
|
|
66
69
|
#
|
|
@@ -78,6 +81,22 @@ module ActionView
|
|
|
78
81
|
# # In config/application.rb
|
|
79
82
|
# config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
|
|
80
83
|
# config.action_view.sanitized_allowed_attributes = ['href', 'title']
|
|
84
|
+
#
|
|
85
|
+
# The default, starting in \Rails 7.1, is to use an HTML5 parser for sanitization (if it is
|
|
86
|
+
# available, see NOTE below). If you wish to revert back to the previous HTML4 behavior, you
|
|
87
|
+
# can do so by setting the following in your application configuration:
|
|
88
|
+
#
|
|
89
|
+
# # In config/application.rb
|
|
90
|
+
# config.action_view.sanitizer_vendor = Rails::HTML4::Sanitizer
|
|
91
|
+
#
|
|
92
|
+
# Or, if you're upgrading from a previous version of \Rails and wish to opt into the HTML5
|
|
93
|
+
# behavior:
|
|
94
|
+
#
|
|
95
|
+
# # In config/application.rb
|
|
96
|
+
# config.action_view.sanitizer_vendor = Rails::HTML5::Sanitizer
|
|
97
|
+
#
|
|
98
|
+
# NOTE: Rails::HTML5::Sanitizer is not supported on JRuby, so on JRuby platforms \Rails will
|
|
99
|
+
# fall back to use Rails::HTML4::Sanitizer.
|
|
81
100
|
def sanitize(html, options = {})
|
|
82
101
|
self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
|
|
83
102
|
end
|
|
@@ -101,7 +120,7 @@ module ActionView
|
|
|
101
120
|
# strip_tags("> A quote from Smith & Wesson")
|
|
102
121
|
# # => > A quote from Smith & Wesson
|
|
103
122
|
def strip_tags(html)
|
|
104
|
-
self.class.full_sanitizer.sanitize(html)
|
|
123
|
+
self.class.full_sanitizer.sanitize(html)&.html_safe
|
|
105
124
|
end
|
|
106
125
|
|
|
107
126
|
# Strips all link tags from +html+ leaving just the link text.
|
|
@@ -125,7 +144,7 @@ module ActionView
|
|
|
125
144
|
attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
|
|
126
145
|
|
|
127
146
|
def sanitizer_vendor
|
|
128
|
-
|
|
147
|
+
ActionView::Helpers::SanitizeHelper.sanitizer_vendor
|
|
129
148
|
end
|
|
130
149
|
|
|
131
150
|
def sanitized_allowed_tags
|
|
@@ -136,7 +155,7 @@ module ActionView
|
|
|
136
155
|
sanitizer_vendor.safe_list_sanitizer.allowed_attributes
|
|
137
156
|
end
|
|
138
157
|
|
|
139
|
-
# Gets the Rails::
|
|
158
|
+
# Gets the Rails::HTML::FullSanitizer instance used by +strip_tags+. Replace with
|
|
140
159
|
# any object that responds to +sanitize+.
|
|
141
160
|
#
|
|
142
161
|
# class Application < Rails::Application
|
|
@@ -146,7 +165,7 @@ module ActionView
|
|
|
146
165
|
@full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
|
|
147
166
|
end
|
|
148
167
|
|
|
149
|
-
# Gets the Rails::
|
|
168
|
+
# Gets the Rails::HTML::LinkSanitizer instance used by +strip_links+.
|
|
150
169
|
# Replace with any object that responds to +sanitize+.
|
|
151
170
|
#
|
|
152
171
|
# class Application < Rails::Application
|
|
@@ -156,7 +175,7 @@ module ActionView
|
|
|
156
175
|
@link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
|
|
157
176
|
end
|
|
158
177
|
|
|
159
|
-
# Gets the Rails::
|
|
178
|
+
# Gets the Rails::HTML::SafeListSanitizer instance used by sanitize and +sanitize_css+.
|
|
160
179
|
# Replace with any object that responds to +sanitize+.
|
|
161
180
|
#
|
|
162
181
|
# class Application < Rails::Application
|
|
@@ -7,8 +7,9 @@ require "action_view/helpers/capture_helper"
|
|
|
7
7
|
require "action_view/helpers/output_safety_helper"
|
|
8
8
|
|
|
9
9
|
module ActionView
|
|
10
|
-
# = Action View Tag Helpers
|
|
11
10
|
module Helpers # :nodoc:
|
|
11
|
+
# = Action View Tag \Helpers
|
|
12
|
+
#
|
|
12
13
|
# Provides methods to generate HTML tags programmatically both as a modern
|
|
13
14
|
# HTML5 compliant builder style and legacy XHTML compliant tags.
|
|
14
15
|
module TagHelper
|
|
@@ -45,8 +46,8 @@ module ActionView
|
|
|
45
46
|
include CaptureHelper
|
|
46
47
|
include OutputSafetyHelper
|
|
47
48
|
|
|
48
|
-
HTML_VOID_ELEMENTS = %i(area base br col
|
|
49
|
-
|
|
49
|
+
HTML_VOID_ELEMENTS = %i(area base br col embed hr img input keygen link meta param source track wbr).to_set
|
|
50
|
+
SVG_SELF_CLOSING_ELEMENTS = %i(animate animateMotion animateTransform circle ellipse line path polygon polyline rect set stop use view).to_set
|
|
50
51
|
|
|
51
52
|
def initialize(view_context)
|
|
52
53
|
@view_context = view_context
|
|
@@ -65,18 +66,24 @@ module ActionView
|
|
|
65
66
|
tag_string(:p, *arguments, **options, &block)
|
|
66
67
|
end
|
|
67
68
|
|
|
68
|
-
def tag_string(name, content = nil,
|
|
69
|
+
def tag_string(name, content = nil, escape: true, **options, &block)
|
|
69
70
|
content = @view_context.capture(self, &block) if block_given?
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
self_closing = SVG_SELF_CLOSING_ELEMENTS.include?(name)
|
|
72
|
+
if (HTML_VOID_ELEMENTS.include?(name) || self_closing) && content.nil?
|
|
73
|
+
"<#{name.to_s.dasherize}#{tag_options(options, escape)}#{self_closing ? " />" : ">"}".html_safe
|
|
72
74
|
else
|
|
73
|
-
content_tag_string(name.to_s.dasherize, content || "", options,
|
|
75
|
+
content_tag_string(name.to_s.dasherize, content || "", options, escape)
|
|
74
76
|
end
|
|
75
77
|
end
|
|
76
78
|
|
|
77
79
|
def content_tag_string(name, content, options, escape = true)
|
|
78
80
|
tag_options = tag_options(options, escape) if options
|
|
79
|
-
|
|
81
|
+
|
|
82
|
+
if escape
|
|
83
|
+
name = ERB::Util.xml_name_escape(name)
|
|
84
|
+
content = ERB::Util.unwrapped_html_escape(content)
|
|
85
|
+
end
|
|
86
|
+
|
|
80
87
|
"<#{name}#{tag_options}>#{PRE_CONTENT_STRINGS[name]}#{content}</#{name}>".html_safe
|
|
81
88
|
end
|
|
82
89
|
|
|
@@ -127,6 +134,8 @@ module ActionView
|
|
|
127
134
|
end
|
|
128
135
|
|
|
129
136
|
def tag_option(key, value, escape)
|
|
137
|
+
key = ERB::Util.xml_name_escape(key) if escape
|
|
138
|
+
|
|
130
139
|
case value
|
|
131
140
|
when Array, Hash
|
|
132
141
|
value = TagHelper.build_tag_values(value) if key.to_s == "class"
|
|
@@ -137,6 +146,7 @@ module ActionView
|
|
|
137
146
|
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
|
|
138
147
|
end
|
|
139
148
|
value = value.gsub('"', """) if value.include?('"')
|
|
149
|
+
|
|
140
150
|
%(#{key}="#{value}")
|
|
141
151
|
end
|
|
142
152
|
|
|
@@ -208,7 +218,7 @@ module ActionView
|
|
|
208
218
|
#
|
|
209
219
|
# Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
|
|
210
220
|
#
|
|
211
|
-
# Data attribute values are encoded to JSON, with the exception of strings, symbols and
|
|
221
|
+
# Data attribute values are encoded to JSON, with the exception of strings, symbols, and
|
|
212
222
|
# BigDecimals.
|
|
213
223
|
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
|
|
214
224
|
# from 1.4.3.
|
|
@@ -216,13 +226,13 @@ module ActionView
|
|
|
216
226
|
# tag.div data: { city_state: %w( Chicago IL ) }
|
|
217
227
|
# # => <div data-city-state="["Chicago","IL"]"></div>
|
|
218
228
|
#
|
|
219
|
-
# The generated attributes are escaped by default. This can be disabled using
|
|
220
|
-
# +
|
|
229
|
+
# The generated tag names and attributes are escaped by default. This can be disabled using
|
|
230
|
+
# +escape+.
|
|
221
231
|
#
|
|
222
232
|
# tag.img src: 'open & shut.png'
|
|
223
233
|
# # => <img src="open & shut.png">
|
|
224
234
|
#
|
|
225
|
-
# tag.img src: 'open & shut.png',
|
|
235
|
+
# tag.img src: 'open & shut.png', escape: false
|
|
226
236
|
# # => <img src="open & shut.png">
|
|
227
237
|
#
|
|
228
238
|
# The tag builder respects
|
|
@@ -240,7 +250,7 @@ module ActionView
|
|
|
240
250
|
# Transforms a Hash into HTML attributes, ready to be interpolated into
|
|
241
251
|
# ERB. Includes or omits boolean attributes based on their truthiness.
|
|
242
252
|
# Transforms keys nested within
|
|
243
|
-
# <tt>aria:</tt> or <tt>data:</tt> objects into
|
|
253
|
+
# <tt>aria:</tt> or <tt>data:</tt> objects into <tt>aria-</tt> and <tt>data-</tt>
|
|
244
254
|
# prefixed attributes:
|
|
245
255
|
#
|
|
246
256
|
# <input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>>
|
|
@@ -251,7 +261,7 @@ module ActionView
|
|
|
251
261
|
#
|
|
252
262
|
# === Legacy syntax
|
|
253
263
|
#
|
|
254
|
-
# The following format is for legacy syntax support. It will be deprecated in future versions of Rails.
|
|
264
|
+
# The following format is for legacy syntax support. It will be deprecated in future versions of \Rails.
|
|
255
265
|
#
|
|
256
266
|
# tag(name, options = nil, open = false, escape = true)
|
|
257
267
|
#
|
|
@@ -300,6 +310,7 @@ module ActionView
|
|
|
300
310
|
if name.nil?
|
|
301
311
|
tag_builder
|
|
302
312
|
else
|
|
313
|
+
name = ERB::Util.xml_name_escape(name) if escape
|
|
303
314
|
"<#{name}#{tag_builder.tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
|
|
304
315
|
end
|
|
305
316
|
end
|
|
@@ -308,7 +319,7 @@ module ActionView
|
|
|
308
319
|
# HTML attributes by passing an attributes hash to +options+.
|
|
309
320
|
# Instead of passing the content as an argument, you can also use a block
|
|
310
321
|
# in which case, you pass your +options+ as the second parameter.
|
|
311
|
-
# Set escape to false to disable
|
|
322
|
+
# Set escape to false to disable escaping.
|
|
312
323
|
# Note: this is legacy syntax, see +tag+ method description for details.
|
|
313
324
|
#
|
|
314
325
|
# ==== Options
|
|
@@ -353,7 +364,7 @@ module ActionView
|
|
|
353
364
|
# token_list(nil, false, 123, "", "foo", { bar: true })
|
|
354
365
|
# # => "123 foo bar"
|
|
355
366
|
def token_list(*args)
|
|
356
|
-
tokens = build_tag_values(*args).flat_map { |value| value.to_s.split(/\s+/) }.uniq
|
|
367
|
+
tokens = build_tag_values(*args).flat_map { |value| CGI.unescape_html(value.to_s).split(/\s+/) }.uniq
|
|
357
368
|
|
|
358
369
|
safe_join(tokens, " ")
|
|
359
370
|
end
|
|
@@ -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
|
|
40
|
+
object.public_send @method_name if object.respond_to?(@method_name)
|
|
40
41
|
else
|
|
41
|
-
object.public_send @method_name
|
|
42
|
+
object.public_send @method_name
|
|
42
43
|
end
|
|
43
44
|
end
|
|
44
45
|
|
|
45
46
|
def value_before_type_cast
|
|
46
|
-
unless object
|
|
47
|
-
method_before_type_cast = @method_name + "_before_type_cast"
|
|
47
|
+
return unless object
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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") || ""
|
|
@@ -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
|
|
@@ -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"]
|
|
10
|
-
options["min"] =
|
|
11
|
-
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
|
|
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
|
|
22
|
-
if value.is_a?
|
|
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
|
|
15
|
-
|
|
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
|
|
@@ -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)
|
|
@@ -34,7 +37,7 @@ module ActionView
|
|
|
34
37
|
# [nil, []]
|
|
35
38
|
# { nil => [] }
|
|
36
39
|
def grouped_choices?
|
|
37
|
-
!@choices.blank? && @choices.first.respond_to?(:
|
|
40
|
+
!@choices.blank? && @choices.first.respond_to?(:second) && Array === @choices.first.second
|
|
38
41
|
end
|
|
39
42
|
end
|
|
40
43
|
end
|