actionview 7.2.2.1 → 8.1.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 +104 -71
- data/README.rdoc +1 -1
- data/lib/action_view/base.rb +11 -11
- data/lib/action_view/buffers.rb +1 -1
- data/lib/action_view/dependency_tracker/erb_tracker.rb +37 -28
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +2 -19
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +7 -1
- data/lib/action_view/digestor.rb +6 -2
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +25 -6
- data/lib/action_view/helpers/atom_feed_helper.rb +1 -3
- data/lib/action_view/helpers/cache_helper.rb +10 -2
- data/lib/action_view/helpers/capture_helper.rb +2 -2
- data/lib/action_view/helpers/controller_helper.rb +6 -2
- data/lib/action_view/helpers/date_helper.rb +28 -4
- data/lib/action_view/helpers/form_helper.rb +103 -103
- data/lib/action_view/helpers/form_options_helper.rb +39 -35
- data/lib/action_view/helpers/form_tag_helper.rb +35 -25
- data/lib/action_view/helpers/javascript_helper.rb +5 -1
- data/lib/action_view/helpers/number_helper.rb +14 -0
- data/lib/action_view/helpers/output_safety_helper.rb +1 -2
- data/lib/action_view/helpers/rendering_helper.rb +160 -50
- data/lib/action_view/helpers/sanitize_helper.rb +6 -0
- data/lib/action_view/helpers/tag_helper.rb +57 -73
- data/lib/action_view/helpers/tags/base.rb +11 -9
- data/lib/action_view/helpers/tags/check_box.rb +9 -3
- 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/tags/datetime_field.rb +1 -1
- data/lib/action_view/helpers/tags/file_field.rb +7 -2
- data/lib/action_view/helpers/tags/hidden_field.rb +1 -1
- data/lib/action_view/helpers/tags/label.rb +3 -10
- data/lib/action_view/helpers/tags/radio_button.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +6 -1
- data/lib/action_view/helpers/tags/select_renderer.rb +6 -4
- data/lib/action_view/helpers/tags/text_area.rb +1 -1
- data/lib/action_view/helpers/tags/text_field.rb +1 -1
- data/lib/action_view/helpers/text_helper.rb +10 -3
- data/lib/action_view/helpers/translation_helper.rb +6 -1
- data/lib/action_view/helpers/url_helper.rb +39 -13
- data/lib/action_view/layouts.rb +7 -7
- data/lib/action_view/locale/en.yml +3 -0
- data/lib/action_view/log_subscriber.rb +1 -4
- data/lib/action_view/railtie.rb +12 -1
- data/lib/action_view/record_identifier.rb +22 -1
- data/lib/action_view/render_parser/prism_render_parser.rb +13 -1
- data/lib/action_view/render_parser/ripper_render_parser.rb +10 -1
- data/lib/action_view/renderer/partial_renderer.rb +18 -2
- data/lib/action_view/renderer/streaming_template_renderer.rb +8 -2
- data/lib/action_view/renderer/template_renderer.rb +3 -3
- data/lib/action_view/rendering.rb +2 -3
- data/lib/action_view/structured_event_subscriber.rb +97 -0
- data/lib/action_view/template/error.rb +18 -3
- data/lib/action_view/template/handlers/erb/erubi.rb +1 -1
- data/lib/action_view/template/handlers/erb.rb +77 -44
- data/lib/action_view/template/raw_file.rb +4 -0
- data/lib/action_view/template/resolver.rb +0 -1
- data/lib/action_view/template.rb +3 -4
- data/lib/action_view/test_case.rb +50 -53
- data/lib/action_view.rb +4 -0
- metadata +15 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5744f9f03363cee6bb6119e05f065b891143193967773bb6c9798ba08f5cf913
|
|
4
|
+
data.tar.gz: 5290a6f008203823f1b6a4aca07916475fc80ca02de83ea5a16e7ebeab02102c
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 56c52dfb00b9d4b7b1785efac2d74a334a65140ef93bf24ace09a2653f8c717a50990ed1b37662bdec4e1af2a2e4624001a5da4276a9610d709b559d541a59de
|
|
7
|
+
data.tar.gz: eb0953de4b065cc23aea27da97268891cb4656dbe2439762a1fa4b9a26436dbd27dd89c4d1f365df5a2525b70fc09ff8eb6a8602da3ffe90acaac7f8687e4bbc
|
data/CHANGELOG.md
CHANGED
|
@@ -1,133 +1,166 @@
|
|
|
1
|
-
## Rails
|
|
1
|
+
## Rails 8.1.2 (January 08, 2026) ##
|
|
2
2
|
|
|
3
|
-
*
|
|
3
|
+
* Fix `file_field` to join mime types with a comma when provided as Array
|
|
4
4
|
|
|
5
|
+
```ruby
|
|
6
|
+
file_field(:article, :image, accept: ['image/png', 'image/gif', 'image/jpeg'])
|
|
7
|
+
```
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
* No changes.
|
|
9
|
-
|
|
9
|
+
Now behaves likes:
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
```
|
|
12
|
+
file_field(:article, :image, accept: 'image/png,image/gif,image/jpeg')
|
|
13
|
+
```
|
|
12
14
|
|
|
13
|
-
*
|
|
15
|
+
*Bogdan Gusiev*
|
|
14
16
|
|
|
17
|
+
* Fix strict locals parsing to handle multiline definitions.
|
|
15
18
|
|
|
16
|
-
|
|
19
|
+
*Said Kaldybaev*
|
|
17
20
|
|
|
18
|
-
*
|
|
21
|
+
* Fix `content_security_policy_nonce` error in mailers when using `content_security_policy_nonce_auto` setting.
|
|
19
22
|
|
|
23
|
+
The `content_security_policy_nonce helper` is provided by `ActionController::ContentSecurityPolicy`, and it relies on `request.content_security_policy_nonc`e. Mailers lack both the module and the request object.
|
|
20
24
|
|
|
21
|
-
|
|
25
|
+
*Jarrett Lusso*
|
|
22
26
|
|
|
23
|
-
* No changes.
|
|
24
27
|
|
|
28
|
+
## Rails 8.1.1 (October 28, 2025) ##
|
|
25
29
|
|
|
26
|
-
|
|
30
|
+
* Respect `remove_hidden_field_autocomplete` config in form builder `hidden_field`.
|
|
27
31
|
|
|
28
|
-
*
|
|
32
|
+
*Rafael Mendonça França*
|
|
29
33
|
|
|
30
|
-
Previously templates defining strict locals wouldn't receive the `local_assigns`
|
|
31
|
-
hash.
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
## Rails 8.1.0 (October 22, 2025) ##
|
|
34
36
|
|
|
35
|
-
*
|
|
37
|
+
* The BEGIN template annotation/comment was previously printed on the same line as the following element. We now insert a newline inside the comment so it spans two lines without adding visible whitespace to the HTML output to enhance readability.
|
|
36
38
|
|
|
39
|
+
Before:
|
|
40
|
+
```
|
|
41
|
+
<!-- BEGIN /Users/siaw23/Desktop/rails/actionview/test/fixtures/actionpack/test/greeting.html.erb --><p>This is grand!</p>
|
|
37
42
|
```
|
|
38
|
-
# Before
|
|
39
|
-
Completed 200 OK in 3804ms (Views: 41.0ms | ActiveRecord: 33.5ms | Allocations: 112788)
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
Completed 200 OK in 3804ms (Views: 41.0ms | ActiveRecord: 33.5ms (2 queries, 1 cached) | Allocations: 112788)
|
|
44
|
+
After:
|
|
43
45
|
```
|
|
46
|
+
<!-- BEGIN /Users/siaw23/Desktop/rails/actionview/test/fixtures/actionpack/test/greeting.html.erb
|
|
47
|
+
--><p>This is grand!</p>
|
|
48
|
+
```
|
|
49
|
+
*Emmanuel Hayford*
|
|
44
50
|
|
|
45
|
-
|
|
51
|
+
* Add structured events for Action View:
|
|
52
|
+
- `action_view.render_template`
|
|
53
|
+
- `action_view.render_partial`
|
|
54
|
+
- `action_view.render_layout`
|
|
55
|
+
- `action_view.render_collection`
|
|
56
|
+
- `action_view.render_start`
|
|
46
57
|
|
|
47
|
-
*
|
|
58
|
+
*Gannon McGibbon*
|
|
48
59
|
|
|
49
|
-
|
|
60
|
+
* Fix label with `for` option not getting prefixed by form `namespace` value
|
|
50
61
|
|
|
51
|
-
*
|
|
62
|
+
*Abeid Ahmed*, *Hartley McGuire*
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
* Add `fetchpriority` to Link headers to match HTML generated by `preload_link_tag`.
|
|
54
65
|
|
|
55
|
-
*
|
|
66
|
+
*Guillermo Iguaran*
|
|
56
67
|
|
|
57
|
-
*
|
|
68
|
+
* Add CSP `nonce` to Link headers generated by `preload_link_tag`.
|
|
58
69
|
|
|
59
|
-
*
|
|
70
|
+
*Alexander Gitter*
|
|
60
71
|
|
|
61
|
-
*
|
|
72
|
+
* Allow `current_page?` to match against specific HTTP method(s) with a `method:` option.
|
|
62
73
|
|
|
63
|
-
|
|
74
|
+
*Ben Sheldon*
|
|
64
75
|
|
|
65
|
-
|
|
76
|
+
* Remove `autocomplete="off"` on hidden inputs generated by the following
|
|
77
|
+
tags:
|
|
66
78
|
|
|
67
|
-
*
|
|
79
|
+
* `form_tag`, `token_tag`, `method_tag`
|
|
68
80
|
|
|
69
|
-
|
|
81
|
+
As well as the hidden parameter fields included in `button_to`,
|
|
82
|
+
`check_box`, `select` (with `multiple`) and `file_field` forms.
|
|
70
83
|
|
|
71
|
-
*
|
|
84
|
+
*nkulway*
|
|
72
85
|
|
|
73
|
-
|
|
86
|
+
* Enable configuring the strategy for tracking dependencies between Action
|
|
87
|
+
View templates.
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
The existing `:regex` strategy is kept as the default, but with
|
|
90
|
+
`load_defaults 8.1` the strategy will be `:ruby` (using a real Ruby parser).
|
|
76
91
|
|
|
77
92
|
*Hartley McGuire*
|
|
78
93
|
|
|
79
|
-
*
|
|
94
|
+
* Introduce `relative_time_in_words` helper
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
relative_time_in_words(3.minutes.from_now) # => "in 3 minutes"
|
|
98
|
+
relative_time_in_words(3.minutes.ago) # => "3 minutes ago"
|
|
99
|
+
relative_time_in_words(10.seconds.ago, include_seconds: true) # => "less than 10 seconds ago"
|
|
100
|
+
```
|
|
80
101
|
|
|
81
|
-
*
|
|
102
|
+
*Matheus Richard*
|
|
82
103
|
|
|
83
|
-
*
|
|
104
|
+
* Make `nonce: false` remove the nonce attribute from `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag`.
|
|
84
105
|
|
|
85
|
-
|
|
106
|
+
*francktrouillez*
|
|
86
107
|
|
|
87
|
-
|
|
108
|
+
* Add `dom_target` helper to create `dom_id`-like strings from an unlimited
|
|
109
|
+
number of objects.
|
|
88
110
|
|
|
89
|
-
*
|
|
111
|
+
*Ben Sheldon*
|
|
90
112
|
|
|
91
|
-
*
|
|
113
|
+
* Respect `html_options[:form]` when `collection_checkboxes` generates the
|
|
114
|
+
hidden `<input>`.
|
|
92
115
|
|
|
93
|
-
*
|
|
116
|
+
*Riccardo Odone*
|
|
94
117
|
|
|
95
|
-
*
|
|
118
|
+
* Layouts have access to local variables passed to `render`.
|
|
96
119
|
|
|
97
|
-
|
|
98
|
-
it would instead return the entire buffer.
|
|
120
|
+
This fixes #31680 which was a regression in Rails 5.1.
|
|
99
121
|
|
|
100
|
-
*
|
|
122
|
+
*Mike Dalessio*
|
|
101
123
|
|
|
102
|
-
*
|
|
124
|
+
* Argument errors related to strict locals in templates now raise an
|
|
125
|
+
`ActionView::StrictLocalsError`, and all other argument errors are reraised as-is.
|
|
103
126
|
|
|
104
|
-
|
|
105
|
-
|
|
127
|
+
Previously, any `ArgumentError` raised during template rendering was swallowed during strict
|
|
128
|
+
local error handling, so that an `ArgumentError` unrelated to strict locals (e.g., a helper
|
|
129
|
+
method invoked with incorrect arguments) would be replaced by a similar `ArgumentError` with an
|
|
130
|
+
unrelated backtrace, making it difficult to debug templates.
|
|
106
131
|
|
|
107
|
-
|
|
132
|
+
Now, any `ArgumentError` unrelated to strict locals is reraised, preserving the original
|
|
133
|
+
backtrace for developers.
|
|
108
134
|
|
|
109
|
-
|
|
135
|
+
Also note that `ActionView::StrictLocalsError` is a subclass of `ArgumentError`, so any existing
|
|
136
|
+
code that rescues `ArgumentError` will continue to work.
|
|
110
137
|
|
|
111
|
-
|
|
112
|
-
specification. If an invalid HTML tag name is provided, the method raises an `ArgumentError`
|
|
113
|
-
with an appropriate error message.
|
|
138
|
+
Fixes #52227.
|
|
114
139
|
|
|
115
|
-
|
|
140
|
+
*Mike Dalessio*
|
|
116
141
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
content_tag("12p") # Starting with a number
|
|
142
|
+
* Improve error highlighting of multi-line methods in ERB templates or
|
|
143
|
+
templates where the error occurs within a do-end block.
|
|
120
144
|
|
|
121
|
-
|
|
122
|
-
content_tag("") # Empty tag name
|
|
145
|
+
*Martin Emde*
|
|
123
146
|
|
|
124
|
-
|
|
125
|
-
|
|
147
|
+
* Fix a crash in ERB template error highlighting when the error occurs on a
|
|
148
|
+
line in the compiled template that is past the end of the source template.
|
|
126
149
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
150
|
+
*Martin Emde*
|
|
151
|
+
|
|
152
|
+
* Improve reliability of ERB template error highlighting.
|
|
153
|
+
Fix infinite loops and crashes in highlighting and
|
|
154
|
+
improve tolerance for alternate ERB handlers.
|
|
155
|
+
|
|
156
|
+
*Martin Emde*
|
|
157
|
+
|
|
158
|
+
* Allow `hidden_field` and `hidden_field_tag` to accept a custom autocomplete value.
|
|
159
|
+
|
|
160
|
+
*brendon*
|
|
161
|
+
|
|
162
|
+
* Add a new configuration `content_security_policy_nonce_auto` for automatically adding a nonce to the tags affected by the directives specified by the `content_security_policy_nonce_directives` configuration option.
|
|
130
163
|
|
|
131
|
-
*
|
|
164
|
+
*francktrouillez*
|
|
132
165
|
|
|
133
|
-
Please check [
|
|
166
|
+
Please check [8-0-stable](https://github.com/rails/rails/blob/8-0-stable/actionview/CHANGELOG.md) for previous changes.
|
data/README.rdoc
CHANGED
|
@@ -35,6 +35,6 @@ Bug reports for the Ruby on \Rails project can be filed here:
|
|
|
35
35
|
|
|
36
36
|
* https://github.com/rails/rails/issues
|
|
37
37
|
|
|
38
|
-
Feature requests should be discussed on the
|
|
38
|
+
Feature requests should be discussed on the rubyonrails-core forum here:
|
|
39
39
|
|
|
40
40
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|
data/lib/action_view/base.rb
CHANGED
|
@@ -4,6 +4,7 @@ require "active_support/core_ext/module/attr_internal"
|
|
|
4
4
|
require "active_support/core_ext/module/attribute_accessors"
|
|
5
5
|
require "active_support/ordered_options"
|
|
6
6
|
require "action_view/log_subscriber"
|
|
7
|
+
require "action_view/structured_event_subscriber"
|
|
7
8
|
require "action_view/helpers"
|
|
8
9
|
require "action_view/context"
|
|
9
10
|
require "action_view/template"
|
|
@@ -181,6 +182,10 @@ module ActionView # :nodoc:
|
|
|
181
182
|
class_attribute :_routes
|
|
182
183
|
class_attribute :logger
|
|
183
184
|
|
|
185
|
+
# Specify whether to omit autocomplete="off" on hidden inputs generated by helpers.
|
|
186
|
+
# Configured via `config.action_view.remove_hidden_field_autocomplete`
|
|
187
|
+
cattr_accessor :remove_hidden_field_autocomplete, default: false
|
|
188
|
+
|
|
184
189
|
class << self
|
|
185
190
|
delegate :erb_trim_mode=, to: "ActionView::Template::Handlers::ERB"
|
|
186
191
|
|
|
@@ -242,8 +247,6 @@ module ActionView # :nodoc:
|
|
|
242
247
|
# :startdoc:
|
|
243
248
|
|
|
244
249
|
def initialize(lookup_context, assigns, controller) # :nodoc:
|
|
245
|
-
@_config = ActiveSupport::InheritableOptions.new
|
|
246
|
-
|
|
247
250
|
@lookup_context = lookup_context
|
|
248
251
|
|
|
249
252
|
@view_renderer = ActionView::Renderer.new @lookup_context
|
|
@@ -267,15 +270,12 @@ module ActionView # :nodoc:
|
|
|
267
270
|
begin
|
|
268
271
|
public_send(method, locals, buffer, **locals, &block)
|
|
269
272
|
rescue ArgumentError => argument_error
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
gsub("no keywords accepted", "no locals accepted").
|
|
277
|
-
concat(" for #{@current_template.short_identifier}")
|
|
278
|
-
)
|
|
273
|
+
public_send_line = __LINE__ - 2
|
|
274
|
+
frame = argument_error.backtrace_locations[1]
|
|
275
|
+
if frame.path == __FILE__ && frame.lineno == public_send_line
|
|
276
|
+
raise StrictLocalsError.new(argument_error, @current_template)
|
|
277
|
+
end
|
|
278
|
+
raise
|
|
279
279
|
end
|
|
280
280
|
else
|
|
281
281
|
public_send(method, locals, buffer, &block)
|
data/lib/action_view/buffers.rb
CHANGED
|
@@ -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
|
-
|
|
94
|
-
render_calls = source.
|
|
93
|
+
dependencies = []
|
|
94
|
+
render_calls = source.scan(/<%(?:(?:(?!<%).)*?\brender\b((?:(?!%>).)*?))%>/m).flatten
|
|
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
|
|
@@ -10,7 +10,7 @@ module ActionView
|
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
def dependencies
|
|
13
|
-
render_dependencies + explicit_dependencies
|
|
13
|
+
WildcardResolver.new(view_paths, render_dependencies + explicit_dependencies).resolve
|
|
14
14
|
end
|
|
15
15
|
|
|
16
16
|
def self.supports_view_paths? # :nodoc:
|
|
@@ -31,29 +31,12 @@ module ActionView
|
|
|
31
31
|
compiled_source = template.handler.call(template, template.source)
|
|
32
32
|
|
|
33
33
|
@parser_class.new(@name, compiled_source).render_calls.filter_map do |render_call|
|
|
34
|
-
next if render_call.end_with?("/_")
|
|
35
34
|
render_call.gsub(%r|/_|, "/")
|
|
36
35
|
end
|
|
37
36
|
end
|
|
38
37
|
|
|
39
38
|
def explicit_dependencies
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
wildcards, explicits = dependencies.partition { |dependency| dependency.end_with?("/*") }
|
|
43
|
-
|
|
44
|
-
(explicits + resolve_directories(wildcards)).uniq
|
|
45
|
-
end
|
|
46
|
-
|
|
47
|
-
def resolve_directories(wildcard_dependencies)
|
|
48
|
-
return [] unless view_paths
|
|
49
|
-
return [] if wildcard_dependencies.empty?
|
|
50
|
-
|
|
51
|
-
# Remove trailing "/*"
|
|
52
|
-
prefixes = wildcard_dependencies.map { |query| query[0..-3] }
|
|
53
|
-
|
|
54
|
-
view_paths.flat_map(&:all_template_paths).uniq.filter_map { |path|
|
|
55
|
-
path.to_s if prefixes.include?(path.prefix)
|
|
56
|
-
}.sort
|
|
39
|
+
template.source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
|
|
57
40
|
end
|
|
58
41
|
end
|
|
59
42
|
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
|
|
@@ -10,6 +10,7 @@ module ActionView
|
|
|
10
10
|
|
|
11
11
|
autoload :ERBTracker
|
|
12
12
|
autoload :RubyTracker
|
|
13
|
+
autoload :WildcardResolver
|
|
13
14
|
|
|
14
15
|
@trackers = Concurrent::Map.new
|
|
15
16
|
|
|
@@ -35,6 +36,11 @@ module ActionView
|
|
|
35
36
|
@trackers.delete(handler)
|
|
36
37
|
end
|
|
37
38
|
|
|
38
|
-
|
|
39
|
+
case ActionView.render_tracker
|
|
40
|
+
when :ruby
|
|
41
|
+
register_tracker :erb, RubyTracker
|
|
42
|
+
else
|
|
43
|
+
register_tracker :erb, ERBTracker
|
|
44
|
+
end
|
|
39
45
|
end
|
|
40
46
|
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
|
|
|
@@ -26,6 +26,8 @@ module ActionView
|
|
|
26
26
|
mattr_accessor :image_decoding
|
|
27
27
|
mattr_accessor :preload_links_header
|
|
28
28
|
mattr_accessor :apply_stylesheet_media_default
|
|
29
|
+
mattr_accessor :auto_include_nonce_for_scripts
|
|
30
|
+
mattr_accessor :auto_include_nonce_for_styles
|
|
29
31
|
|
|
30
32
|
# Returns an HTML script tag for each of the +sources+ provided.
|
|
31
33
|
#
|
|
@@ -115,11 +117,11 @@ module ActionView
|
|
|
115
117
|
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
|
116
118
|
preload_links = []
|
|
117
119
|
use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
|
|
118
|
-
nopush = options["nopush"].nil?
|
|
120
|
+
nopush = options["nopush"].nil? || options.delete("nopush")
|
|
119
121
|
crossorigin = options.delete("crossorigin")
|
|
120
122
|
crossorigin = "anonymous" if crossorigin == true
|
|
121
123
|
integrity = options["integrity"]
|
|
122
|
-
rel = options["type"] == "module" ? "modulepreload" : "preload"
|
|
124
|
+
rel = options["type"] == "module" || options["type"] == :module ? "modulepreload" : "preload"
|
|
123
125
|
|
|
124
126
|
sources_tags = sources.uniq.map { |source|
|
|
125
127
|
href = path_to_javascript(source, path_options)
|
|
@@ -127,6 +129,7 @@ module ActionView
|
|
|
127
129
|
preload_link = "<#{href}>; rel=#{rel}; as=script"
|
|
128
130
|
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
|
|
129
131
|
preload_link += "; integrity=#{integrity}" unless integrity.nil?
|
|
132
|
+
preload_link += "; nonce=#{content_security_policy_nonce}" if options["nonce"] == true
|
|
130
133
|
preload_link += "; nopush" if nopush
|
|
131
134
|
preload_links << preload_link
|
|
132
135
|
end
|
|
@@ -134,8 +137,10 @@ module ActionView
|
|
|
134
137
|
"src" => href,
|
|
135
138
|
"crossorigin" => crossorigin
|
|
136
139
|
}.merge!(options)
|
|
137
|
-
if tag_options["nonce"] == true
|
|
140
|
+
if tag_options["nonce"] == true || (!tag_options.key?("nonce") && auto_include_nonce_for_scripts)
|
|
138
141
|
tag_options["nonce"] = content_security_policy_nonce
|
|
142
|
+
elsif tag_options["nonce"] == false
|
|
143
|
+
tag_options.delete("nonce")
|
|
139
144
|
end
|
|
140
145
|
content_tag("script", "", tag_options)
|
|
141
146
|
}.join("\n").html_safe
|
|
@@ -206,7 +211,7 @@ module ActionView
|
|
|
206
211
|
preload_links = []
|
|
207
212
|
crossorigin = options.delete("crossorigin")
|
|
208
213
|
crossorigin = "anonymous" if crossorigin == true
|
|
209
|
-
nopush = options["nopush"].nil?
|
|
214
|
+
nopush = options["nopush"].nil? || options.delete("nopush")
|
|
210
215
|
integrity = options["integrity"]
|
|
211
216
|
|
|
212
217
|
sources_tags = sources.uniq.map { |source|
|
|
@@ -215,6 +220,7 @@ module ActionView
|
|
|
215
220
|
preload_link = "<#{href}>; rel=preload; as=style"
|
|
216
221
|
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
|
|
217
222
|
preload_link += "; integrity=#{integrity}" unless integrity.nil?
|
|
223
|
+
preload_link += "; nonce=#{content_security_policy_nonce}" if options["nonce"] == true
|
|
218
224
|
preload_link += "; nopush" if nopush
|
|
219
225
|
preload_links << preload_link
|
|
220
226
|
end
|
|
@@ -223,8 +229,10 @@ module ActionView
|
|
|
223
229
|
"crossorigin" => crossorigin,
|
|
224
230
|
"href" => href
|
|
225
231
|
}.merge!(options)
|
|
226
|
-
if tag_options["nonce"] == true
|
|
232
|
+
if tag_options["nonce"] == true || (!tag_options.key?("nonce") && auto_include_nonce_for_styles && respond_to?(:content_security_policy_nonce))
|
|
227
233
|
tag_options["nonce"] = content_security_policy_nonce
|
|
234
|
+
elsif tag_options["nonce"] == false
|
|
235
|
+
tag_options.delete("nonce")
|
|
228
236
|
end
|
|
229
237
|
|
|
230
238
|
if apply_stylesheet_media_default && tag_options["media"].blank?
|
|
@@ -360,8 +368,16 @@ module ActionView
|
|
|
360
368
|
crossorigin = options.delete(:crossorigin)
|
|
361
369
|
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
|
|
362
370
|
integrity = options[:integrity]
|
|
371
|
+
fetchpriority = options.delete(:fetchpriority)
|
|
363
372
|
nopush = options.delete(:nopush) || false
|
|
364
|
-
rel = mime_type == "module" ? "modulepreload" : "preload"
|
|
373
|
+
rel = mime_type == "module" || mime_type == :module ? "modulepreload" : "preload"
|
|
374
|
+
add_nonce = content_security_policy_nonce &&
|
|
375
|
+
respond_to?(:request) &&
|
|
376
|
+
request.content_security_policy_nonce_directives&.include?("#{as_type}-src")
|
|
377
|
+
|
|
378
|
+
if add_nonce
|
|
379
|
+
options[:nonce] = content_security_policy_nonce
|
|
380
|
+
end
|
|
365
381
|
|
|
366
382
|
link_tag = tag.link(
|
|
367
383
|
rel: rel,
|
|
@@ -369,12 +385,15 @@ module ActionView
|
|
|
369
385
|
as: as_type,
|
|
370
386
|
type: mime_type,
|
|
371
387
|
crossorigin: crossorigin,
|
|
388
|
+
fetchpriority: fetchpriority,
|
|
372
389
|
**options.symbolize_keys)
|
|
373
390
|
|
|
374
391
|
preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}"
|
|
375
392
|
preload_link += "; type=#{mime_type}" if mime_type
|
|
376
393
|
preload_link += "; crossorigin=#{crossorigin}" if crossorigin
|
|
394
|
+
preload_link += "; fetchpriority=#{fetchpriority}" if fetchpriority
|
|
377
395
|
preload_link += "; integrity=#{integrity}" if integrity
|
|
396
|
+
preload_link += "; nonce=#{content_security_policy_nonce}" if add_nonce
|
|
378
397
|
preload_link += "; nopush" if nopush
|
|
379
398
|
|
|
380
399
|
send_preload_links_header([preload_link])
|