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.
Files changed (63) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +104 -71
  3. data/README.rdoc +1 -1
  4. data/lib/action_view/base.rb +11 -11
  5. data/lib/action_view/buffers.rb +1 -1
  6. data/lib/action_view/dependency_tracker/erb_tracker.rb +37 -28
  7. data/lib/action_view/dependency_tracker/ruby_tracker.rb +2 -19
  8. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  9. data/lib/action_view/dependency_tracker.rb +7 -1
  10. data/lib/action_view/digestor.rb +6 -2
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/asset_tag_helper.rb +25 -6
  13. data/lib/action_view/helpers/atom_feed_helper.rb +1 -3
  14. data/lib/action_view/helpers/cache_helper.rb +10 -2
  15. data/lib/action_view/helpers/capture_helper.rb +2 -2
  16. data/lib/action_view/helpers/controller_helper.rb +6 -2
  17. data/lib/action_view/helpers/date_helper.rb +28 -4
  18. data/lib/action_view/helpers/form_helper.rb +103 -103
  19. data/lib/action_view/helpers/form_options_helper.rb +39 -35
  20. data/lib/action_view/helpers/form_tag_helper.rb +35 -25
  21. data/lib/action_view/helpers/javascript_helper.rb +5 -1
  22. data/lib/action_view/helpers/number_helper.rb +14 -0
  23. data/lib/action_view/helpers/output_safety_helper.rb +1 -2
  24. data/lib/action_view/helpers/rendering_helper.rb +160 -50
  25. data/lib/action_view/helpers/sanitize_helper.rb +6 -0
  26. data/lib/action_view/helpers/tag_helper.rb +57 -73
  27. data/lib/action_view/helpers/tags/base.rb +11 -9
  28. data/lib/action_view/helpers/tags/check_box.rb +9 -3
  29. data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -3
  30. data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
  31. data/lib/action_view/helpers/tags/datetime_field.rb +1 -1
  32. data/lib/action_view/helpers/tags/file_field.rb +7 -2
  33. data/lib/action_view/helpers/tags/hidden_field.rb +1 -1
  34. data/lib/action_view/helpers/tags/label.rb +3 -10
  35. data/lib/action_view/helpers/tags/radio_button.rb +1 -1
  36. data/lib/action_view/helpers/tags/select.rb +6 -1
  37. data/lib/action_view/helpers/tags/select_renderer.rb +6 -4
  38. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  39. data/lib/action_view/helpers/tags/text_field.rb +1 -1
  40. data/lib/action_view/helpers/text_helper.rb +10 -3
  41. data/lib/action_view/helpers/translation_helper.rb +6 -1
  42. data/lib/action_view/helpers/url_helper.rb +39 -13
  43. data/lib/action_view/layouts.rb +7 -7
  44. data/lib/action_view/locale/en.yml +3 -0
  45. data/lib/action_view/log_subscriber.rb +1 -4
  46. data/lib/action_view/railtie.rb +12 -1
  47. data/lib/action_view/record_identifier.rb +22 -1
  48. data/lib/action_view/render_parser/prism_render_parser.rb +13 -1
  49. data/lib/action_view/render_parser/ripper_render_parser.rb +10 -1
  50. data/lib/action_view/renderer/partial_renderer.rb +18 -2
  51. data/lib/action_view/renderer/streaming_template_renderer.rb +8 -2
  52. data/lib/action_view/renderer/template_renderer.rb +3 -3
  53. data/lib/action_view/rendering.rb +2 -3
  54. data/lib/action_view/structured_event_subscriber.rb +97 -0
  55. data/lib/action_view/template/error.rb +18 -3
  56. data/lib/action_view/template/handlers/erb/erubi.rb +1 -1
  57. data/lib/action_view/template/handlers/erb.rb +77 -44
  58. data/lib/action_view/template/raw_file.rb +4 -0
  59. data/lib/action_view/template/resolver.rb +0 -1
  60. data/lib/action_view/template.rb +3 -4
  61. data/lib/action_view/test_case.rb +50 -53
  62. data/lib/action_view.rb +4 -0
  63. metadata +15 -16
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 129f00c083e1a4443010be161169e51eea090543c6618289968ed2cdb1c65c76
4
- data.tar.gz: 568d88d12f7fc363958af2f8bf45308199dc0c221a05fafb9415a96b3eaddc8a
3
+ metadata.gz: 5744f9f03363cee6bb6119e05f065b891143193967773bb6c9798ba08f5cf913
4
+ data.tar.gz: 5290a6f008203823f1b6a4aca07916475fc80ca02de83ea5a16e7ebeab02102c
5
5
  SHA512:
