actionview 5.1.7 → 5.2.8.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.

Files changed (109) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +119 -178
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +4 -4
  5. data/lib/action_view/base.rb +8 -10
  6. data/lib/action_view/buffers.rb +2 -0
  7. data/lib/action_view/context.rb +2 -2
  8. data/lib/action_view/dependency_tracker.rb +2 -0
  9. data/lib/action_view/digestor.rb +7 -7
  10. data/lib/action_view/flows.rb +2 -0
  11. data/lib/action_view/gem_version.rb +5 -3
  12. data/lib/action_view/helpers/active_model_helper.rb +9 -3
  13. data/lib/action_view/helpers/asset_tag_helper.rb +180 -34
  14. data/lib/action_view/helpers/asset_url_helper.rb +19 -17
  15. data/lib/action_view/helpers/atom_feed_helper.rb +3 -1
  16. data/lib/action_view/helpers/cache_helper.rb +24 -14
  17. data/lib/action_view/helpers/capture_helper.rb +9 -7
  18. data/lib/action_view/helpers/controller_helper.rb +3 -1
  19. data/lib/action_view/helpers/csp_helper.rb +24 -0
  20. data/lib/action_view/helpers/csrf_helper.rb +4 -2
  21. data/lib/action_view/helpers/date_helper.rb +7 -5
  22. data/lib/action_view/helpers/debug_helper.rb +4 -2
  23. data/lib/action_view/helpers/form_helper.rb +53 -70
  24. data/lib/action_view/helpers/form_options_helper.rb +23 -17
  25. data/lib/action_view/helpers/form_tag_helper.rb +23 -11
  26. data/lib/action_view/helpers/javascript_helper.rb +20 -5
  27. data/lib/action_view/helpers/number_helper.rb +2 -0
  28. data/lib/action_view/helpers/output_safety_helper.rb +2 -0
  29. data/lib/action_view/helpers/record_tag_helper.rb +3 -1
  30. data/lib/action_view/helpers/rendering_helper.rb +3 -1
  31. data/lib/action_view/helpers/sanitize_helper.rb +3 -1
  32. data/lib/action_view/helpers/tag_helper.rb +39 -8
  33. data/lib/action_view/helpers/tags/base.rb +12 -10
  34. data/lib/action_view/helpers/tags/check_box.rb +3 -1
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +2 -0
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +2 -0
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +2 -0
  39. data/lib/action_view/helpers/tags/collection_select.rb +3 -1
  40. data/lib/action_view/helpers/tags/color_field.rb +3 -1
  41. data/lib/action_view/helpers/tags/date_field.rb +2 -0
  42. data/lib/action_view/helpers/tags/date_select.rb +3 -1
  43. data/lib/action_view/helpers/tags/datetime_field.rb +3 -1
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +2 -0
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -1
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +2 -4
  51. data/lib/action_view/helpers/tags/month_field.rb +2 -0
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +2 -0
  54. data/lib/action_view/helpers/tags/placeholderable.rb +2 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +3 -1
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +2 -0
  58. data/lib/action_view/helpers/tags/select.rb +4 -2
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +3 -1
  61. data/lib/action_view/helpers/tags/text_field.rb +3 -1
  62. data/lib/action_view/helpers/tags/time_field.rb +2 -0
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +2 -0
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +2 -0
  68. data/lib/action_view/helpers/tags.rb +3 -1
  69. data/lib/action_view/helpers/text_helper.rb +9 -7
  70. data/lib/action_view/helpers/translation_helper.rb +17 -5
  71. data/lib/action_view/helpers/url_helper.rb +28 -4
  72. data/lib/action_view/helpers.rb +4 -0
  73. data/lib/action_view/layouts.rb +7 -5
  74. data/lib/action_view/log_subscriber.rb +5 -3
  75. data/lib/action_view/lookup_context.rb +4 -4
  76. data/lib/action_view/model_naming.rb +2 -0
  77. data/lib/action_view/path_set.rb +2 -0
  78. data/lib/action_view/railtie.rb +11 -2
  79. data/lib/action_view/record_identifier.rb +2 -0
  80. data/lib/action_view/renderer/abstract_renderer.rb +2 -0
  81. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +4 -2
  82. data/lib/action_view/renderer/partial_renderer.rb +13 -11
  83. data/lib/action_view/renderer/renderer.rb +2 -0
  84. data/lib/action_view/renderer/streaming_template_renderer.rb +5 -1
  85. data/lib/action_view/renderer/template_renderer.rb +2 -0
  86. data/lib/action_view/rendering.rb +3 -5
  87. data/lib/action_view/routing_url_for.rb +2 -0
  88. data/lib/action_view/tasks/cache_digests.rake +2 -0
  89. data/lib/action_view/template/error.rb +2 -3
  90. data/lib/action_view/template/handlers/builder.rb +3 -4
  91. data/lib/action_view/template/handlers/erb/erubi.rb +2 -0
  92. data/lib/action_view/template/handlers/erb.rb +5 -9
  93. data/lib/action_view/template/handlers/html.rb +2 -0
  94. data/lib/action_view/template/handlers/raw.rb +2 -0
  95. data/lib/action_view/template/handlers.rb +3 -1
  96. data/lib/action_view/template/html.rb +3 -1
  97. data/lib/action_view/template/resolver.rb +7 -6
  98. data/lib/action_view/template/text.rb +3 -1
  99. data/lib/action_view/template/types.rb +3 -1
  100. data/lib/action_view/template.rb +6 -4
  101. data/lib/action_view/test_case.rb +21 -5
  102. data/lib/action_view/testing/resolvers.rb +3 -1
  103. data/lib/action_view/version.rb +2 -0
  104. data/lib/action_view/view_paths.rb +3 -3
  105. data/lib/action_view.rb +4 -3
  106. data/lib/assets/compiled/rails-ujs.js +52 -15
  107. metadata +15 -16
  108. data/lib/action_view/template/handlers/erb/deprecated_erubis.rb +0 -9
  109. data/lib/action_view/template/handlers/erb/erubis.rb +0 -81
