actionview 6.0.0.beta1 → 6.1.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (86) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +273 -119
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -3
  5. data/lib/action_view/base.rb +81 -15
  6. data/lib/action_view/cache_expiry.rb +52 -0
  7. data/lib/action_view/context.rb +0 -5
  8. data/lib/action_view/dependency_tracker.rb +10 -4
  9. data/lib/action_view/digestor.rb +11 -19
  10. data/lib/action_view/flows.rb +0 -1
  11. data/lib/action_view/gem_version.rb +3 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +0 -1
  13. data/lib/action_view/helpers/asset_tag_helper.rb +62 -22
  14. data/lib/action_view/helpers/asset_url_helper.rb +6 -4
  15. data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
  16. data/lib/action_view/helpers/cache_helper.rb +16 -23
  17. data/lib/action_view/helpers/csp_helper.rb +4 -2
  18. data/lib/action_view/helpers/date_helper.rb +5 -6
  19. data/lib/action_view/helpers/form_helper.rb +70 -34
  20. data/lib/action_view/helpers/form_options_helper.rb +10 -18
  21. data/lib/action_view/helpers/form_tag_helper.rb +12 -9
  22. data/lib/action_view/helpers/javascript_helper.rb +7 -5
  23. data/lib/action_view/helpers/number_helper.rb +9 -8
  24. data/lib/action_view/helpers/output_safety_helper.rb +1 -1
  25. data/lib/action_view/helpers/rendering_helper.rb +17 -7
  26. data/lib/action_view/helpers/sanitize_helper.rb +10 -16
  27. data/lib/action_view/helpers/tag_helper.rb +94 -19
  28. data/lib/action_view/helpers/tags/base.rb +10 -7
  29. data/lib/action_view/helpers/tags/check_box.rb +0 -1
  30. data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
  31. data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
  32. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
  33. data/lib/action_view/helpers/tags/color_field.rb +0 -1
  34. data/lib/action_view/helpers/tags/date_field.rb +1 -2
  35. data/lib/action_view/helpers/tags/date_select.rb +2 -3
  36. data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
  37. data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -2
  38. data/lib/action_view/helpers/tags/label.rb +4 -1
  39. data/lib/action_view/helpers/tags/month_field.rb +1 -2
  40. data/lib/action_view/helpers/tags/radio_button.rb +0 -1
  41. data/lib/action_view/helpers/tags/select.rb +1 -2
  42. data/lib/action_view/helpers/tags/text_field.rb +0 -1
  43. data/lib/action_view/helpers/tags/time_field.rb +1 -2
  44. data/lib/action_view/helpers/tags/week_field.rb +1 -2
  45. data/lib/action_view/helpers/text_helper.rb +2 -3
  46. data/lib/action_view/helpers/translation_helper.rb +98 -51
  47. data/lib/action_view/helpers/url_helper.rb +124 -16
  48. data/lib/action_view/layouts.rb +8 -10
  49. data/lib/action_view/log_subscriber.rb +26 -11
  50. data/lib/action_view/lookup_context.rb +59 -31
  51. data/lib/action_view/path_set.rb +3 -12
  52. data/lib/action_view/railtie.rb +39 -41
  53. data/lib/action_view/record_identifier.rb +0 -1
  54. data/lib/action_view/renderer/abstract_renderer.rb +142 -11
  55. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  56. data/lib/action_view/renderer/object_renderer.rb +34 -0
  57. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +35 -29
  58. data/lib/action_view/renderer/partial_renderer.rb +21 -273
  59. data/lib/action_view/renderer/renderer.rb +59 -4
  60. data/lib/action_view/renderer/streaming_template_renderer.rb +9 -7
  61. data/lib/action_view/renderer/template_renderer.rb +35 -27
  62. data/lib/action_view/rendering.rb +49 -29
  63. data/lib/action_view/routing_url_for.rb +1 -1
  64. data/lib/action_view/template/error.rb +30 -15
  65. data/lib/action_view/template/handlers/builder.rb +2 -2
  66. data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
  67. data/lib/action_view/template/handlers/erb.rb +14 -19
  68. data/lib/action_view/template/handlers/html.rb +1 -1
  69. data/lib/action_view/template/handlers/raw.rb +2 -2
  70. data/lib/action_view/template/handlers.rb +1 -1
  71. data/lib/action_view/template/html.rb +5 -6
  72. data/lib/action_view/template/inline.rb +22 -0
  73. data/lib/action_view/template/raw_file.rb +25 -0
  74. data/lib/action_view/template/renderable.rb +24 -0
  75. data/lib/action_view/template/resolver.rb +141 -140
  76. data/lib/action_view/template/sources/file.rb +17 -0
  77. data/lib/action_view/template/sources.rb +13 -0
  78. data/lib/action_view/template/text.rb +2 -3
  79. data/lib/action_view/template.rb +49 -75
  80. data/lib/action_view/test_case.rb +20 -28
  81. data/lib/action_view/testing/resolvers.rb +18 -27
  82. data/lib/action_view/unbound_template.rb +31 -0
  83. data/lib/action_view/view_paths.rb +59 -38
  84. data/lib/action_view.rb +7 -2
  85. data/lib/assets/compiled/rails-ujs.js +25 -16
  86. metadata +30 -18
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ class CacheExpiry
5
+ class Executor
6
+ def initialize(watcher:)
7
+ @cache_expiry = CacheExpiry.new(watcher: watcher)
8
+ end
9
+
10
+ def before(target)
11
+ @cache_expiry.clear_cache_if_necessary
12
+ end
13
+ end
14
+
15
+ def initialize(watcher:)
16
+ @watched_dirs = nil
17
+ @watcher_class = watcher
18
+ @watcher = nil
19
+ @mutex = Mutex.new
20
+ end
21
+
22
+ def clear_cache_if_necessary
23
+ @mutex.synchronize do
24
+ watched_dirs = dirs_to_watch
25
+ return if watched_dirs.empty?
26
+
27
+ if watched_dirs != @watched_dirs
28
+ @watched_dirs = watched_dirs
29
+ @watcher = @watcher_class.new([], watched_dirs) do
30
+ clear_cache
31
+ end
32
+ @watcher.execute
33
+ else
34
+ @watcher.execute_if_updated
35
+ end
36
+ end
37
+ end
38
+
39
+ def clear_cache
40
+ ActionView::LookupContext::DetailsKey.clear
41
+ end
42
+
43
+ private
44
+ def dirs_to_watch
45
+ all_view_paths.grep(FileSystemResolver).map!(&:path).tap(&:uniq!).sort!
46
+ end
47
+
48
+ def all_view_paths
49
+ ActionView::ViewPaths.all_view_paths.flat_map(&:paths)
50
+ end
51
+ end
52
+ end
@@ -1,10 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionView
4
- module CompiledTemplates #:nodoc:
5
- # holds compiled template code
6
- end
7
-
8
4
  # = Action View Context
