actionview 7.0.1 → 7.1.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +281 -202
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +3 -3
  5. data/app/assets/javascripts/rails-ujs.esm.js +693 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +33 -12
  8. data/lib/action_view/buffers.rb +106 -8
  9. data/lib/action_view/cache_expiry.rb +40 -43
  10. data/lib/action_view/context.rb +1 -1
  11. data/lib/action_view/deprecator.rb +7 -0
  12. data/lib/action_view/digestor.rb +1 -1
  13. data/lib/action_view/gem_version.rb +2 -2
  14. data/lib/action_view/helpers/active_model_helper.rb +1 -1
  15. data/lib/action_view/helpers/asset_tag_helper.rb +133 -48
  16. data/lib/action_view/helpers/asset_url_helper.rb +13 -12
  17. data/lib/action_view/helpers/atom_feed_helper.rb +5 -5
  18. data/lib/action_view/helpers/cache_helper.rb +3 -9
  19. data/lib/action_view/helpers/capture_helper.rb +26 -12
  20. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  21. data/lib/action_view/helpers/controller_helper.rb +6 -0
  22. data/lib/action_view/helpers/csp_helper.rb +2 -2
  23. data/lib/action_view/helpers/csrf_helper.rb +3 -3
  24. data/lib/action_view/helpers/date_helper.rb +76 -64
  25. data/lib/action_view/helpers/debug_helper.rb +3 -3
  26. data/lib/action_view/helpers/form_helper.rb +62 -31
  27. data/lib/action_view/helpers/form_options_helper.rb +6 -3
  28. data/lib/action_view/helpers/form_tag_helper.rb +88 -44
  29. data/lib/action_view/helpers/javascript_helper.rb +1 -0
  30. data/lib/action_view/helpers/number_helper.rb +15 -13
  31. data/lib/action_view/helpers/output_safety_helper.rb +4 -4
  32. data/lib/action_view/helpers/rendering_helper.rb +5 -6
  33. data/lib/action_view/helpers/sanitize_helper.rb +34 -15
  34. data/lib/action_view/helpers/tag_helper.rb +27 -16
  35. data/lib/action_view/helpers/tags/base.rb +11 -52
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +1 -0
  37. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +1 -0
  38. data/lib/action_view/helpers/tags/collection_select.rb +3 -0
  39. data/lib/action_view/helpers/tags/date_field.rb +1 -1
  40. data/lib/action_view/helpers/tags/date_select.rb +2 -0
  41. data/lib/action_view/helpers/tags/datetime_field.rb +14 -6
  42. data/lib/action_view/helpers/tags/datetime_local_field.rb +11 -2
  43. data/lib/action_view/helpers/tags/grouped_collection_select.rb +3 -0
  44. data/lib/action_view/helpers/tags/month_field.rb +1 -1
  45. data/lib/action_view/helpers/tags/select.rb +4 -1
  46. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  47. data/lib/action_view/helpers/tags/time_field.rb +1 -1
  48. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -0
  49. data/lib/action_view/helpers/tags/week_field.rb +1 -1
  50. data/lib/action_view/helpers/tags/weekday_select.rb +3 -0
  51. data/lib/action_view/helpers/tags.rb +2 -0
  52. data/lib/action_view/helpers/text_helper.rb +33 -17
  53. data/lib/action_view/helpers/translation_helper.rb +6 -6
  54. data/lib/action_view/helpers/url_helper.rb +90 -65
  55. data/lib/action_view/helpers.rb +2 -0
  56. data/lib/action_view/layouts.rb +13 -8
  57. data/lib/action_view/log_subscriber.rb +49 -32
  58. data/lib/action_view/lookup_context.rb +29 -13
  59. data/lib/action_view/path_registry.rb +57 -0
  60. data/lib/action_view/path_set.rb +13 -14
  61. data/lib/action_view/railtie.rb +26 -3
  62. data/lib/action_view/record_identifier.rb +16 -9
  63. data/lib/action_view/renderer/abstract_renderer.rb +1 -1
  64. data/lib/action_view/renderer/collection_renderer.rb +9 -1
  65. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +21 -3
  66. data/lib/action_view/renderer/partial_renderer.rb +3 -2
  67. data/lib/action_view/renderer/renderer.rb +2 -0
  68. data/lib/action_view/renderer/streaming_template_renderer.rb +3 -2
  69. data/lib/action_view/renderer/template_renderer.rb +3 -2
  70. data/lib/action_view/rendering.rb +24 -6
  71. data/lib/action_view/ripper_ast_parser.rb +6 -6
  72. data/lib/action_view/routing_url_for.rb +7 -4
  73. data/lib/action_view/template/error.rb +14 -1
  74. data/lib/action_view/template/handlers/builder.rb +4 -4
  75. data/lib/action_view/template/handlers/erb/erubi.rb +23 -27
  76. data/lib/action_view/template/handlers/erb.rb +73 -1
  77. data/lib/action_view/template/handlers.rb +1 -1
  78. data/lib/action_view/template/html.rb +1 -1
  79. data/lib/action_view/template/raw_file.rb +1 -1
  80. data/lib/action_view/template/renderable.rb +1 -1
  81. data/lib/action_view/template/resolver.rb +15 -5
  82. data/lib/action_view/template/text.rb +1 -1
  83. data/lib/action_view/template/types.rb +25 -34
  84. data/lib/action_view/template.rb +227 -53
  85. data/lib/action_view/template_path.rb +2 -0
  86. data/lib/action_view/test_case.rb +174 -21
  87. data/lib/action_view/unbound_template.rb +15 -5
  88. data/lib/action_view/version.rb +1 -1
  89. data/lib/action_view/view_paths.rb +19 -28
  90. data/lib/action_view.rb +4 -1
  91. data/lib/assets/compiled/rails-ujs.js +36 -5
  92. metadata +27 -27