@@ -1,5 +1,9 @@
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"
6
+ require "active_support/core_ext/object/try"
3
7
  require "action_view/helpers/asset_url_helper"
4
8
  require "action_view/helpers/tag_helper"
5
9
 
@@ -11,7 +15,7 @@ module ActionView
11
15
  # the assets exist before linking to them:
12
16
  #
13
17
  # image_tag("rails.png")
14
- # # => <img alt="Rails" src="/assets/rails.png" />
18
+ # # => <img src="/assets/rails.png" />
15
19
  # stylesheet_link_tag("application")
16
20
  # # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
17
21
  module AssetTagHelper
@@ -35,19 +39,24 @@ module ActionView
35
39
  # When the Asset Pipeline is enabled, you can pass the name of your manifest as
36
40
  # source, and include other JavaScript or CoffeeScript files inside the manifest.
37
41
  #
42
+ # If the server supports Early Hints header links for these assets will be
43
+ # automatically pushed.
44
+ #
38
45
  # ==== Options
39
46
  #
40
47
  # When the last parameter is a hash you can add HTML attributes using that
41
48
  # parameter. The following options are supported:
42
49
  #
43
- # * <tt>:extname</tt> - Append an extension to the generated url unless the extension
44
- # already exists. This only applies for relative urls.
45
- # * <tt>:protocol</tt> - Sets the protocol of the generated url, this option only
46
- # applies when a relative url and +host+ options are provided.
47
- # * <tt>:host</tt> - When a relative url is provided the host is added to the
50
+ # * <tt>:extname</tt> - Append an extension to the generated URL unless the extension
51
+ # already exists. This only applies for relative URLs.
52
+ # * <tt>:protocol</tt> - Sets the protocol of the generated URL. This option only
53
+ # applies when a relative URL and +host+ options are provided.
54
+ # * <tt>:host</tt> - When a relative URL is provided the host is added to the
48
55
  # that path.
49
56
  # * <tt>:skip_pipeline</tt> - This option is used to bypass the asset pipeline
50
57
  # when it is set to true.
58
+ # * <tt>:nonce<tt> - When set to true, adds an automatic nonce value if
59
+ # you have Content Security Policy enabled.
51
60
  #
52
61
  # ==== Examples
53
62
  #
@@ -72,15 +81,29 @@ module ActionView
72
81
  #
73
82
  # javascript_include_tag "http://www.example.com/xmlhr.js"
74
83
  # # => <script src="http://www.example.com/xmlhr.js"></script>
84
+ #
85
+ # javascript_include_tag "http://www.example.com/xmlhr.js", nonce: true
86
+ # # => <script src="http://www.example.com/xmlhr.js" nonce="..."></script>
75
87
  def javascript_include_tag(*sources)
76
88
  options = sources.extract_options!.stringify_keys
77
89
  path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