9
5
  #
10
6
  # Action View contexts are supplied to Action Controller to render a template.
@@ -16,7 +12,6 @@ module ActionView
16
12
  # object that includes this module (although you can call _prepare_context
17
13
  # defined below).
18
14
  module Context
19
- include CompiledTemplates
20
15
  attr_accessor :output_buffer, :view_flow
21
16
 
22
17
  # Prepares the context by setting the appropriate instance variables.
@@ -130,8 +130,9 @@ module ActionView
130
130
 
131
131
  def add_dependencies(render_dependencies, arguments, pattern)
132
132
  arguments.scan(pattern) do
133
- add_dynamic_dependency(render_dependencies, Regexp.last_match[:dynamic])
134
- add_static_dependency(render_dependencies, Regexp.last_match[:static])
133
+ match = Regexp.last_match
134
+ add_dynamic_dependency(render_dependencies, match[:dynamic])
135
+ add_static_dependency(render_dependencies, match[:static], match[:quote])
135
136
  end
136
137
  end
137
138
 
@@ -141,7 +142,12 @@ module ActionView
141
142
  end
142
143
  end
143
144
 
144
- def add_static_dependency(dependencies, dependency)
145
+ def add_static_dependency(dependencies, dependency, quote_type)
146
+ if quote_type == '"'
147
+ # Ignore if there is interpolation
148
+ return if dependency.include?('#{')
149
+ end
150
+
145
151
  if dependency