@@ -7,8 +7,9 @@ require "action_view/helpers/asset_url_helper"
7
7
  require "action_view/helpers/tag_helper"
8
8
 
9
9
  module ActionView
10
- # = Action View Asset Tag Helpers
11
10
  module Helpers # :nodoc:
11
+ # = Action View Asset Tag \Helpers
12
+ #
12
13
  # This module provides methods for generating HTML that links views to assets such
13
14
  # as images, JavaScripts, stylesheets, and feeds. These methods do not verify
14
15
  # the assets exist before linking to them:
@@ -41,13 +42,14 @@ module ActionView
41
42
  # When the Asset Pipeline is enabled, you can pass the name of your manifest as
42
43
  # source, and include other JavaScript or CoffeeScript files inside the manifest.
43
44
  #
44
- # If the server supports Early Hints header links for these assets will be
45
- # automatically pushed.
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.
46
48
  #
47
49
  # ==== Options
48
50
  #
49
51
  # When the last parameter is a hash you can add HTML attributes using that
50
- # parameter. The following options are supported:
52
+ # parameter. This includes but is not limited to the following options:
51
53
  #
52
54
  # * <tt>:extname</tt> - Append an extension to the generated URL unless the extension
53
55
  # already exists. This only applies for relative URLs.
@@ -59,6 +61,20 @@ module ActionView
59
61
  # when it is set to true.
60
62
  # * <tt>:nonce</tt> - When set to true, adds an automatic nonce value if
61
63
  # you have Content Security Policy enabled.
64
+ # * <tt>:async</tt> - When set to +true+, adds the +async+ HTML
65
+ # attribute, allowing the script to be fetched in parallel to be parsed
66
+ # and evaluated as soon as possible.
67
+ # * <tt>:defer</tt> - When set to +true+, adds the +defer+ HTML
68
+ # attribute, which indicates to the browser that the script is meant to
69
+ # be executed after the document has been parsed. Additionally, prevents
70
+ # sending the Preload Links header.
71
+ #
72
+ # Any other specified options will be treated as HTML attributes for the
73
+ # +script+ tag.
74
+ #
75
+ # For more information regarding how the <tt>:async</tt> and <tt>:defer</tt>
76
+ # options affect the <tt><script></tt> tag, please refer to the
77
+ # {MDN docs}[https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script].
62
78
  #
63
79
  # ==== Examples
64
80
  #
@@ -86,10 +102,17 @@ module ActionView
86
102
  #
87
103
  # javascript_include_tag "http://www.example.com/xmlhr.js", nonce: true
88
104
  # # => <script src="http://www.example.com/xmlhr.js" nonce="..."></script>
105
+ #
106
+ # javascript_include_tag "http://www.example.com/xmlhr.js", async: true
107
+ # # => <script src="http://www.example.com/xmlhr.js" async="async"></script>
108
+ #
109
+ # javascript_include_tag "http://www.example.com/xmlhr.js", defer: true
110
+ # # => <script src="http://www.example.com/xmlhr.js" defer="defer"></script>
89
111
  def javascript_include_tag(*sources)
90
112
  options = sources.extract_options!.stringify_keys
