actionview 5.1.4 → 6.1.1
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 +5 -5
- data/CHANGELOG.md +199 -168
- data/MIT-LICENSE +1 -1
- data/README.rdoc +7 -5
- data/lib/action_view.rb +10 -4
- data/lib/action_view/base.rb +87 -23
- data/lib/action_view/buffers.rb +17 -0
- data/lib/action_view/cache_expiry.rb +52 -0
- data/lib/action_view/context.rb +7 -11
- data/lib/action_view/dependency_tracker.rb +12 -4
- data/lib/action_view/digestor.rb +24 -23
- data/lib/action_view/flows.rb +2 -1
- data/lib/action_view/gem_version.rb +4 -2
- data/lib/action_view/helpers.rb +4 -2
- data/lib/action_view/helpers/active_model_helper.rb +9 -4
- data/lib/action_view/helpers/asset_tag_helper.rb +220 -57
- data/lib/action_view/helpers/asset_url_helper.rb +28 -23
- data/lib/action_view/helpers/atom_feed_helper.rb +5 -2
- data/lib/action_view/helpers/cache_helper.rb +39 -28
- data/lib/action_view/helpers/capture_helper.rb +13 -7
- data/lib/action_view/helpers/controller_helper.rb +3 -1
- data/lib/action_view/helpers/csp_helper.rb +26 -0
- data/lib/action_view/helpers/csrf_helper.rb +5 -3
- data/lib/action_view/helpers/date_helper.rb +78 -33
- data/lib/action_view/helpers/debug_helper.rb +4 -2
- data/lib/action_view/helpers/form_helper.rb +357 -106
- data/lib/action_view/helpers/form_options_helper.rb +45 -39
- data/lib/action_view/helpers/form_tag_helper.rb +42 -27
- data/lib/action_view/helpers/javascript_helper.rb +28 -12
- data/lib/action_view/helpers/number_helper.rb +16 -8
- data/lib/action_view/helpers/output_safety_helper.rb +3 -1
- data/lib/action_view/helpers/rendering_helper.rb +20 -9
- data/lib/action_view/helpers/sanitize_helper.rb +15 -19
- data/lib/action_view/helpers/tag_helper.rb +100 -24
- data/lib/action_view/helpers/tags.rb +3 -1
- data/lib/action_view/helpers/tags/base.rb +30 -21
- data/lib/action_view/helpers/tags/check_box.rb +3 -2
- data/lib/action_view/helpers/tags/checkable.rb +4 -2
- data/lib/action_view/helpers/tags/collection_check_boxes.rb +2 -1
- data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
- data/lib/action_view/helpers/tags/collection_radio_buttons.rb +2 -1
- data/lib/action_view/helpers/tags/collection_select.rb +3 -1
- data/lib/action_view/helpers/tags/color_field.rb +4 -3
- data/lib/action_view/helpers/tags/date_field.rb +3 -2
- data/lib/action_view/helpers/tags/date_select.rb +5 -4
- data/lib/action_view/helpers/tags/datetime_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
- data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
- data/lib/action_view/helpers/tags/email_field.rb +2 -0
- data/lib/action_view/helpers/tags/file_field.rb +2 -0
- data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -1
- data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
- data/lib/action_view/helpers/tags/label.rb +6 -5
- data/lib/action_view/helpers/tags/month_field.rb +3 -2
- data/lib/action_view/helpers/tags/number_field.rb +2 -0
- data/lib/action_view/helpers/tags/password_field.rb +2 -0
- data/lib/action_view/helpers/tags/placeholderable.rb +2 -0
- data/lib/action_view/helpers/tags/radio_button.rb +3 -2
- data/lib/action_view/helpers/tags/range_field.rb +2 -0
- data/lib/action_view/helpers/tags/search_field.rb +2 -0
- data/lib/action_view/helpers/tags/select.rb +4 -3
- data/lib/action_view/helpers/tags/tel_field.rb +2 -0
- data/lib/action_view/helpers/tags/text_area.rb +3 -1
- data/lib/action_view/helpers/tags/text_field.rb +3 -2
- data/lib/action_view/helpers/tags/time_field.rb +3 -2
- data/lib/action_view/helpers/tags/time_select.rb +2 -0
- data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
- data/lib/action_view/helpers/tags/translator.rb +3 -6
- data/lib/action_view/helpers/tags/url_field.rb +2 -0
- data/lib/action_view/helpers/tags/week_field.rb +3 -2
- data/lib/action_view/helpers/text_helper.rb +11 -10
- data/lib/action_view/helpers/translation_helper.rb +102 -52
- data/lib/action_view/helpers/url_helper.rb +150 -32
- data/lib/action_view/layouts.rb +15 -15
- data/lib/action_view/log_subscriber.rb +32 -15
- data/lib/action_view/lookup_context.rb +67 -39
- data/lib/action_view/model_naming.rb +2 -0
- data/lib/action_view/path_set.rb +5 -12
- data/lib/action_view/railtie.rb +46 -21
- data/lib/action_view/record_identifier.rb +4 -3
- data/lib/action_view/renderer/abstract_renderer.rb +144 -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.rb +33 -283
- data/lib/action_view/renderer/partial_renderer/collection_caching.rb +64 -17
- data/lib/action_view/renderer/renderer.rb +61 -4
- data/lib/action_view/renderer/streaming_template_renderer.rb +14 -8
- data/lib/action_view/renderer/template_renderer.rb +36 -26
- data/lib/action_view/rendering.rb +57 -38
- data/lib/action_view/routing_url_for.rb +15 -12
- data/lib/action_view/tasks/cache_digests.rake +2 -0
- data/lib/action_view/template.rb +69 -76
- data/lib/action_view/template/error.rb +32 -18
- data/lib/action_view/template/handlers.rb +4 -2
- data/lib/action_view/template/handlers/builder.rb +5 -6
- data/lib/action_view/template/handlers/erb.rb +20 -19
- data/lib/action_view/template/handlers/erb/erubi.rb +17 -9
- data/lib/action_view/template/handlers/html.rb +3 -1
- data/lib/action_view/template/handlers/raw.rb +4 -2
- data/lib/action_view/template/html.rb +8 -7
- 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 +194 -152
- data/lib/action_view/template/sources.rb +13 -0
- data/lib/action_view/template/sources/file.rb +17 -0
- data/lib/action_view/template/text.rb +5 -4
- data/lib/action_view/template/types.rb +3 -1
- data/lib/action_view/test_case.rb +38 -30
- data/lib/action_view/testing/resolvers.rb +20 -27
- data/lib/action_view/unbound_template.rb +31 -0
- data/lib/action_view/version.rb +2 -0
- data/lib/action_view/view_paths.rb +61 -40
- data/lib/assets/compiled/rails-ujs.js +84 -23
- metadata +34 -23
- data/lib/action_view/helpers/record_tag_helper.rb +0 -21
- data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +0 -9
- data/lib/action_view/template/handlers/erb/erubis.rb +0 -81
data/lib/action_view/flows.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module ActionView
|
2
4
|
# Returns the version of the currently loaded Action View as a <tt>Gem::Version</tt>
|
3
5
|
def self.gem_version
|
@@ -5,9 +7,9 @@ module ActionView
|
|
5
7
|
end
|
6
8
|
|
7
9
|
module VERSION
|
8
|
-
MAJOR =
|
10
|
+
MAJOR = 6
|
9
11
|
MINOR = 1
|
10
|
-
TINY =
|
12
|
+
TINY = 1
|
11
13
|
PRE = nil
|
12
14
|
|
13
15
|
STRING = [MAJOR, MINOR, TINY, PRE].compact.join(".")
|
data/lib/action_view/helpers.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/benchmarkable"
|
2
4
|
|
3
5
|
module ActionView #:nodoc:
|
@@ -11,6 +13,7 @@ module ActionView #:nodoc:
|
|
11
13
|
autoload :CacheHelper
|
12
14
|
autoload :CaptureHelper
|
13
15
|
autoload :ControllerHelper
|
16
|
+
autoload :CspHelper
|
14
17
|
autoload :CsrfHelper
|
15
18
|
autoload :DateHelper
|
16
19
|
autoload :DebugHelper
|
@@ -20,7 +23,6 @@ module ActionView #:nodoc:
|
|
20
23
|
autoload :JavaScriptHelper, "action_view/helpers/javascript_helper"
|
21
24
|
autoload :NumberHelper
|
22
25
|
autoload :OutputSafetyHelper
|
23
|
-
autoload :RecordTagHelper
|
24
26
|
autoload :RenderingHelper
|
25
27
|
autoload :SanitizeHelper
|
26
28
|
autoload :TagHelper
|
@@ -44,6 +46,7 @@ module ActionView #:nodoc:
|
|
44
46
|
include CacheHelper
|
45
47
|
include CaptureHelper
|
46
48
|
include ControllerHelper
|
49
|
+
include CspHelper
|
47
50
|
include CsrfHelper
|
48
51
|
include DateHelper
|
49
52
|
include DebugHelper
|
@@ -53,7 +56,6 @@ module ActionView #:nodoc:
|
|
53
56
|
include JavaScriptHelper
|
54
57
|
include NumberHelper
|
55
58
|
include OutputSafetyHelper
|
56
|
-
include RecordTagHelper
|
57
59
|
include RenderingHelper
|
58
60
|
include SanitizeHelper
|
59
61
|
include TagHelper
|
@@ -1,9 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/module/attribute_accessors"
|
2
4
|
require "active_support/core_ext/enumerable"
|
3
5
|
|
4
6
|
module ActionView
|
5
7
|
# = Active Model Helpers
|
6
|
-
module Helpers
|
8
|
+
module Helpers #:nodoc:
|
7
9
|
module ActiveModelHelper
|
8
10
|
end
|
9
11
|
|
@@ -15,8 +17,8 @@ module ActionView
|
|
15
17
|
end
|
16
18
|
end
|
17
19
|
|
18
|
-
def content_tag(*)
|
19
|
-
error_wrapping(super)
|
20
|
+
def content_tag(type, options, *)
|
21
|
+
select_markup_helper?(type) ? super : error_wrapping(super)
|
20
22
|
end
|
21
23
|
|
22
24
|
def tag(type, options, *)
|
@@ -36,11 +38,14 @@ module ActionView
|
|
36
38
|
end
|
37
39
|
|
38
40
|
private
|
39
|
-
|
40
41
|
def object_has_errors?
|
41
42
|
object.respond_to?(:errors) && object.errors.respond_to?(:[]) && error_message.present?
|
42
43
|
end
|
43
44
|
|
45
|
+
def select_markup_helper?(type)
|
46
|
+
["optgroup", "option"].include?(type)
|
47
|
+
end
|
48
|
+
|
44
49
|
def tag_generate_errors?(options)
|
45
50
|
options["type"] != "hidden"
|
46
51
|
end
|
@@ -1,5 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "active_support/core_ext/array/extract_options"
|
2
4
|
require "active_support/core_ext/hash/keys"
|
5
|
+
require "active_support/core_ext/object/inclusion"
|
3
6
|
require "action_view/helpers/asset_url_helper"
|
4
7
|
require "action_view/helpers/tag_helper"
|
5
8
|
|
@@ -11,7 +14,7 @@ module ActionView
|
|
11
14
|
# the assets exist before linking to them:
|
12
15
|
#
|
13
16
|
# image_tag("rails.png")
|
14
|
-
# # => <img
|
17
|
+
# # => <img src="/assets/rails.png" />
|
15
18
|
# stylesheet_link_tag("application")
|
16
19
|
# # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
|
17
20
|
module AssetTagHelper
|
@@ -20,13 +23,15 @@ module ActionView
|
|
20
23
|
include AssetUrlHelper
|
21
24
|
include TagHelper
|
22
25
|
|
26
|
+
mattr_accessor :preload_links_header
|
27
|
+
|
23
28
|
# Returns an HTML script tag for each of the +sources+ provided.
|
24
29
|
#
|
25
30
|
# Sources may be paths to JavaScript files. Relative paths are assumed to be relative
|
26
31
|
# to <tt>assets/javascripts</tt>, full paths are assumed to be relative to the document
|
27
32
|
# root. Relative paths are idiomatic, use absolute paths only when needed.
|
28
33
|
#
|
29
|
-
# When passing paths, the ".js" extension is optional.
|
34
|
+
# When passing paths, the ".js" extension is optional. If you do not want ".js"
|
30
35
|
# appended to the path <tt>extname: false</tt> can be set on the options.
|
31
36
|
#
|
32
37
|
# You can modify the HTML attributes of the script tag by passing a hash as the
|
@@ -35,19 +40,24 @@ module ActionView
|
|
35
40
|
# When the Asset Pipeline is enabled, you can pass the name of your manifest as
|
36
41
|
# source, and include other JavaScript or CoffeeScript files inside the manifest.
|
37
42
|
#
|
43
|
+
# If the server supports Early Hints header links for these assets will be
|
44
|
+
# automatically pushed.
|
45
|
+
#
|
38
46
|
# ==== Options
|
39
47
|
#
|
40
48
|
# When the last parameter is a hash you can add HTML attributes using that
|
41
49
|
# parameter. The following options are supported:
|
42
50
|
#
|
43
|
-
# * <tt>:extname</tt> - Append an extension to the generated
|
44
|
-
# already exists. This only applies for relative
|
45
|
-
# * <tt>:protocol</tt> - Sets the protocol of the generated
|
46
|
-
# applies when a relative
|
47
|
-
# * <tt>:host</tt> - When a relative
|
51
|
+
# * <tt>:extname</tt> - Append an extension to the generated URL unless the extension
|
52
|
+
# already exists. This only applies for relative URLs.
|
53
|
+
# * <tt>:protocol</tt> - Sets the protocol of the generated URL. This option only
|
54
|
+
# applies when a relative URL and +host+ options are provided.
|
55
|
+
# * <tt>:host</tt> - When a relative URL is provided the host is added to the
|
48
56
|
# that path.
|
49
57
|
# * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
|
50
58
|
# when it is set to true.
|
59
|
+
# * <tt>:nonce</tt> - When set to true, adds an automatic nonce value if
|
60
|
+
# you have Content Security Policy enabled.
|
51
61
|
#
|
52
62
|
# ==== Examples
|
53
63
|
#
|
@@ -72,15 +82,42 @@ module ActionView
|
|
72
82
|
#
|
73
83
|
# javascript_include_tag "http://www.example.com/xmlhr.js"
|
74
84
|
# # => <script src="http://www.example.com/xmlhr.js"></script>
|
85
|
+
#
|
86
|
+
# javascript_include_tag "http://www.example.com/xmlhr.js", nonce: true
|
87
|
+
# # => <script src="http://www.example.com/xmlhr.js" nonce="..."></script>
|
75
88
|
def javascript_include_tag(*sources)
|
76
89
|
options = sources.extract_options!.stringify_keys
|
77
90
|
path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
|
78
|
-
|
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"]
|
96
|
+
|
97
|
+
sources_tags = sources.uniq.map { |source|
|
98
|
+
href = path_to_javascript(source, path_options)
|
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
|
79
106
|
tag_options = {
|
80
|
-
"src" =>
|
107
|
+
"src" => href,
|
108
|
+
"crossorigin" => crossorigin
|
81
109
|
}.merge!(options)
|
82
|
-
|
110
|
+
if tag_options["nonce"] == true
|
111
|
+
tag_options["nonce"] = content_security_policy_nonce
|
112
|
+
end
|
113
|
+
content_tag("script", "", tag_options)
|
83
114
|
}.join("\n").html_safe
|
115
|
+
|
116
|
+
if preload_links_header
|
117
|
+
send_preload_links_header(preload_links)
|
118
|
+
end
|
119
|
+
|
120
|
+
sources_tags
|
84
121
|
end
|
85
122
|
|
86
123
|
# Returns a stylesheet link tag for the sources specified as arguments. If
|
@@ -90,6 +127,9 @@ module ActionView
|
|
90
127
|
# to "screen", so you must explicitly set it to "all" for the stylesheet(s) to
|
91
128
|
# apply to all media types.
|
92
129
|
#
|
130
|
+
# If the server supports Early Hints header links for these assets will be
|
131
|
+
# automatically pushed.
|
132
|
+
#
|
93
133
|
# stylesheet_link_tag "style"
|
94
134
|
# # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
|
95
135
|
#
|
@@ -111,20 +151,41 @@ module ActionView
|
|
111
151
|
def stylesheet_link_tag(*sources)
|
112
152
|
options = sources.extract_options!.stringify_keys
|
113
153
|
path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys
|
114
|
-
|
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"]
|
159
|
+
|
160
|
+
sources_tags = sources.uniq.map { |source|
|
161
|
+
href = path_to_stylesheet(source, path_options)
|
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
|
115
169
|
tag_options = {
|
116
170
|
"rel" => "stylesheet",
|
117
171
|
"media" => "screen",
|
118
|
-
"
|
172
|
+
"crossorigin" => crossorigin,
|
173
|
+
"href" => href
|
119
174
|
}.merge!(options)
|
120
175
|
tag(:link, tag_options)
|
121
176
|
}.join("\n").html_safe
|
177
|
+
|
178
|
+
if preload_links_header
|
179
|
+
send_preload_links_header(preload_links)
|
180
|
+
end
|
181
|
+
|
182
|
+
sources_tags
|
122
183
|
end
|
123
184
|
|
124
185
|
# Returns a link tag that browsers and feed readers can use to auto-detect
|
125
|
-
# an RSS or
|
126
|
-
# <tt>:atom</tt>. Control the link options in url_for format
|
127
|
-
# +url_options+. You can modify the LINK tag itself in +tag_options+.
|
186
|
+
# an RSS, Atom, or JSON feed. The +type+ can be <tt>:rss</tt> (default),
|
187
|
+
# <tt>:atom</tt>, or <tt>:json</tt>. Control the link options in url_for format
|
188
|
+
# using the +url_options+. You can modify the LINK tag itself in +tag_options+.
|
128
189
|
#
|
129
190
|
# ==== Options
|
130
191
|
#
|
@@ -138,6 +199,8 @@ module ActionView
|
|
138
199
|
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
|
139
200
|
# auto_discovery_link_tag(:atom)
|
140
201
|
# # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
|
202
|
+
# auto_discovery_link_tag(:json)
|
203
|
+
# # => <link rel="alternate" type="application/json" title="JSON" href="http://www.currenthost.com/controller/action" />
|
141
204
|
# auto_discovery_link_tag(:rss, {action: "feed"})
|
142
205
|
# # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
|
143
206
|
# auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
|
@@ -147,8 +210,8 @@ module ActionView
|
|
147
210
|
# auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
|
148
211
|
# # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed.rss" />
|
149
212
|
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
|
150
|
-
if !(type == :rss || type == :atom) && tag_options[:type].blank?
|
151
|
-
raise ArgumentError.new("You should pass :type tag_option key explicitly, because you have passed #{type} type other than :rss or :
|
213
|
+
if !(type == :rss || type == :atom || type == :json) && tag_options[:type].blank?
|
214
|
+
raise ArgumentError.new("You should pass :type tag_option key explicitly, because you have passed #{type} type other than :rss, :atom, or :json.")
|
152
215
|
end
|
153
216
|
|
154
217
|
tag(
|
@@ -195,80 +258,143 @@ module ActionView
|
|
195
258
|
}.merge!(options.symbolize_keys))
|
196
259
|
end
|
197
260
|
|
261
|
+
# Returns a link tag that browsers can use to preload the +source+.
|
262
|
+
# The +source+ can be the path of a resource managed by asset pipeline,
|
263
|
+
# a full path, or an URI.
|
264
|
+
#
|
265
|
+
# ==== Options
|
266
|
+
#
|
267
|
+
# * <tt>:type</tt> - Override the auto-generated mime type, defaults to the mime type for +source+ extension.
|
268
|
+
# * <tt>:as</tt> - Override the auto-generated value for as attribute, calculated using +source+ extension and mime type.
|
269
|
+
# * <tt>:crossorigin</tt> - Specify the crossorigin attribute, required to load cross-origin resources.
|
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.
|
272
|
+
#
|
273
|
+
# ==== Examples
|
274
|
+
#
|
275
|
+
# preload_link_tag("custom_theme.css")
|
276
|
+
# # => <link rel="preload" href="/assets/custom_theme.css" as="style" type="text/css" />
|
277
|
+
#
|
278
|
+
# preload_link_tag("/videos/video.webm")
|
279
|
+
# # => <link rel="preload" href="/videos/video.mp4" as="video" type="video/webm" />
|
280
|
+
#
|
281
|
+
# preload_link_tag(post_path(format: :json), as: "fetch")
|
282
|
+
# # => <link rel="preload" href="/posts.json" as="fetch" type="application/json" />
|
283
|
+
#
|
284
|
+
# preload_link_tag("worker.js", as: "worker")
|
285
|
+
# # => <link rel="preload" href="/assets/worker.js" as="worker" type="text/javascript" />
|
286
|
+
#
|
287
|
+
# preload_link_tag("//example.com/font.woff2")
|
288
|
+
# # => <link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="anonymous"/>
|
289
|
+
#
|
290
|
+
# preload_link_tag("//example.com/font.woff2", crossorigin: "use-credentials")
|
291
|
+
# # => <link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="use-credentials" />
|
292
|
+
#
|
293
|
+
# preload_link_tag("/media/audio.ogg", nopush: true)
|
294
|
+
# # => <link rel="preload" href="/media/audio.ogg" as="audio" type="audio/ogg" />
|
295
|
+
#
|
296
|
+
def preload_link_tag(source, options = {})
|
297
|
+
href = asset_path(source, skip_pipeline: options.delete(:skip_pipeline))
|
298
|
+
extname = File.extname(source).downcase.delete(".")
|
299
|
+
mime_type = options.delete(:type) || Template::Types[extname]&.to_s
|
300
|
+
as_type = options.delete(:as) || resolve_link_as(extname, mime_type)
|
301
|
+
crossorigin = options.delete(:crossorigin)
|
302
|
+
crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
|
303
|
+
integrity = options[:integrity]
|
304
|
+
nopush = options.delete(:nopush) || false
|
305
|
+
|
306
|
+
link_tag = tag.link(**{
|
307
|
+
rel: "preload",
|
308
|
+
href: href,
|
309
|
+
as: as_type,
|
310
|
+
type: mime_type,
|
311
|
+
crossorigin: crossorigin
|
312
|
+
}.merge!(options.symbolize_keys))
|
313
|
+
|
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
|
319
|
+
|
320
|
+
send_preload_links_header([preload_link])
|
321
|
+
|
322
|
+
link_tag
|
323
|
+
end
|
324
|
+
|
198
325
|
# Returns an HTML image tag for the +source+. The +source+ can be a full
|
199
|
-
# path or
|
326
|
+
# path, a file, or an Active Storage attachment.
|
200
327
|
#
|
201
328
|
# ==== Options
|
202
329
|
#
|
203
330
|
# You can add HTML attributes using the +options+. The +options+ supports
|
204
|
-
#
|
331
|
+
# additional keys for convenience and conformance:
|
205
332
|
#
|
206
|
-
# * <tt>:alt</tt> - If no alt text is given, the file name part of the
|
207
|
-
# +source+ is used (capitalized and without the extension)
|
208
333
|
# * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
|
209
334
|
# width="30" and height="45", and "50" becomes width="50" and height="50".
|
210
335
|
# <tt>:size</tt> will be ignored if the value is not in the correct format.
|
336
|
+
# * <tt>:srcset</tt> - If supplied as a hash or array of <tt>[source, descriptor]</tt>
|
337
|
+
# pairs, each image path will be expanded before the list is formatted as a string.
|
211
338
|
#
|
212
339
|
# ==== Examples
|
213
340
|
#
|
341
|
+
# Assets (images that are part of your app):
|
342
|
+
#
|
214
343
|
# image_tag("icon")
|
215
|
-
# # => <img
|
344
|
+
# # => <img src="/assets/icon" />
|
216
345
|
# image_tag("icon.png")
|
217
|
-
# # => <img
|
346
|
+
# # => <img src="/assets/icon.png" />
|
218
347
|
# image_tag("icon.png", size: "16x10", alt: "Edit Entry")
|
219
348
|
# # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
|
220
349
|
# image_tag("/icons/icon.gif", size: "16")
|
221
|
-
# # => <img src="/icons/icon.gif" width="16" height="16"
|
350
|
+
# # => <img src="/icons/icon.gif" width="16" height="16" />
|
222
351
|
# image_tag("/icons/icon.gif", height: '32', width: '32')
|
223
|
-
# # => <img
|
352
|
+
# # => <img height="32" src="/icons/icon.gif" width="32" />
|
224
353
|
# image_tag("/icons/icon.gif", class: "menu_icon")
|
225
|
-
# # => <img
|
354
|
+
# # => <img class="menu_icon" src="/icons/icon.gif" />
|
226
355
|
# image_tag("/icons/icon.gif", data: { title: 'Rails Application' })
|
227
356
|
# # => <img data-title="Rails Application" src="/icons/icon.gif" />
|
357
|
+
# image_tag("icon.png", srcset: { "icon_2x.png" => "2x", "icon_4x.png" => "4x" })
|
358
|
+
# # => <img src="/assets/icon.png" srcset="/assets/icon_2x.png 2x, /assets/icon_4x.png 4x">
|
359
|
+
# image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw")
|
360
|
+
# # => <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw">
|
361
|
+
#
|
362
|
+
# Active Storage blobs (images that are uploaded by the users of your app):
|
363
|
+
#
|
364
|
+
# image_tag(user.avatar)
|
365
|
+
# # => <img src="/rails/active_storage/blobs/.../tiger.jpg" />
|
366
|
+
# image_tag(user.avatar.variant(resize_to_limit: [100, 100]))
|
367
|
+
# # => <img src="/rails/active_storage/representations/.../tiger.jpg" />
|
368
|
+
# image_tag(user.avatar.variant(resize_to_limit: [100, 100]), size: '100')
|
369
|
+
# # => <img width="100" height="100" src="/rails/active_storage/representations/.../tiger.jpg" />
|
228
370
|
def image_tag(source, options = {})
|
229
371
|
options = options.symbolize_keys
|
230
372
|
check_for_image_tag_errors(options)
|
373
|
+
skip_pipeline = options.delete(:skip_pipeline)
|
231
374
|
|
232
|
-
|
375
|
+
options[:src] = resolve_image_source(source, skip_pipeline)
|
233
376
|
|
234
|
-
|
235
|
-
options[:
|
377
|
+
if options[:srcset] && !options[:srcset].is_a?(String)
|
378
|
+
options[:srcset] = options[:srcset].map do |src_path, size|
|
379
|
+
src_path = path_to_image(src_path, skip_pipeline: skip_pipeline)
|
380
|
+
"#{src_path} #{size}"
|
381
|
+
end.join(", ")
|
236
382
|
end
|
237
383
|
|
238
384
|
options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
|
239
385
|
tag("img", options)
|
240
386
|
end
|
241
387
|
|
242
|
-
# Returns a string suitable for an HTML image tag alt attribute.
|
243
|
-
# The +src+ argument is meant to be an image file path.
|
244
|
-
# The method removes the basename of the file path and the digest,
|
245
|
-
# if any. It also removes hyphens and underscores from file names and
|
246
|
-
# replaces them with spaces, returning a space-separated, titleized
|
247
|
-
# string.
|
248
|
-
#
|
249
|
-
# ==== Examples
|
250
|
-
#
|
251
|
-
# image_alt('rails.png')
|
252
|
-
# # => Rails
|
253
|
-
#
|
254
|
-
# image_alt('hyphenated-file-name.png')
|
255
|
-
# # => Hyphenated file name
|
256
|
-
#
|
257
|
-
# image_alt('underscored_file_name.png')
|
258
|
-
# # => Underscored file name
|
259
|
-
def image_alt(src)
|
260
|
-
File.basename(src, ".*".freeze).sub(/-[[:xdigit:]]{32,64}\z/, "".freeze).tr("-_".freeze, " ".freeze).capitalize
|
261
|
-
end
|
262
|
-
|
263
388
|
# Returns an HTML video tag for the +sources+. If +sources+ is a string,
|
264
389
|
# a single video tag will be returned. If +sources+ is an array, a video
|
265
390
|
# tag with nested source tags for each source will be returned. The
|
266
|
-
# +sources+ can be full paths or files that
|
391
|
+
# +sources+ can be full paths or files that exist in your public videos
|
267
392
|
# directory.
|
268
393
|
#
|
269
394
|
# ==== Options
|
270
|
-
#
|
271
|
-
#
|
395
|
+
#
|
396
|
+
# When the last parameter is a hash you can add HTML attributes using that
|
397
|
+
# parameter. The following options are supported:
|
272
398
|
#
|
273
399
|
# * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
|
274
400
|
# before the video loads. The path is calculated like the +src+ of +image_tag+.
|
@@ -285,7 +411,7 @@ module ActionView
|
|
285
411
|
# video_tag("trailer.ogg")
|
286
412
|
# # => <video src="/videos/trailer.ogg"></video>
|
287
413
|
# video_tag("trailer.ogg", controls: true, preload: 'none')
|
288
|
-
# # => <video preload="none" controls="controls" src="/videos/trailer.ogg"
|
414
|
+
# # => <video preload="none" controls="controls" src="/videos/trailer.ogg"></video>
|
289
415
|
# video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
|
290
416
|
# # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png"></video>
|
291
417
|
# video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png", poster_skip_pipeline: true)
|
@@ -312,9 +438,14 @@ module ActionView
|
|
312
438
|
end
|
313
439
|
end
|
314
440
|
|
315
|
-
# Returns an HTML audio tag for the +
|
316
|
-
#
|
317
|
-
#
|
441
|
+
# Returns an HTML audio tag for the +sources+. If +sources+ is a string,
|
442
|
+
# a single audio tag will be returned. If +sources+ is an array, an audio
|
443
|
+
# tag with nested source tags for each source will be returned. The
|
444
|
+
# +sources+ can be full paths or files that exist in your public audios
|
445
|
+
# directory.
|
446
|
+
#
|
447
|
+
# When the last parameter is a hash you can add HTML attributes using that
|
448
|
+
# parameter.
|
318
449
|
#
|
319
450
|
# audio_tag("sound")
|
320
451
|
# # => <audio src="/audios/sound"></audio>
|
@@ -346,6 +477,16 @@ module ActionView
|
|
346
477
|
end
|
347
478
|
end
|
348
479
|
|
480
|
+
def resolve_image_source(source, skip_pipeline)
|
481
|
+
if source.is_a?(Symbol) || source.is_a?(String)
|
482
|
+
path_to_image(source, skip_pipeline: skip_pipeline)
|
483
|
+
else
|
484
|
+
polymorphic_url(source)
|
485
|
+
end
|
486
|
+
rescue NoMethodError => e
|
487
|
+
raise ArgumentError, "Can't resolve image into URL: #{e}"
|
488
|
+
end
|
489
|
+
|
349
490
|
def extract_dimensions(size)
|
350
491
|
size = size.to_s
|
351
492
|
if /\A\d+x\d+\z/.match?(size)
|
@@ -360,6 +501,28 @@ module ActionView
|
|
360
501
|
raise ArgumentError, "Cannot pass a :size option with a :height or :width option"
|
361
502
|
end
|
362
503
|
end
|
504
|
+
|
505
|
+
def resolve_link_as(extname, mime_type)
|
506
|
+
if extname == "js"
|
507
|
+
"script"
|
508
|
+
elsif extname == "css"
|
509
|
+
"style"
|
510
|
+
elsif extname == "vtt"
|
511
|
+
"track"
|
512
|
+
elsif (type = mime_type.to_s.split("/")[0]) && type.in?(%w(audio video font))
|
513
|
+
type
|
514
|
+
end
|
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
|
363
526
|
end
|
364
527
|
end
|
365
528
|
end
|