146
152
  if dependency.include?("/")
147
153
  dependencies << dependency
@@ -164,7 +170,7 @@ module ActionView
164
170
  def explicit_dependencies
165
171
  dependencies = source.scan(EXPLICIT_DEPENDENCY).flatten.uniq
166
172
 
167
- wildcards, explicits = dependencies.partition { |dependency| dependency[-1] == "*" }
173
+ wildcards, explicits = dependencies.partition { |dependency| dependency.end_with?("*") }
168
174
 
169
175
  (explicits + resolve_directories(wildcards)).uniq
170
176
  end
@@ -6,23 +6,18 @@ module ActionView
6
6
  class Digestor
7
7
  @@digest_mutex = Mutex.new
8
8
 
9
- module PerExecutionDigestCacheExpiry
10
- def self.before(target)
11
- ActionView::LookupContext::DetailsKey.clear
12
- end
13
- end
14
-
15
9
  class << self
16
10
  # Supported options:
17
11
  #
18
- # * <tt>name</tt> - Template name
19
- # * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
20
- # * <tt>dependencies</tt> - An array of dependent views
21
- def digest(name:, finder:, dependencies: nil)
12
+ # * <tt>name</tt> - Template name
13
+ # * <tt>format</tt> - Template format
14
+ # * <tt>finder</tt> - An instance of <tt>ActionView::LookupContext</tt>
15
+ # * <tt>dependencies</tt> - An array of dependent views
16
+ def digest(name:, format: nil, finder:, dependencies: nil)
22
17
  if dependencies.nil? || dependencies.empty?
23
- cache_key = "#{name}.#{finder.rendered_format}"
18
+ cache_key = "#{name}.#{format}"
24
19
  else
25
- cache_key = [ name, finder.rendered_format, dependencies ].flatten.compact.join(".")
20
+ cache_key = [ name, format, dependencies ].flatten.compact.join(".")
26
21
  end
27
22
 
28
23
  # this is a correctly done double-checked locking idiom
@@ -46,10 +41,9 @@ module ActionView
46
41
  # Create a dependency tree for template named +name+.
47
42
  def tree(name, finder, partial = false, seen = {})
48
43
  logical_name = name.gsub(%r|/_|, "/")
44
+ interpolated = name.include?("#")
49
45
 
50
- if template = find_template(finder, logical_name, [], partial, [])
51
- finder.rendered_format ||= template.formats.first
52
-
46
+ if !interpolated && (template = find_template(finder, logical_name, [], partial, []))
53
47
  if node = seen[template.identifier] # handle cycles in the tree
54
48
  node
55
49
  else
@@ -62,7 +56,7 @@ module ActionView
62
56
  node
63
57
  end
64
58
  else
65
- unless name.include?("#") # Dynamic template partial names can never be tracked
59
+ unless interpolated # Dynamic template partial names can never be tracked
66
60
  logger.error " Couldn't find template for digesting: #{name}"
67
61
  end
68
62
 
@@ -73,9 +67,7 @@ module ActionView
73
67
  private
74
68
  def find_template(finder, name, prefixes, partial, keys)
75
69
  finder.disable_cache do
76
- format = finder.rendered_format
77
- result = finder.find_all(name, prefixes, partial, keys, formats: [format]).first if format
78
- result || finder.find_all(name, prefixes, partial, keys).first
70
+ finder.find_all(name, prefixes, partial, keys).first
79
71
  end
80
72
  end
81
73
  end
@@ -68,7 +68,6 @@ module ActionView
68
68
  end
69
69
 
70
70
  private
71
-
72
71
  def inside_fiber?
73
72
  Fiber.current.object_id != @root
74
73
  end
@@ -8,9 +8,9 @@ module ActionView
8
8
 
9
9
  module VERSION
10
10
  MAJOR = 6