91
113
  path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
92
114
  preload_links = []
115
+ use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
93
116
  nopush = options["nopush"].nil? ? true : options.delete("nopush")
94
117
  crossorigin = options.delete("crossorigin")
95
118
  crossorigin = "anonymous" if crossorigin == true
@@ -98,7 +121,7 @@ module ActionView
98
121
 
99
122
  sources_tags = sources.uniq.map { |source|
100
123
  href = path_to_javascript(source, path_options)
101
- if preload_links_header && !options["defer"] && href.present? && !href.start_with?("data:")
124
+ if use_preload_links_header && !options["defer"] && href.present? && !href.start_with?("data:")
102
125
  preload_link = "<#{href}>; rel=#{rel}; as=script"
103
126
  preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
104
127
  preload_link += "; integrity=#{integrity}" unless integrity.nil?
@@ -115,7 +138,7 @@ module ActionView
115
138
  content_tag("script", "", tag_options)
116
139
  }.join("\n").html_safe
117
140
 
118
- if preload_links_header
141
+ if use_preload_links_header
119
142
  send_preload_links_header(preload_links)
120
143
  end
121
144
 
@@ -130,8 +153,8 @@ module ActionView
130
153
  # set <tt>extname: false</tt> in the options.
131
154
  # You can modify the link attributes by passing a hash as the last argument.
132
155
  #
133
- # If the server supports Early Hints header links for these assets will be
134
- # automatically pushed.
156
+ # If the server supports HTTP Early Hints, \Rails will push a <tt>103 Early
157
+ # Hints</tt> response that links to the assets.
135
158
  #
136
159
  # ==== Options
137
160
  #
@@ -170,6 +193,7 @@ module ActionView
170
193
  def stylesheet_link_tag(*sources)
171
194
  options = sources.extract_options!.stringify_keys
172
195
  path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys
196
+ use_preload_links_header = options["preload_links_header"].nil? ? preload_links_header : options.delete("preload_links_header")
173
197
  preload_links = []
174
198
  crossorigin = options.delete("crossorigin")
175
199
  crossorigin = "anonymous" if crossorigin == true
@@ -178,7 +202,7 @@ module ActionView
178
202
 
179
203
  sources_tags = sources.uniq.map { |source|
180
204
  href = path_to_stylesheet(source, path_options)
181
- if preload_links_header && href.present? && !href.start_with?("data:")
205
+ if use_preload_links_header && href.present? && !href.start_with?("data:")
182
206
  preload_link = "<#{href}>; rel=preload; as=style"
183
207
  preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil?
184
208
  preload_link += "; integrity=#{integrity}" unless integrity.nil?
@@ -198,7 +222,7 @@ module ActionView
198
222
  tag(:link, tag_options)
199
223
  }.join("\n").html_safe
200
224
 
201
- if preload_links_header
225
+ if use_preload_links_header
202
226
  send_preload_links_header(preload_links)
203
227
  end
204
228
 
@@ -325,16 +349,17 @@ module ActionView
325
349
  crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font")
326
350
  integrity = options[:integrity]
327
351
  nopush = options.delete(:nopush) || false
352
+ rel = mime_type == "module" ? "modulepreload" : "preload"
328
353
 
329
354
  link_tag = tag.link(**{
330
- rel: "preload",
355
+ rel: rel,
331
356
  href: href,
332
357
  as: as_type,
333
358
  type: mime_type,
334
359
  crossorigin: crossorigin
335
360
  }.merge!(options.symbolize_keys))
336
361
 
337
- preload_link = "<#{href}>; rel=preload; as=#{as_type}"
362
+ preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}"
338
363
  preload_link += "; type=#{mime_type}" if mime_type
339
364
  preload_link += "; crossorigin=#{crossorigin}" if crossorigin
340
365
  preload_link += "; integrity=#{integrity}" if integrity
@@ -395,7 +420,7 @@ module ActionView
395
420
  check_for_image_tag_errors(options)
396
421
  skip_pipeline = options.delete(:skip_pipeline)
397
422
 
398
- options[:src] = resolve_image_source(source, skip_pipeline)
423
+ options[:src] = resolve_asset_source("image", source, skip_pipeline)
399
424
 
400
425
  if options[:srcset] && !options[:srcset].is_a?(String)
401
426
  options[:srcset] = options[:srcset].map do |src_path, size|
@@ -412,11 +437,72 @@ module ActionView
412
437
  tag("img", options)
413
438
  end