78
- sources.uniq.map { |source|
90
+ early_hints_links = []
91
+
92
+ sources_tags = sources.uniq.map { |source|
93
+ href = path_to_javascript(source, path_options)
94
+ early_hints_links << "<#{href}>; rel=preload; as=script"
79
95
  tag_options = {
80
- "src" => path_to_javascript(source, path_options)
96
+ "src" => href
81
97
  }.merge!(options)
98
+ if tag_options["nonce"] == true
99
+ tag_options["nonce"] = content_security_policy_nonce
100
+ end
82
101
  content_tag("script".freeze, "", tag_options)
83
102
  }.join("\n").html_safe
103
+
104
+ request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) && request
105
+
106
+ sources_tags
84
107
  end
85
108
 
86
109
  # Returns a stylesheet link tag for the sources specified as arguments. If
@@ -90,6 +113,9 @@ module ActionView
90
113
  # to "screen", so you must explicitly set it to "all" for the stylesheet(s) to
91
114
  # apply to all media types.
92
115
  #
116
+ # If the server supports Early Hints header links for these assets will be
117
+ # automatically pushed.
118
+ #
93
119
  # stylesheet_link_tag "style"
94
120
  # # => <link href="/assets/style.css" media="screen" rel="stylesheet" />
95
121
  #
@@ -111,20 +137,28 @@ module ActionView
111
137
  def stylesheet_link_tag(*sources)
112
138
  options = sources.extract_options!.stringify_keys
113
139
  path_options = options.extract!("protocol", "host", "skip_pipeline").symbolize_keys
114
- sources.uniq.map { |source|
140
+ early_hints_links = []
141
+
142
+ sources_tags = sources.uniq.map { |source|
143
+ href = path_to_stylesheet(source, path_options)
144
+ early_hints_links << "<#{href}>; rel=preload; as=style"
115
145
  tag_options = {
116
146
  "rel" => "stylesheet",
117
147
  "media" => "screen",
118
- "href" => path_to_stylesheet(source, path_options)
148
+ "href" => href
119
149
  }.merge!(options)
120
150
  tag(:link, tag_options)
121
151
  }.join("\n").html_safe
152
+
153
+ request.send_early_hints("Link" => early_hints_links.join("\n")) if respond_to?(:request) && request
154
+
155
+ sources_tags
122
156
  end
123
157
 
124
158
  # Returns a link tag that browsers and feed readers can use to auto-detect
125
- # an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
126
- # <tt>:atom</tt>. Control the link options in url_for format using the
127
- # +url_options+. You can modify the LINK tag itself in +tag_options+.
159
+ # an RSS, Atom, or JSON feed. The +type+ can be <tt>:rss</tt> (default),
160
+ # <tt>:atom</tt>, or <tt>:json</tt>. Control the link options in url_for format
161
+ # using the +url_options+. You can modify the LINK tag itself in +tag_options+.
128
162
  #
129
163
  # ==== Options
130
164
  #
@@ -138,6 +172,8 @@ module ActionView
138
172
  # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/action" />
139
173
  # auto_discovery_link_tag(:atom)
140
174
  # # => <link rel="alternate" type="application/atom+xml" title="ATOM" href="http://www.currenthost.com/controller/action" />
175
+ # auto_discovery_link_tag(:json)
176
+ # # => <link rel="alternate" type="application/json" title="JSON" href="http://www.currenthost.com/controller/action" />
141
177
  # auto_discovery_link_tag(:rss, {action: "feed"})
142
178
  # # => <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.currenthost.com/controller/feed" />
143
179
  # auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
@@ -147,8 +183,8 @@ module ActionView
147
183
  # auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
148
184
  # # => <link rel="alternate" type="application/rss+xml" title="Example RSS" href="http://www.example.com/feed.rss" />
149
185
  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 :atom.")
186
+ if !(type == :rss || type == :atom || type == :json) && tag_options[:type].blank?
187
+ raise ArgumentError.new("You should pass :type tag_option key explicitly, because you have passed #{type} type other than :rss, :atom, or :json.")
152
188
  end
153
189
 
154
190
  tag(
@@ -195,44 +231,124 @@ module ActionView
195
231
  }.merge!(options.symbolize_keys))
196
232
  end
197
233
 