11
- MINOR = 0
12
- TINY = 0
13
- PRE = "beta1"
11
+ MINOR = 1
12
+ TINY = 4
13
+ PRE = nil
14
14
 
15
15
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
16
16
  end
@@ -38,7 +38,6 @@ module ActionView
38
38
  end
39
39
 
40
40
  private
41
-
42
41
  def object_has_errors?
43
42
  object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
44
43
  end
@@ -3,7 +3,6 @@
3
3
  require "active_support/core_ext/array/extract_options"
4
4
  require "active_support/core_ext/hash/keys"
5
5
  require "active_support/core_ext/object/inclusion"
6
- require "active_support/core_ext/object/try"
7
6
  require "action_view/helpers/asset_url_helper"
8
7
  require "action_view/helpers/tag_helper"
9
8
 
@@ -24,13 +23,15 @@ module ActionView
24
23
  include AssetUrlHelper
25
24
  include TagHelper
26
25
 
26
+ mattr_accessor :preload_links_header
27
+
27
28
  # Returns an HTML script tag for each of the +sources+ provided.
28
29
  #
29
30
  # Sources may be paths to JavaScript files. Relative paths are assumed to be relative
30
31
  # to <tt>assets/javascripts</tt>, full paths are assumed to be relative to the document
31
32
  # root. Relative paths are idiomatic, use absolute paths only when needed.
32
33
  #
33
- # When passing paths, the ".js" extension is optional. If you do not want ".js"
34
+ # When passing paths, the ".js" extension is optional. If you do not want ".js"
34
35
  # appended to the path <tt>extname: false</tt> can be set on the options.
35
36
  #
36
37
  # You can modify the HTML attributes of the script tag by passing a hash as the
@@ -87,13 +88,24 @@ module ActionView
87
88
  def javascript_include_tag(*sources)
88
89
  options = sources.extract_options!.stringify_keys
89
90
  path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
90
- early_hints_links = []
91
+ preload_links = []
92
+ nopush = options["nopush"].nil? ? true : options.delete("nopush")
93
+ crossorigin = options.delete("crossorigin")
94
+ crossorigin = "anonymous" if crossorigin == true
95
+ integrity = options["integrity"]
91
96
 
92
97
  sources_tags = sources.uniq.map { |source|
93
98
  href = path_to_javascript(source, path_options)
94
- early_hints_links << "<#{href}>; rel=preload; as=script"
99
+ if preload_links_header && !options["defer"]
100
+ preload_link = "<#{href}>; rel=preload; as=script"
101
+ preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
102
+ preload_link += "; integrity=#{integrity}" unless integrity.nil?
103
+ preload_link += "; nopush" if nopush
104
+ preload_links << preload_link
105
+ end
95
106
  tag_options = {
96
- "src" => href
107
+ "src" => href,
108
+ "crossorigin" => crossorigin
97
109
  }.merge!(options)
98
110
  if tag_options["nonce"] == true
99
111
  tag_options["nonce"] = content_security_policy_nonce
@@ -101,7 +113,9 @@ module ActionView
101
113
  content_tag("script", "", tag_options)
102
114
  }.join("\n").html_safe
103
115
 
104
- request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) && request
116
+ if preload_links_header
117
+ send_preload_links_header(preload_links)
118
+ end
105
119
 
106
120
  sources_tags
107
121
  end
@@ -137,20 +151,33 @@ module ActionView
137
151
  def stylesheet_link_tag(*sources)
138
152
  options = sources.extract_options!.stringify_keys
139
153
  path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys
140
- early_hints_links = []
154
+ preload_links = []
155
+ crossorigin = options.delete("crossorigin")
156
+ crossorigin = "anonymous" if crossorigin == true
157
+ nopush = options["nopush"].nil? ? true : options.delete("nopush")
158
+ integrity = options["integrity"]
141
159
 