414
439
 
440
+ # Returns an HTML picture tag for the +sources+. If +sources+ is a string,
441
+ # a single picture tag will be returned. If +sources+ is an array, a picture
442
+ # tag with nested source tags for each source will be returned. The
443
+ # +sources+ can be full paths, files that exist in your public images
444
+ # directory, or Active Storage attachments. Since the picture tag requires
445
+ # an img tag, the last element you provide will be used for the img tag.
446
+ # For complete control over the picture tag, a block can be passed, which
447
+ # will populate the contents of the tag accordingly.
448
+ #
449
+ # ==== Options
450
+ #
451
+ # When the last parameter is a hash you can add HTML attributes using that
452
+ # parameter. Apart from all the HTML supported options, the following are supported:
453
+ #
454
+ # * <tt>:image</tt> - Hash of options that are passed directly to the +image_tag+ helper.
455
+ #
456
+ # ==== Examples
457
+ #
458
+ # picture_tag("picture.webp")
459
+ # # => <picture><img src="/images/picture.webp" /></picture>
460
+ # picture_tag("gold.png", :image => { :size => "20" })
461
+ # # => <picture><img height="20" src="/images/gold.png" width="20" /></picture>
462
+ # picture_tag("gold.png", :image => { :size => "45x70" })
463
+ # # => <picture><img height="70" src="/images/gold.png" width="45" /></picture>
464
+ # picture_tag("picture.webp", "picture.png")
465
+ # # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img src="/images/picture.png" /></picture>
466
+ # picture_tag("picture.webp", "picture.png", :image => { alt: "Image" })
467
+ # # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img alt="Image" src="/images/picture.png" /></picture>
468
+ # picture_tag(["picture.webp", "picture.png"], :image => { alt: "Image" })
469
+ # # => <picture><source srcset="/images/picture.webp" /><source srcset="/images/picture.png" /><img alt="Image" src="/images/picture.png" /></picture>
470
+ # picture_tag(:class => "my-class") { tag(:source, :srcset => image_path("picture.webp")) + image_tag("picture.png", :alt => "Image") }
471
+ # # => <picture class="my-class"><source srcset="/images/picture.webp" /><img alt="Image" src="/images/picture.png" /></picture>
472
+ # picture_tag { tag(:source, :srcset => image_path("picture-small.webp"), :media => "(min-width: 600px)") + tag(:source, :srcset => image_path("picture-big.webp")) + image_tag("picture.png", :alt => "Image") }
473
+ # # => <picture><source srcset="/images/picture-small.webp" media="(min-width: 600px)" /><source srcset="/images/picture-big.webp" /><img alt="Image" src="/images/picture.png" /></picture>
474
+ #
475
+ # Active Storage blobs (images that are uploaded by the users of your app):
476
+ #
477
+ # picture_tag(user.profile_picture)
478
+ # # => <picture><img src="/rails/active_storage/blobs/.../profile_picture.webp" /></picture>
479
+ def picture_tag(*sources, &block)
480
+ sources.flatten!
481
+ options = sources.extract_options!.symbolize_keys
482
+ image_options = options.delete(:image) || {}
483
+ skip_pipeline = options.delete(:skip_pipeline)
484
+
485
+ content_tag("picture", options) do
486
+ if block.present?
487
+ capture(&block).html_safe
488
+ elsif sources.size <= 1
489
+ image_tag(sources.last, image_options)
490
+ else
491
+ source_tags = sources.map do |source|
492
+ tag("source",
493
+ srcset: resolve_asset_source("image", source, skip_pipeline),
494
+ type: Template::Types[File.extname(source)[1..]]&.to_s)
495
+ end
496
+ safe_join(source_tags << image_tag(sources.last, image_options))
497
+ end
498
+ end
499
+ end
500
+
415
501
  # Returns an HTML video tag for the +sources+. If +sources+ is a string,
416
502
  # a single video tag will be returned. If +sources+ is an array, a video
417
503
  # tag with nested source tags for each source will be returned. The
418
- # +sources+ can be full paths or files that exist in your public videos
419
- # directory.
504
+ # +sources+ can be full paths, files that exist in your public videos
505
+ # directory, or Active Storage attachments.
420
506
  #
421
507
  # ==== Options
422
508
  #
@@ -455,6 +541,11 @@ module ActionView
455
541
  # # => <video><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
456
542
  # video_tag(["trailer.ogg", "trailer.flv"], size: "160x120")