234
+ # Returns a link tag that browsers can use to preload the +source+.
235
+ # The +source+ can be the path of a resource managed by asset pipeline,
236
+ # a full path, or an URI.
237
+ #
238
+ # ==== Options
239
+ #
240
+ # * <tt>:type</tt> - Override the auto-generated mime type, defaults to the mime type for +source+ extension.
241
+ # * <tt>:as</tt> - Override the auto-generated value for as attribute, calculated using +source+ extension and mime type.
242
+ # * <tt>:crossorigin</tt> - Specify the crossorigin attribute, required to load cross-origin resources.
243
+ # * <tt>:nopush</tt> - Specify if the use of server push is not desired for the resource. Defaults to +false+.
244
+ #
245
+ # ==== Examples
246
+ #
247
+ # preload_link_tag("custom_theme.css")
248
+ # # => <link rel="preload" href="/assets/custom_theme.css" as="style" type="text/css" />
249
+ #
250
+ # preload_link_tag("/videos/video.webm")
251
+ # # => <link rel="preload" href="/videos/video.mp4" as="video" type="video/webm" />
252
+ #
253
+ # preload_link_tag(post_path(format: :json), as: "fetch")
254
+ # # => <link rel="preload" href="/posts.json" as="fetch" type="application/json" />
255
+ #
256
+ # preload_link_tag("worker.js", as: "worker")
257
+ # # => <link rel="preload" href="/assets/worker.js" as="worker" type="text/javascript" />
258
+ #
259
+ # preload_link_tag("//example.com/font.woff2")
260
+ # # => <link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="anonymous"/>
261
+ #
262
+ # preload_link_tag("//example.com/font.woff2", crossorigin: "use-credentials")
263
+ # # => <link rel="preload" href="//example.com/font.woff2" as="font" type="font/woff2" crossorigin="use-credentials" />
264
+ #
265
+ # preload_link_tag("/media/audio.ogg", nopush: true)
266
+ # # => <link rel="preload" href="/media/audio.ogg" as="audio" type="audio/ogg" />
267
+ #
268
+ def preload_link_tag(source, options = {})
269
+ href = asset_path(source, skip_pipeline: options.delete(:skip_pipeline))
270
+ extname = File.extname(source).downcase.delete(".")
271
+ mime_type = options.delete(:type) || Template::Types[extname].try(:to_s)
272
+ as_type = options.delete(:as) || resolve_link_as(extname, mime_type)
273
+ crossorigin = options.delete(:crossorigin)
274
+ crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
275
+ nopush = options.delete(:nopush) || false
276
+
277
+ link_tag = tag.link({
278
+ rel: "preload",
279
+ href: href,
280
+ as: as_type,
281
+ type: mime_type,
282
+ crossorigin: crossorigin
283
+ }.merge!(options.symbolize_keys))
284
+
285
+ early_hints_link = "<#{href}>; rel=preload; as=#{as_type}"
286
+ early_hints_link += "; type=#{mime_type}" if mime_type
287
+ early_hints_link += "; crossorigin=#{crossorigin}" if crossorigin
288
+ early_hints_link += "; nopush" if nopush
289
+
290
+ request.send_early_hints("Link" => early_hints_link) if respond_to?(:request) && request
291
+
292
+ link_tag
293
+ end
294
+
198
295
  # Returns an HTML image tag for the +source+. The +source+ can be a full
199
- # path or a file.
296
+ # path, a file, or an Active Storage attachment.
200
297
  #
201
298
  # ==== Options
202
299
  #
203
300
  # You can add HTML attributes using the +options+. The +options+ supports
204
- # two additional keys for convenience and conformance:
301
+ # additional keys for convenience and conformance:
205
302
  #
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
303
  # * <tt>:size</tt> - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
209
304
  # width="30" and height="45", and "50" becomes width="50" and height="50".
210
305
  # <tt>:size</tt> will be ignored if the value is not in the correct format.
306
+ # * <tt>:srcset</tt> - If supplied as a hash or array of <tt>[source, descriptor]</tt>
307
+ # pairs, each image path will be expanded before the list is formatted as a string.
211
308
  #
212
309
  # ==== Examples
213
310
  #
311
+ # Assets (images that are part of your app):
312
+ #
214
313
  # image_tag("icon")
215
- # # => <img alt="Icon" src="/assets/icon" />
314
+ # # => <img src="/assets/icon" />
216
315
  # image_tag("icon.png")
217
- # # => <img alt="Icon" src="/assets/icon.png" />
316
+ # # => <img src="/assets/icon.png" />
218
317
  # image_tag("icon.png", size: "16x10", alt: "Edit Entry")
219
318
  # # => <img src="/assets/icon.png" width="16" height="10" alt="Edit Entry" />
220
319
  # image_tag("/icons/icon.gif", size: "16")
221
- # # => <img src="/icons/icon.gif" width="16" height="16" alt="Icon" />
320
+ # # => <img src="/icons/icon.gif" width="16" height="16" />
222
321
  # image_tag("/icons/icon.gif", height: '32', width: '32')
223
- # # => <img alt="Icon" height="32" src="/icons/icon.gif" width="32" />
322
+ # # => <img height="32" src="/icons/icon.gif" width="32" />
224
323
  # image_tag("/icons/icon.gif", class: "menu_icon")