142
160
  sources_tags = sources.uniq.map { |source|
143
161
  href = path_to_stylesheet(source, path_options)
144
- early_hints_links << "<#{href}>; rel=preload; as=style"
162
+ if preload_links_header
163
+ preload_link = "<#{href}>; rel=preload; as=style"
164
+ preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
165
+ preload_link += "; integrity=#{integrity}" unless integrity.nil?
166
+ preload_link += "; nopush" if nopush
167
+ preload_links << preload_link
168
+ end
145
169
  tag_options = {
146
170
  "rel" => "stylesheet",
147
171
  "media" => "screen",
172
+ "crossorigin" => crossorigin,
148
173
  "href" => href
149
174
  }.merge!(options)
150
175
  tag(:link, tag_options)
151
176
  }.join("\n").html_safe
152
177
 
153
- request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) && request
178
+ if preload_links_header
179
+ send_preload_links_header(preload_links)
180
+ end
154
181
 
155
182
  sources_tags
156
183
  end
@@ -241,6 +268,7 @@ module ActionView
241
268
  # * <tt>:as</tt> - Override the auto-generated value for as attribute, calculated using +source+ extension and mime type.
242
269
  # * <tt>:crossorigin</tt> - Specify the crossorigin attribute, required to load cross-origin resources.
243
270
  # * <tt>:nopush</tt> - Specify if the use of server push is not desired for the resource. Defaults to +false+.
271
+ # * <tt>:integrity</tt> - Specify the integrity attribute.
244
272
  #
245
273
  # ==== Examples
246
274
  #
@@ -266,15 +294,16 @@ module ActionView
266
294
  # # => <link rel="preload" href="/media/audio.ogg" as="audio" type="audio/ogg" />
267
295
  #
268
296
  def preload_link_tag(source, options = {})
269
- href = asset_path(source, skip_pipeline: options.delete(:skip_pipeline))
297
+ href = path_to_asset(source, skip_pipeline: options.delete(:skip_pipeline))
270
298
  extname = File.extname(source).downcase.delete(".")
271
- mime_type = options.delete(:type) || Template::Types[extname].try(:to_s)
299
+ mime_type = options.delete(:type) || Template::Types[extname]&.to_s
272
300
  as_type = options.delete(:as) || resolve_link_as(extname, mime_type)
273
301
  crossorigin = options.delete(:crossorigin)
274
302
  crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
303
+ integrity = options[:integrity]
275
304
  nopush = options.delete(:nopush) || false
276
305
 