457
543
  # # => <video height="120" width="160"><source src="/videos/trailer.ogg" /><source src="/videos/trailer.flv" /></video>
544
+ #
545
+ # Active Storage blobs (videos that are uploaded by the users of your app):
546
+ #
547
+ # video_tag(user.intro_video)
548
+ # # => <video src="/rails/active_storage/blobs/.../intro_video.mp4"></video>
458
549
  def video_tag(*sources)
459
550
  options = sources.extract_options!.symbolize_keys
460
551
  public_poster_folder = options.delete(:poster_skip_pipeline)
@@ -468,8 +559,8 @@ module ActionView
468
559
  # Returns an HTML audio tag for the +sources+. If +sources+ is a string,
469
560
  # a single audio tag will be returned. If +sources+ is an array, an audio
470
561
  # tag with nested source tags for each source will be returned. The
471
- # +sources+ can be full paths or files that exist in your public audios
472
- # directory.
562
+ # +sources+ can be full paths, files that exist in your public audios
563
+ # directory, or Active Storage attachments.
473
564
  #
474
565
  # When the last parameter is a hash you can add HTML attributes using that
475
566
  # parameter.
@@ -482,6 +573,11 @@ module ActionView
482
573
  # # => <audio autoplay="autoplay" controls="controls" src="/audios/sound.wav"></audio>
483
574
  # audio_tag("sound.wav", "sound.mid")
484
575
  # # => <audio><source src="/audios/sound.wav" /><source src="/audios/sound.mid" /></audio>
576
+ #
577
+ # Active Storage blobs (audios that are uploaded by the users of your app):
578
+ #
579
+ # audio_tag(user.name_pronunciation_audio)
580
+ # # => <audio src="/rails/active_storage/blobs/.../name_pronunciation_audio.mp3"></audio>
485
581
  def audio_tag(*sources)
486
582
  multiple_sources_tag_builder("audio", sources)
487
583
  end
@@ -496,29 +592,29 @@ module ActionView
496
592
 
497
593
  if sources.size > 1
498
594
  content_tag(type, options) do
499
- safe_join sources.map { |source| tag("source", src: send("path_to_#{type}", source, skip_pipeline: skip_pipeline)) }
595
+ safe_join sources.map { |source| tag("source", src: resolve_asset_source(type, source, skip_pipeline)) }
500
596
  end
501
597
  else
502
- options[:src] = send("path_to_#{type}", sources.first, skip_pipeline: skip_pipeline)
598
+ options[:src] = resolve_asset_source(type, sources.first, skip_pipeline)
503
599
  content_tag(type, nil, options)
504
600
  end
505
601
  end
506
602
 
507
- def resolve_image_source(source, skip_pipeline)
603
+ def resolve_asset_source(asset_type, source, skip_pipeline)
508
604
  if source.is_a?(Symbol) || source.is_a?(String)
509
- path_to_image(source, skip_pipeline: skip_pipeline)
605
+ path_to_asset(source, type: asset_type.to_sym, skip_pipeline: skip_pipeline)
510
606
  else
511
607
  polymorphic_url(source)
512
608
  end
513
609
  rescue NoMethodError => e
514
- raise ArgumentError, "Can't resolve image into URL: #{e}"
610
+ raise ArgumentError, "Can't resolve #{asset_type} into URL: #{e}"
515
611
  end
516
612
 
517
613
  def extract_dimensions(size)
518
614
  size = size.to_s
519
- if /\A\d+x\d+\z/.match?(size)
615
+ if /\A\d+(?:\.\d+)?x\d+(?:\.\d+)?\z/.match?(size)
520
616
  size.split("x")
521
- elsif /\A\d+\z/.match?(size)
617
+ elsif /\A\d+(?:\.\d+)?\z/.match?(size)
522
618
  [size, size]
523
619
  end
524
620
  end
@@ -539,40 +635,29 @@ module ActionView
539
635
  end
540
636
  end
541
637
 
542
- MAX_HEADER_SIZE = 8_000 # Some HTTP client and proxies have a 8kiB header limit
638
+ # Some HTTP client and proxies have a 4kiB header limit, but more importantly
639
+ # including preload links has diminishing returns so it's best to not go overboard
640
+ MAX_HEADER_SIZE = 1_000 # :nodoc:
641
+
543
642
  def send_preload_links_header(preload_links, max_header_size: MAX_HEADER_SIZE)
544
643
  return if preload_links.empty?
545
- return if respond_to?(:response) && response.sending?
644
+ response_present = respond_to?(:response) && response
645
+ return if response_present && response.sending?
546
646
 