225
- # # => <img alt="Icon" class="menu_icon" src="/icons/icon.gif" />
324
+ # # => <img class="menu_icon" src="/icons/icon.gif" />
226
325
  # image_tag("/icons/icon.gif", data: { title: 'Rails Application' })
227
326
  # # => <img data-title="Rails Application" src="/icons/icon.gif" />
327
+ # image_tag("icon.png", srcset: { "icon_2x.png" => "2x", "icon_4x.png" => "4x" })
328
+ # # => <img src="/assets/icon.png" srcset="/assets/icon_2x.png 2x, /assets/icon_4x.png 4x">
329
+ # image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw")
330
+ # # => <img src="/assets/pic.jpg" srcset="/assets/pic_1024.jpg 1024w, /assets/pic_1980.jpg 1980w" sizes="100vw">
331
+ #
332
+ # Active Storage (images that are uploaded by the users of your app):
333
+ #
334
+ # image_tag(user.avatar)
335
+ # # => <img src="/rails/active_storage/blobs/.../tiger.jpg" />
336
+ # image_tag(user.avatar.variant(resize: "100x100"))
337
+ # # => <img src="/rails/active_storage/variants/.../tiger.jpg" />
338
+ # image_tag(user.avatar.variant(resize: "100x100"), size: '100')
339
+ # # => <img width="100" height="100" src="/rails/active_storage/variants/.../tiger.jpg" />
228
340
  def image_tag(source, options = {})
229
341
  options = options.symbolize_keys
230
342
  check_for_image_tag_errors(options)
343
+ skip_pipeline = options.delete(:skip_pipeline)
231
344
 
232
- src = options[:src] = path_to_image(source, skip_pipeline: options.delete(:skip_pipeline))
345
+ options[:src] = resolve_image_source(source, skip_pipeline)
233
346
 
234
- unless src.start_with?("cid:") || src.start_with?("data:") || src.blank?
235
- options[:alt] = options.fetch(:alt) { image_alt(src) }
347
+ if options[:srcset] && !options[:srcset].is_a?(String)
348
+ options[:srcset] = options[:srcset].map do |src_path, size|
349
+ src_path = path_to_image(src_path, skip_pipeline: skip_pipeline)
350
+ "#{src_path} #{size}"
351
+ end.join(", ")
236
352
  end
237
353
 
238
354
  options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size]
@@ -257,18 +373,21 @@ module ActionView
257
373
  # image_alt('underscored_file_name.png')
258
374
  # # => Underscored file name
259
375
  def image_alt(src)
376
+ ActiveSupport::Deprecation.warn("image_alt is deprecated and will be removed from Rails 6.0. You must explicitly set alt text on images.")
377
+
260
378
  File.basename(src, ".*".freeze).sub(/-[[:xdigit:]]{32,64}\z/, "".freeze).tr("-_".freeze, " ".freeze).capitalize
261
379
  end
262
380
 
263
381
  # Returns an HTML video tag for the +sources+. If +sources+ is a string,
264
382
  # a single video tag will be returned. If +sources+ is an array, a video
265
383
  # tag with nested source tags for each source will be returned. The
266
- # +sources+ can be full paths or files that exists in your public videos
384
+ # +sources+ can be full paths or files that exist in your public videos
267
385
  # directory.
268
386
  #
269
387
  # ==== Options
270
- # You can add HTML attributes using the +options+. The +options+ supports
271
- # two additional keys for convenience and conformance:
388
+ #
389
+ # When the last parameter is a hash you can add HTML attributes using that
390
+ # parameter. The following options are supported:
272
391
  #
273
392
  # * <tt>:poster</tt> - Set an image (like a screenshot) to be shown
274
393
  # before the video loads. The path is calculated like the +src+ of +image_tag+.
@@ -285,7 +404,7 @@ module ActionView
285
404
  # video_tag("trailer.ogg")
286
405
  # # => <video src="/videos/trailer.ogg"></video>
287
406
  # video_tag("trailer.ogg", controls: true, preload: 'none')
288
- # # => <video preload="none" controls="controls" src="/videos/trailer.ogg" ></video>
407
+ # # => <video preload="none" controls="controls" src="/videos/trailer.ogg"></video>
289
408
  # video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
290
409
  # # => <video src="/videos/trailer.m4v" width="16" height="10" poster="/assets/screenshot.png"></video>
291
410
  # video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png", poster_skip_pipeline: true)
@@ -312,9 +431,14 @@ module ActionView
312
431
  end
313
432
  end
314
433
 
