actionview 6.1.7.2 → 7.1.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +299 -277
- data/MIT-LICENSE +2 -1
- data/README.rdoc +3 -3
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +37 -19
- data/lib/action_view/buffers.rb +107 -9
- data/lib/action_view/cache_expiry.rb +48 -37
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
- data/lib/action_view/dependency_tracker.rb +6 -147
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +8 -5
- data/lib/action_view/flows.rb +4 -4
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +200 -60
- data/lib/action_view/helpers/asset_url_helper.rb +22 -21
- data/lib/action_view/helpers/atom_feed_helper.rb +8 -9
- data/lib/action_view/helpers/cache_helper.rb +55 -12
- data/lib/action_view/helpers/capture_helper.rb +34 -14
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +8 -2
- data/lib/action_view/helpers/csp_helper.rb +3 -3
- data/lib/action_view/helpers/csrf_helper.rb +4 -4
- data/lib/action_view/helpers/date_helper.rb +123 -57
- data/lib/action_view/helpers/debug_helper.rb +6 -4
- data/lib/action_view/helpers/form_helper.rb +253 -97
- data/lib/action_view/helpers/form_options_helper.rb +72 -34
- data/lib/action_view/helpers/form_tag_helper.rb +189 -58
- data/lib/action_view/helpers/javascript_helper.rb +4 -5
- data/lib/action_view/helpers/number_helper.rb +43 -335
- data/lib/action_view/helpers/output_safety_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +6 -7
- data/lib/action_view/helpers/sanitize_helper.rb +54 -24
- data/lib/action_view/helpers/tag_helper.rb +42 -35
- data/lib/action_view/helpers/tags/base.rb +16 -77
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- 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 +4 -1
- 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/file_field.rb +16 -0
- 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 +11 -2
- 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 +31 -0
- data/lib/action_view/helpers/tags.rb +5 -2
- data/lib/action_view/helpers/text_helper.rb +180 -97
- data/lib/action_view/helpers/translation_helper.rb +14 -45
- data/lib/action_view/helpers/url_helper.rb +230 -132
- data/lib/action_view/helpers.rb +27 -25
- data/lib/action_view/layouts.rb +15 -10
- data/lib/action_view/log_subscriber.rb +49 -32
- data/lib/action_view/lookup_context.rb +58 -61
- data/lib/action_view/model_naming.rb +2 -2
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +28 -35
- data/lib/action_view/railtie.rb +44 -9
- data/lib/action_view/record_identifier.rb +16 -9
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +3 -3
- data/lib/action_view/renderer/collection_renderer.rb +10 -2
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
- data/lib/action_view/renderer/partial_renderer.rb +3 -36
- data/lib/action_view/renderer/renderer.rb +6 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +6 -5
- data/lib/action_view/renderer/template_renderer.rb +9 -4
- data/lib/action_view/rendering.rb +25 -7
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +8 -5
- data/lib/action_view/template/error.rb +122 -14
- 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 +79 -1
- data/lib/action_view/template/handlers.rb +4 -4
- data/lib/action_view/template/html.rb +4 -4
- data/lib/action_view/template/inline.rb +3 -3
- data/lib/action_view/template/raw_file.rb +4 -4
- data/lib/action_view/template/renderable.rb +1 -1
- data/lib/action_view/template/resolver.rb +96 -313
- data/lib/action_view/template/text.rb +4 -4
- data/lib/action_view/template/types.rb +25 -32
- data/lib/action_view/template.rb +245 -41
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +66 -0
- data/lib/action_view/test_case.rb +182 -23
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +43 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +19 -28
- data/lib/action_view.rb +6 -4
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +32 -25
@@ -3,20 +3,23 @@
|
|
3
3
|
require "rails-html-sanitizer"
|
4
4
|
|
5
5
|
module ActionView
|
6
|
-
|
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 +javascript:+, 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.
|
@@ -26,26 +29,31 @@ module ActionView
|
|
26
29
|
#
|
27
30
|
# ==== Options
|
28
31
|
#
|
29
|
-
#
|
30
|
-
#
|
31
|
-
#
|
32
|
+
# [+:tags+]
|
33
|
+
# An array of allowed tags.
|
34
|
+
#
|
35
|
+
# [+:attributes+]
|
36
|
+
# An array of allowed attributes.
|
37
|
+
#
|
38
|
+
# [+:scrubber+]
|
39
|
+
# A {Rails::HTML scrubber}[https://github.com/rails/rails-html-sanitizer]
|
32
40
|
# or {Loofah::Scrubber}[https://github.com/flavorjones/loofah] object that
|
33
41
|
# defines custom sanitization rules. A custom scrubber takes precedence over
|
34
42
|
# custom tags and attributes.
|
35
43
|
#
|
36
44
|
# ==== Examples
|
37
45
|
#
|
38
|
-
# Normal use
|
46
|
+
# ===== Normal use
|
39
47
|
#
|
40
48
|
# <%= sanitize @comment.body %>
|
41
49
|
#
|
42
|
-
# Providing custom lists of permitted tags and attributes
|
50
|
+
# ===== Providing custom lists of permitted tags and attributes
|
43
51
|
#
|
44
52
|
# <%= sanitize @comment.body, tags: %w(strong em a), attributes: %w(href) %>
|
45
53
|
#
|
46
|
-
# Providing a custom Rails::
|
54
|
+
# ===== Providing a custom +Rails::HTML+ scrubber
|
47
55
|
#
|
48
|
-
# class CommentScrubber < Rails::
|
56
|
+
# class CommentScrubber < Rails::HTML::PermitScrubber
|
49
57
|
# def initialize
|
50
58
|
# super
|
51
59
|
# self.tags = %w( form script comment blockquote )
|
@@ -57,32 +65,54 @@ module ActionView
|
|
57
65
|
# end
|
58
66
|
# end
|
59
67
|
#
|
68
|
+
# <code></code>
|
69
|
+
#
|
60
70
|
# <%= sanitize @comment.body, scrubber: CommentScrubber.new %>
|
61
71
|
#
|
62
72
|
# See {Rails HTML Sanitizer}[https://github.com/rails/rails-html-sanitizer] for
|
63
|
-
# documentation about Rails::
|
73
|
+
# documentation about +Rails::HTML+ scrubbers.
|
64
74
|
#
|
65
|
-
# Providing a custom Loofah::Scrubber
|
75
|
+
# ===== Providing a custom +Loofah::Scrubber+
|
66
76
|
#
|
67
77
|
# scrubber = Loofah::Scrubber.new do |node|
|
68
78
|
# node.remove if node.name == 'script'
|
69
79
|
# end
|
70
80
|
#
|
81
|
+
# <code></code>
|
82
|
+
#
|
71
83
|
# <%= sanitize @comment.body, scrubber: scrubber %>
|
72
84
|
#
|
73
85
|
# See {Loofah's documentation}[https://github.com/flavorjones/loofah] for more
|
74
|
-
# information about defining custom Loofah::Scrubber objects.
|
86
|
+
# information about defining custom +Loofah::Scrubber+ objects.
|
87
|
+
#
|
88
|
+
# ==== Global Configuration
|
75
89
|
#
|
76
90
|
# To set the default allowed tags or attributes across your application:
|
77
91
|
#
|
78
92
|
# # In config/application.rb
|
79
93
|
# config.action_view.sanitized_allowed_tags = ['strong', 'em', 'a']
|
80
94
|
# config.action_view.sanitized_allowed_attributes = ['href', 'title']
|
95
|
+
#
|
96
|
+
# The default, starting in \Rails 7.1, is to use an HTML5 parser for sanitization (if it is
|
97
|
+
# available, see NOTE below). If you wish to revert back to the previous HTML4 behavior, you
|
98
|
+
# can do so by setting the following in your application configuration:
|
99
|
+
#
|
100
|
+
# # In config/application.rb
|
101
|
+
# config.action_view.sanitizer_vendor = Rails::HTML4::Sanitizer
|
102
|
+
#
|
103
|
+
# Or, if you're upgrading from a previous version of \Rails and wish to opt into the HTML5
|
104
|
+
# behavior:
|
105
|
+
#
|
106
|
+
# # In config/application.rb
|
107
|
+
# config.action_view.sanitizer_vendor = Rails::HTML5::Sanitizer
|
108
|
+
#
|
109
|
+
# NOTE: +Rails::HTML5::Sanitizer+ is not supported on JRuby, so on JRuby platforms \Rails will
|
110
|
+
# fall back to using +Rails::HTML4::Sanitizer+.
|
81
111
|
def sanitize(html, options = {})
|
82
112
|
self.class.safe_list_sanitizer.sanitize(html, options)&.html_safe
|
83
113
|
end
|
84
114
|
|
85
|
-
# Sanitizes a block of CSS code. Used by
|
115
|
+
# Sanitizes a block of CSS code. Used by #sanitize when it comes across a style attribute.
|
86
116
|
def sanitize_css(style)
|
87
117
|
self.class.safe_list_sanitizer.sanitize_css(style)
|
88
118
|
end
|
@@ -101,7 +131,7 @@ module ActionView
|
|
101
131
|
# strip_tags("> A quote from Smith & Wesson")
|
102
132
|
# # => > A quote from Smith & Wesson
|
103
133
|
def strip_tags(html)
|
104
|
-
self.class.full_sanitizer.sanitize(html)
|
134
|
+
self.class.full_sanitizer.sanitize(html)&.html_safe
|
105
135
|
end
|
106
136
|
|
107
137
|
# Strips all link tags from +html+ leaving just the link text.
|
@@ -121,11 +151,11 @@ module ActionView
|
|
121
151
|
self.class.link_sanitizer.sanitize(html)
|
122
152
|
end
|
123
153
|
|
124
|
-
module ClassMethods
|
154
|
+
module ClassMethods # :nodoc:
|
125
155
|
attr_writer :full_sanitizer, :link_sanitizer, :safe_list_sanitizer
|
126
156
|
|
127
157
|
def sanitizer_vendor
|
128
|
-
|
158
|
+
ActionView::Helpers::SanitizeHelper.sanitizer_vendor
|
129
159
|
end
|
130
160
|
|
131
161
|
def sanitized_allowed_tags
|
@@ -136,7 +166,7 @@ module ActionView
|
|
136
166
|
sanitizer_vendor.safe_list_sanitizer.allowed_attributes
|
137
167
|
end
|
138
168
|
|
139
|
-
# Gets the Rails::
|
169
|
+
# Gets the Rails::HTML::FullSanitizer instance used by +strip_tags+. Replace with
|
140
170
|
# any object that responds to +sanitize+.
|
141
171
|
#
|
142
172
|
# class Application < Rails::Application
|
@@ -146,7 +176,7 @@ module ActionView
|
|
146
176
|
@full_sanitizer ||= sanitizer_vendor.full_sanitizer.new
|
147
177
|
end
|
148
178
|
|
149
|
-
# Gets the Rails::
|
179
|
+
# Gets the Rails::HTML::LinkSanitizer instance used by +strip_links+.
|
150
180
|
# Replace with any object that responds to +sanitize+.
|
151
181
|
#
|
152
182
|
# class Application < Rails::Application
|
@@ -156,7 +186,7 @@ module ActionView
|
|
156
186
|
@link_sanitizer ||= sanitizer_vendor.link_sanitizer.new
|
157
187
|
end
|
158
188
|
|
159
|
-
# Gets the Rails::
|
189
|
+
# Gets the Rails::HTML::SafeListSanitizer instance used by sanitize and +sanitize_css+.
|
160
190
|
# Replace with any object that responds to +sanitize+.
|
161
191
|
#
|
162
192
|
# class Application < Rails::Application
|
@@ -1,15 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/enumerable"
|
3
4
|
require "active_support/core_ext/string/output_safety"
|
4
5
|
require "set"
|
6
|
+
require "action_view/helpers/capture_helper"
|
7
|
+
require "action_view/helpers/output_safety_helper"
|
5
8
|
|
6
9
|
module ActionView
|
7
|
-
|
8
|
-
|
10
|
+
module Helpers # :nodoc:
|
11
|
+
# = Action View Tag \Helpers
|
12
|
+
#
|
9
13
|
# Provides methods to generate HTML tags programmatically both as a modern
|
10
14
|
# HTML5 compliant builder style and legacy XHTML compliant tags.
|
11
15
|
module TagHelper
|
12
|
-
extend ActiveSupport::Concern
|
13
16
|
include CaptureHelper
|
14
17
|
include OutputSafetyHelper
|
15
18
|
|
@@ -39,26 +42,35 @@ module ActionView
|
|
39
42
|
PRE_CONTENT_STRINGS[:textarea] = "\n"
|
40
43
|
PRE_CONTENT_STRINGS["textarea"] = "\n"
|
41
44
|
|
42
|
-
class TagBuilder
|
45
|
+
class TagBuilder # :nodoc:
|
43
46
|
include CaptureHelper
|
44
47
|
include OutputSafetyHelper
|
45
48
|
|
46
|
-
|
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
|
47
51
|
|
48
52
|
def initialize(view_context)
|
49
53
|
@view_context = view_context
|
50
54
|
end
|
51
55
|
|
56
|
+
# Transforms a Hash into HTML Attributes, ready to be interpolated into
|
57
|
+
# ERB.
|
58
|
+
#
|
59
|
+
# <input <%= tag.attributes(type: :text, aria: { label: "Search" }) %> >
|
60
|
+
# # => <input type="text" aria-label="Search">
|
61
|
+
def attributes(attributes)
|
62
|
+
tag_options(attributes.to_h).to_s.strip.html_safe
|
63
|
+
end
|
64
|
+
|
52
65
|
def p(*arguments, **options, &block)
|
53
66
|
tag_string(:p, *arguments, **options, &block)
|
54
67
|
end
|
55
68
|
|
56
|
-
def tag_string(name, content = nil, **options, &block)
|
57
|
-
escape = handle_deprecated_escape_options(options)
|
58
|
-
|
69
|
+
def tag_string(name, content = nil, escape: true, **options, &block)
|
59
70
|
content = @view_context.capture(self, &block) if block_given?
|
60
|
-
|
61
|
-
|
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
|
62
74
|
else
|
63
75
|
content_tag_string(name.to_s.dasherize, content || "", options, escape)
|
64
76
|
end
|
@@ -128,6 +140,8 @@ module ActionView
|
|
128
140
|
when Array, Hash
|
129
141
|
value = TagHelper.build_tag_values(value) if key.to_s == "class"
|
130
142
|
value = escape ? safe_join(value, " ") : value.join(" ")
|
143
|
+
when Regexp
|
144
|
+
value = escape ? ERB::Util.unwrapped_html_escape(value.source) : value.source
|
131
145
|
else
|
132
146
|
value = escape ? ERB::Util.unwrapped_html_escape(value) : value.to_s
|
133
147
|
end
|
@@ -149,27 +163,6 @@ module ActionView
|
|
149
163
|
true
|
150
164
|
end
|
151
165
|
|
152
|
-
def handle_deprecated_escape_options(options)
|
153
|
-
# The option :escape_attributes has been merged into the options hash to be
|
154
|
-
# able to warn when it is used, so we need to handle default values here.
|
155
|
-
escape_option_provided = options.has_key?(:escape)
|
156
|
-
escape_attributes_option_provided = options.has_key?(:escape_attributes)
|
157
|
-
|
158
|
-
if escape_attributes_option_provided
|
159
|
-
ActiveSupport::Deprecation.warn(<<~MSG)
|
160
|
-
Use of the option :escape_attributes is deprecated. It currently \
|
161
|
-
escapes both names and values of tags and attributes and it is \
|
162
|
-
equivalent to :escape. If any of them are enabled, the escaping \
|
163
|
-
is fully enabled.
|
164
|
-
MSG
|
165
|
-
end
|
166
|
-
|
167
|
-
return true unless escape_option_provided || escape_attributes_option_provided
|
168
|
-
escape_option = options.delete(:escape)
|
169
|
-
escape_attributes_option = options.delete(:escape_attributes)
|
170
|
-
escape_option || escape_attributes_option
|
171
|
-
end
|
172
|
-
|
173
166
|
def method_missing(called, *args, **options, &block)
|
174
167
|
tag_string(called, *args, **options, &block)
|
175
168
|
end
|
@@ -225,7 +218,7 @@ module ActionView
|
|
225
218
|
#
|
226
219
|
# Thus <tt>data-user-id</tt> can be accessed as <tt>dataset.userId</tt>.
|
227
220
|
#
|
228
|
-
# 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
|
229
222
|
# BigDecimals.
|
230
223
|
# This may come in handy when using jQuery's HTML5-aware <tt>.data()</tt>
|
231
224
|
# from 1.4.3.
|
@@ -252,9 +245,23 @@ module ActionView
|
|
252
245
|
# # A void element:
|
253
246
|
# tag.br # => <br>
|
254
247
|
#
|
248
|
+
# === Building HTML attributes
|
249
|
+
#
|
250
|
+
# Transforms a Hash into HTML attributes, ready to be interpolated into
|
251
|
+
# ERB. Includes or omits boolean attributes based on their truthiness.
|
252
|
+
# Transforms keys nested within
|
253
|
+
# <tt>aria:</tt> or <tt>data:</tt> objects into <tt>aria-</tt> and <tt>data-</tt>
|
254
|
+
# prefixed attributes:
|
255
|
+
#
|
256
|
+
# <input <%= tag.attributes(type: :text, aria: { label: "Search" }) %>>
|
257
|
+
# # => <input type="text" aria-label="Search">
|
258
|
+
#
|
259
|
+
# <button <%= tag.attributes id: "call-to-action", disabled: false, aria: { expanded: false } %> class="primary">Get Started!</button>
|
260
|
+
# # => <button id="call-to-action" aria-expanded="false" class="primary">Get Started!</button>
|
261
|
+
#
|
255
262
|
# === Legacy syntax
|
256
263
|
#
|
257
|
-
# 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.
|
258
265
|
#
|
259
266
|
# tag(name, options = nil, open = false, escape = true)
|
260
267
|
#
|
@@ -357,7 +364,7 @@ module ActionView
|
|
357
364
|
# token_list(nil, false, 123, "", "foo", { bar: true })
|
358
365
|
# # => "123 foo bar"
|
359
366
|
def token_list(*args)
|
360
|
-
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
|
361
368
|
|
362
369
|
safe_join(tokens, " ")
|
363
370
|
end
|
@@ -377,7 +384,7 @@ module ActionView
|
|
377
384
|
# cdata_section("hello]]>world")
|
378
385
|
# # => <![CDATA[hello]]]]><![CDATA[>world]]>
|
379
386
|
def cdata_section(content)
|
380
|
-
splitted = content.to_s.gsub(/\]\]
|
387
|
+
splitted = content.to_s.gsub(/\]\]>/, "]]]]><![CDATA[>")
|
381
388
|
"<![CDATA[#{splitted}]]>".html_safe
|
382
389
|
end
|
383
390
|
|
@@ -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
|
|
@@ -97,7 +98,7 @@ module ActionView
|
|
97
98
|
options["name"] = options.fetch("name") { tag_name(options["multiple"], index) }
|
98
99
|
|
99
100
|
if generate_ids?
|
100
|
-
options["id"] = options.fetch("id") { tag_id(index) }
|
101
|
+
options["id"] = options.fetch("id") { tag_id(index, options.delete("namespace")) }
|
101
102
|
if namespace = options.delete("namespace")
|
102
103
|
options["id"] = options["id"] ? "#{namespace}_#{options['id']}" : namespace
|
103
104
|
end
|
@@ -105,31 +106,11 @@ module ActionView
|
|
105
106
|
end
|
106
107
|
|
107
108
|
def tag_name(multiple = false, index = nil)
|
108
|
-
|
109
|
-
case
|
110
|
-
when @object_name.empty?
|
111
|
-
"#{sanitized_method_name}#{multiple ? "[]" : ""}"
|
112
|
-
when index
|
113
|
-
"#{@object_name}[#{index}][#{sanitized_method_name}]#{multiple ? "[]" : ""}"
|
114
|
-
else
|
115
|
-
"#{@object_name}[#{sanitized_method_name}]#{multiple ? "[]" : ""}"
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
def tag_id(index = nil)
|
120
|
-
# a little duplication to construct fewer strings
|
121
|
-
case
|
122
|
-
when @object_name.empty?
|
123
|
-
sanitized_method_name.dup
|
124
|
-
when index
|
125
|
-
"#{sanitized_object_name}_#{index}_#{sanitized_method_name}"
|
126
|
-
else
|
127
|
-
"#{sanitized_object_name}_#{sanitized_method_name}"
|
128
|
-
end
|
109
|
+
@template_object.field_name(@object_name, sanitized_method_name, multiple: multiple, index: index)
|
129
110
|
end
|
130
111
|
|
131
|
-
def
|
132
|
-
@
|
112
|
+
def tag_id(index = nil, namespace = nil)
|
113
|
+
@template_object.field_id(@object_name, @method_name, index: index, namespace: namespace)
|
133
114
|
end
|
134
115
|
|
135
116
|
def sanitized_method_name
|
@@ -137,49 +118,7 @@ module ActionView
|
|
137
118
|
end
|
138
119
|
|
139
120
|
def sanitized_value(value)
|
140
|
-
value.to_s.gsub(/[\s
|
141
|
-
end
|
142
|
-
|
143
|
-
def select_content_tag(option_tags, options, html_options)
|
144
|
-
html_options = html_options.stringify_keys
|
145
|
-
add_default_name_and_id(html_options)
|
146
|
-
|
147
|
-
if placeholder_required?(html_options)
|
148
|
-
raise ArgumentError, "include_blank cannot be false for a required field." if options[:include_blank] == false
|
149
|
-
options[:include_blank] ||= true unless options[:prompt]
|
150
|
-
end
|
151
|
-
|
152
|
-
value = options.fetch(:selected) { value() }
|
153
|
-
select = content_tag("select", add_options(option_tags, options, value), html_options)
|
154
|
-
|
155
|
-
if html_options["multiple"] && options.fetch(:include_hidden, true)
|
156
|
-
tag("input", disabled: html_options["disabled"], name: html_options["name"], type: "hidden", value: "", autocomplete: "off") + select
|
157
|
-
else
|
158
|
-
select
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def placeholder_required?(html_options)
|
163
|
-
# See https://html.spec.whatwg.org/multipage/forms.html#attr-select-required
|
164
|
-
html_options["required"] && !html_options["multiple"] && html_options.fetch("size", 1).to_i == 1
|
165
|
-
end
|
166
|
-
|
167
|
-
def add_options(option_tags, options, value = nil)
|
168
|
-
if options[:include_blank]
|
169
|
-
content = (options[:include_blank] if options[:include_blank].is_a?(String))
|
170
|
-
label = (" " unless content)
|
171
|
-
option_tags = tag_builder.content_tag_string("option", content, value: "", label: label) + "\n" + option_tags
|
172
|
-
end
|
173
|
-
|
174
|
-
if value.blank? && options[:prompt]
|
175
|
-
tag_options = { value: "" }.tap do |prompt_opts|
|
176
|
-
prompt_opts[:disabled] = true if options[:disabled] == ""
|
177
|
-
prompt_opts[:selected] = true if options[:selected] == ""
|
178
|
-
end
|
179
|
-
option_tags = tag_builder.content_tag_string("option", prompt_text(options[:prompt]), tag_options) + "\n" + option_tags
|
180
|
-
end
|
181
|
-
|
182
|
-
option_tags
|
121
|
+
value.to_s.gsub(/[\s.]/, "_").gsub(/[^-[[:word:]]]/, "").downcase
|
183
122
|
end
|
184
123
|
|
185
124
|
def name_and_id_index(options)
|
@@ -5,7 +5,7 @@ require "action_view/helpers/tags/checkable"
|
|
5
5
|
module ActionView
|
6
6
|
module Helpers
|
7
7
|
module Tags # :nodoc:
|
8
|
-
class CheckBox < Base
|
8
|
+
class CheckBox < Base # :nodoc:
|
9
9
|
include Checkable
|
10
10
|
|
11
11
|
def initialize(object_name, method_name, template_object, checked_value, unchecked_value, options)
|
@@ -3,7 +3,10 @@
|
|
3
3
|
module ActionView
|
4
4
|
module Helpers
|
5
5
|
module Tags # :nodoc:
|
6
|
-
class CollectionSelect < Base
|
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,22 @@ module ActionView
|
|
4
4
|
module Helpers
|
5
5
|
module Tags # :nodoc:
|
6
6
|
class FileField < TextField # :nodoc:
|
7
|
+
def render
|
8
|
+
include_hidden = @options.delete(:include_hidden)
|
9
|
+
options = @options.stringify_keys
|
10
|
+
add_default_name_and_id(options)
|
11
|
+
|
12
|
+
if options["multiple"] && include_hidden
|
13
|
+
hidden_field_for_multiple_file(options) + super
|
14
|
+
else
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
def hidden_field_for_multiple_file(options)
|
21
|
+
tag("input", "name" => options["name"], "type" => "hidden", "value" => "", "autocomplete" => "off")
|
22
|
+
end
|
7
23
|
end
|
8
24
|
end
|
9
25
|
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
|