547
647
  if respond_to?(:request) && request
548
648
  request.send_early_hints("Link" => preload_links.join("\n"))
549
649
  end
550
650
 
551
- if respond_to?(:response) && response
552
- header = response.headers["Link"]
553
- header = header ? header.dup : +""
554
-
555
- # rindex count characters not bytes, but we assume non-ascii characters
556
- # are rare in urls, and we have a 192 bytes margin.
557
- last_line_offset = header.rindex("\n")
558
- last_line_size = if last_line_offset
559
- header.bytesize - last_line_offset
560
- else
561
- header.bytesize
562
- end
563
-
651
+ if response_present
652
+ header = +response.headers["Link"].to_s
564
653
  preload_links.each do |link|
565
- if link.bytesize + last_line_size + 1 < max_header_size
566
- unless header.empty?
567
- header << ","
568
- last_line_size += 1
569
- end
654
+ break if header.bytesize + link.bytesize > max_header_size
655
+
656
+ if header.empty?
657
+ header << link
570
658
  else
571
- header << "\n"
572
- last_line_size = 0
659
+ header << "," << link
573
660
  end
574
- header << link
575
- last_line_size += link.bytesize
576
661
  end
577
662
 
578
663
  response.headers["Link"] = header
@@ -3,8 +3,9 @@
3
3
  require "zlib"
4
4
 
5
5
  module ActionView
6
- # = Action View Asset URL Helpers
7
6
  module Helpers # :nodoc:
7
+ # = Action View Asset URL \Helpers
8
+ #
8
9
  # This module provides methods for generating asset paths and
9
10
  # URLs.
10
11
  #
@@ -16,8 +17,8 @@ module ActionView
16
17
  #
17
18
  # === Using asset hosts
18
19
  #
19
- # By default, Rails links to these assets on the current host in the public
20
- # folder, but you can direct Rails to link to assets from a dedicated asset
20
+ # By default, \Rails links to these assets on the current host in the public
21
+ # folder, but you can direct \Rails to link to assets from a dedicated asset
21
22
  # server by setting <tt>ActionController::Base.asset_host</tt> in the application
22
23
  # configuration, typically in <tt>config/environments/production.rb</tt>.
23
24
  # For example, you'd define <tt>assets.example.com</tt> to be your asset
@@ -196,7 +197,7 @@ module ActionView
196
197
  source = "#{source}#{extname}"
197
198
  end
198
199
 
199
- if source[0] != ?/
200
+ unless source.start_with?(?/)
200
201
  if options[:skip_pipeline]
201
202
  source = public_compute_asset_path(source, options)
202
203
  else
@@ -219,7 +220,7 @@ module ActionView
219
220
 
220
221
  # Computes the full URL to an asset in the public directory. This
221
222
  # will use +asset_path+ internally, so most of their behaviors
222
- # will be the same. If :host options is set, it overwrites global
223
+ # will be the same. If +:host+ options is set, it overwrites global
223
224
  # +config.action_controller.asset_host+ setting.
224
225
  #
225
226
  # All other options provided are forwarded to +asset_path+ call.
@@ -324,7 +325,7 @@ module ActionView
324
325
 
325
326
  # Computes the full URL to a JavaScript asset in the public javascripts directory.
326
327
  # This will use +javascript_path+ internally, so most of their behaviors will be the same.
327
- # Since +javascript_url+ is based on +asset_url+ method you can set :host options. If :host
328
+ # Since +javascript_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
328
329
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
329
330
  #
330
331
  # javascript_url "js/xmlhr.js", host: "http://stage.example.com" # => http://stage.example.com/assets/js/xmlhr.js
@@ -351,7 +352,7 @@ module ActionView
351
352
 
352
353
  # Computes the full URL to a stylesheet asset in the public stylesheets directory.
353
354
  # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
354
- # Since +stylesheet_url+ is based on +asset_url+ method you can set :host options. If :host
355
+ # Since +stylesheet_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
355
356
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
356
357
  #
357
358
  # stylesheet_url "css/style.css", host: "http://stage.example.com" # => http://stage.example.com/assets/css/style.css
@@ -372,7 +373,7 @@ module ActionView
372
373
  # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
373
374
  #
374
375
  # If you have images as application resources this method may conflict with their named routes.
375
- # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
376
+ # The alias +path_to_image+ is provided to avoid that. \Rails uses the alias internally, and
376
377
  # plugin authors are encouraged to do so.
377
378
  def image_path(source, options = {})