315
- # Returns an HTML audio tag for the +source+.
316
- # The +source+ can be full path or file that exists in
317
- # your public audios directory.
434
+ # Returns an HTML audio tag for the +sources+. If +sources+ is a string,
435
+ # a single audio tag will be returned. If +sources+ is an array, an audio
436
+ # tag with nested source tags for each source will be returned. The
437
+ # +sources+ can be full paths or files that exist in your public audios
438
+ # directory.
439
+ #
440
+ # When the last parameter is a hash you can add HTML attributes using that
441
+ # parameter.
318
442
  #
319
443
  # audio_tag("sound")
320
444
  # # => <audio src="/audios/sound"></audio>
@@ -346,6 +470,16 @@ module ActionView
346
470
  end
347
471
  end
348
472
 
473
+ def resolve_image_source(source, skip_pipeline)
474
+ if source.is_a?(Symbol) || source.is_a?(String)
475
+ path_to_image(source, skip_pipeline: skip_pipeline)
476
+ else
477
+ polymorphic_url(source)
478
+ end
479
+ rescue NoMethodError => e
480
+ raise ArgumentError, "Can't resolve image into URL: #{e}"
481
+ end
482
+
349
483
  def extract_dimensions(size)
350
484
  size = size.to_s
351
485
  if /\A\d+x\d+\z/.match?(size)
@@ -360,6 +494,18 @@ module ActionView
360
494
  raise ArgumentError, "Cannot pass a :size option with a :height or :width option"
361
495
  end
362
496
  end
497
+
498
+ def resolve_link_as(extname, mime_type)
499
+ if extname == "js"
500
+ "script"
501
+ elsif extname == "css"
502
+ "style"
503
+ elsif extname == "vtt"
504
+ "track"
505
+ elsif (type = mime_type.to_s.split("/")[0]) && type.in?(%w(audio video font))
506
+ type
507
+ end
508
+ end
363
509
  end
364
510
  end
365
511
  end
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "zlib"
2
4
 
3
5
  module ActionView
4
6
  # = Action View Asset URL Helpers
5
- module Helpers
7
+ module Helpers #:nodoc:
6
8
  # This module provides methods for generating asset paths and
7
- # urls.
9
+ # URLs.
8
10
  #
9
11
  # image_path("rails.png")
10
12
  # # => "/assets/rails.png"
@@ -27,7 +29,7 @@ module ActionView
27
29
  # Helpers take that into account:
28
30
  #
29
31
  # image_tag("rails.png")
30
- # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
32
+ # # => <img src="http://assets.example.com/assets/rails.png" />
31
33
  # stylesheet_link_tag("application")
32
34
  # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
33
35
  #
@@ -40,7 +42,7 @@ module ActionView
40
42
  # "assets0.example.com", ..., "assets3.example.com".
41
43
  #
42
44
  # image_tag("rails.png")
43
- # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
45
+ # # => <img src="http://assets0.example.com/assets/rails.png" />
44
46
  # stylesheet_link_tag("application")
45
47
  # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
46
48
  #
@@ -55,8 +57,8 @@ module ActionView
55
57
  # You can read more about setting up your DNS CNAME records from your ISP.
56
58
  #
57
59
  # Note: This is purely a browser performance optimization and is not meant
58
- # for server load balancing. See http://www.die.net/musings/page_load_time/
59
- # for background and http://www.browserscope.org/?category=network for
60
+ # for server load balancing. See https://www.die.net/musings/page_load_time/
61
+ # for background and https://www.browserscope.org/?category=network for
60
62
  # connection limit data.
61
63
  #
62
64
  # Alternatively, you can exert more control over the asset host by setting
@@ -66,7 +68,7 @@ module ActionView
66
68
  # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
67
69
  # }
68
70
  # image_tag("rails.png")
69
- # # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
71
+ # # => <img src="http://assets1.example.com/assets/rails.png" />
70
72
  # stylesheet_link_tag("application")
71
73
  # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
72
74
  #
@@ -85,7 +87,7 @@ module ActionView
85
87
  # end
86
88
  # }
87
89
  # image_tag("rails.png")
88
- # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
90
+ # # => <img src="http://assets.example.com/assets/rails.png" />
89
91
  # stylesheet_link_tag("application")
90
92
  # # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
91
93
  #
@@ -95,7 +97,7 @@ module ActionView
95
97
  # still sending assets for plain HTTP requests from asset hosts. If you don't
96
98
  # have SSL certificates for each of the asset hosts this technique allows you
97
99
  # to avoid warnings in the client about mixed media.