277
- link_tag = tag.link({
306
+ link_tag = tag.link(**{
278
307
  rel: "preload",
279
308
  href: href,
280
309
  as: as_type,
@@ -282,12 +311,13 @@ module ActionView
282
311
  crossorigin: crossorigin
283
312
  }.merge!(options.symbolize_keys))
284
313
 
285
- early_hints_link = "<#{href}>; rel=preload; as=#{as_type}"
286
- early_hints_link += "; type=#{mime_type}" if mime_type
287
- early_hints_link += "; crossorigin=#{crossorigin}" if crossorigin
288
- early_hints_link += "; nopush" if nopush
314
+ preload_link = "<#{href}>; rel=preload; as=#{as_type}"
315
+ preload_link += "; type=#{mime_type}" if mime_type
316
+ preload_link += "; crossorigin=#{crossorigin}" if crossorigin
317
+ preload_link += "; integrity=#{integrity}" if integrity
318
+ preload_link += "; nopush" if nopush
289
319
 
290
- request.send_early_hints("Link" => early_hints_link) if respond_to?(:request) && request
320
+ send_preload_links_header([preload_link])
291
321
 
292
322
  link_tag
293
323
  end
@@ -329,14 +359,14 @@ module ActionView
329
359
  # image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw")
330
360
  # # => <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw">
331
361
  #
332
- # Active Storage (images that are uploaded by the users of your app):
362
+ # Active Storage blobs (images that are uploaded by the users of your app):
333
363
  #
334
364
  # image_tag(user.avatar)
335
365
  # # => <img src="/rails/active_storage/blobs/.../tiger.jpg" />
336
- # image_tag(user.avatar.variant(resize_to_fit: [100, 100]))
337
- # # => <img src="/rails/active_storage/variants/.../tiger.jpg" />
338
- # image_tag(user.avatar.variant(resize_to_fit: [100, 100]), size: '100')
339
- # # => <img width="100" height="100" src="/rails/active_storage/variants/.../tiger.jpg" />
366
+ # image_tag(user.avatar.variant(resize_to_limit: [100, 100]))
367
+ # # => <img src="/rails/active_storage/representations/.../tiger.jpg" />
368
+ # image_tag(user.avatar.variant(resize_to_limit: [100, 100]), size: '100')
369
+ # # => <img width="100" height="100" src="/rails/active_storage/representations/.../tiger.jpg" />
340
370
  def image_tag(source, options = {})
341
371
  options = options.symbolize_keys
342
372
  check_for_image_tag_errors(options)
@@ -483,6 +513,16 @@ module ActionView
483
513
  type
484
514
  end
485
515
  end
516
+
517
+ def send_preload_links_header(preload_links)
518
+ if respond_to?(:request) && request
519
+ request.send_early_hints("Link" => preload_links.join("\n"))
520
+ end
521
+
522
+ if respond_to?(:response) && response
523
+ response.headers["Link"] = [response.headers["Link"].presence, *preload_links].compact.join(",")
524
+ end
525
+ end
486
526
  end
487
527
  end
488
528
  end
@@ -52,7 +52,7 @@ module ActionView
52
52
  # solution being slower. You should be sure to measure your actual
53
53
  # performance across targeted browsers both before and after this change.
54
54
  #
55
- # To implement the corresponding hosts you can either setup four actual
55
+ # To implement the corresponding hosts you can either set up four actual
56
56
  # hosts or use wildcard DNS to CNAME the wildcard to a single asset host.
57
57
  # You can read more about setting up your DNS CNAME records from your ISP.
58
58
  #
@@ -80,7 +80,7 @@ module ActionView
80
80
  # absolute path of the asset, for example "/assets/rails.png".
81
81
  #
82
82
  # ActionController::Base.asset_host = Proc.new { |source|
83
- # if source.ends_with?('.css')
83
+ # if source.end_with?('.css')
84
84
  # "http://stylesheets.example.com"
85
85
  # else
86
86
  # "http://assets.example.com"
@@ -98,7 +98,7 @@ module ActionView
98
98
  # have SSL certificates for each of the asset hosts this technique allows you
99
99
  # to avoid warnings in the client about mixed media.
100
100
  # Note that the +request+ parameter might not be supplied, e.g. when the assets
101
- # are precompiled with the command `rails assets:precompile`. Make sure to use a
101
+ # are precompiled with the command <tt>bin/rails assets:precompile</tt>. Make sure to use a
102
102
  # +Proc+ instead of a lambda, since a +Proc+ allows missing parameters and sets them
103
103
  # to +nil+.
104
104
  #
@@ -133,6 +133,8 @@ module ActionView
133
133
  # which is implemented by sprockets-rails.
134
134
  #
135
135
  # asset_path("application.js") # => "/assets/application-60aa4fdc5cea14baf5400fba1abf4f2a46a5166bad4772b1effe341570f07de9.js"
136
+ # asset_path('application.js', host: 'example.com') # => "//example.com/assets/application.js"
137
+ # asset_path("application.js", host: 'example.com', protocol: 'https') # => "https://example.com/assets/application.js"
136
138
  #
137
139
  # === Without the asset pipeline (<tt>skip_pipeline: true</tt>)
138
140
  #
@@ -204,7 +206,7 @@ module ActionView
204
206
 
205
207
  relative_url_root = defined?(config.relative_url_root) && config.relative_url_root
206
208
  if relative_url_root
207
- source = File.join(relative_url_root, source) unless source.starts_with?("#{relative_url_root}/")
209
+ source = File.join(relative_url_root, source) unless source.start_with?("#{relative_url_root}/")
208
210
  end
209
211
 
210
212
  if host = compute_asset_host(source, options)
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "set"
4
+ require "active_support/core_ext/symbol/starts_ends_with"
4
5
 
5
6
  module ActionView
6
7
  # = Action View Atom Feed Helpers
@@ -115,7 +116,7 @@ module ActionView
115
116
  end
116
117
 
117
118
  feed_opts = { "xml:lang" => options[:language] || "en-US", "xmlns" => "http://www.w3.org/2005/Atom" }
118
- feed_opts.merge!(options).reject! { |k, v| !k.to_s.match(/^xml/) }
119
+ feed_opts.merge!(options).select! { |k, _| k.start_with?("xml") }
119
120
 
120
121
  xml.feed(feed_opts) do
121
122
  xml.id(options[:id] || "tag:#{request.host},#{options[:schema_date]}:#{request.fullpath.split(".")[0]}")
@@ -24,8 +24,8 @@ module ActionView
24
24
  # This approach will assume that when a new topic is added, you'll touch
25
25
  # the project. The cache key generated from this call will be something like:
26
26
  #
27
- # views/template/action.html.erb:7a1156131a6928cb0026877f8b749ac9/projects/123
28
- # ^template path ^template tree digest ^class ^id
27
+ # views/template/action:7a1156131a6928cb0026877f8b749ac9/projects/123
28
+ # ^template path ^template tree digest ^class ^id
29
29
  #
30
30
  # This cache key is stable, but it's combined with a cache version derived from the project
31
31
  # record. When the project updated_at is touched, the #cache_version changes, even
@@ -165,8 +165,8 @@ module ActionView
165
165
  # expire the cache.
166
166
  def cache(name = {}, options = {}, &block)
167
167
  if controller.respond_to?(:perform_caching) && controller.perform_caching
168
- name_options = options.slice(:skip_digest, :virtual_path)
169
- safe_concat(fragment_for(cache_fragment_name(name, name_options), options, &block))
168
+ name_options = options.slice(:skip_digest)
169
+ safe_concat(fragment_for(cache_fragment_name(name, **name_options), options, &block))
170
170
  else
171
171
  yield
172
172
  end
@@ -205,37 +205,30 @@ module ActionView
205
205
  # fragments can be manually bypassed. This is useful when cache fragments
206
206
  # cannot be manually expired unless you know the exact key which is the
207
207
  # case when using memcached.
208
- #
209
- # The digest will be generated using +virtual_path:+ if it is provided.
210
- #
211
- def cache_fragment_name(name = {}, skip_digest: nil, virtual_path: nil, digest_path: nil)
208
+ def cache_fragment_name(name = {}, skip_digest: nil, digest_path: nil)
212
209
  if skip_digest
213
210
  name
214
211
  else
215
- fragment_name_with_digest(name, virtual_path, digest_path)
212
+ fragment_name_with_digest(name, digest_path)
216
213
  end
217
214
  end
218
215
 
219
- def digest_path_from_virtual(virtual_path) # :nodoc:
220
- digest = Digestor.digest(name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies)
216
+ def digest_path_from_template(template) # :nodoc:
217
+ digest = Digestor.digest(name: template.virtual_path, format: template.format, finder: lookup_context, dependencies: view_cache_dependencies)
221
218
 
222
219
  if digest.present?
223
- "#{virtual_path}:#{digest}"
220
+ "#{template.virtual_path}:#{digest}"
224
221
  else
225
- virtual_path
222
+ template.virtual_path
226
223
  end
227
224
  end
228
225
 
229
226
  private
227
+ def fragment_name_with_digest(name, digest_path)
228
+ name = controller.url_for(name).split("://").last if name.is_a?(Hash)
230
229
 
231
- def fragment_name_with_digest(name, virtual_path, digest_path)
232
- virtual_path ||= @virtual_path
233
-
234
- if virtual_path || digest_path
235
- name = controller.url_for(name).split("://").last if name.is_a?(Hash)
236
-
237
- digest_path ||= digest_path_from_virtual(virtual_path)
238
-
230
+ if @current_template&.virtual_path || digest_path
231
+ digest_path ||= digest_path_from_template(@current_template)
239
232
  [ digest_path, name ]
240
233
  else
241
234
  name
@@ -244,10 +237,10 @@ module ActionView
244
237
 
245
238
  def fragment_for(name = {}, options = nil, &block)
246
239
  if content = read_fragment_for(name, options)
247
- @view_renderer.cache_hits[@virtual_path] = :hit if defined?(@view_renderer)
240
+ @view_renderer.cache_hits[@current_template&.virtual_path] = :hit if defined?(@view_renderer)
248
241
  content
249
242
  else
250
- @view_renderer.cache_hits[@virtual_path] = :miss if defined?(@view_renderer)
243
+ @view_renderer.cache_hits[@current_template&.virtual_path] = :miss if defined?(@view_renderer)
251
244
  write_fragment_for(name, options, &block)
252
245
  end
253
246
  end
@@ -14,9 +14,11 @@ module ActionView
14
14
  # This is used by the Rails UJS helper to create dynamically
15
15
  # loaded inline <script> elements.
16
16
  #
17
- def csp_meta_tag
17
+ def csp_meta_tag(**options)
18
18
  if content_security_policy?
19
- tag("meta", name: "csp-nonce", content: content_security_policy_nonce)
19
+ options[:name] = "csp-nonce"
20
+ options[:content] = content_security_policy_nonce
21
+ tag("meta", options)
20
22
  end
21
23
  end
22
24
  end
@@ -197,8 +197,8 @@ module ActionView
197
197
  # and +:name+ (string). A format string would be something like "%{name} (%<number>02d)" for example.
198
198
  # See <tt>Kernel.sprintf</tt> for documentation on format sequences.
199
199
  # * <tt>:date_separator</tt> - Specifies a string to separate the date fields. Default is "" (i.e. nothing).
200
- # * <tt>:time_separator</tt> - Specifies a string to separate the time fields. Default is "" (i.e. nothing).
201
- # * <tt>:datetime_separator</tt>- Specifies a string to separate the date and time fields. Default is "" (i.e. nothing).
200
+ # * <tt>:time_separator</tt> - Specifies a string to separate the time fields. Default is " : ".
201
+ # * <tt>:datetime_separator</tt>- Specifies a string to separate the date and time fields. Default is " &mdash; ".
202
202
  # * <tt>:start_year</tt> - Set the start year for the year select. Default is <tt>Date.today.year - 5</tt> if
203
203
  # you are creating new record. While editing existing record, <tt>:start_year</tt> defaults to
204
204
  # the current selected year minus 5.
@@ -688,7 +688,6 @@ module ActionView
688
688
  end
689
689
 
690
690
  private
691
-
692
691
  def normalize_distance_of_time_argument_to_time(value)
693
692
  if value.is_a?(Numeric)
694
693
  Time.at(value)
@@ -831,7 +830,7 @@ module ActionView
831
830
  end
832
831
 
833
832
  def select_year
834
- if !@datetime || @datetime == 0
833
+ if !year || @datetime == 0
835
834
  val = "1"
836
835
  middle_year = Date.today.year
837
836
  else
@@ -1054,7 +1053,7 @@ module ActionView
1054
1053
  select_options[:class] = css_class_attribute(type, select_options[:class], @options[:with_css_classes]) if @options[:with_css_classes]
1055
1054
 
1056
1055
  select_html = +"\n"
1057
- select_html << content_tag("option", "", value: "") + "\n" if @options[:include_blank]
1056
+ select_html << content_tag("option", "", value: "", label: " ") + "\n" if @options[:include_blank]
1058
1057
  select_html << prompt_option_tag(type, @options[:prompt]) + "\n" if @options[:prompt]
1059
1058
  select_html << select_options_as_html
1060
1059
 
@@ -1139,7 +1138,7 @@ module ActionView
1139
1138
  first_visible = order.find { |type| !@options[:"discard_#{type}"] }
1140
1139
  order.reverse_each do |type|
1141
1140
  separator = separator(type) unless type == first_visible # don't add before first visible field
1142
- select.insert(0, separator.to_s + send("select_#{type}").to_s)
1141
+ select.insert(0, separator.to_s + public_send("select_#{type}").to_s)
1143
1142
  end
1144
1143
  select.html_safe
1145
1144
  end