378
379
  path_to_asset(source, { type: :image }.merge!(options))
@@ -381,7 +382,7 @@ module ActionView
381
382
 
382
383
  # Computes the full URL to an image asset.
383
384
  # This will use +image_path+ internally, so most of their behaviors will be the same.
384
- # Since +image_url+ is based on +asset_url+ method you can set :host options. If :host
385
+ # Since +image_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
385
386
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
386
387
  #
387
388
  # image_url "edit.png", host: "http://stage.example.com" # => http://stage.example.com/assets/edit.png
@@ -407,7 +408,7 @@ module ActionView
407
408
 
408
409
  # Computes the full URL to a video asset in the public videos directory.
409
410
  # This will use +video_path+ internally, so most of their behaviors will be the same.
410
- # Since +video_url+ is based on +asset_url+ method you can set :host options. If :host
411
+ # Since +video_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
411
412
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
412
413
  #
413
414
  # video_url "hd.avi", host: "http://stage.example.com" # => http://stage.example.com/videos/hd.avi
@@ -433,7 +434,7 @@ module ActionView
433
434
 
434
435
  # Computes the full URL to an audio asset in the public audios directory.
435
436
  # This will use +audio_path+ internally, so most of their behaviors will be the same.
436
- # Since +audio_url+ is based on +asset_url+ method you can set :host options. If :host
437
+ # Since +audio_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
437
438
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
438
439
  #
439
440
  # audio_url "horse.wav", host: "http://stage.example.com" # => http://stage.example.com/audios/horse.wav
@@ -458,7 +459,7 @@ module ActionView
458
459
 
459
460
  # Computes the full URL to a font asset.
460
461
  # This will use +font_path+ internally, so most of their behaviors will be the same.
461
- # Since +font_url+ is based on +asset_url+ method you can set :host options. If :host
462
+ # Since +font_url+ is based on +asset_url+ method you can set +:host+ options. If +:host+
462
463
  # options is set, it overwrites global +config.action_controller.asset_host+ setting.
463
464
  #
464
465
  # font_url "font.ttf", host: "http://stage.example.com" # => http://stage.example.com/fonts/font.ttf
@@ -3,8 +3,8 @@
3
3
  require "set"
4
4
 
5
5
  module ActionView
6
- # = Action View Atom Feed Helpers
7
6
  module Helpers # :nodoc:
7
+ # = Action View Atom Feed \Helpers
8
8
  module AtomFeedHelper
9
9
  # Adds easy defaults to writing Atom feeds with the Builder template engine (this does not work on ERB or any other
10
10
  # template languages).
@@ -82,9 +82,9 @@ module ActionView
82
82
  # end
83
83
  #
84
84
  # The Atom spec defines five elements (content rights title subtitle
85
- # summary) which may directly contain xhtml content if type: 'xhtml'
85
+ # summary) which may directly contain XHTML content if type: 'xhtml'
86
86
  # is specified as an attribute. If so, this helper will take care of
87
- # the enclosing div and xhtml namespace declaration. Example usage:
87
+ # the enclosing div and XHTML namespace declaration. Example usage:
88
88
  #
89
89
  # entry.summary type: 'xhtml' do |xhtml|
90
90
  # xhtml.p pluralize(order.line_items.count, "line item")
@@ -102,7 +102,7 @@ module ActionView
102
102
  options[:schema_date] = "2005" # The Atom spec copyright date
103
103
  end
104
104
 
105
- xml = options.delete(:xml) || eval("xml", block.binding)
105
+ xml = options.delete(:xml) || block.binding.local_variable_get(:xml)
106
106
  xml.instruct!
107
107
  if options[:instruct]
108
108
  options[:instruct].each do |target, attrs|
@@ -134,7 +134,7 @@ module ActionView
134
134
  end
135
135
 
136
136
  private
137
- # Delegate to xml builder, first wrapping the element in an xhtml
137
+ # Delegate to XML Builder, first wrapping the element in an XHTML
138
138
  # namespaced div element if the method and arguments indicate
139
139
  # that an xhtml_block? is desired.
140
140
  def method_missing(method, *arguments, &block)
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActionView
4
- # = Action View Cache Helper
5
4
  module Helpers # :nodoc:
5
+ # = Action View Cache \Helpers
6
6
  module CacheHelper
7
7
  class UncacheableFragmentError < StandardError; end
8
8
 
@@ -281,14 +281,8 @@ module ActionView
281
281
  controller.read_fragment(name, options)
282
282
  end