98
- # Note that the request parameter might not be supplied, e.g. when the assets
100
+ # Note that the +request+ parameter might not be supplied, e.g. when the assets
99
101
  # are precompiled via a Rake task. Make sure to use a +Proc+ instead of a lambda,
100
102
  # since a +Proc+ allows missing parameters and sets them to +nil+.
101
103
  #
@@ -147,13 +149,13 @@ module ActionView
147
149
  # Below lists scenarios that apply to +asset_path+ whether or not you're
148
150
  # using the asset pipeline.
149
151
  #
150
- # - All fully qualified urls are returned immediately. This bypasses the
152
+ # - All fully qualified URLs are returned immediately. This bypasses the
151
153
  # asset pipeline and all other behavior described.
152
154
  #
153
155
  # asset_path("http://www.example.com/js/xmlhr.js") # => "http://www.example.com/js/xmlhr.js"
154
156
  #
155
157
  # - All assets that begin with a forward slash are assumed to be full
156
- # urls and will not be expanded. This will bypass the asset pipeline.
158
+ # URLs and will not be expanded. This will bypass the asset pipeline.
157
159
  #
158
160
  # asset_path("/foo.png") # => "/foo.png"
159
161
  #
@@ -322,7 +324,7 @@ module ActionView
322
324
  # Since +javascript_url+ is based on +asset_url+ method you can set :host options. If :host
323
325
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
324
326
  #
325
- # javascript_url "js/xmlhr.js", host: "http://stage.example.com" # => http://stage.example.com/assets/dir/xmlhr.js
327
+ # javascript_url "js/xmlhr.js", host: "http://stage.example.com" # => http://stage.example.com/assets/js/xmlhr.js
326
328
  #
327
329
  def javascript_url(source, options = {})
328
330
  url_to_asset(source, { type: :javascript }.merge!(options))
@@ -349,7 +351,7 @@ module ActionView
349
351
  # Since +stylesheet_url+ is based on +asset_url+ method you can set :host options. If :host
350
352
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
351
353
  #
352
- # stylesheet_url "css/style.css", host: "http://stage.example.com" # => http://stage.example.com/css/style.css
354
+ # stylesheet_url "css/style.css", host: "http://stage.example.com" # => http://stage.example.com/assets/css/style.css
353
355
  #
354
356
  def stylesheet_url(source, options = {})
355
357
  url_to_asset(source, { type: :stylesheet }.merge!(options))
@@ -379,7 +381,7 @@ module ActionView
379
381
  # Since +image_url+ is based on +asset_url+ method you can set :host options. If :host
380
382
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
381
383
  #
382
- # image_url "edit.png", host: "http://stage.example.com" # => http://stage.example.com/edit.png
384
+ # image_url "edit.png", host: "http://stage.example.com" # => http://stage.example.com/assets/edit.png
383
385
  #
384
386
  def image_url(source, options = {})
385
387
  url_to_asset(source, { type: :image }.merge!(options))
@@ -405,7 +407,7 @@ module ActionView
405
407
  # Since +video_url+ is based on +asset_url+ method you can set :host options. If :host
406
408
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
407
409
  #
408
- # video_url "hd.avi", host: "http://stage.example.com" # => http://stage.example.com/hd.avi
410
+ # video_url "hd.avi", host: "http://stage.example.com" # => http://stage.example.com/videos/hd.avi
409
411
  #
410
412
  def video_url(source, options = {})
411
413
  url_to_asset(source, { type: :video }.merge!(options))
@@ -431,7 +433,7 @@ module ActionView
431
433
  # Since +audio_url+ is based on +asset_url+ method you can set :host options. If :host
432
434
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
433
435
  #
434
- # audio_url "horse.wav", host: "http://stage.example.com" # => http://stage.example.com/horse.wav
436
+ # audio_url "horse.wav", host: "http://stage.example.com" # => http://stage.example.com/audios/horse.wav
435
437
  #
436
438
  def audio_url(source, options = {})
437
439
  url_to_asset(source, { type: :audio }.merge!(options))
@@ -456,7 +458,7 @@ module ActionView
456
458
  # Since +font_url+ is based on +asset_url+ method you can set :host options. If :host
457
459
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
458
460
  #
459
- # font_url "font.ttf", host: "http://stage.example.com" # => http://stage.example.com/font.ttf
461
+ # font_url "font.ttf", host: "http://stage.example.com" # => http://stage.example.com/fonts/font.ttf
460
462
  #
461
463
  def font_url(source, options = {})
462
464
  url_to_asset(source, { type: :font }.merge!(options))
@@ -1,8 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "set"
2
4
 
3
5
  module ActionView
