actionview 7.1.2 → 8.0.2
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 +51 -382
- data/lib/action_view/base.rb +25 -11
- data/lib/action_view/cache_expiry.rb +9 -3
- data/lib/action_view/dependency_tracker/erb_tracker.rb +36 -27
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +2 -1
- data/lib/action_view/digestor.rb +6 -2
- data/lib/action_view/gem_version.rb +2 -2
- data/lib/action_view/helpers/asset_tag_helper.rb +18 -6
- data/lib/action_view/helpers/atom_feed_helper.rb +0 -2
- data/lib/action_view/helpers/cache_helper.rb +14 -6
- data/lib/action_view/helpers/csrf_helper.rb +1 -1
- data/lib/action_view/helpers/date_helper.rb +3 -3
- data/lib/action_view/helpers/form_helper.rb +282 -273
- data/lib/action_view/helpers/form_options_helper.rb +23 -21
- data/lib/action_view/helpers/form_tag_helper.rb +104 -69
- data/lib/action_view/helpers/number_helper.rb +35 -329
- data/lib/action_view/helpers/output_safety_helper.rb +5 -6
- data/lib/action_view/helpers/rendering_helper.rb +160 -50
- data/lib/action_view/helpers/sanitize_helper.rb +31 -14
- data/lib/action_view/helpers/tag_helper.rb +196 -19
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -3
- data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
- data/lib/action_view/helpers/text_helper.rb +125 -69
- data/lib/action_view/helpers/url_helper.rb +6 -80
- data/lib/action_view/layouts.rb +11 -13
- data/lib/action_view/log_subscriber.rb +8 -4
- data/lib/action_view/railtie.rb +0 -1
- data/lib/action_view/record_identifier.rb +1 -1
- data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
- data/lib/action_view/{ripper_ast_parser.rb → render_parser/ripper_render_parser.rb} +162 -10
- data/lib/action_view/render_parser.rb +21 -169
- data/lib/action_view/renderer/abstract_renderer.rb +1 -1
- data/lib/action_view/renderer/partial_renderer.rb +2 -2
- data/lib/action_view/renderer/renderer.rb +32 -38
- data/lib/action_view/renderer/streaming_template_renderer.rb +0 -1
- data/lib/action_view/renderer/template_renderer.rb +3 -3
- data/lib/action_view/rendering.rb +6 -7
- data/lib/action_view/template/error.rb +11 -0
- data/lib/action_view/template/handlers/erb.rb +45 -37
- data/lib/action_view/template/renderable.rb +7 -1
- data/lib/action_view/template/resolver.rb +0 -3
- data/lib/action_view/template.rb +46 -12
- data/lib/action_view/test_case.rb +14 -16
- data/lib/action_view/unbound_template.rb +4 -4
- data/lib/action_view.rb +1 -1
- metadata +17 -19
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +0 -59
- data/lib/assets/compiled/rails-ujs.js +0 -777
|
@@ -74,7 +74,7 @@ module ActionView
|
|
|
74
74
|
end
|
|
75
75
|
|
|
76
76
|
def dependencies
|
|
77
|
-
render_dependencies + explicit_dependencies
|
|
77
|
+
WildcardResolver.new(@view_paths, render_dependencies + explicit_dependencies).resolve
|
|
78
78
|
end
|
|
79
79
|
|
|
80
80
|
attr_reader :name, :template
|
|
@@ -90,15 +90,15 @@ module ActionView
|
|
|
90
90
|
end
|
|
91
91
|
|
|
92
92
|
def render_dependencies
|
|
93
|
-
|
|
93
|
+
dependencies = []
|
|
94
94
|
render_calls = source.split(/\brender\b/).drop(1)
|
|
95
95
|
|
|
96
96
|
render_calls.each do |arguments|
|
|
97
|
-
add_dependencies(
|
|
98
|
-
add_dependencies(
|
|
97
|
+
add_dependencies(dependencies, arguments, LAYOUT_DEPENDENCY)
|
|
98
|
+
add_dependencies(dependencies, arguments, RENDER_ARGUMENTS)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
|
|
101
|
+
dependencies
|
|
102
102
|
end
|
|
103
103
|
|
|
104
104
|
def add_dependencies(render_dependencies, arguments, pattern)
|
|
@@ -116,12 +116,37 @@ module ActionView
|
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
def add_static_dependency(dependencies, dependency, quote_type)
|
|
119
|
-
if quote_type == '"'
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
119
|
+
if quote_type == '"' && dependency.include?('#{')
|
|
120
|
+
scanner = StringScanner.new(dependency)
|
|
121
|
+
|
|
122
|
+
wildcard_dependency = +""
|
|
123
|
+
|
|
124
|
+
while !scanner.eos?
|
|
125
|
+
if scanner.scan_until(/\#{/)
|
|
126
|
+
unmatched_brackets = 1
|
|
127
|
+
wildcard_dependency << scanner.pre_match
|
|
128
|
+
|
|
129
|
+
while unmatched_brackets > 0 && !scanner.eos?
|
|
130
|
+
found = scanner.scan_until(/[{}]/)
|
|
131
|
+
return unless found
|
|
132
|
+
|
|
133
|
+
case scanner.matched
|
|
134
|
+
when "{"
|
|
135
|
+
unmatched_brackets += 1
|
|
136
|
+
when "}"
|
|
137
|
+
unmatched_brackets -= 1
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
wildcard_dependency << "*"
|
|
142
|
+
else
|
|
143
|
+
wildcard_dependency << scanner.rest
|
|
144
|
+
scanner.terminate
|
|
145
|
+
end
|
|
146
|
+
end
|
|
123
147
|
|
|
124
|
-
|
|
148
|
+
dependencies << wildcard_dependency
|
|
149
|
+
elsif dependency
|
|
125
150
|
if dependency.include?("/")
|
|
126
151
|
dependencies << dependency
|
|
127
152
|
else
|
|
@@ -130,24 +155,8 @@ module ActionView
|
|
|
130
155
|
end
|
|
131
156
|
end
|
|
132
157
|
|
|
133
|
-
def resolve_directories(wildcard_dependencies)
|
|
134
|
-
return [] unless @view_paths
|
|
135
|
-
return [] if wildcard_dependencies.empty?
|
|
136
|
-
|
|
137
|
-
# Remove trailing "/*"
|
|
138
|
-
prefixes = wildcard_dependencies.map { |query| query[0..-3] }
|
|
139
|
-
|
|
140
|
-
@view_paths.flat_map(&:all_template_paths).uniq.filter_map { |path|
|
|
141
|
-
path.to_s if prefixes.include?(path.prefix)
|
|
142
|
-
}.sort
|
|
143
|
-
end
|
|
144
|
-
|
|
145
158
|
def explicit_dependencies
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
wildcards, explicits = dependencies.partition { |dependency| dependency.end_with?("/*") }
|
|
149
|
-
|
|
150
|
-
(explicits + resolve_directories(wildcards)).uniq
|
|
159
|
+
source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
|
|
151
160
|
end
|
|
152
161
|
end
|
|
153
162
|
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
class DependencyTracker # :nodoc:
|
|
5
|
+
class RubyTracker # :nodoc:
|
|
6
|
+
EXPLICIT_DEPENDENCY = /# Template Dependency: (\S+)/
|
|
7
|
+
|
|
8
|
+
def self.call(name, template, view_paths = nil)
|
|
9
|
+
new(name, template, view_paths).dependencies
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def dependencies
|
|
13
|
+
WildcardResolver.new(view_paths, render_dependencies + explicit_dependencies).resolve
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def self.supports_view_paths? # :nodoc:
|
|
17
|
+
true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def initialize(name, template, view_paths = nil, parser_class: RenderParser::Default)
|
|
21
|
+
@name, @template, @view_paths = name, template, view_paths
|
|
22
|
+
@parser_class = parser_class
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
private
|
|
26
|
+
attr_reader :template, :name, :view_paths
|
|
27
|
+
|
|
28
|
+
def render_dependencies
|
|
29
|
+
return [] unless template.source.include?("render")
|
|
30
|
+
|
|
31
|
+
compiled_source = template.handler.call(template, template.source)
|
|
32
|
+
|
|
33
|
+
@parser_class.new(@name, compiled_source).render_calls.filter_map do |render_call|
|
|
34
|
+
render_call.gsub(%r|/_|, "/")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def explicit_dependencies
|
|
39
|
+
template.source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ActionView
|
|
4
|
+
class DependencyTracker # :nodoc:
|
|
5
|
+
class WildcardResolver # :nodoc:
|
|
6
|
+
def initialize(view_paths, dependencies)
|
|
7
|
+
@view_paths = view_paths
|
|
8
|
+
|
|
9
|
+
@wildcard_dependencies, @explicit_dependencies =
|
|
10
|
+
dependencies.partition { |dependency| dependency.end_with?("/*") }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def resolve
|
|
14
|
+
return explicit_dependencies.uniq if !view_paths || wildcard_dependencies.empty?
|
|
15
|
+
|
|
16
|
+
(explicit_dependencies + resolved_wildcard_dependencies).uniq
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
attr_reader :explicit_dependencies, :wildcard_dependencies, :view_paths
|
|
21
|
+
|
|
22
|
+
def resolved_wildcard_dependencies
|
|
23
|
+
# Remove trailing "/*"
|
|
24
|
+
prefixes = wildcard_dependencies.map { |query| query[0..-3] }
|
|
25
|
+
|
|
26
|
+
view_paths.flat_map(&:all_template_paths).uniq.filter_map { |path|
|
|
27
|
+
path.to_s if prefixes.include?(path.prefix)
|
|
28
|
+
}.sort
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
data/lib/action_view/digestor.rb
CHANGED
|
@@ -107,8 +107,12 @@ module ActionView
|
|
|
107
107
|
end.join("-")
|
|
108
108
|
end
|
|
109
109
|
|
|
110
|
-
def to_dep_map
|
|
111
|
-
|
|
110
|
+
def to_dep_map(seen = Set.new.compare_by_identity)
|
|
111
|
+
if seen.add?(self)
|
|
112
|
+
children.any? ? { name => children.map { |c| c.to_dep_map(seen) } } : name
|
|
113
|
+
else # the tree has a cycle
|
|
114
|
+
name
|
|
115
|
+
end
|
|
112
116
|
end
|
|
113
117
|
end
|
|
114
118
|
|
|
@@ -68,6 +68,8 @@ module ActionView
|
|
|
68
68
|
# attribute, which indicates to the browser that the script is meant to
|
|
69
69
|
# be executed after the document has been parsed. Additionally, prevents
|
|
70
70
|
# sending the Preload Links header.
|
|
71
|
+
# * <tt>:nopush</tt> - Specify if the use of server push is not desired
|
|
72
|
+
# for the script. Defaults to +true+.
|
|
71
73
|
#
|
|
72
74
|
# Any other specified options will be treated as HTML attributes for the
|
|
73
75
|
# +script+ tag.
|
|
@@ -166,6 +168,10 @@ module ActionView
|
|
|
166
168
|
# that path.
|
|
167
169
|
# * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
|
|
168
170
|
# when it is set to true.
|
|
171
|
+
# * <tt>:nonce</tt> - When set to true, adds an automatic nonce value if
|
|
172
|
+
# you have Content Security Policy enabled.
|
|
173
|
+
# * <tt>:nopush</tt> - Specify if the use of server push is not desired
|
|
174
|
+
# for the stylesheet. Defaults to +true+.
|
|
169
175
|
#
|
|
170
176
|
# ==== Examples
|
|
171
177
|
#
|
|
@@ -190,6 +196,9 @@ module ActionView
|
|
|
190
196
|
# stylesheet_link_tag "random.styles", "/css/stylish"
|
|
191
197
|
# # => <link href="/assets/random.styles" rel="stylesheet" />
|
|
192
198
|
# # <link href="/css/stylish.css" rel="stylesheet" />
|
|
199
|
+
#
|
|
200
|
+
# stylesheet_link_tag "style", nonce: true
|
|
201
|
+
# # => <link href="/assets/style.css" rel="stylesheet" nonce="..." />
|
|
193
202
|
def stylesheet_link_tag(*sources)
|
|
194
203
|
options = sources.extract_options!.stringify_keys
|
|
195
204
|
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
|
@@ -214,6 +223,9 @@ module ActionView
|
|
|
214
223
|
"crossorigin" => crossorigin,
|
|
215
224
|
"href" => href
|
|
216
225
|
}.merge!(options)
|
|
226
|
+
if tag_options["nonce"] == true
|
|
227
|
+
tag_options["nonce"] = content_security_policy_nonce
|
|
228
|
+
end
|
|
217
229
|
|
|
218
230
|
if apply_stylesheet_media_default && tag_options["media"].blank?
|
|
219
231
|
tag_options["media"] = "screen"
|
|
@@ -351,13 +363,13 @@ module ActionView
|
|
|
351
363
|
nopush = options.delete(:nopush) || false
|
|
352
364
|
rel = mime_type == "module" ? "modulepreload" : "preload"
|
|
353
365
|
|
|
354
|
-
link_tag = tag.link(
|
|
366
|
+
link_tag = tag.link(
|
|
355
367
|
rel: rel,
|
|
356
368
|
href: href,
|
|
357
369
|
as: as_type,
|
|
358
370
|
type: mime_type,
|
|
359
|
-
crossorigin: crossorigin
|
|
360
|
-
|
|
371
|
+
crossorigin: crossorigin,
|
|
372
|
+
**options.symbolize_keys)
|
|
361
373
|
|
|
362
374
|
preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}"
|
|
363
375
|
preload_link += "; type=#{mime_type}" if mime_type
|
|
@@ -645,11 +657,11 @@ module ActionView
|
|
|
645
657
|
return if response_present && response.sending?
|
|
646
658
|
|
|
647
659
|
if respond_to?(:request) && request
|
|
648
|
-
request.send_early_hints("
|
|
660
|
+
request.send_early_hints("link" => preload_links.join(","))
|
|
649
661
|
end
|
|
650
662
|
|
|
651
663
|
if response_present
|
|
652
|
-
header = +response.headers["
|
|
664
|
+
header = +response.headers["link"].to_s
|
|
653
665
|
preload_links.each do |link|
|
|
654
666
|
break if header.bytesize + link.bytesize > max_header_size
|
|
655
667
|
|
|
@@ -660,7 +672,7 @@ module ActionView
|
|
|
660
672
|
end
|
|
661
673
|
end
|
|
662
674
|
|
|
663
|
-
response.headers["
|
|
675
|
+
response.headers["link"] = header
|
|
664
676
|
end
|
|
665
677
|
end
|
|
666
678
|
end
|
|
@@ -76,11 +76,11 @@ module ActionView
|
|
|
76
76
|
# render 'comments/comments'
|
|
77
77
|
# render('comments/comments')
|
|
78
78
|
#
|
|
79
|
-
# render "header" translates to render("comments/header")
|
|
79
|
+
# render "header" # translates to render("comments/header")
|
|
80
80
|
#
|
|
81
|
-
# render(@topic) translates to render("topics/topic")
|
|
82
|
-
# render(topics) translates to render("topics/topic")
|
|
83
|
-
# render(message.topics) translates to render("topics/topic")
|
|
81
|
+
# render(@topic) # translates to render("topics/topic")
|
|
82
|
+
# render(topics) # translates to render("topics/topic")
|
|
83
|
+
# render(message.topics) # translates to render("topics/topic")
|
|
84
84
|
#
|
|
85
85
|
# It's not possible to derive all render calls like that, though.
|
|
86
86
|
# Here are a few examples of things that can't be derived:
|
|
@@ -93,6 +93,14 @@ module ActionView
|
|
|
93
93
|
# render partial: 'attachments/attachment', collection: group_of_attachments
|
|
94
94
|
# render partial: 'documents/document', collection: @project.documents.where(published: true).order('created_at')
|
|
95
95
|
#
|
|
96
|
+
# One last type of dependency can be determined implicitly:
|
|
97
|
+
#
|
|
98
|
+
# render "maintenance_tasks/runs/info/#{run.status}"
|
|
99
|
+
#
|
|
100
|
+
# Because the value passed to render ends in interpolation, Action View
|
|
101
|
+
# will mark all partials within the "maintenance_tasks/runs/info" folder as
|
|
102
|
+
# dependencies.
|
|
103
|
+
#
|
|
96
104
|
# === Explicit dependencies
|
|
97
105
|
#
|
|
98
106
|
# Sometimes you'll have template dependencies that can't be derived at all. This is typically
|
|
@@ -189,7 +197,7 @@ module ActionView
|
|
|
189
197
|
CachingRegistry.caching?
|
|
190
198
|
end
|
|
191
199
|
|
|
192
|
-
# Raises
|
|
200
|
+
# Raises UncacheableFragmentError when called from within a +cache+ block.
|
|
193
201
|
#
|
|
194
202
|
# Useful to denote helper methods that can't participate in fragment caching:
|
|
195
203
|
#
|
|
@@ -198,7 +206,7 @@ module ActionView
|
|
|
198
206
|
# "#{project.name} - #{Time.now}"
|
|
199
207
|
# end
|
|
200
208
|
#
|
|
201
|
-
# # Which will then raise if used within a
|
|
209
|
+
# # Which will then raise if used within a `cache` block:
|
|
202
210
|
# <% cache project do %>
|
|
203
211
|
# <%= project_name_with_time(project) %>
|
|
204
212
|
# <% end %>
|
|
@@ -17,7 +17,7 @@ module ActionView
|
|
|
17
17
|
# You don't need to use these tags for regular forms as they generate their own hidden fields.
|
|
18
18
|
#
|
|
19
19
|
# For Ajax requests other than GETs, extract the "csrf-token" from the meta-tag and send as the
|
|
20
|
-
# +X-CSRF-Token+ HTTP header.
|
|
20
|
+
# +X-CSRF-Token+ HTTP header.
|
|
21
21
|
#
|
|
22
22
|
def csrf_meta_tags
|
|
23
23
|
if defined?(protect_against_forgery?) && protect_against_forgery?
|
|
@@ -1228,7 +1228,7 @@ module ActionView
|
|
|
1228
1228
|
class FormBuilder
|
|
1229
1229
|
# Wraps ActionView::Helpers::DateHelper#date_select for form builders:
|
|
1230
1230
|
#
|
|
1231
|
-
# <%=
|
|
1231
|
+
# <%= form_with model: @person do |f| %>
|
|
1232
1232
|
# <%= f.date_select :birth_date %>
|
|
1233
1233
|
# <%= f.submit %>
|
|
1234
1234
|
# <% end %>
|
|
@@ -1240,7 +1240,7 @@ module ActionView
|
|
|
1240
1240
|
|
|
1241
1241
|
# Wraps ActionView::Helpers::DateHelper#time_select for form builders:
|
|
1242
1242
|
#
|
|
1243
|
-
# <%=
|
|
1243
|
+
# <%= form_with model: @race do |f| %>
|
|
1244
1244
|
# <%= f.time_select :average_lap %>
|
|
1245
1245
|
# <%= f.submit %>
|
|
1246
1246
|
# <% end %>
|
|
@@ -1252,7 +1252,7 @@ module ActionView
|
|
|
1252
1252
|
|
|
1253
1253
|
# Wraps ActionView::Helpers::DateHelper#datetime_select for form builders:
|
|
1254
1254
|
#
|
|
1255
|
-
# <%=
|
|
1255
|
+
# <%= form_with model: @person do |f| %>
|
|
1256
1256
|
# <%= f.datetime_select :last_request_at %>
|
|
1257
1257
|
# <%= f.submit %>
|
|
1258
1258
|
# <% end %>
|