actionview 6.1.7.2 → 7.1.3
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 +299 -277
- data/MIT-LICENSE +2 -1
- data/README.rdoc +3 -3
- data/app/assets/javascripts/rails-ujs.esm.js +686 -0
- data/app/assets/javascripts/rails-ujs.js +630 -0
- data/lib/action_view/base.rb +37 -19
- data/lib/action_view/buffers.rb +107 -9
- data/lib/action_view/cache_expiry.rb +48 -37
- data/lib/action_view/context.rb +1 -1
- data/lib/action_view/dependency_tracker/erb_tracker.rb +154 -0
- data/lib/action_view/dependency_tracker/ripper_tracker.rb +59 -0
- data/lib/action_view/dependency_tracker.rb +6 -147
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +8 -5
- data/lib/action_view/flows.rb +4 -4
- data/lib/action_view/gem_version.rb +4 -4
- data/lib/action_view/helpers/active_model_helper.rb +3 -3
- data/lib/action_view/helpers/asset_tag_helper.rb +200 -60
- data/lib/action_view/helpers/asset_url_helper.rb +22 -21
- data/lib/action_view/helpers/atom_feed_helper.rb +8 -9
- data/lib/action_view/helpers/cache_helper.rb +55 -12
- data/lib/action_view/helpers/capture_helper.rb +34 -14
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +8 -2
- data/lib/action_view/helpers/csp_helper.rb +3 -3
- data/lib/action_view/helpers/csrf_helper.rb +4 -4
- data/lib/action_view/helpers/date_helper.rb +123 -57
- data/lib/action_view/helpers/debug_helper.rb +6 -4
- data/lib/action_view/helpers/form_helper.rb +253 -97
- data/lib/action_view/helpers/form_options_helper.rb +72 -34
- data/lib/action_view/helpers/form_tag_helper.rb +189 -58
- data/lib/action_view/helpers/javascript_helper.rb +4 -5
- data/lib/action_view/helpers/number_helper.rb +43 -335
- data/lib/action_view/helpers/output_safety_helper.rb +6 -6
- data/lib/action_view/helpers/rendering_helper.rb +6 -7
- data/lib/action_view/helpers/sanitize_helper.rb +54 -24
- data/lib/action_view/helpers/tag_helper.rb +42 -35
- data/lib/action_view/helpers/tags/base.rb +16 -77
- data/lib/action_view/helpers/tags/check_box.rb +1 -1
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
- data/lib/action_view/helpers/tags/collection_select.rb +4 -1
- data/lib/action_view/helpers/tags/date_field.rb +1 -1
- data/lib/action_view/helpers/tags/date_select.rb +2 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
- data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
- data/lib/action_view/helpers/tags/file_field.rb +16 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
- data/lib/action_view/helpers/tags/month_field.rb +1 -1
- data/lib/action_view/helpers/tags/select.rb +4 -1
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/time_field.rb +11 -2
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
- data/lib/action_view/helpers/tags/week_field.rb +1 -1
- data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
- data/lib/action_view/helpers/tags.rb +5 -2
- data/lib/action_view/helpers/text_helper.rb +180 -97
- data/lib/action_view/helpers/translation_helper.rb +14 -45
- data/lib/action_view/helpers/url_helper.rb +230 -132
- data/lib/action_view/helpers.rb +27 -25
- data/lib/action_view/layouts.rb +15 -10
- data/lib/action_view/log_subscriber.rb +49 -32
- data/lib/action_view/lookup_context.rb +58 -61
- data/lib/action_view/model_naming.rb +2 -2
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +28 -35
- data/lib/action_view/railtie.rb +44 -9
- data/lib/action_view/record_identifier.rb +16 -9
- data/lib/action_view/render_parser.rb +188 -0
- data/lib/action_view/renderer/abstract_renderer.rb +3 -3
- data/lib/action_view/renderer/collection_renderer.rb +10 -2
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
- data/lib/action_view/renderer/partial_renderer.rb +3 -36
- data/lib/action_view/renderer/renderer.rb +6 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +6 -5
- data/lib/action_view/renderer/template_renderer.rb +9 -4
- data/lib/action_view/rendering.rb +25 -7
- data/lib/action_view/ripper_ast_parser.rb +198 -0
- data/lib/action_view/routing_url_for.rb +8 -5
- data/lib/action_view/template/error.rb +122 -14
- data/lib/action_view/template/handlers/builder.rb +4 -4
- data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
- data/lib/action_view/template/handlers/erb.rb +79 -1
- data/lib/action_view/template/handlers.rb +4 -4
- data/lib/action_view/template/html.rb +4 -4
- data/lib/action_view/template/inline.rb +3 -3
- data/lib/action_view/template/raw_file.rb +4 -4
- data/lib/action_view/template/renderable.rb +1 -1
- data/lib/action_view/template/resolver.rb +96 -313
- data/lib/action_view/template/text.rb +4 -4
- data/lib/action_view/template/types.rb +25 -32
- data/lib/action_view/template.rb +245 -41
- data/lib/action_view/template_details.rb +66 -0
- data/lib/action_view/template_path.rb +66 -0
- data/lib/action_view/test_case.rb +182 -23
- data/lib/action_view/testing/resolvers.rb +11 -12
- data/lib/action_view/unbound_template.rb +43 -7
- data/lib/action_view/version.rb +1 -1
- data/lib/action_view/view_paths.rb +19 -28
- data/lib/action_view.rb +6 -4
- data/lib/assets/compiled/rails-ujs.js +36 -5
- metadata +32 -25
|
@@ -7,8 +7,9 @@ require "action_view/helpers/asset_url_helper"
|
|
|
7
7
|
require "action_view/helpers/tag_helper"
|
|
8
8
|
|
|
9
9
|
module ActionView
|
|
10
|
-
|
|
11
|
-
|
|
10
|
+
module Helpers # :nodoc:
|
|
11
|
+
# = Action View Asset Tag \Helpers
|
|
12
|
+
#
|
|
12
13
|
# This module provides methods for generating HTML that links views to assets such
|
|
13
14
|
# as images, JavaScripts, stylesheets, and feeds. These methods do not verify
|
|
14
15
|
# the assets exist before linking to them:
|
|
@@ -16,14 +17,15 @@ module ActionView
|
|
|
16
17
|
# image_tag("rails.png")
|
|
17
18
|
# # => <img src="/assets/rails.png" />
|
|
18
19
|
# stylesheet_link_tag("application")
|
|
19
|
-
# # => <link href="/assets/application.css?body=1"
|
|
20
|
+
# # => <link href="/assets/application.css?body=1" rel="stylesheet" />
|
|
20
21
|
module AssetTagHelper
|
|
21
|
-
extend ActiveSupport::Concern
|
|
22
|
-
|
|
23
22
|
include AssetUrlHelper
|
|
24
23
|
include TagHelper
|
|
25
24
|
|
|
25
|
+
mattr_accessor :image_loading
|
|
26
|
+
mattr_accessor :image_decoding
|
|
26
27
|
mattr_accessor :preload_links_header
|
|
28
|
+
mattr_accessor :apply_stylesheet_media_default
|
|
27
29
|
|
|
28
30
|
# Returns an HTML script tag for each of the +sources+ provided.
|
|
29
31
|
#
|
|
@@ -40,13 +42,14 @@ module ActionView
|
|
|
40
42
|
# When the Asset Pipeline is enabled, you can pass the name of your manifest as
|
|
41
43
|
# source, and include other JavaScript or CoffeeScript files inside the manifest.
|
|
42
44
|
#
|
|
43
|
-
# If the server supports Early Hints
|
|
44
|
-
#
|
|
45
|
+
# If the server supports HTTP Early Hints, and the +defer+ option is not
|
|
46
|
+
# enabled, \Rails will push a <tt>103 Early Hints</tt> response that links
|
|
47
|
+
# to the assets.
|
|
45
48
|
#
|
|
46
49
|
# ==== Options
|
|
47
50
|
#
|
|
48
51
|
# When the last parameter is a hash you can add HTML attributes using that
|
|
49
|
-
# parameter.
|
|
52
|
+
# parameter. This includes but is not limited to the following options:
|
|
50
53
|
#
|
|
51
54
|
# * <tt>:extname</tt> - Append an extension to the generated URL unless the extension
|
|
52
55
|
# already exists. This only applies for relative URLs.
|
|
@@ -58,6 +61,20 @@ module ActionView
|
|
|
58
61
|
# when it is set to true.
|
|
59
62
|
# * <tt>:nonce</tt> - When set to true, adds an automatic nonce value if
|
|
60
63
|
# you have Content Security Policy enabled.
|
|
64
|
+
# * <tt>:async</tt> - When set to +true+, adds the +async+ HTML
|
|
65
|
+
# attribute, allowing the script to be fetched in parallel to be parsed
|
|
66
|
+
# and evaluated as soon as possible.
|
|
67
|
+
# * <tt>:defer</tt> - When set to +true+, adds the +defer+ HTML
|
|
68
|
+
# attribute, which indicates to the browser that the script is meant to
|
|
69
|
+
# be executed after the document has been parsed. Additionally, prevents
|
|
70
|
+
# sending the Preload Links header.
|
|
71
|
+
#
|
|
72
|
+
# Any other specified options will be treated as HTML attributes for the
|
|
73
|
+
# +script+ tag.
|
|
74
|
+
#
|
|
75
|
+
# For more information regarding how the <tt>:async</tt> and <tt>:defer</tt>
|
|
76
|
+
# options affect the <tt><script></tt> tag, please refer to the
|
|
77
|
+
# {MDN docs}[https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script].
|
|
61
78
|
#
|
|
62
79
|
# ==== Examples
|
|
63
80
|
#
|
|
@@ -85,19 +102,27 @@ module ActionView
|
|
|
85
102
|
#
|
|
86
103
|
# javascript_include_tag "http://www.example.com/xmlhr.js", nonce: true
|
|
87
104
|
# # => <script src="http://www.example.com/xmlhr.js" nonce="..."></script>
|
|
105
|
+
#
|
|
106
|
+
# javascript_include_tag "http://www.example.com/xmlhr.js", async: true
|
|
107
|
+
# # => <script src="http://www.example.com/xmlhr.js" async="async"></script>
|
|
108
|
+
#
|
|
109
|
+
# javascript_include_tag "http://www.example.com/xmlhr.js", defer: true
|
|
110
|
+
# # => <script src="http://www.example.com/xmlhr.js" defer="defer"></script>
|
|
88
111
|
def javascript_include_tag(*sources)
|
|
89
112
|
options = sources.extract_options!.stringify_keys
|
|
90
113
|
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
|
91
114
|
preload_links = []
|
|
115
|
+
use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
|
|
92
116
|
nopush = options["nopush"].nil? ? true : options.delete("nopush")
|
|
93
117
|
crossorigin = options.delete("crossorigin")
|
|
94
118
|
crossorigin = "anonymous" if crossorigin == true
|
|
95
119
|
integrity = options["integrity"]
|
|
120
|
+
rel = options["type"] == "module" ? "modulepreload" : "preload"
|
|
96
121
|
|
|
97
122
|
sources_tags = sources.uniq.map { |source|
|
|
98
123
|
href = path_to_javascript(source, path_options)
|
|
99
|
-
if
|
|
100
|
-
preload_link = "<#{href}>; rel
|
|
124
|
+
if use_preload_links_header && !options["defer"] && href.present? && !href.start_with?("data:")
|
|
125
|
+
preload_link = "<#{href}>; rel=#{rel}; as=script"
|
|
101
126
|
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
|
|
102
127
|
preload_link += "; integrity=#{integrity}" unless integrity.nil?
|
|
103
128
|
preload_link += "; nopush" if nopush
|
|
@@ -113,31 +138,48 @@ module ActionView
|
|
|
113
138
|
content_tag("script", "", tag_options)
|
|
114
139
|
}.join("\n").html_safe
|
|
115
140
|
|
|
116
|
-
if
|
|
141
|
+
if use_preload_links_header
|
|
117
142
|
send_preload_links_header(preload_links)
|
|
118
143
|
end
|
|
119
144
|
|
|
120
145
|
sources_tags
|
|
121
146
|
end
|
|
122
147
|
|
|
123
|
-
# Returns a stylesheet link tag for the sources specified as arguments.
|
|
124
|
-
#
|
|
148
|
+
# Returns a stylesheet link tag for the sources specified as arguments.
|
|
149
|
+
#
|
|
150
|
+
# When passing paths, the <tt>.css</tt> extension is optional.
|
|
151
|
+
# If you don't specify an extension, <tt>.css</tt> will be appended automatically.
|
|
152
|
+
# If you do not want <tt>.css</tt> appended to the path,
|
|
153
|
+
# set <tt>extname: false</tt> in the options.
|
|
125
154
|
# You can modify the link attributes by passing a hash as the last argument.
|
|
126
|
-
# For historical reasons, the 'media' attribute will always be present and defaults
|
|
127
|
-
# to "screen", so you must explicitly set it to "all" for the stylesheet(s) to
|
|
128
|
-
# apply to all media types.
|
|
129
155
|
#
|
|
130
|
-
# If the server supports Early Hints
|
|
131
|
-
#
|
|
156
|
+
# If the server supports HTTP Early Hints, \Rails will push a <tt>103 Early
|
|
157
|
+
# Hints</tt> response that links to the assets.
|
|
158
|
+
#
|
|
159
|
+
# ==== Options
|
|
160
|
+
#
|
|
161
|
+
# * <tt>:extname</tt> - Append an extension to the generated URL unless the extension
|
|
162
|
+
# already exists. This only applies for relative URLs.
|
|
163
|
+
# * <tt>:protocol</tt> - Sets the protocol of the generated URL. This option only
|
|
164
|
+
# applies when a relative URL and +host+ options are provided.
|
|
165
|
+
# * <tt>:host</tt> - When a relative URL is provided the host is added to the
|
|
166
|
+
# that path.
|
|
167
|
+
# * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
|
|
168
|
+
# when it is set to true.
|
|
169
|
+
#
|
|
170
|
+
# ==== Examples
|
|
132
171
|
#
|
|
133
172
|
# stylesheet_link_tag "style"
|
|
134
|
-
# # => <link href="/assets/style.css"
|
|
173
|
+
# # => <link href="/assets/style.css" rel="stylesheet" />
|
|
135
174
|
#
|
|
136
175
|
# stylesheet_link_tag "style.css"
|
|
137
|
-
# # => <link href="/assets/style.css"
|
|
176
|
+
# # => <link href="/assets/style.css" rel="stylesheet" />
|
|
138
177
|
#
|
|
139
178
|
# stylesheet_link_tag "http://www.example.com/style.css"
|
|
140
|
-
# # => <link href="http://www.example.com/style.css"
|
|
179
|
+
# # => <link href="http://www.example.com/style.css" rel="stylesheet" />
|
|
180
|
+
#
|
|
181
|
+
# stylesheet_link_tag "style.less", extname: false, skip_pipeline: true, rel: "stylesheet/less"
|
|
182
|
+
# # => <link href="/stylesheets/style.less" rel="stylesheet/less">
|
|
141
183
|
#
|
|
142
184
|
# stylesheet_link_tag "style", media: "all"
|
|
143
185
|
# # => <link href="/assets/style.css" media="all" rel="stylesheet" />
|
|
@@ -146,11 +188,12 @@ module ActionView
|
|
|
146
188
|
# # => <link href="/assets/style.css" media="print" rel="stylesheet" />
|
|
147
189
|
#
|
|
148
190
|
# stylesheet_link_tag "random.styles", "/css/stylish"
|
|
149
|
-
# # => <link href="/assets/random.styles"
|
|
150
|
-
# # <link href="/css/stylish.css"
|
|
191
|
+
# # => <link href="/assets/random.styles" rel="stylesheet" />
|
|
192
|
+
# # <link href="/css/stylish.css" rel="stylesheet" />
|
|
151
193
|
def stylesheet_link_tag(*sources)
|
|
152
194
|
options = sources.extract_options!.stringify_keys
|
|
153
|
-
path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys
|
|
195
|
+
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
|
196
|
+
use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
|
|
154
197
|
preload_links = []
|
|
155
198
|
crossorigin = options.delete("crossorigin")
|
|
156
199
|
crossorigin = "anonymous" if crossorigin == true
|
|
@@ -159,7 +202,7 @@ module ActionView
|
|
|
159
202
|
|
|
160
203
|
sources_tags = sources.uniq.map { |source|
|
|
161
204
|
href = path_to_stylesheet(source, path_options)
|
|
162
|
-
if
|
|
205
|
+
if use_preload_links_header && href.present? && !href.start_with?("data:")
|
|
163
206
|
preload_link = "<#{href}>; rel=preload; as=style"
|
|
164
207
|
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
|
|
165
208
|
preload_link += "; integrity=#{integrity}" unless integrity.nil?
|
|
@@ -168,14 +211,18 @@ module ActionView
|
|
|
168
211
|
end
|
|
169
212
|
tag_options = {
|
|
170
213
|
"rel" => "stylesheet",
|
|
171
|
-
"media" => "screen",
|
|
172
214
|
"crossorigin" => crossorigin,
|
|
173
215
|
"href" => href
|
|
174
216
|
}.merge!(options)
|
|
217
|
+
|
|
218
|
+
if apply_stylesheet_media_default && tag_options["media"].blank?
|
|
219
|
+
tag_options["media"] = "screen"
|
|
220
|
+
end
|
|
221
|
+
|
|
175
222
|
tag(:link, tag_options)
|
|
176
223
|
}.join("\n").html_safe
|
|
177
224
|
|
|
178
|
-
if
|
|
225
|
+
if use_preload_links_header
|
|
179
226
|
send_preload_links_header(preload_links)
|
|
180
227
|
end
|
|
181
228
|
|
|
@@ -235,14 +282,14 @@ module ActionView
|
|
|
235
282
|
#
|
|
236
283
|
# The helper gets the name of the favicon file as first argument, which
|
|
237
284
|
# defaults to "favicon.ico", and also supports +:rel+ and +:type+ options
|
|
238
|
-
# to override their defaults, "
|
|
285
|
+
# to override their defaults, "icon" and "image/x-icon"
|
|
239
286
|
# respectively:
|
|
240
287
|
#
|
|
241
288
|
# favicon_link_tag
|
|
242
|
-
# # => <link href="/assets/favicon.ico" rel="
|
|
289
|
+
# # => <link href="/assets/favicon.ico" rel="icon" type="image/x-icon" />
|
|
243
290
|
#
|
|
244
291
|
# favicon_link_tag 'myicon.ico'
|
|
245
|
-
# # => <link href="/assets/myicon.ico" rel="
|
|
292
|
+
# # => <link href="/assets/myicon.ico" rel="icon" type="image/x-icon" />
|
|
246
293
|
#
|
|
247
294
|
# Mobile Safari looks for a different link tag, pointing to an image that
|
|
248
295
|
# will be used if you add the page to the home screen of an iOS device.
|
|
@@ -252,7 +299,7 @@ module ActionView
|
|
|
252
299
|
# # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
|
|
253
300
|
def favicon_link_tag(source = "favicon.ico", options = {})
|
|
254
301
|
tag("link", {
|
|
255
|
-
rel: "
|
|
302
|
+
rel: "icon",
|
|
256
303
|
type: "image/x-icon",
|
|
257
304
|
href: path_to_image(source, skip_pipeline: options.delete(:skip_pipeline))
|
|
258
305
|
}.merge!(options.symbolize_keys))
|
|
@@ -302,16 +349,17 @@ module ActionView
|
|
|
302
349
|
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
|
|
303
350
|
integrity = options[:integrity]
|
|
304
351
|
nopush = options.delete(:nopush) || false
|
|
352
|
+
rel = mime_type == "module" ? "modulepreload" : "preload"
|
|
305
353
|
|
|
306
354
|
link_tag = tag.link(**{
|
|
307
|
-
rel:
|
|
355
|
+
rel: rel,
|
|
308
356
|
href: href,
|
|
309
357
|
as: as_type,
|
|
310
358
|
type: mime_type,
|
|
311
359
|
crossorigin: crossorigin
|
|
312
360
|
}.merge!(options.symbolize_keys))
|
|
313
361
|
|
|
314
|
-
preload_link = "<#{href}>; rel
|
|
362
|
+
preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}"
|
|
315
363
|
preload_link += "; type=#{mime_type}" if mime_type
|
|
316
364
|
preload_link += "; crossorigin=#{crossorigin}" if crossorigin
|
|
317
365
|
preload_link += "; integrity=#{integrity}" if integrity
|
|
@@ -330,8 +378,8 @@ module ActionView
|
|
|
330
378
|
# You can add HTML attributes using the +options+. The +options+ supports
|
|
331
379
|
# additional keys for convenience and conformance:
|
|
332
380
|
#
|
|
333
|
-
# * <tt>:size</tt> - Supplied as "{
|
|
334
|
-
# width="30"
|
|
381
|
+
# * <tt>:size</tt> - Supplied as <tt>"#{width}x#{height}"</tt> or <tt>"#{number}"</tt>, so <tt>"30x45"</tt> becomes
|
|
382
|
+
# <tt>width="30" height="45"</tt>, and <tt>"50"</tt> becomes <tt>width="50" height="50"</tt>.
|
|
335
383
|
# <tt>:size</tt> will be ignored if the value is not in the correct format.
|
|
336
384
|
# * <tt>:srcset</tt> - If supplied as a hash or array of <tt>[source, descriptor]</tt>
|
|
337
385
|
# pairs, each image path will be expanded before the list is formatted as a string.
|
|
@@ -372,7 +420,7 @@ module ActionView
|
|
|
372
420
|
check_for_image_tag_errors(options)
|
|
373
421
|
skip_pipeline = options.delete(:skip_pipeline)
|
|
374
422
|
|
|
375
|
-
options[:src] =
|
|
423
|
+
options[:src] = resolve_asset_source("image", source, skip_pipeline)
|
|
376
424
|
|
|
377
425
|
if options[:srcset] && !options[:srcset].is_a?(String)
|
|
378
426
|
options[:srcset] = options[:srcset].map do |src_path, size|
|
|
@@ -382,14 +430,79 @@ module ActionView
|
|
|
382
430
|
end
|
|
383
431
|
|
|
384
432
|
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
|
|
433
|
+
|
|
434
|
+
options[:loading] ||= image_loading if image_loading
|
|
435
|
+
options[:decoding] ||= image_decoding if image_decoding
|
|
436
|
+
|
|
385
437
|
tag("img", options)
|
|
386
438
|
end
|
|
387
439
|
|
|
440
|
+
# Returns an HTML picture tag for the +sources+. If +sources+ is a string,
|
|
441
|
+
# a single picture tag will be returned. If +sources+ is an array, a picture
|
|
442
|
+
# tag with nested source tags for each source will be returned. The
|
|
443
|
+
# +sources+ can be full paths, files that exist in your public images
|
|
444
|
+
# directory, or Active Storage attachments. Since the picture tag requires
|
|
445
|
+
# an img tag, the last element you provide will be used for the img tag.
|
|
446
|
+
# For complete control over the picture tag, a block can be passed, which
|
|
447
|
+
# will populate the contents of the tag accordingly.
|
|
448
|
+
#
|
|
449
|
+
# ==== Options
|
|
450
|
+
#
|
|
451
|
+
# When the last parameter is a hash you can add HTML attributes using that
|
|
452
|
+
# parameter. Apart from all the HTML supported options, the following are supported:
|
|
453
|
+
#
|
|
454
|
+
# * <tt>:image</tt> - Hash of options that are passed directly to the +image_tag+ helper.
|
|
455
|
+
#
|
|
456
|
+
# ==== Examples
|
|
457
|
+
#
|
|
458
|
+
# picture_tag("picture.webp")
|
|
459
|
+
# # => <picture><img src="/images/picture.webp" /></picture>
|
|
460
|
+
# picture_tag("gold.png", :image => { :size => "20" })
|
|
461
|
+
# # => <picture><img height="20" src="/images/gold.png" width="20" /></picture>
|
|
462
|
+
# picture_tag("gold.png", :image => { :size => "45x70" })
|
|
463
|
+
# # => <picture><img height="70" src="/images/gold.png" width="45" /></picture>
|
|
464
|
+
# picture_tag("picture.webp", "picture.png")
|
|
465
|
+
# # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img src="/images/picture.png" /></picture>
|
|
466
|
+
# picture_tag("picture.webp", "picture.png", :image => { alt: "Image" })
|
|
467
|
+
# # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img alt="Image" src="/images/picture.png" /></picture>
|
|
468
|
+
# picture_tag(["picture.webp", "picture.png"], :image => { alt: "Image" })
|
|
469
|
+
# # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img alt="Image" src="/images/picture.png" /></picture>
|
|
470
|
+
# picture_tag(:class => "my-class") { tag(:source, :srcset => image_path("picture.webp")) + image_tag("picture.png", :alt => "Image") }
|
|
471
|
+
# # => <picture class="my-class"><source srcset="/images/picture.webp" /><img alt="Image" src="/images/picture.png" /></picture>
|
|
472
|
+
# picture_tag { tag(:source, :srcset => image_path("picture-small.webp"), :media => "(min-width: 600px)") + tag(:source, :srcset => image_path("picture-big.webp")) + image_tag("picture.png", :alt => "Image") }
|
|
473
|
+
# # => <picture><source srcset="/images/picture-small.webp" media="(min-width: 600px)" /><source srcset="/images/picture-big.webp" /><img alt="Image" src="/images/picture.png" /></picture>
|
|
474
|
+
#
|
|
475
|
+
# Active Storage blobs (images that are uploaded by the users of your app):
|
|
476
|
+
#
|
|
477
|
+
# picture_tag(user.profile_picture)
|
|
478
|
+
# # => <picture><img src="/rails/active_storage/blobs/.../profile_picture.webp" /></picture>
|
|
479
|
+
def picture_tag(*sources, &block)
|
|
480
|
+
sources.flatten!
|
|
481
|
+
options = sources.extract_options!.symbolize_keys
|
|
482
|
+
image_options = options.delete(:image) || {}
|
|
483
|
+
skip_pipeline = options.delete(:skip_pipeline)
|
|
484
|
+
|
|
485
|
+
content_tag("picture", options) do
|
|
486
|
+
if block.present?
|
|
487
|
+
capture(&block).html_safe
|
|
488
|
+
elsif sources.size <= 1
|
|
489
|
+
image_tag(sources.last, image_options)
|
|
490
|
+
else
|
|
491
|
+
source_tags = sources.map do |source|
|
|
492
|
+
tag("source",
|
|
493
|
+
srcset: resolve_asset_source("image", source, skip_pipeline),
|
|
494
|
+
type: Template::Types[File.extname(source)[1..]]&.to_s)
|
|
495
|
+
end
|
|
496
|
+
safe_join(source_tags << image_tag(sources.last, image_options))
|
|
497
|
+
end
|
|
498
|
+
end
|
|
499
|
+
end
|
|
500
|
+
|
|
388
501
|
# Returns an HTML video tag for the +sources+. If +sources+ is a string,
|
|
389
502
|
# a single video tag will be returned. If +sources+ is an array, a video
|
|
390
503
|
# tag with nested source tags for each source will be returned. The
|
|
391
|
-
# +sources+ can be full paths
|
|
392
|
-
# directory.
|
|
504
|
+
# +sources+ can be full paths, files that exist in your public videos
|
|
505
|
+
# directory, or Active Storage attachments.
|
|
393
506
|
#
|
|
394
507
|
# ==== Options
|
|
395
508
|
#
|
|
@@ -398,8 +511,8 @@ module ActionView
|
|
|
398
511
|
#
|
|
399
512
|
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
|
|
400
513
|
# before the video loads. The path is calculated like the +src+ of +image_tag+.
|
|
401
|
-
# * <tt>:size</tt> - Supplied as "{
|
|
402
|
-
# width="30"
|
|
514
|
+
# * <tt>:size</tt> - Supplied as <tt>"#{width}x#{height}"</tt> or <tt>"#{number}"</tt>, so <tt>"30x45"</tt> becomes
|
|
515
|
+
# <tt>width="30" height="45"</tt>, and <tt>"50"</tt> becomes <tt>width="50" height="50"</tt>.
|
|
403
516
|
# <tt>:size</tt> will be ignored if the value is not in the correct format.
|
|
404
517
|
# * <tt>:poster_skip_pipeline</tt> will bypass the asset pipeline when using
|
|
405
518
|
# the <tt>:poster</tt> option instead using an asset in the public folder.
|
|
@@ -428,6 +541,11 @@ module ActionView
|
|
|
428
541
|
# # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
|
|
429
542
|
# video_tag(["trailer.ogg", "trailer.flv"], size: "160x120")
|
|
430
543
|
# # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
|
|
544
|
+
#
|
|
545
|
+
# Active Storage blobs (videos that are uploaded by the users of your app):
|
|
546
|
+
#
|
|
547
|
+
# video_tag(user.intro_video)
|
|
548
|
+
# # => <video src="/rails/active_storage/blobs/.../intro_video.mp4"></video>
|
|
431
549
|
def video_tag(*sources)
|
|
432
550
|
options = sources.extract_options!.symbolize_keys
|
|
433
551
|
public_poster_folder = options.delete(:poster_skip_pipeline)
|
|
@@ -441,8 +559,8 @@ module ActionView
|
|
|
441
559
|
# Returns an HTML audio tag for the +sources+. If +sources+ is a string,
|
|
442
560
|
# a single audio tag will be returned. If +sources+ is an array, an audio
|
|
443
561
|
# tag with nested source tags for each source will be returned. The
|
|
444
|
-
# +sources+ can be full paths
|
|
445
|
-
# directory.
|
|
562
|
+
# +sources+ can be full paths, files that exist in your public audios
|
|
563
|
+
# directory, or Active Storage attachments.
|
|
446
564
|
#
|
|
447
565
|
# When the last parameter is a hash you can add HTML attributes using that
|
|
448
566
|
# parameter.
|
|
@@ -455,6 +573,11 @@ module ActionView
|
|
|
455
573
|
# # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav"></audio>
|
|
456
574
|
# audio_tag("sound.wav", "sound.mid")
|
|
457
575
|
# # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
|
|
576
|
+
#
|
|
577
|
+
# Active Storage blobs (audios that are uploaded by the users of your app):
|
|
578
|
+
#
|
|
579
|
+
# audio_tag(user.name_pronunciation_audio)
|
|
580
|
+
# # => <audio src="/rails/active_storage/blobs/.../name_pronunciation_audio.mp3"></audio>
|
|
458
581
|
def audio_tag(*sources)
|
|
459
582
|
multiple_sources_tag_builder("audio", sources)
|
|
460
583
|
end
|
|
@@ -469,29 +592,29 @@ module ActionView
|
|
|
469
592
|
|
|
470
593
|
if sources.size > 1
|
|
471
594
|
content_tag(type, options) do
|
|
472
|
-
safe_join sources.map { |source| tag("source", src:
|
|
595
|
+
safe_join sources.map { |source| tag("source", src: resolve_asset_source(type, source, skip_pipeline)) }
|
|
473
596
|
end
|
|
474
597
|
else
|
|
475
|
-
options[:src] =
|
|
598
|
+
options[:src] = resolve_asset_source(type, sources.first, skip_pipeline)
|
|
476
599
|
content_tag(type, nil, options)
|
|
477
600
|
end
|
|
478
601
|
end
|
|
479
602
|
|
|
480
|
-
def
|
|
603
|
+
def resolve_asset_source(asset_type, source, skip_pipeline)
|
|
481
604
|
if source.is_a?(Symbol) || source.is_a?(String)
|
|
482
|
-
|
|
605
|
+
path_to_asset(source, type: asset_type.to_sym, skip_pipeline: skip_pipeline)
|
|
483
606
|
else
|
|
484
607
|
polymorphic_url(source)
|
|
485
608
|
end
|
|
486
609
|
rescue NoMethodError => e
|
|
487
|
-
raise ArgumentError, "Can't resolve
|
|
610
|
+
raise ArgumentError, "Can't resolve #{asset_type} into URL: #{e}"
|
|
488
611
|
end
|
|
489
612
|
|
|
490
613
|
def extract_dimensions(size)
|
|
491
614
|
size = size.to_s
|
|
492
|
-
if /\A\d+x\d
|
|
615
|
+
if /\A\d+(?:\.\d+)?x\d+(?:\.\d+)?\z/.match?(size)
|
|
493
616
|
size.split("x")
|
|
494
|
-
elsif /\A\d
|
|
617
|
+
elsif /\A\d+(?:\.\d+)?\z/.match?(size)
|
|
495
618
|
[size, size]
|
|
496
619
|
end
|
|
497
620
|
end
|
|
@@ -503,24 +626,41 @@ module ActionView
|
|
|
503
626
|
end
|
|
504
627
|
|
|
505
628
|
def resolve_link_as(extname, mime_type)
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
"
|
|
512
|
-
elsif (type = mime_type.to_s.split("/")[0]) && type.in?(%w(audio video font image))
|
|
513
|
-
type
|
|
629
|
+
case extname
|
|
630
|
+
when "js" then "script"
|
|
631
|
+
when "css" then "style"
|
|
632
|
+
when "vtt" then "track"
|
|
633
|
+
else
|
|
634
|
+
mime_type.to_s.split("/").first.presence_in(%w(audio video font image))
|
|
514
635
|
end
|
|
515
636
|
end
|
|
516
637
|
|
|
517
|
-
|
|
638
|
+
# Some HTTP client and proxies have a 4kiB header limit, but more importantly
|
|
639
|
+
# including preload links has diminishing returns so it's best to not go overboard
|
|
640
|
+
MAX_HEADER_SIZE = 1_000 # :nodoc:
|
|
641
|
+
|
|
642
|
+
def send_preload_links_header(preload_links, max_header_size: MAX_HEADER_SIZE)
|
|
643
|
+
return if preload_links.empty?
|
|
644
|
+
response_present = respond_to?(:response) && response
|
|
645
|
+
return if response_present && response.sending?
|
|
646
|
+
|
|
518
647
|
if respond_to?(:request) && request
|
|
519
648
|
request.send_early_hints("Link" => preload_links.join("\n"))
|
|
520
649
|
end
|
|
521
650
|
|
|
522
|
-
if
|
|
523
|
-
|
|
651
|
+
if response_present
|
|
652
|
+
header = +response.headers["Link"].to_s
|
|
653
|
+
preload_links.each do |link|
|
|
654
|
+
break if header.bytesize + link.bytesize > max_header_size
|
|
655
|
+
|
|
656
|
+
if header.empty?
|
|
657
|
+
header << link
|
|
658
|
+
else
|
|
659
|
+
header << "," << link
|
|
660
|
+
end
|
|
661
|
+
end
|
|
662
|
+
|
|
663
|
+
response.headers["Link"] = header
|
|
524
664
|
end
|
|
525
665
|
end
|
|
526
666
|
end
|
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
require "zlib"
|
|
4
4
|
|
|
5
5
|
module ActionView
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
module Helpers # :nodoc:
|
|
7
|
+
# = Action View Asset URL \Helpers
|
|
8
|
+
#
|
|
8
9
|
# This module provides methods for generating asset paths and
|
|
9
10
|
# URLs.
|
|
10
11
|
#
|
|
@@ -16,8 +17,8 @@ module ActionView
|
|
|
16
17
|
#
|
|
17
18
|
# === Using asset hosts
|
|
18
19
|
#
|
|
19
|
-
# By default, Rails links to these assets on the current host in the public
|
|
20
|
-
# folder, but you can direct Rails to link to assets from a dedicated asset
|
|
20
|
+
# By default, \Rails links to these assets on the current host in the public
|
|
21
|
+
# folder, but you can direct \Rails to link to assets from a dedicated asset
|
|
21
22
|
# server by setting <tt>ActionController::Base.asset_host</tt> in the application
|
|
22
23
|
# configuration, typically in <tt>config/environments/production.rb</tt>.
|
|
23
24
|
# For example, you'd define <tt>assets.example.com</tt> to be your asset
|
|
@@ -31,7 +32,7 @@ module ActionView
|
|
|
31
32
|
# image_tag("rails.png")
|
|
32
33
|
# # => <img src="http://assets.example.com/assets/rails.png" />
|
|
33
34
|
# stylesheet_link_tag("application")
|
|
34
|
-
# # => <link href="http://assets.example.com/assets/application.css"
|
|
35
|
+
# # => <link href="http://assets.example.com/assets/application.css" rel="stylesheet" />
|
|
35
36
|
#
|
|
36
37
|
# Browsers open a limited number of simultaneous connections to a single
|
|
37
38
|
# host. The exact number varies by browser and version. This limit may cause
|
|
@@ -44,7 +45,7 @@ module ActionView
|
|
|
44
45
|
# image_tag("rails.png")
|
|
45
46
|
# # => <img src="http://assets0.example.com/assets/rails.png" />
|
|
46
47
|
# stylesheet_link_tag("application")
|
|
47
|
-
# # => <link href="http://assets2.example.com/assets/application.css"
|
|
48
|
+
# # => <link href="http://assets2.example.com/assets/application.css" rel="stylesheet" />
|
|
48
49
|
#
|
|
49
50
|
# This may improve the asset loading performance of your application.
|
|
50
51
|
# It is also possible the combination of additional connection overhead
|
|
@@ -65,12 +66,12 @@ module ActionView
|
|
|
65
66
|
# +asset_host+ to a proc like this:
|
|
66
67
|
#
|
|
67
68
|
# ActionController::Base.asset_host = Proc.new { |source|
|
|
68
|
-
# "http://assets#{Digest::
|
|
69
|
+
# "http://assets#{OpenSSL::Digest::SHA256.hexdigest(source).to_i(16) % 2 + 1}.example.com"
|
|
69
70
|
# }
|
|
70
71
|
# image_tag("rails.png")
|
|
71
72
|
# # => <img src="http://assets1.example.com/assets/rails.png" />
|
|
72
73
|
# stylesheet_link_tag("application")
|
|
73
|
-
# # => <link href="http://assets2.example.com/assets/application.css"
|
|
74
|
+
# # => <link href="http://assets2.example.com/assets/application.css" rel="stylesheet" />
|
|
74
75
|
#
|
|
75
76
|
# The example above generates "http://assets1.example.com" and
|
|
76
77
|
# "http://assets2.example.com". This option is useful for example if
|
|
@@ -89,7 +90,7 @@ module ActionView
|
|
|
89
90
|
# image_tag("rails.png")
|
|
90
91
|
# # => <img src="http://assets.example.com/assets/rails.png" />
|
|
91
92
|
# stylesheet_link_tag("application")
|
|
92
|
-
# # => <link href="http://stylesheets.example.com/assets/application.css"
|
|
93
|
+
# # => <link href="http://stylesheets.example.com/assets/application.css" rel="stylesheet" />
|
|
93
94
|
#
|
|
94
95
|
# Alternatively you may ask for a second parameter +request+. That one is
|
|
95
96
|
# particularly useful for serving assets from an SSL-protected page. The
|
|
@@ -121,7 +122,7 @@ module ActionView
|
|
|
121
122
|
URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}i
|
|
122
123
|
|
|
123
124
|
# This is the entry point for all assets.
|
|
124
|
-
# When using
|
|
125
|
+
# When using an asset pipeline gem (e.g. propshaft or sprockets-rails), the
|
|
125
126
|
# behavior is "enhanced". You can bypass the asset pipeline by passing in
|
|
126
127
|
# <tt>skip_pipeline: true</tt> to the options.
|
|
127
128
|
#
|
|
@@ -130,7 +131,7 @@ module ActionView
|
|
|
130
131
|
# === With the asset pipeline
|
|
131
132
|
#
|
|
132
133
|
# All options passed to +asset_path+ will be passed to +compute_asset_path+
|
|
133
|
-
# which is implemented by
|
|
134
|
+
# which is implemented by asset pipeline gems.
|
|
134
135
|
#
|
|
135
136
|
# asset_path("application.js") # => "/assets/application-60aa4fdc5cea14baf5400fba1abf4f2a46a5166bad4772b1effe341570f07de9.js"
|
|
136
137
|
# asset_path('application.js', host: 'example.com') # => "//example.com/assets/application.js"
|
|
@@ -190,13 +191,13 @@ module ActionView
|
|
|
190
191
|
return "" if source.blank?
|
|
191
192
|
return source if URI_REGEXP.match?(source)
|
|
192
193
|
|
|
193
|
-
tail, source = source[/([
|
|
194
|
+
tail, source = source[/([?#].+)$/], source.sub(/([?#].+)$/, "")
|
|
194
195
|
|
|
195
196
|
if extname = compute_asset_extname(source, options)
|
|
196
197
|
source = "#{source}#{extname}"
|
|
197
198
|
end
|
|
198
199
|
|
|
199
|
-
|
|
200
|
+
unless source.start_with?(?/)
|
|
200
201
|
if options[:skip_pipeline]
|
|
201
202
|
source = public_compute_asset_path(source, options)
|
|
202
203
|
else
|
|
@@ -219,7 +220,7 @@ module ActionView
|
|
|
219
220
|
|
|
220
221
|
# Computes the full URL to an asset in the public directory. This
|
|
221
222
|
# will use +asset_path+ internally, so most of their behaviors
|
|
222
|
-
# will be the same. If
|
|
223
|
+
# will be the same. If +:host+ options is set, it overwrites global
|
|
223
224
|
# +config.action_controller.asset_host+ setting.
|
|
224
225
|
#
|
|
225
226
|
# All other options provided are forwarded to +asset_path+ call.
|
|
@@ -324,7 +325,7 @@ module ActionView
|
|
|
324
325
|
|
|
325
326
|
# Computes the full URL to a JavaScript asset in the public javascripts directory.
|
|
326
327
|
# This will use +javascript_path+ internally, so most of their behaviors will be the same.
|
|
327
|
-
# Since +javascript_url+ is based on +asset_url+ method you can set
|
|
328
|
+
# Since +javascript_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
|
|
328
329
|
# options is set, it overwrites global +config.action_controller.asset_host+ setting.
|
|
329
330
|
#
|
|
330
331
|
# javascript_url "js/xmlhr.js", host: "http://stage.example.com" # => http://stage.example.com/assets/js/xmlhr.js
|
|
@@ -351,7 +352,7 @@ module ActionView
|
|
|
351
352
|
|
|
352
353
|
# Computes the full URL to a stylesheet asset in the public stylesheets directory.
|
|
353
354
|
# This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
|
|
354
|
-
# Since +stylesheet_url+ is based on +asset_url+ method you can set
|
|
355
|
+
# Since +stylesheet_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
|
|
355
356
|
# options is set, it overwrites global +config.action_controller.asset_host+ setting.
|
|
356
357
|
#
|
|
357
358
|
# stylesheet_url "css/style.css", host: "http://stage.example.com" # => http://stage.example.com/assets/css/style.css
|
|
@@ -372,7 +373,7 @@ module ActionView
|
|
|
372
373
|
# image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
|
|
373
374
|
#
|
|
374
375
|
# If you have images as application resources this method may conflict with their named routes.
|
|
375
|
-
# The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
|
|
376
|
+
# The alias +path_to_image+ is provided to avoid that. \Rails uses the alias internally, and
|
|
376
377
|
# plugin authors are encouraged to do so.
|
|
377
378
|
def image_path(source, options = {})
|
|
378
379
|
path_to_asset(source, { type: :image }.merge!(options))
|
|
@@ -381,7 +382,7 @@ module ActionView
|
|
|
381
382
|
|
|
382
383
|
# Computes the full URL to an image asset.
|
|
383
384
|
# This will use +image_path+ internally, so most of their behaviors will be the same.
|
|
384
|
-
# Since +image_url+ is based on +asset_url+ method you can set
|
|
385
|
+
# Since +image_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
|
|
385
386
|
# options is set, it overwrites global +config.action_controller.asset_host+ setting.
|
|
386
387
|
#
|
|
387
388
|
# image_url "edit.png", host: "http://stage.example.com" # => http://stage.example.com/assets/edit.png
|
|
@@ -407,7 +408,7 @@ module ActionView
|
|
|
407
408
|
|
|
408
409
|
# Computes the full URL to a video asset in the public videos directory.
|
|
409
410
|
# This will use +video_path+ internally, so most of their behaviors will be the same.
|
|
410
|
-
# Since +video_url+ is based on +asset_url+ method you can set
|
|
411
|
+
# Since +video_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
|
|
411
412
|
# options is set, it overwrites global +config.action_controller.asset_host+ setting.
|
|
412
413
|
#
|
|
413
414
|
# video_url "hd.avi", host: "http://stage.example.com" # => http://stage.example.com/videos/hd.avi
|
|
@@ -433,7 +434,7 @@ module ActionView
|
|
|
433
434
|
|
|
434
435
|
# Computes the full URL to an audio asset in the public audios directory.
|
|
435
436
|
# This will use +audio_path+ internally, so most of their behaviors will be the same.
|
|
436
|
-
# Since +audio_url+ is based on +asset_url+ method you can set
|
|
437
|
+
# Since +audio_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
|
|
437
438
|
# options is set, it overwrites global +config.action_controller.asset_host+ setting.
|
|
438
439
|
#
|
|
439
440
|
# audio_url "horse.wav", host: "http://stage.example.com" # => http://stage.example.com/audios/horse.wav
|
|
@@ -458,7 +459,7 @@ module ActionView
|
|
|
458
459
|
|
|
459
460
|
# Computes the full URL to a font asset.
|
|
460
461
|
# This will use +font_path+ internally, so most of their behaviors will be the same.
|
|
461
|
-
# Since +font_url+ is based on +asset_url+ method you can set
|
|
462
|
+
# Since +font_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
|
|
462
463
|
# options is set, it overwrites global +config.action_controller.asset_host+ setting.
|
|
463
464
|
#
|
|
464
465
|
# font_url "font.ttf", host: "http://stage.example.com" # => http://stage.example.com/fonts/font.ttf
|