6
- metadata.gz: de08bd40788b58e9ba6be2169a5ebbc39c6b4f1122d1564b64cf0a6af066f855d4a1ad2d1721ab849fde78ee87c496c61579afd59d1a3062dba6135b6652bee4
7
- data.tar.gz: cf2f5461be068f0d96d287bbdabe2daf9b3d76a4e80c9db31041d9242f77986b1d5d6774d6db95f5f2b068cbce2161b13335891bfd86d271994d49321bc88733
6
+ metadata.gz: 56c52dfb00b9d4b7b1785efac2d74a334a65140ef93bf24ace09a2653f8c717a50990ed1b37662bdec4e1af2a2e4624001a5da4276a9610d709b559d541a59de
7
+ data.tar.gz: eb0953de4b065cc23aea27da97268891cb4656dbe2439762a1fa4b9a26436dbd27dd89c4d1f365df5a2525b70fc09ff8eb6a8602da3ffe90acaac7f8687e4bbc
data/CHANGELOG.md CHANGED
@@ -1,133 +1,166 @@
1
- ## Rails 7.2.2.1 (December 10, 2024) ##
1
+ ## Rails 8.1.2 (January 08, 2026) ##
2
2
 
3
- * No changes.
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
- ## Rails 7.2.2 (October 30, 2024) ##
7
-
8
- * No changes.
9
-
9
+ Now behaves likes:
10
10
 
11
- ## Rails 7.2.1.2 (October 23, 2024) ##
11
+ ```
12
+ file_field(:article, :image, accept: 'image/png,image/gif,image/jpeg')
13
+ ```
12
14
 
13
- * No changes.
15
+ *Bogdan Gusiev*
14
16
 
17
+ * Fix strict locals parsing to handle multiline definitions.
15
18
 
16
- ## Rails 7.2.1.1 (October 15, 2024) ##
19
+ *Said Kaldybaev*
17
20
 
18
- * No changes.
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
- ## Rails 7.2.1 (August 22, 2024) ##
25
+ *Jarrett Lusso*
22
26
 
23
- * No changes.
24
27
 
28
+ ## Rails 8.1.1 (October 28, 2025) ##
25
29
 
26
- ## Rails 7.2.0 (August 09, 2024) ##
30
+ * Respect `remove_hidden_field_autocomplete` config in form builder `hidden_field`.
27
31
 
28
- * Fix templates with strict locals to also include `local_assigns`.
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
- *Jean Boussier*
35
+ ## Rails 8.1.0 (October 22, 2025) ##
34
36
 
35
- * Add queries count to template rendering instrumentation.
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
- # After
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
- *fatkodima*
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
- * Raise `ArgumentError` if `:renderable` object does not respond to `#render_in`.
58
+ *Gannon McGibbon*
48
59
 
49
- *Sean Doyle*
60
+ * Fix label with `for` option not getting prefixed by form `namespace` value
50
61
 
51
- * Add the `nonce: true` option for `stylesheet_link_tag` helper to support automatic nonce generation for Content Security Policy.
62
+ *Abeid Ahmed*, *Hartley McGuire*
52
63
 
53
- Works the same way as `javascript_include_tag nonce: true` does.
64
+ * Add `fetchpriority` to Link headers to match HTML generated by `preload_link_tag`.
54
65
 
55
- *Akhil G Krishnan*, *AJ Esler*
66
+ *Guillermo Iguaran*
56
67
 
57
- * Parse `ActionView::TestCase#rendered` HTML content as `Nokogiri::XML::DocumentFragment` instead of `Nokogiri::XML::Document`.
68
+ * Add CSP `nonce` to Link headers generated by `preload_link_tag`.
58
69
 
59
- *Sean Doyle*
70
+ *Alexander Gitter*
60
71
 
61
- * Rename `ActionView::TestCase::Behavior::Content` to `ActionView::TestCase::Behavior::RenderedViewContent`.
72
+ * Allow `current_page?` to match against specific HTTP method(s) with a `method:` option.
62
73
 
63
- Make `RenderedViewContent` inherit from `String`. Make private API with `:nodoc:`
74
+ *Ben Sheldon*
64
75
 
65
- *Sean Doyle*
76
+ * Remove `autocomplete="off"` on hidden inputs generated by the following
77
+ tags:
66
78
 
67
- * Deprecate passing `nil` as value for the `model:` argument to the `form_with` method.
79
+ * `form_tag`, `token_tag`, `method_tag`
68
80
 
69
- *Collin Jilbert*
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
- * Alias `field_set_tag` helper to `fieldset_tag` to match `<fieldset>` element.
84
+ *nkulway*
72
85
 
73
- *Sean Doyle*
86
+ * Enable configuring the strategy for tracking dependencies between Action
87
+ View templates.
74
88
 
75
- * Deprecate passing content to void elements when using `tag.br` type tag builders.
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
- * Fix the `number_to_human_size` view helper to correctly work with negative numbers.
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
- *Earlopain*
102
+ *Matheus Richard*
82
103
 
83
- * Automatically discard the implicit locals injected by collection rendering for template that can't accept them.
104
+ * Make `nonce: false` remove the nonce attribute from `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag`.
84
105
 