4
6
  # = Action View Atom Feed Helpers
5
- module Helpers
7
+ module Helpers #:nodoc:
6
8
  module AtomFeedHelper
7
9
  # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
8
10
  # template languages).
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  # = Action View Cache Helper
3
- module Helpers
5
+ module Helpers #:nodoc:
4
6
  module CacheHelper
5
7
  # This helper exposes a method for caching fragments of a view
6
8
  # rather than an entire action or page. This technique is useful
@@ -8,10 +10,9 @@ module ActionView
8
10
  # fragments, and so on. This method takes a block that contains
9
11
  # the content you wish to cache.
10
12
  #
11
- # The best way to use this is by doing key-based cache expiration
12
- # on top of a cache store like Memcached that'll automatically
13
- # kick out old entries. For more on key-based expiration, see:
14
- # http://signalvnoise.com/posts/3113-how-key-based-cache-expiration-works
13
+ # The best way to use this is by doing recyclable key-based cache expiration
14
+ # on top of a cache store like Memcached or Redis that'll automatically
15
+ # kick out old entries.
15
16
  #
16
17
  # When using this method, you list the cache dependency as the name of the cache, like so:
17
18
  #
@@ -23,10 +24,14 @@ module ActionView
23
24
  # This approach will assume that when a new topic is added, you'll touch
24
25
  # the project. The cache key generated from this call will be something like:
25
26
  #
26
- # views/projects/123-20120806214154/7a1156131a6928cb0026877f8b749ac9
27
- # ^class ^id ^updated_at ^template tree digest
27
+ # views/template/action.html.erb:7a1156131a6928cb0026877f8b749ac9/projects/123
28
+ # ^template path ^template tree digest ^class ^id
28
29
  #
29
- # The cache is thus automatically bumped whenever the project updated_at is touched.
30
+ # This cache key is stable, but it's combined with a cache version derived from the project
31
+ # record. When the project updated_at is touched, the #cache_version changes, even
32
+ # if the key stays stable. This means that unlike a traditional key-based cache expiration
33
+ # approach, you won't be generating cache trash, unused keys, simply because the dependent
34
+ # record is updated.
30
35
  #
31
36
  # If your template cache depends on multiple sources (try to avoid this to keep things simple),
32
37
  # you can name all these dependencies as part of an array:
@@ -106,9 +111,9 @@ module ActionView
106
111
  # <%= render_categorizable_events @person.events %>
107
112
  #
108
113
  # This marks every template in the directory as a dependency. To find those
109
- # templates, the wildcard path must be absolutely defined from app/views or paths
114
+ # templates, the wildcard path must be absolutely defined from <tt>app/views</tt> or paths
110
115
  # otherwise added with +prepend_view_path+ or +append_view_path+.
111
- # This way the wildcard for `app/views/recordings/events` would be `recordings/events/*` etc.
116
+ # This way the wildcard for <tt>app/views/recordings/events</tt> would be <tt>recordings/events/*</tt> etc.
112
117
  #
113
118
  # The pattern used to match explicit dependencies is <tt>/# Template Dependency: (\S+)/</tt>,
114
119
  # so it's important that you type it out just so.
@@ -128,14 +133,14 @@ module ActionView
128
133
  #
129
134
  # === Collection Caching
130
135
  #
131
- # When rendering a collection of objects that each use the same partial, a `cached`
136
+ # When rendering a collection of objects that each use the same partial, a <tt>:cached</tt>
132
137
  # option can be passed.
133
138
  #
134
139
  # For collections rendered such:
135
140
  #
136
141
  # <%= render partial: 'projects/project', collection: @projects, cached: true %>
137
142
  #
138
- # The `cached: true` will make Action View's rendering read several templates
143
+ # The <tt>cached: true</tt> will make Action View's rendering read several templates
139
144
  # from cache at once instead of one call per template.
140
145
  #
141
146
  # Templates in the collection not already cached are written to cache.
@@ -215,10 +220,15 @@ module ActionView
215
220
 
216
221
  def fragment_name_with_digest(name, virtual_path)
217
222
  virtual_path ||= @virtual_path
223
+
218
224
  if virtual_path
219
225
  name = controller.url_for(name).split("://").last if name.is_a?(Hash)
220
- digest = Digestor.digest name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies
221
- [ name, digest ]
226
+
227
+ if digest = Digestor.digest(name: virtual_path, finder: lookup_context, dependencies: view_cache_dependencies).presence
228
+ [ "#{virtual_path}:#{digest}", name ]
229
+ else
230
+ [ virtual_path, name ]
231
+ end
222
232
  else
223
233
  name
224
234
  end