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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +273 -119
- data/MIT-LICENSE +1 -1
- data/README.rdoc +5 -3
- data/lib/action_view/base.rb +81 -15
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +0 -5
- data/lib/action_view/dependency_tracker.rb +10 -4
- data/lib/action_view/digestor.rb +11 -19
- data/lib/action_view/flows.rb +0 -1
- data/lib/action_view/gem_version.rb +3 -3
- data/lib/action_view/helpers/active_model_helper.rb +0 -1
- data/lib/action_view/helpers/asset_tag_helper.rb +62 -22
- data/lib/action_view/helpers/asset_url_helper.rb +6 -4
- data/lib/action_view/helpers/atom_feed_helper.rb +2 -1
- data/lib/action_view/helpers/cache_helper.rb +16 -23
- data/lib/action_view/helpers/csp_helper.rb +4 -2
- data/lib/action_view/helpers/date_helper.rb +5 -6
- data/lib/action_view/helpers/form_helper.rb +70 -34
- data/lib/action_view/helpers/form_options_helper.rb +10 -18
- data/lib/action_view/helpers/form_tag_helper.rb +12 -9
- data/lib/action_view/helpers/javascript_helper.rb +7 -5
- data/lib/action_view/helpers/number_helper.rb +9 -8
- data/lib/action_view/helpers/output_safety_helper.rb +1 -1
- data/lib/action_view/helpers/rendering_helper.rb +17 -7
- data/lib/action_view/helpers/sanitize_helper.rb +10 -16
- data/lib/action_view/helpers/tag_helper.rb +94 -19
- data/lib/action_view/helpers/tags/base.rb +10 -7
- data/lib/action_view/helpers/tags/check_box.rb +0 -1
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +0 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +0 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +0 -1
- data/lib/action_view/helpers/tags/color_field.rb +0 -1
- data/lib/action_view/helpers/tags/date_field.rb +1 -2
- data/lib/action_view/helpers/tags/date_select.rb +2 -3
- data/lib/action_view/helpers/tags/datetime_field.rb +0 -1
- data/lib/action_view/helpers/tags/datetime_local_field.rb +1 -2
- data/lib/action_view/helpers/tags/label.rb +4 -1
- data/lib/action_view/helpers/tags/month_field.rb +1 -2
- data/lib/action_view/helpers/tags/radio_button.rb +0 -1
- data/lib/action_view/helpers/tags/select.rb +1 -2
- data/lib/action_view/helpers/tags/text_field.rb +0 -1
- data/lib/action_view/helpers/tags/time_field.rb +1 -2
- data/lib/action_view/helpers/tags/week_field.rb +1 -2
- data/lib/action_view/helpers/text_helper.rb +2 -3
- data/lib/action_view/helpers/translation_helper.rb +98 -51
- data/lib/action_view/helpers/url_helper.rb +124 -16
- data/lib/action_view/layouts.rb +8 -10
- data/lib/action_view/log_subscriber.rb +26 -11
- data/lib/action_view/lookup_context.rb +59 -31
- data/lib/action_view/path_set.rb +3 -12
- data/lib/action_view/railtie.rb +39 -41
- data/lib/action_view/record_identifier.rb +0 -1
- data/lib/action_view/renderer/abstract_renderer.rb +142 -11
- data/lib/action_view/renderer/collection_renderer.rb +196 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +35 -29
- data/lib/action_view/renderer/partial_renderer.rb +21 -273
- data/lib/action_view/renderer/renderer.rb +59 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +9 -7
- data/lib/action_view/renderer/template_renderer.rb +35 -27
- data/lib/action_view/rendering.rb +49 -29
- data/lib/action_view/routing_url_for.rb +1 -1
- data/lib/action_view/template/error.rb +30 -15
- data/lib/action_view/template/handlers/builder.rb +2 -2
- data/lib/action_view/template/handlers/erb/erubi.rb +15 -9
- data/lib/action_view/template/handlers/erb.rb +14 -19
- data/lib/action_view/template/handlers/html.rb +1 -1
- data/lib/action_view/template/handlers/raw.rb +2 -2
- data/lib/action_view/template/handlers.rb +1 -1
- data/lib/action_view/template/html.rb +5 -6
- data/lib/action_view/template/inline.rb +22 -0
- data/lib/action_view/template/raw_file.rb +25 -0
- data/lib/action_view/template/renderable.rb +24 -0
- data/lib/action_view/template/resolver.rb +141 -140
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/text.rb +2 -3
- data/lib/action_view/template.rb +49 -75
- data/lib/action_view/test_case.rb +20 -28
- data/lib/action_view/testing/resolvers.rb +18 -27
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/view_paths.rb +59 -38
- data/lib/action_view.rb +7 -2
- data/lib/assets/compiled/rails-ujs.js +25 -16
- 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
|
data/lib/action_view/context.rb
CHANGED
@@ -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
|
-
|
134
|
-
|
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
|
173
|
+
wildcards, explicits = dependencies.partition { |dependency| dependency.end_with?("*") }
|
168
174
|
|
169
175
|
(explicits + resolve_directories(wildcards)).uniq
|
170
176
|
end
|
data/lib/action_view/digestor.rb
CHANGED
@@ -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>
|
19
|
-
# * <tt>
|
20
|
-
# * <tt>
|
21
|
-
|
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}.#{
|
18
|
+
cache_key = "#{name}.#{format}"
|
24
19
|
else
|
25
|
-
cache_key = [ name,
|
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
|
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
|
-
|
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
|
data/lib/action_view/flows.rb
CHANGED
@@ -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.
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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 =
|
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]
|
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
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
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
|
-
|
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(
|
337
|
-
# # => <img src="/rails/active_storage/
|
338
|
-
# image_tag(user.avatar.variant(
|
339
|
-
# # => <img width="100" height="100" src="/rails/active_storage/
|
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
|
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.
|
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
|
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.
|
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).
|
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
|
28
|
-
# ^template path
|
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
|
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,
|
212
|
+
fragment_name_with_digest(name, digest_path)
|
216
213
|
end
|
217
214
|
end
|
218
215
|
|
219
|
-
def
|
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
|
-
|
232
|
-
|
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
|
-
|
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 "
|
201
|
-
# * <tt>:datetime_separator</tt>- Specifies a string to separate the date and time fields. Default is "
|
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 " — ".
|
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
|
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 +
|
1141
|
+
select.insert(0, separator.to_s + public_send("select_#{type}").to_s)
|
1143
1142
|
end
|
1144
1143
|
select.html_safe
|
1145
1144
|
end
|