85
- When rendering a collection, two implicit variables are injected, which breaks templates with strict locals.
106
+ *francktrouillez*
86
107
 
87
- Now they are only passed if the template will actually accept them.
108
+ * Add `dom_target` helper to create `dom_id`-like strings from an unlimited
109
+ number of objects.
88
110
 
89
- *Yasha Krasnou*, *Jean Boussier*
111
+ *Ben Sheldon*
90
112
 
91
- * Fix `@rails/ujs` calling `start()` an extra time when using bundlers.
113
+ * Respect `html_options[:form]` when `collection_checkboxes` generates the
114
+ hidden `<input>`.
92
115
 
93
- *Hartley McGuire*, *Ryunosuke Sato*
116
+ *Riccardo Odone*
94
117
 
95
- * Fix the `capture` view helper compatibility with HAML and Slim.
118
+ * Layouts have access to local variables passed to `render`.
96
119
 
97
- When a blank string was captured in HAML or Slim (and possibly other template engines)
98
- it would instead return the entire buffer.
120
+ This fixes #31680 which was a regression in Rails 5.1.
99
121
 
100
- *Jean Boussier*
122
+ *Mike Dalessio*
101
123
 
102
- * Updated `@rails/ujs` files to ignore certain data-* attributes when element is contenteditable.
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
- This fix was already landed in >= 7.0.4.3, < 7.1.0.
105
- [[CVE-2023-23913](https://github.com/advisories/GHSA-xp5h-f8jf-rc8q)]
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
- *Ryunosuke Sato*
132
+ Now, any `ArgumentError` unrelated to strict locals is reraised, preserving the original
133
+ backtrace for developers.
108
134
 
109
- * Added validation for HTML tag names in the `tag` and `content_tag` helper method.
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
- The `tag` and `content_tag` method now checks that the provided tag name adheres to the HTML
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
- Examples:
140
+ *Mike Dalessio*
116
141
 
117
- ```ruby
118
- # Raises ArgumentError: Invalid HTML5 tag name: 12p
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
- # Raises ArgumentError: Invalid HTML5 tag name: ""
122
- content_tag("") # Empty tag name
145
+ *Martin Emde*
123
146
 
124
- # Raises ArgumentError: Invalid HTML5 tag name: div/
125
- tag("div/") # Contains a solidus
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
- # Raises ArgumentError: Invalid HTML5 tag name: "image file"
128
- tag("image file") # Contains a space
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
- *Akhil G Krishnan*
164
+ *francktrouillez*
132
165
 
133
- Please check [7-1-stable](https://github.com/rails/rails/blob/7-1-stable/actionview/CHANGELOG.md) for previous changes.
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 rails-core mailing list here:
38
+ Feature requests should be discussed on the rubyonrails-core forum here:
39
39
 
40
40
  * https://discuss.rubyonrails.org/c/rubyonrails-core
@@ -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
- raise(
271
- ArgumentError,
272
- argument_error.
273
- message.
274
- gsub("unknown keyword:", "unknown local:").
275
- gsub("missing keyword:", "missing local:").
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)
@@ -45,7 +45,7 @@ module ActionView
45
45
  @raw_buffer << if value.html_safe?
46
46
  value
47
47
  else
48
- CGI.escapeHTML(value)
48
+ ERB::Util.unwrapped_html_escape(value)
49
49
  end
50
50
  end
51
51
  self
@@ -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
- render_dependencies = []
94
- render_calls = source.split(/\brender\b/).drop(1)
93
+ dependencies = []
94
+ render_calls = source.scan(/<%(?:(?:(?!<%).)*?\brender\b((?:(?!%>).)*?))%>/m).flatten
95
95
 
96
96
  render_calls.each do |arguments|
97
- add_dependencies(render_dependencies, arguments, LAYOUT_DEPENDENCY)
98
- add_dependencies(render_dependencies, arguments, RENDER_ARGUMENTS)
97
+ add_dependencies(dependencies, arguments, LAYOUT_DEPENDENCY)
98
+ add_dependencies(dependencies, arguments, RENDER_ARGUMENTS)
99
99
  end
100
100
 
101
- render_dependencies.uniq
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
- # Ignore if there is interpolation
121
- return if dependency.include?('#{')
122
- end
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
- if dependency
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
- dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
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
- dependencies = template.source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
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
- register_tracker :erb, ERBTracker
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
@@ -107,8 +107,12 @@ module ActionView
107
107
  end.join("-")
108
108
  end
109
109
 
110
- def to_dep_map
111
- children.any? ? { name => children.map(&:to_dep_map) } : name
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
 
@@ -7,10 +7,10 @@ module ActionView
7
7
  end
8
8
 
9
9
  module VERSION
10
- MAJOR = 7
11
- MINOR = 2
10
+ MAJOR = 8
11
+ MINOR = 1
12
12
  TINY = 2
13
- PRE = "1"
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -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? ? true : options.delete("nopush")
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? ? true : options.delete("nopush")
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])