283
283
 
284
- def write_fragment_for(name, options)
285
- pos = output_buffer.length
286
- yield
287
- output_safe = output_buffer.html_safe?
288
- fragment = output_buffer.slice!(pos..-1)
289
- if output_safe
290
- self.output_buffer = output_buffer.class.new(output_buffer)
291
- end
284
+ def write_fragment_for(name, options, &block)
285
+ fragment = output_buffer.capture(&block)
292
286
  controller.write_fragment(name, fragment, options)
293
287
  end
294
288
 
@@ -3,18 +3,22 @@
3
3
  require "active_support/core_ext/string/output_safety"
4
4
 
5
5
  module ActionView
6
- # = Action View Capture Helper
7
6
  module Helpers # :nodoc:
8
- # CaptureHelper exposes methods to let you extract generated markup which
7
+ # = Action View Capture \Helpers
8
+ #
9
+ # \CaptureHelper exposes methods to let you extract generated markup which
9
10
  # can be used in other parts of a template or layout file.
10
11
  #
11
- # It provides a method to capture blocks into variables through capture and
12
- # a way to capture a block of markup for use in a layout through {content_for}[rdoc-ref:ActionView::Helpers::CaptureHelper#content_for].
12
+ # It provides a method to capture blocks into variables through #capture and
13
+ # a way to capture a block of markup for use in a layout through #content_for.
14
+ #
15
+ # As well as provides a method when using streaming responses through #provide.
16
+ # See ActionController::Streaming for more information.
13
17
  module CaptureHelper
14
- # The capture method extracts part of a template as a String object.
18
+ # The capture method extracts part of a template as a string object.
15
19
  # You can then use this object anywhere in your templates, layout, or helpers.
16
20
  #
17
- # The capture method can be used in ERB templates...
21
+ # The capture method can be used in \ERB templates...
18
22
  #
19
23
  # <% @greeting = capture do %>
20
24
  # Welcome to my shiny new web page! The date and time is
@@ -40,11 +44,18 @@ module ActionView
40
44
  #
41
45
  # @greeting # => "Welcome to my shiny new web page! The date and time is 2018-09-06 11:09:16 -0500"
42
46
  #
43
- def capture(*args)
47
+ def capture(*args, &block)
44
48
  value = nil
45
- buffer = with_output_buffer { value = yield(*args) }
46
- if (string = buffer.presence || value) && string.is_a?(String)
47
- ERB::Util.html_escape string
49
+ @output_buffer ||= ActionView::OutputBuffer.new
50
+ buffer = @output_buffer.capture { value = yield(*args) }
51
+
52
+ case string = buffer.presence || value
53
+ when OutputBuffer
54
+ string.to_s
55
+ when ActiveSupport::SafeBuffer
56
+ string
57
+ when String
58
+ ERB::Util.html_escape(string)
48
59
  end
49
60
  end
50
61
 
@@ -121,7 +132,7 @@ module ActionView
121
132
  # <li><%= link_to 'Home', action: 'index' %></li>
122
133
  # <% end %>
123
134
  #
124
- # And in another place:
135
+ # And in another place:
125
136
  #
126
137
  # <% content_for :navigation do %>
127
138
  # <li><%= link_to 'Login', action: 'login' %></li>
@@ -137,7 +148,7 @@ module ActionView
137
148
  # <li><%= link_to 'Home', action: 'index' %></li>
138
149
  # <% end %>
139
150
  #
140
- # <%# Add some other content, or use a different template: %>
151
+ # <%# Add some other content, or use a different template: %>
141
152
  #
142
153
  # <% content_for :navigation, flush: true do %>
143
154
  # <li><%= link_to 'Login', action: 'login' %></li>
@@ -172,6 +183,8 @@ module ActionView
172
183
  # concatenate several times to the same buffer when rendering a given
173
184
  # template, you should use +content_for+, if not, use +provide+ to tell
174
185
  # the layout to stop looking for more contents.
186
+ #
187
+ # See ActionController::Streaming for more information.
175
188
  def provide(name, content = nil, &block)
176
189
  content = capture(&block) if block_given?
177
190
  result = @view_flow.append!(name, content) if content
@@ -179,6 +192,7 @@ module ActionView
179
192
  end
180
193
 
181
194
  # <tt>content_for?</tt> checks whether any content has been captured yet using <tt>content_for</tt>.
195
+ #
182
196
  # Useful to render parts of your layout differently based on what is in your views.
183
197
  #
184
198
  # <%# This is the layout %>