omg-actionview 8.0.0.alpha1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +25 -0
- data/MIT-LICENSE +21 -0
- data/README.rdoc +40 -0
- 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 +316 -0
- data/lib/action_view/buffers.rb +165 -0
- data/lib/action_view/cache_expiry.rb +69 -0
- data/lib/action_view/context.rb +32 -0
- data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
- data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
- data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
- data/lib/action_view/dependency_tracker.rb +41 -0
- data/lib/action_view/deprecator.rb +7 -0
- data/lib/action_view/digestor.rb +130 -0
- data/lib/action_view/flows.rb +75 -0
- data/lib/action_view/gem_version.rb +17 -0
- data/lib/action_view/helpers/active_model_helper.rb +54 -0
- data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
- data/lib/action_view/helpers/asset_url_helper.rb +473 -0
- data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
- data/lib/action_view/helpers/cache_helper.rb +315 -0
- data/lib/action_view/helpers/capture_helper.rb +236 -0
- data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
- data/lib/action_view/helpers/controller_helper.rb +42 -0
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +35 -0
- data/lib/action_view/helpers/date_helper.rb +1266 -0
- data/lib/action_view/helpers/debug_helper.rb +38 -0
- data/lib/action_view/helpers/form_helper.rb +2765 -0
- data/lib/action_view/helpers/form_options_helper.rb +927 -0
- data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
- data/lib/action_view/helpers/javascript_helper.rb +96 -0
- data/lib/action_view/helpers/number_helper.rb +165 -0
- data/lib/action_view/helpers/output_safety_helper.rb +70 -0
- data/lib/action_view/helpers/rendering_helper.rb +218 -0
- data/lib/action_view/helpers/sanitize_helper.rb +201 -0
- data/lib/action_view/helpers/tag_helper.rb +621 -0
- data/lib/action_view/helpers/tags/base.rb +138 -0
- data/lib/action_view/helpers/tags/check_box.rb +65 -0
- data/lib/action_view/helpers/tags/checkable.rb +18 -0
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
- data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
- data/lib/action_view/helpers/tags/collection_select.rb +33 -0
- data/lib/action_view/helpers/tags/color_field.rb +26 -0
- data/lib/action_view/helpers/tags/date_field.rb +14 -0
- data/lib/action_view/helpers/tags/date_select.rb +75 -0
- data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
- data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
- data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
- data/lib/action_view/helpers/tags/email_field.rb +10 -0
- data/lib/action_view/helpers/tags/file_field.rb +26 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
- data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
- data/lib/action_view/helpers/tags/label.rb +84 -0
- data/lib/action_view/helpers/tags/month_field.rb +14 -0
- data/lib/action_view/helpers/tags/number_field.rb +20 -0
- data/lib/action_view/helpers/tags/password_field.rb +14 -0
- data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
- data/lib/action_view/helpers/tags/radio_button.rb +32 -0
- data/lib/action_view/helpers/tags/range_field.rb +10 -0
- data/lib/action_view/helpers/tags/search_field.rb +27 -0
- data/lib/action_view/helpers/tags/select.rb +45 -0
- data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
- data/lib/action_view/helpers/tags/tel_field.rb +10 -0
- data/lib/action_view/helpers/tags/text_area.rb +24 -0
- data/lib/action_view/helpers/tags/text_field.rb +33 -0
- data/lib/action_view/helpers/tags/time_field.rb +23 -0
- data/lib/action_view/helpers/tags/time_select.rb +10 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
- data/lib/action_view/helpers/tags/translator.rb +39 -0
- data/lib/action_view/helpers/tags/url_field.rb +10 -0
- data/lib/action_view/helpers/tags/week_field.rb +14 -0
- data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
- data/lib/action_view/helpers/tags.rb +47 -0
- data/lib/action_view/helpers/text_helper.rb +568 -0
- data/lib/action_view/helpers/translation_helper.rb +161 -0
- data/lib/action_view/helpers/url_helper.rb +812 -0
- data/lib/action_view/helpers.rb +68 -0
- data/lib/action_view/layouts.rb +434 -0
- data/lib/action_view/locale/en.yml +56 -0
- data/lib/action_view/log_subscriber.rb +132 -0
- data/lib/action_view/lookup_context.rb +299 -0
- data/lib/action_view/model_naming.rb +14 -0
- data/lib/action_view/path_registry.rb +57 -0
- data/lib/action_view/path_set.rb +84 -0
- data/lib/action_view/railtie.rb +132 -0
- data/lib/action_view/record_identifier.rb +118 -0
- data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
- data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
- data/lib/action_view/render_parser.rb +40 -0
- data/lib/action_view/renderer/abstract_renderer.rb +186 -0
- data/lib/action_view/renderer/collection_renderer.rb +204 -0
- data/lib/action_view/renderer/object_renderer.rb +34 -0
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
- data/lib/action_view/renderer/partial_renderer.rb +267 -0
- data/lib/action_view/renderer/renderer.rb +107 -0
- data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
- data/lib/action_view/renderer/template_renderer.rb +115 -0
- data/lib/action_view/rendering.rb +190 -0
- data/lib/action_view/routing_url_for.rb +149 -0
- data/lib/action_view/tasks/cache_digests.rake +25 -0
- data/lib/action_view/template/error.rb +264 -0
- data/lib/action_view/template/handlers/builder.rb +25 -0
- data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
- data/lib/action_view/template/handlers/erb.rb +157 -0
- data/lib/action_view/template/handlers/html.rb +11 -0
- data/lib/action_view/template/handlers/raw.rb +11 -0
- data/lib/action_view/template/handlers.rb +66 -0
- data/lib/action_view/template/html.rb +33 -0
- 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 +30 -0
- data/lib/action_view/template/resolver.rb +212 -0
- 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 +32 -0
- data/lib/action_view/template/types.rb +50 -0
- data/lib/action_view/template.rb +580 -0
- 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 +449 -0
- data/lib/action_view/testing/resolvers.rb +44 -0
- data/lib/action_view/unbound_template.rb +67 -0
- data/lib/action_view/version.rb +10 -0
- data/lib/action_view/view_paths.rb +117 -0
- data/lib/action_view.rb +104 -0
- metadata +275 -0
@@ -0,0 +1,680 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "active_support/core_ext/array/extract_options"
|
4
|
+
require "active_support/core_ext/hash/keys"
|
5
|
+
require "active_support/core_ext/object/inclusion"
|
6
|
+
require "action_view/helpers/asset_url_helper"
|
7
|
+
require "action_view/helpers/tag_helper"
|
8
|
+
|
9
|
+
module ActionView
|
10
|
+
module Helpers # :nodoc:
|
11
|
+
# = Action View Asset Tag \Helpers
|
12
|
+
#
|
13
|
+
# This module provides methods for generating HTML that links views to assets such
|
14
|
+
# as images, JavaScripts, stylesheets, and feeds. These methods do not verify
|
15
|
+
# the assets exist before linking to them:
|
16
|
+
#
|
17
|
+
# image_tag("rails.png")
|
18
|
+
# # => <img src="/assets/rails.png" />
|
19
|
+
# stylesheet_link_tag("application")
|
20
|
+
# # => <link href="/assets/application.css?body=1" rel="stylesheet" />
|
21
|
+
module AssetTagHelper
|
22
|
+
include AssetUrlHelper
|
23
|
+
include TagHelper
|
24
|
+
|
25
|
+
mattr_accessor :image_loading
|
26
|
+
mattr_accessor :image_decoding
|
27
|
+
mattr_accessor :preload_links_header
|
28
|
+
mattr_accessor :apply_stylesheet_media_default
|
29
|
+
|
30
|
+
# Returns an HTML script tag for each of the +sources+ provided.
|
31
|
+
#
|
32
|
+
# Sources may be paths to JavaScript files. Relative paths are assumed to be relative
|
33
|
+
# to <tt>assets/javascripts</tt>, full paths are assumed to be relative to the document
|
34
|
+
# root. Relative paths are idiomatic, use absolute paths only when needed.
|
35
|
+
#
|
36
|
+
# When passing paths, the ".js" extension is optional. If you do not want ".js"
|
37
|
+
# appended to the path <tt>extname: false</tt> can be set on the options.
|
38
|
+
#
|
39
|
+
# You can modify the HTML attributes of the script tag by passing a hash as the
|
40
|
+
# last argument.
|
41
|
+
#
|
42
|
+
# When the Asset Pipeline is enabled, you can pass the name of your manifest as
|
43
|
+
# source, and include other JavaScript or CoffeeScript files inside the manifest.
|
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.
|
48
|
+
#
|
49
|
+
# ==== Options
|
50
|
+
#
|
51
|
+
# When the last parameter is a hash you can add HTML attributes using that
|
52
|
+
# parameter. This includes but is not limited to the following options:
|
53
|
+
#
|
54
|
+
# * <tt>:extname</tt> - Append an extension to the generated URL unless the extension
|
55
|
+
# already exists. This only applies for relative URLs.
|
56
|
+
# * <tt>:protocol</tt> - Sets the protocol of the generated URL. This option only
|
57
|
+
# applies when a relative URL and +host+ options are provided.
|
58
|
+
# * <tt>:host</tt> - When a relative URL is provided the host is added to the
|
59
|
+
# that path.
|
60
|
+
# * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
|
61
|
+
# when it is set to true.
|
62
|
+
# * <tt>:nonce</tt> - When set to true, adds an automatic nonce value if
|
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
|
+
# * <tt>:nopush</tt> - Specify if the use of server push is not desired
|
72
|
+
# for the script. Defaults to +true+.
|
73
|
+
#
|
74
|
+
# Any other specified options will be treated as HTML attributes for the
|
75
|
+
# +script+ tag.
|
76
|
+
#
|
77
|
+
# For more information regarding how the <tt>:async</tt> and <tt>:defer</tt>
|
78
|
+
# options affect the <tt><script></tt> tag, please refer to the
|
79
|
+
# {MDN docs}[https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script].
|
80
|
+
#
|
81
|
+
# ==== Examples
|
82
|
+
#
|
83
|
+
# javascript_include_tag "xmlhr"
|
84
|
+
# # => <script src="/assets/xmlhr.debug-1284139606.js"></script>
|
85
|
+
#
|
86
|
+
# javascript_include_tag "xmlhr", host: "localhost", protocol: "https"
|
87
|
+
# # => <script src="https://localhost/assets/xmlhr.debug-1284139606.js"></script>
|
88
|
+
#
|
89
|
+
# javascript_include_tag "template.jst", extname: false
|
90
|
+
# # => <script src="/assets/template.debug-1284139606.jst"></script>
|
91
|
+
#
|
92
|
+
# javascript_include_tag "xmlhr.js"
|
93
|
+
# # => <script src="/assets/xmlhr.debug-1284139606.js"></script>
|
94
|
+
#
|
95
|
+
# javascript_include_tag "common.javascript", "/elsewhere/cools"
|
96
|
+
# # => <script src="/assets/common.javascript.debug-1284139606.js"></script>
|
97
|
+
# # <script src="/elsewhere/cools.debug-1284139606.js"></script>
|
98
|
+
#
|
99
|
+
# javascript_include_tag "http://www.example.com/xmlhr"
|
100
|
+
# # => <script src="http://www.example.com/xmlhr"></script>
|
101
|
+
#
|
102
|
+
# javascript_include_tag "http://www.example.com/xmlhr.js"
|
103
|
+
# # => <script src="http://www.example.com/xmlhr.js"></script>
|
104
|
+
#
|
105
|
+
# javascript_include_tag "http://www.example.com/xmlhr.js", nonce: true
|
106
|
+
# # => <script src="http://www.example.com/xmlhr.js" nonce="..."></script>
|
107
|
+
#
|
108
|
+
# javascript_include_tag "http://www.example.com/xmlhr.js", async: true
|
109
|
+
# # => <script src="http://www.example.com/xmlhr.js" async="async"></script>
|
110
|
+
#
|
111
|
+
# javascript_include_tag "http://www.example.com/xmlhr.js", defer: true
|
112
|
+
# # => <script src="http://www.example.com/xmlhr.js" defer="defer"></script>
|
113
|
+
def javascript_include_tag(*sources)
|
114
|
+
options = sources.extract_options!.stringify_keys
|
115
|
+
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
116
|
+
preload_links = []
|
117
|
+
use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
|
118
|
+
nopush = options["nopush"].nil? ? true : options.delete("nopush")
|
119
|
+
crossorigin = options.delete("crossorigin")
|
120
|
+
crossorigin = "anonymous" if crossorigin == true
|
121
|
+
integrity = options["integrity"]
|
122
|
+
rel = options["type"] == "module" ? "modulepreload" : "preload"
|
123
|
+
|
124
|
+
sources_tags = sources.uniq.map { |source|
|
125
|
+
href = path_to_javascript(source, path_options)
|
126
|
+
if use_preload_links_header && !options["defer"] && href.present? && !href.start_with?("data:")
|
127
|
+
preload_link = "<#{href}>; rel=#{rel}; as=script"
|
128
|
+
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
|
129
|
+
preload_link += "; integrity=#{integrity}" unless integrity.nil?
|
130
|
+
preload_link += "; nopush" if nopush
|
131
|
+
preload_links << preload_link
|
132
|
+
end
|
133
|
+
tag_options = {
|
134
|
+
"src" => href,
|
135
|
+
"crossorigin" => crossorigin
|
136
|
+
}.merge!(options)
|
137
|
+
if tag_options["nonce"] == true
|
138
|
+
tag_options["nonce"] = content_security_policy_nonce
|
139
|
+
end
|
140
|
+
content_tag("script", "", tag_options)
|
141
|
+
}.join("\n").html_safe
|
142
|
+
|
143
|
+
if use_preload_links_header
|
144
|
+
send_preload_links_header(preload_links)
|
145
|
+
end
|
146
|
+
|
147
|
+
sources_tags
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns a stylesheet link tag for the sources specified as arguments.
|
151
|
+
#
|
152
|
+
# When passing paths, the <tt>.css</tt> extension is optional.
|
153
|
+
# If you don't specify an extension, <tt>.css</tt> will be appended automatically.
|
154
|
+
# If you do not want <tt>.css</tt> appended to the path,
|
155
|
+
# set <tt>extname: false</tt> in the options.
|
156
|
+
# You can modify the link attributes by passing a hash as the last argument.
|
157
|
+
#
|
158
|
+
# If the server supports HTTP Early Hints, \Rails will push a <tt>103 Early
|
159
|
+
# Hints</tt> response that links to the assets.
|
160
|
+
#
|
161
|
+
# ==== Options
|
162
|
+
#
|
163
|
+
# * <tt>:extname</tt> - Append an extension to the generated URL unless the extension
|
164
|
+
# already exists. This only applies for relative URLs.
|
165
|
+
# * <tt>:protocol</tt> - Sets the protocol of the generated URL. This option only
|
166
|
+
# applies when a relative URL and +host+ options are provided.
|
167
|
+
# * <tt>:host</tt> - When a relative URL is provided the host is added to the
|
168
|
+
# that path.
|
169
|
+
# * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
|
170
|
+
# when it is set to true.
|
171
|
+
# * <tt>:nonce</tt> - When set to true, adds an automatic nonce value if
|
172
|
+
# you have Content Security Policy enabled.
|
173
|
+
# * <tt>:nopush</tt> - Specify if the use of server push is not desired
|
174
|
+
# for the stylesheet. Defaults to +true+.
|
175
|
+
#
|
176
|
+
# ==== Examples
|
177
|
+
#
|
178
|
+
# stylesheet_link_tag "style"
|
179
|
+
# # => <link href="/assets/style.css" rel="stylesheet" />
|
180
|
+
#
|
181
|
+
# stylesheet_link_tag "style.css"
|
182
|
+
# # => <link href="/assets/style.css" rel="stylesheet" />
|
183
|
+
#
|
184
|
+
# stylesheet_link_tag "http://www.example.com/style.css"
|
185
|
+
# # => <link href="http://www.example.com/style.css" rel="stylesheet" />
|
186
|
+
#
|
187
|
+
# stylesheet_link_tag "style.less", extname: false, skip_pipeline: true, rel: "stylesheet/less"
|
188
|
+
# # => <link href="/stylesheets/style.less" rel="stylesheet/less">
|
189
|
+
#
|
190
|
+
# stylesheet_link_tag "style", media: "all"
|
191
|
+
# # => <link href="/assets/style.css" media="all" rel="stylesheet" />
|
192
|
+
#
|
193
|
+
# stylesheet_link_tag "style", media: "print"
|
194
|
+
# # => <link href="/assets/style.css" media="print" rel="stylesheet" />
|
195
|
+
#
|
196
|
+
# stylesheet_link_tag "random.styles", "/css/stylish"
|
197
|
+
# # => <link href="/assets/random.styles" rel="stylesheet" />
|
198
|
+
# # <link href="/css/stylish.css" rel="stylesheet" />
|
199
|
+
#
|
200
|
+
# stylesheet_link_tag "style", nonce: true
|
201
|
+
# # => <link href="/assets/style.css" rel="stylesheet" nonce="..." />
|
202
|
+
def stylesheet_link_tag(*sources)
|
203
|
+
options = sources.extract_options!.stringify_keys
|
204
|
+
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
205
|
+
use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
|
206
|
+
preload_links = []
|
207
|
+
crossorigin = options.delete("crossorigin")
|
208
|
+
crossorigin = "anonymous" if crossorigin == true
|
209
|
+
nopush = options["nopush"].nil? ? true : options.delete("nopush")
|
210
|
+
integrity = options["integrity"]
|
211
|
+
|
212
|
+
sources_tags = sources.uniq.map { |source|
|
213
|
+
href = path_to_stylesheet(source, path_options)
|
214
|
+
if use_preload_links_header && href.present? && !href.start_with?("data:")
|
215
|
+
preload_link = "<#{href}>; rel=preload; as=style"
|
216
|
+
preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
|
217
|
+
preload_link += "; integrity=#{integrity}" unless integrity.nil?
|
218
|
+
preload_link += "; nopush" if nopush
|
219
|
+
preload_links << preload_link
|
220
|
+
end
|
221
|
+
tag_options = {
|
222
|
+
"rel" => "stylesheet",
|
223
|
+
"crossorigin" => crossorigin,
|
224
|
+
"href" => href
|
225
|
+
}.merge!(options)
|
226
|
+
if tag_options["nonce"] == true
|
227
|
+
tag_options["nonce"] = content_security_policy_nonce
|
228
|
+
end
|
229
|
+
|
230
|
+
if apply_stylesheet_media_default && tag_options["media"].blank?
|
231
|
+
tag_options["media"] = "screen"
|
232
|
+
end
|
233
|
+
|
234
|
+
tag(:link, tag_options)
|
235
|
+
}.join("\n").html_safe
|
236
|
+
|
237
|
+
if use_preload_links_header
|
238
|
+
send_preload_links_header(preload_links)
|
239
|
+
end
|
240
|
+
|
241
|
+
sources_tags
|
242
|
+
end
|
243
|
+
|
244
|
+
# Returns a link tag that browsers and feed readers can use to auto-detect
|
245
|
+
# an RSS, Atom, or JSON feed. The +type+ can be <tt>:rss</tt> (default),
|
246
|
+
# <tt>:atom</tt>, or <tt>:json</tt>. Control the link options in url_for format
|
247
|
+
# using the +url_options+. You can modify the LINK tag itself in +tag_options+.
|
248
|
+
#
|
249
|
+
# ==== Options
|
250
|
+
#
|
251
|
+
# * <tt>:rel</tt> - Specify the relation of this link, defaults to "alternate"
|
252
|
+
# * <tt>:type</tt> - Override the auto-generated mime type
|
253
|
+
# * <tt>:title</tt> - Specify the title of the link, defaults to the +type+
|
254
|
+
#
|
255
|
+
# ==== Examples
|
256
|
+
#
|
257
|
+
# auto_discovery_link_tag
|
258
|
+
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
|
259
|
+
# auto_discovery_link_tag(:atom)
|
260
|
+
# # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
|
261
|
+
# auto_discovery_link_tag(:json)
|
262
|
+
# # => <link rel="alternate" type="application/json" title="JSON" href="http://www.currenthost.com/controller/action" />
|
263
|
+
# auto_discovery_link_tag(:rss, {action: "feed"})
|
264
|
+
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
|
265
|
+
# auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
|
266
|
+
# # => <link rel="alternate" type="application/rss+xml" title="My RSS" href="http://www.currenthost.com/controller/feed" />
|
267
|
+
# auto_discovery_link_tag(:rss, {controller: "news", action: "feed"})
|
268
|
+
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/news/feed" />
|
269
|
+
# auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
|
270
|
+
# # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed.rss" />
|
271
|
+
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
|
272
|
+
if !(type == :rss || type == :atom || type == :json) && tag_options[:type].blank?
|
273
|
+
raise ArgumentError.new("You should pass :type tag_option key explicitly, because you have passed #{type} type other than :rss, :atom, or :json.")
|
274
|
+
end
|
275
|
+
|
276
|
+
tag(
|
277
|
+
"link",
|
278
|
+
"rel" => tag_options[:rel] || "alternate",
|
279
|
+
"type" => tag_options[:type] || Template::Types[type].to_s,
|
280
|
+
"title" => tag_options[:title] || type.to_s.upcase,
|
281
|
+
"href" => url_options.is_a?(Hash) ? url_for(url_options.merge(only_path: false)) : url_options
|
282
|
+
)
|
283
|
+
end
|
284
|
+
|
285
|
+
# Returns a link tag for a favicon managed by the asset pipeline.
|
286
|
+
#
|
287
|
+
# If a page has no link like the one generated by this helper, browsers
|
288
|
+
# ask for <tt>/favicon.ico</tt> automatically, and cache the file if the
|
289
|
+
# request succeeds. If the favicon changes it is hard to get it updated.
|
290
|
+
#
|
291
|
+
# To have better control applications may let the asset pipeline manage
|
292
|
+
# their favicon storing the file under <tt>app/assets/images</tt>, and
|
293
|
+
# using this helper to generate its corresponding link tag.
|
294
|
+
#
|
295
|
+
# The helper gets the name of the favicon file as first argument, which
|
296
|
+
# defaults to "favicon.ico", and also supports +:rel+ and +:type+ options
|
297
|
+
# to override their defaults, "icon" and "image/x-icon"
|
298
|
+
# respectively:
|
299
|
+
#
|
300
|
+
# favicon_link_tag
|
301
|
+
# # => <link href="/assets/favicon.ico" rel="icon" type="image/x-icon" />
|
302
|
+
#
|
303
|
+
# favicon_link_tag 'myicon.ico'
|
304
|
+
# # => <link href="/assets/myicon.ico" rel="icon" type="image/x-icon" />
|
305
|
+
#
|
306
|
+
# Mobile Safari looks for a different link tag, pointing to an image that
|
307
|
+
# will be used if you add the page to the home screen of an iOS device.
|
308
|
+
# The following call would generate such a tag:
|
309
|
+
#
|
310
|
+
# favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
|
311
|
+
# # => <link href="/assets/mb-icon.png" rel="apple-touch-icon" type="image/png" />
|
312
|
+
def favicon_link_tag(source = "favicon.ico", options = {})
|
313
|
+
tag("link", {
|
314
|
+
rel: "icon",
|
315
|
+
type: "image/x-icon",
|
316
|
+
href: path_to_image(source, skip_pipeline: options.delete(:skip_pipeline))
|
317
|
+
}.merge!(options.symbolize_keys))
|
318
|
+
end
|
319
|
+
|
320
|
+
# Returns a link tag that browsers can use to preload the +source+.
|
321
|
+
# The +source+ can be the path of a resource managed by asset pipeline,
|
322
|
+
# a full path, or an URI.
|
323
|
+
#
|
324
|
+
# ==== Options
|
325
|
+
#
|
326
|
+
# * <tt>:type</tt> - Override the auto-generated mime type, defaults to the mime type for +source+ extension.
|
327
|
+
# * <tt>:as</tt> - Override the auto-generated value for as attribute, calculated using +source+ extension and mime type.
|
328
|
+
# * <tt>:crossorigin</tt> - Specify the crossorigin attribute, required to load cross-origin resources.
|
329
|
+
# * <tt>:nopush</tt> - Specify if the use of server push is not desired for the resource. Defaults to +false+.
|
330
|
+
# * <tt>:integrity</tt> - Specify the integrity attribute.
|
331
|
+
#
|
332
|
+
# ==== Examples
|
333
|
+
#
|
334
|
+
# preload_link_tag("custom_theme.css")
|
335
|
+
# # => <link rel="preload" href="/assets/custom_theme.css" as="style" type="text/css" />
|
336
|
+
#
|
337
|
+
# preload_link_tag("/videos/video.webm")
|
338
|
+
# # => <link rel="preload" href="/videos/video.mp4" as="video" type="video/webm" />
|
339
|
+
#
|
340
|
+
# preload_link_tag(post_path(format: :json), as: "fetch")
|
341
|
+
# # => <link rel="preload" href="/posts.json" as="fetch" type="application/json" />
|
342
|
+
#
|
343
|
+
# preload_link_tag("worker.js", as: "worker")
|
344
|
+
# # => <link rel="preload" href="/assets/worker.js" as="worker" type="text/javascript" />
|
345
|
+
#
|
346
|
+
# preload_link_tag("//example.com/font.woff2")
|
347
|
+
# # => <link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="anonymous"/>
|
348
|
+
#
|
349
|
+
# preload_link_tag("//example.com/font.woff2", crossorigin: "use-credentials")
|
350
|
+
# # => <link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="use-credentials" />
|
351
|
+
#
|
352
|
+
# preload_link_tag("/media/audio.ogg", nopush: true)
|
353
|
+
# # => <link rel="preload" href="/media/audio.ogg" as="audio" type="audio/ogg" />
|
354
|
+
#
|
355
|
+
def preload_link_tag(source, options = {})
|
356
|
+
href = path_to_asset(source, skip_pipeline: options.delete(:skip_pipeline))
|
357
|
+
extname = File.extname(source).downcase.delete(".")
|
358
|
+
mime_type = options.delete(:type) || Template::Types[extname]&.to_s
|
359
|
+
as_type = options.delete(:as) || resolve_link_as(extname, mime_type)
|
360
|
+
crossorigin = options.delete(:crossorigin)
|
361
|
+
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
|
362
|
+
integrity = options[:integrity]
|
363
|
+
nopush = options.delete(:nopush) || false
|
364
|
+
rel = mime_type == "module" ? "modulepreload" : "preload"
|
365
|
+
|
366
|
+
link_tag = tag.link(
|
367
|
+
rel: rel,
|
368
|
+
href: href,
|
369
|
+
as: as_type,
|
370
|
+
type: mime_type,
|
371
|
+
crossorigin: crossorigin,
|
372
|
+
**options.symbolize_keys)
|
373
|
+
|
374
|
+
preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}"
|
375
|
+
preload_link += "; type=#{mime_type}" if mime_type
|
376
|
+
preload_link += "; crossorigin=#{crossorigin}" if crossorigin
|
377
|
+
preload_link += "; integrity=#{integrity}" if integrity
|
378
|
+
preload_link += "; nopush" if nopush
|
379
|
+
|
380
|
+
send_preload_links_header([preload_link])
|
381
|
+
|
382
|
+
link_tag
|
383
|
+
end
|
384
|
+
|
385
|
+
# Returns an HTML image tag for the +source+. The +source+ can be a full
|
386
|
+
# path, a file, or an Active Storage attachment.
|
387
|
+
#
|
388
|
+
# ==== Options
|
389
|
+
#
|
390
|
+
# You can add HTML attributes using the +options+. The +options+ supports
|
391
|
+
# additional keys for convenience and conformance:
|
392
|
+
#
|
393
|
+
# * <tt>:size</tt> - Supplied as <tt>"#{width}x#{height}"</tt> or <tt>"#{number}"</tt>, so <tt>"30x45"</tt> becomes
|
394
|
+
# <tt>width="30" height="45"</tt>, and <tt>"50"</tt> becomes <tt>width="50" height="50"</tt>.
|
395
|
+
# <tt>:size</tt> will be ignored if the value is not in the correct format.
|
396
|
+
# * <tt>:srcset</tt> - If supplied as a hash or array of <tt>[source, descriptor]</tt>
|
397
|
+
# pairs, each image path will be expanded before the list is formatted as a string.
|
398
|
+
#
|
399
|
+
# ==== Examples
|
400
|
+
#
|
401
|
+
# Assets (images that are part of your app):
|
402
|
+
#
|
403
|
+
# image_tag("icon")
|
404
|
+
# # => <img src="/assets/icon" />
|
405
|
+
# image_tag("icon.png")
|
406
|
+
# # => <img src="/assets/icon.png" />
|
407
|
+
# image_tag("icon.png", size: "16x10", alt: "Edit Entry")
|
408
|
+
# # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
|
409
|
+
# image_tag("/icons/icon.gif", size: "16")
|
410
|
+
# # => <img src="/icons/icon.gif" width="16" height="16" />
|
411
|
+
# image_tag("/icons/icon.gif", height: '32', width: '32')
|
412
|
+
# # => <img height="32" src="/icons/icon.gif" width="32" />
|
413
|
+
# image_tag("/icons/icon.gif", class: "menu_icon")
|
414
|
+
# # => <img class="menu_icon" src="/icons/icon.gif" />
|
415
|
+
# image_tag("/icons/icon.gif", data: { title: 'Rails Application' })
|
416
|
+
# # => <img data-title="Rails Application" src="/icons/icon.gif" />
|
417
|
+
# image_tag("icon.png", srcset: { "icon_2x.png" => "2x", "icon_4x.png" => "4x" })
|
418
|
+
# # => <img src="/assets/icon.png" srcset="/assets/icon_2x.png 2x, /assets/icon_4x.png 4x">
|
419
|
+
# image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw")
|
420
|
+
# # => <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw">
|
421
|
+
#
|
422
|
+
# Active Storage blobs (images that are uploaded by the users of your app):
|
423
|
+
#
|
424
|
+
# image_tag(user.avatar)
|
425
|
+
# # => <img src="/rails/active_storage/blobs/.../tiger.jpg" />
|
426
|
+
# image_tag(user.avatar.variant(resize_to_limit: [100, 100]))
|
427
|
+
# # => <img src="/rails/active_storage/representations/.../tiger.jpg" />
|
428
|
+
# image_tag(user.avatar.variant(resize_to_limit: [100, 100]), size: '100')
|
429
|
+
# # => <img width="100" height="100" src="/rails/active_storage/representations/.../tiger.jpg" />
|
430
|
+
def image_tag(source, options = {})
|
431
|
+
options = options.symbolize_keys
|
432
|
+
check_for_image_tag_errors(options)
|
433
|
+
skip_pipeline = options.delete(:skip_pipeline)
|
434
|
+
|
435
|
+
options[:src] = resolve_asset_source("image", source, skip_pipeline)
|
436
|
+
|
437
|
+
if options[:srcset] && !options[:srcset].is_a?(String)
|
438
|
+
options[:srcset] = options[:srcset].map do |src_path, size|
|
439
|
+
src_path = path_to_image(src_path, skip_pipeline: skip_pipeline)
|
440
|
+
"#{src_path} #{size}"
|
441
|
+
end.join(", ")
|
442
|
+
end
|
443
|
+
|
444
|
+
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
|
445
|
+
|
446
|
+
options[:loading] ||= image_loading if image_loading
|
447
|
+
options[:decoding] ||= image_decoding if image_decoding
|
448
|
+
|
449
|
+
tag("img", options)
|
450
|
+
end
|
451
|
+
|
452
|
+
# Returns an HTML picture tag for the +sources+. If +sources+ is a string,
|
453
|
+
# a single picture tag will be returned. If +sources+ is an array, a picture
|
454
|
+
# tag with nested source tags for each source will be returned. The
|
455
|
+
# +sources+ can be full paths, files that exist in your public images
|
456
|
+
# directory, or Active Storage attachments. Since the picture tag requires
|
457
|
+
# an img tag, the last element you provide will be used for the img tag.
|
458
|
+
# For complete control over the picture tag, a block can be passed, which
|
459
|
+
# will populate the contents of the tag accordingly.
|
460
|
+
#
|
461
|
+
# ==== Options
|
462
|
+
#
|
463
|
+
# When the last parameter is a hash you can add HTML attributes using that
|
464
|
+
# parameter. Apart from all the HTML supported options, the following are supported:
|
465
|
+
#
|
466
|
+
# * <tt>:image</tt> - Hash of options that are passed directly to the +image_tag+ helper.
|
467
|
+
#
|
468
|
+
# ==== Examples
|
469
|
+
#
|
470
|
+
# picture_tag("picture.webp")
|
471
|
+
# # => <picture><img src="/images/picture.webp" /></picture>
|
472
|
+
# picture_tag("gold.png", :image => { :size => "20" })
|
473
|
+
# # => <picture><img height="20" src="/images/gold.png" width="20" /></picture>
|
474
|
+
# picture_tag("gold.png", :image => { :size => "45x70" })
|
475
|
+
# # => <picture><img height="70" src="/images/gold.png" width="45" /></picture>
|
476
|
+
# picture_tag("picture.webp", "picture.png")
|
477
|
+
# # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img src="/images/picture.png" /></picture>
|
478
|
+
# picture_tag("picture.webp", "picture.png", :image => { alt: "Image" })
|
479
|
+
# # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img alt="Image" src="/images/picture.png" /></picture>
|
480
|
+
# picture_tag(["picture.webp", "picture.png"], :image => { alt: "Image" })
|
481
|
+
# # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img alt="Image" src="/images/picture.png" /></picture>
|
482
|
+
# picture_tag(:class => "my-class") { tag(:source, :srcset => image_path("picture.webp")) + image_tag("picture.png", :alt => "Image") }
|
483
|
+
# # => <picture class="my-class"><source srcset="/images/picture.webp" /><img alt="Image" src="/images/picture.png" /></picture>
|
484
|
+
# 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") }
|
485
|
+
# # => <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>
|
486
|
+
#
|
487
|
+
# Active Storage blobs (images that are uploaded by the users of your app):
|
488
|
+
#
|
489
|
+
# picture_tag(user.profile_picture)
|
490
|
+
# # => <picture><img src="/rails/active_storage/blobs/.../profile_picture.webp" /></picture>
|
491
|
+
def picture_tag(*sources, &block)
|
492
|
+
sources.flatten!
|
493
|
+
options = sources.extract_options!.symbolize_keys
|
494
|
+
image_options = options.delete(:image) || {}
|
495
|
+
skip_pipeline = options.delete(:skip_pipeline)
|
496
|
+
|
497
|
+
content_tag("picture", options) do
|
498
|
+
if block.present?
|
499
|
+
capture(&block).html_safe
|
500
|
+
elsif sources.size <= 1
|
501
|
+
image_tag(sources.last, image_options)
|
502
|
+
else
|
503
|
+
source_tags = sources.map do |source|
|
504
|
+
tag("source",
|
505
|
+
srcset: resolve_asset_source("image", source, skip_pipeline),
|
506
|
+
type: Template::Types[File.extname(source)[1..]]&.to_s)
|
507
|
+
end
|
508
|
+
safe_join(source_tags << image_tag(sources.last, image_options))
|
509
|
+
end
|
510
|
+
end
|
511
|
+
end
|
512
|
+
|
513
|
+
# Returns an HTML video tag for the +sources+. If +sources+ is a string,
|
514
|
+
# a single video tag will be returned. If +sources+ is an array, a video
|
515
|
+
# tag with nested source tags for each source will be returned. The
|
516
|
+
# +sources+ can be full paths, files that exist in your public videos
|
517
|
+
# directory, or Active Storage attachments.
|
518
|
+
#
|
519
|
+
# ==== Options
|
520
|
+
#
|
521
|
+
# When the last parameter is a hash you can add HTML attributes using that
|
522
|
+
# parameter. The following options are supported:
|
523
|
+
#
|
524
|
+
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
|
525
|
+
# before the video loads. The path is calculated like the +src+ of +image_tag+.
|
526
|
+
# * <tt>:size</tt> - Supplied as <tt>"#{width}x#{height}"</tt> or <tt>"#{number}"</tt>, so <tt>"30x45"</tt> becomes
|
527
|
+
# <tt>width="30" height="45"</tt>, and <tt>"50"</tt> becomes <tt>width="50" height="50"</tt>.
|
528
|
+
# <tt>:size</tt> will be ignored if the value is not in the correct format.
|
529
|
+
# * <tt>:poster_skip_pipeline</tt> will bypass the asset pipeline when using
|
530
|
+
# the <tt>:poster</tt> option instead using an asset in the public folder.
|
531
|
+
#
|
532
|
+
# ==== Examples
|
533
|
+
#
|
534
|
+
# video_tag("trailer")
|
535
|
+
# # => <video src="/videos/trailer"></video>
|
536
|
+
# video_tag("trailer.ogg")
|
537
|
+
# # => <video src="/videos/trailer.ogg"></video>
|
538
|
+
# video_tag("trailer.ogg", controls: true, preload: 'none')
|
539
|
+
# # => <video preload="none" controls="controls" src="/videos/trailer.ogg"></video>
|
540
|
+
# video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
|
541
|
+
# # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png"></video>
|
542
|
+
# video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png", poster_skip_pipeline: true)
|
543
|
+
# # => <video src="/videos/trailer.m4v" width="16" height="10" poster="screenshot.png"></video>
|
544
|
+
# video_tag("/trailers/hd.avi", size: "16x16")
|
545
|
+
# # => <video src="/trailers/hd.avi" width="16" height="16"></video>
|
546
|
+
# video_tag("/trailers/hd.avi", size: "16")
|
547
|
+
# # => <video height="16" src="/trailers/hd.avi" width="16"></video>
|
548
|
+
# video_tag("/trailers/hd.avi", height: '32', width: '32')
|
549
|
+
# # => <video height="32" src="/trailers/hd.avi" width="32"></video>
|
550
|
+
# video_tag("trailer.ogg", "trailer.flv")
|
551
|
+
# # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
|
552
|
+
# video_tag(["trailer.ogg", "trailer.flv"])
|
553
|
+
# # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
|
554
|
+
# video_tag(["trailer.ogg", "trailer.flv"], size: "160x120")
|
555
|
+
# # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
|
556
|
+
#
|
557
|
+
# Active Storage blobs (videos that are uploaded by the users of your app):
|
558
|
+
#
|
559
|
+
# video_tag(user.intro_video)
|
560
|
+
# # => <video src="/rails/active_storage/blobs/.../intro_video.mp4"></video>
|
561
|
+
def video_tag(*sources)
|
562
|
+
options = sources.extract_options!.symbolize_keys
|
563
|
+
public_poster_folder = options.delete(:poster_skip_pipeline)
|
564
|
+
sources << options
|
565
|
+
multiple_sources_tag_builder("video", sources) do |tag_options|
|
566
|
+
tag_options[:poster] = path_to_image(tag_options[:poster], skip_pipeline: public_poster_folder) if tag_options[:poster]
|
567
|
+
tag_options[:width], tag_options[:height] = extract_dimensions(tag_options.delete(:size)) if tag_options[:size]
|
568
|
+
end
|
569
|
+
end
|
570
|
+
|
571
|
+
# Returns an HTML audio tag for the +sources+. If +sources+ is a string,
|
572
|
+
# a single audio tag will be returned. If +sources+ is an array, an audio
|
573
|
+
# tag with nested source tags for each source will be returned. The
|
574
|
+
# +sources+ can be full paths, files that exist in your public audios
|
575
|
+
# directory, or Active Storage attachments.
|
576
|
+
#
|
577
|
+
# When the last parameter is a hash you can add HTML attributes using that
|
578
|
+
# parameter.
|
579
|
+
#
|
580
|
+
# audio_tag("sound")
|
581
|
+
# # => <audio src="/audios/sound"></audio>
|
582
|
+
# audio_tag("sound.wav")
|
583
|
+
# # => <audio src="/audios/sound.wav"></audio>
|
584
|
+
# audio_tag("sound.wav", autoplay: true, controls: true)
|
585
|
+
# # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav"></audio>
|
586
|
+
# audio_tag("sound.wav", "sound.mid")
|
587
|
+
# # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
|
588
|
+
#
|
589
|
+
# Active Storage blobs (audios that are uploaded by the users of your app):
|
590
|
+
#
|
591
|
+
# audio_tag(user.name_pronunciation_audio)
|
592
|
+
# # => <audio src="/rails/active_storage/blobs/.../name_pronunciation_audio.mp3"></audio>
|
593
|
+
def audio_tag(*sources)
|
594
|
+
multiple_sources_tag_builder("audio", sources)
|
595
|
+
end
|
596
|
+
|
597
|
+
private
|
598
|
+
def multiple_sources_tag_builder(type, sources)
|
599
|
+
options = sources.extract_options!.symbolize_keys
|
600
|
+
skip_pipeline = options.delete(:skip_pipeline)
|
601
|
+
sources.flatten!
|
602
|
+
|
603
|
+
yield options if block_given?
|
604
|
+
|
605
|
+
if sources.size > 1
|
606
|
+
content_tag(type, options) do
|
607
|
+
safe_join sources.map { |source| tag("source", src: resolve_asset_source(type, source, skip_pipeline)) }
|
608
|
+
end
|
609
|
+
else
|
610
|
+
options[:src] = resolve_asset_source(type, sources.first, skip_pipeline)
|
611
|
+
content_tag(type, nil, options)
|
612
|
+
end
|
613
|
+
end
|
614
|
+
|
615
|
+
def resolve_asset_source(asset_type, source, skip_pipeline)
|
616
|
+
if source.is_a?(Symbol) || source.is_a?(String)
|
617
|
+
path_to_asset(source, type: asset_type.to_sym, skip_pipeline: skip_pipeline)
|
618
|
+
else
|
619
|
+
polymorphic_url(source)
|
620
|
+
end
|
621
|
+
rescue NoMethodError => e
|
622
|
+
raise ArgumentError, "Can't resolve #{asset_type} into URL: #{e}"
|
623
|
+
end
|
624
|
+
|
625
|
+
def extract_dimensions(size)
|
626
|
+
size = size.to_s
|
627
|
+
if /\A\d+(?:\.\d+)?x\d+(?:\.\d+)?\z/.match?(size)
|
628
|
+
size.split("x")
|
629
|
+
elsif /\A\d+(?:\.\d+)?\z/.match?(size)
|
630
|
+
[size, size]
|
631
|
+
end
|
632
|
+
end
|
633
|
+
|
634
|
+
def check_for_image_tag_errors(options)
|
635
|
+
if options[:size] && (options[:height] || options[:width])
|
636
|
+
raise ArgumentError, "Cannot pass a :size option with a :height or :width option"
|
637
|
+
end
|
638
|
+
end
|
639
|
+
|
640
|
+
def resolve_link_as(extname, mime_type)
|
641
|
+
case extname
|
642
|
+
when "js" then "script"
|
643
|
+
when "css" then "style"
|
644
|
+
when "vtt" then "track"
|
645
|
+
else
|
646
|
+
mime_type.to_s.split("/").first.presence_in(%w(audio video font image))
|
647
|
+
end
|
648
|
+
end
|
649
|
+
|
650
|
+
# Some HTTP client and proxies have a 4kiB header limit, but more importantly
|
651
|
+
# including preload links has diminishing returns so it's best to not go overboard
|
652
|
+
MAX_HEADER_SIZE = 1_000 # :nodoc:
|
653
|
+
|
654
|
+
def send_preload_links_header(preload_links, max_header_size: MAX_HEADER_SIZE)
|
655
|
+
return if preload_links.empty?
|
656
|
+
response_present = respond_to?(:response) && response
|
657
|
+
return if response_present && response.sending?
|
658
|
+
|
659
|
+
if respond_to?(:request) && request
|
660
|
+
request.send_early_hints("link" => preload_links.join(","))
|
661
|
+
end
|
662
|
+
|
663
|
+
if response_present
|
664
|
+
header = +response.headers["link"].to_s
|
665
|
+
preload_links.each do |link|
|
666
|
+
break if header.bytesize + link.bytesize > max_header_size
|
667
|
+
|
668
|
+
if header.empty?
|
669
|
+
header << link
|
670
|
+
else
|
671
|
+
header << "," << link
|
672
|
+
end
|
673
|
+
end
|
674
|
+
|
675
|
+
response.headers["link"] = header
|
676
|
+
end
|
677
|
+
end
|
678
|
+
end
|
679
|
+
end
|
680
|
+
end
|