actionview 7.2.2.2 → 8.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +69 -78
  3. data/README.rdoc +1 -1
  4. data/lib/action_view/base.rb +6 -9
  5. data/lib/action_view/dependency_tracker/erb_tracker.rb +36 -27
  6. data/lib/action_view/dependency_tracker/ruby_tracker.rb +2 -19
  7. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  8. data/lib/action_view/dependency_tracker.rb +1 -0
  9. data/lib/action_view/digestor.rb +6 -2
  10. data/lib/action_view/gem_version.rb +4 -4
  11. data/lib/action_view/helpers/asset_tag_helper.rb +2 -2
  12. data/lib/action_view/helpers/atom_feed_helper.rb +1 -3
  13. data/lib/action_view/helpers/cache_helper.rb +10 -2
  14. data/lib/action_view/helpers/date_helper.rb +11 -4
  15. data/lib/action_view/helpers/form_helper.rb +104 -103
  16. data/lib/action_view/helpers/form_options_helper.rb +31 -25
  17. data/lib/action_view/helpers/form_tag_helper.rb +20 -17
  18. data/lib/action_view/helpers/output_safety_helper.rb +1 -2
  19. data/lib/action_view/helpers/rendering_helper.rb +160 -50
  20. data/lib/action_view/helpers/sanitize_helper.rb +6 -0
  21. data/lib/action_view/helpers/tag_helper.rb +26 -39
  22. data/lib/action_view/helpers/tags/base.rb +9 -9
  23. data/lib/action_view/helpers/tags/check_box.rb +2 -2
  24. data/lib/action_view/helpers/tags/collection_check_boxes.rb +4 -3
  25. data/lib/action_view/helpers/tags/collection_helpers.rb +2 -1
  26. data/lib/action_view/helpers/tags/file_field.rb +1 -1
  27. data/lib/action_view/helpers/tags/label.rb +3 -10
  28. data/lib/action_view/helpers/tags/radio_button.rb +1 -1
  29. data/lib/action_view/helpers/tags/select_renderer.rb +1 -1
  30. data/lib/action_view/helpers/tags/text_area.rb +1 -1
  31. data/lib/action_view/helpers/tags/text_field.rb +1 -1
  32. data/lib/action_view/helpers/text_helper.rb +10 -3
  33. data/lib/action_view/helpers/url_helper.rb +2 -4
  34. data/lib/action_view/layouts.rb +7 -7
  35. data/lib/action_view/record_identifier.rb +1 -1
  36. data/lib/action_view/render_parser/prism_render_parser.rb +13 -1
  37. data/lib/action_view/render_parser/ripper_render_parser.rb +10 -1
  38. data/lib/action_view/renderer/partial_renderer.rb +2 -2
  39. data/lib/action_view/renderer/streaming_template_renderer.rb +8 -2
  40. data/lib/action_view/renderer/template_renderer.rb +3 -3
  41. data/lib/action_view/rendering.rb +2 -3
  42. data/lib/action_view/template/error.rb +11 -0
  43. data/lib/action_view/template/handlers/erb/erubi.rb +1 -1
  44. data/lib/action_view/template/handlers/erb.rb +45 -37
  45. data/lib/action_view/template/raw_file.rb +4 -0
  46. data/lib/action_view/template/resolver.rb +0 -1
  47. data/lib/action_view/template.rb +1 -2
  48. data/lib/action_view/test_case.rb +0 -1
  49. data/lib/action_view.rb +1 -0
  50. metadata +12 -11
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "cgi"
3
+ require "cgi/escape"
4
+ require "cgi/util" if RUBY_VERSION < "3.5"
4
5
  require "erb"
5
6
  require "active_support/core_ext/string/output_safety"
6
7
  require "active_support/core_ext/array/extract_options"
@@ -496,7 +497,8 @@ module ActionView
496
497
  # <option value="France">France</option>
497
498
  # </optgroup>
498
499
  #
499
- # Parameters:
500
+ # ==== Parameters
501
+ #
500
502
  # * +grouped_options+ - Accepts a nested array or hash of strings. The first value serves as the
501
503
  # <tt><optgroup></tt> label while the second value must be an array of options. The second value can be a
502
504
  # nested array of text-value pairs. See <tt>options_for_select</tt> for more info.
@@ -507,7 +509,8 @@ module ActionView
507
509
  # which will have the +selected+ attribute set. Note: It is possible for this value to match multiple options
508
510
  # as you might have the same option in multiple groups. Each will then get <tt>selected="selected"</tt>.
509
511
  #
510
- # Options:
512
+ # ==== Options
513
+ #
511
514
  # * <tt>:prompt</tt> - set to true or a prompt string. When the select element doesn't have a value yet, this
512
515
  # prepends an option with a generic prompt - "Please select" - or the given prompt string.
513
516
  # * <tt>:divider</tt> - the divider for the options groups.
@@ -599,7 +602,8 @@ module ActionView
599
602
 
600
603
  # Returns a string of option tags for the days of the week.
601
604
  #
602
- # Options:
605
+ # ====Options
606
+ #
603
607
  # * <tt>:index_as_value</tt> - Defaults to false, set to true to use the indexes from
604
608
  # <tt>I18n.translate("date.day_names")</tt> as the values. By default, Sunday is always 0.
605
609
  # * <tt>:day_format</tt> - The I18n key of the array to use for the weekday options.
@@ -686,7 +690,7 @@ module ActionView
686
690
  # if a +User+ model has a +category_id+ field and in the form no category is selected, no +category_id+ parameter is sent. So,
687
691
  # any strong parameters idiom like:
688
692
  #
689
- # params.require(:user).permit(...)
693
+ # params.expect(user: [...])
690
694
  #
691
695
  # will raise an error since no <tt>{user: ...}</tt> will be present.
692
696
  #
@@ -723,7 +727,7 @@ module ActionView
723
727
  # end
724
728
  #
725
729
  # Sample usage (selecting the associated Author for an instance of Post, <tt>@post</tt>):
726
- # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial)
730
+ # collection_checkboxes(:post, :author_ids, Author.all, :id, :name_with_initial)
727
731
  #
728
732
  # If <tt>@post.author_ids</tt> is already <tt>[1]</tt>, this would return:
729
733
  # <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" checked="checked" />
@@ -736,8 +740,8 @@ module ActionView
736
740
  #
737
741
  # It is also possible to customize the way the elements will be shown by
738
742
  # giving a block to the method:
739
- # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
740
- # b.label { b.check_box }
743
+ # collection_checkboxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
744
+ # b.label { b.checkbox }
741
745
  # end
742
746
  #
743
747
  # The argument passed to the block is a special kind of builder for this
@@ -746,17 +750,17 @@ module ActionView
746
750
  # Using it, you can change the label and check box display order or even
747
751
  # use the label as wrapper, as in the example above.
748
752
  #
749
- # The builder methods <tt>label</tt> and <tt>check_box</tt> also accept
753
+ # The builder methods <tt>label</tt> and <tt>checkbox</tt> also accept
750
754
  # extra HTML options:
751
- # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
752
- # b.label(class: "check_box") { b.check_box(class: "check_box") }
755
+ # collection_checkboxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
756
+ # b.label(class: "checkbox") { b.checkbox(class: "checkbox") }
753
757
  # end
754
758
  #
755
759
  # There are also three special methods available: <tt>object</tt>, <tt>text</tt> and
756
760
  # <tt>value</tt>, which are the current item being rendered, its text and value methods,
757
761
  # respectively. You can use them like this:
758
- # collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
759
- # b.label(:"data-value" => b.value) { b.check_box + b.text }
762
+ # collection_checkboxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
763
+ # b.label(:"data-value" => b.value) { b.checkbox + b.text }
760
764
  # end
761
765
  #
762
766
  # ==== Gotcha
@@ -779,9 +783,10 @@ module ActionView
779
783
  #
780
784
  # In the rare case you don't want this hidden field, you can pass the
781
785
  # <tt>include_hidden: false</tt> option to the helper method.
782
- def collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
786
+ def collection_checkboxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
783
787
  Tags::CollectionCheckBoxes.new(object, method, self, collection, value_method, text_method, options, html_options).render(&block)
784
788
  end
789
+ alias_method :collection_check_boxes, :collection_checkboxes
785
790
 
786
791
  private
787
792
  def option_html_attributes(element)
@@ -839,7 +844,7 @@ module ActionView
839
844
  class FormBuilder
840
845
  # Wraps ActionView::Helpers::FormOptionsHelper#select for form builders:
841
846
  #
842
- # <%= form_for @post do |f| %>
847
+ # <%= form_with model: @post do |f| %>
843
848
  # <%= f.select :person_id, Person.all.collect { |p| [ p.name, p.id ] }, include_blank: true %>
844
849
  # <%= f.submit %>
845
850
  # <% end %>
@@ -851,7 +856,7 @@ module ActionView
851
856
 
852
857
  # Wraps ActionView::Helpers::FormOptionsHelper#collection_select for form builders:
853
858
  #
854
- # <%= form_for @post do |f| %>
859
+ # <%= form_with model: @post do |f| %>
855
860
  # <%= f.collection_select :person_id, Author.all, :id, :name_with_initial, prompt: true %>
856
861
  # <%= f.submit %>
857
862
  # <% end %>
@@ -863,7 +868,7 @@ module ActionView
863
868
 
864
869
  # Wraps ActionView::Helpers::FormOptionsHelper#grouped_collection_select for form builders:
865
870
  #
866
- # <%= form_for @city do |f| %>
871
+ # <%= form_with model: @city do |f| %>
867
872
  # <%= f.grouped_collection_select :country_id, @continents, :countries, :name, :id, :name %>
868
873
  # <%= f.submit %>
869
874
  # <% end %>
@@ -875,7 +880,7 @@ module ActionView
875
880
 
876
881
  # Wraps ActionView::Helpers::FormOptionsHelper#time_zone_select for form builders:
877
882
  #
878
- # <%= form_for @user do |f| %>
883
+ # <%= form_with model: @user do |f| %>
879
884
  # <%= f.time_zone_select :time_zone, nil, include_blank: true %>
880
885
  # <%= f.submit %>
881
886
  # <% end %>
@@ -887,7 +892,7 @@ module ActionView
887
892
 
888
893
  # Wraps ActionView::Helpers::FormOptionsHelper#weekday_select for form builders:
889
894
  #
890
- # <%= form_for @user do |f| %>
895
+ # <%= form_with model: @user do |f| %>
891
896
  # <%= f.weekday_select :weekday, include_blank: true %>
892
897
  # <%= f.submit %>
893
898
  # <% end %>
@@ -897,21 +902,22 @@ module ActionView
897
902
  @template.weekday_select(@object_name, method, objectify_options(options), @default_html_options.merge(html_options))
898
903
  end
899
904
 
900
- # Wraps ActionView::Helpers::FormOptionsHelper#collection_check_boxes for form builders:
905
+ # Wraps ActionView::Helpers::FormOptionsHelper#collection_checkboxes for form builders:
901
906
  #
902
- # <%= form_for @post do |f| %>
903
- # <%= f.collection_check_boxes :author_ids, Author.all, :id, :name_with_initial %>
907
+ # <%= form_with model: @post do |f| %>
908
+ # <%= f.collection_checkboxes :author_ids, Author.all, :id, :name_with_initial %>
904
909
  # <%= f.submit %>
905
910
  # <% end %>
906
911
  #
907
912
  # Please refer to the documentation of the base helper for details.
908
- def collection_check_boxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
909
- @template.collection_check_boxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_html_options.merge(html_options), &block)
913
+ def collection_checkboxes(method, collection, value_method, text_method, options = {}, html_options = {}, &block)
914
+ @template.collection_checkboxes(@object_name, method, collection, value_method, text_method, objectify_options(options), @default_html_options.merge(html_options), &block)
910
915
  end
916
+ alias_method :collection_check_boxes, :collection_checkboxes
911
917
 
912
918
  # Wraps ActionView::Helpers::FormOptionsHelper#collection_radio_buttons for form builders:
913
919
  #
914
- # <%= form_for @post do |f| %>
920
+ # <%= form_with model: @post do |f| %>
915
921
  # <%= f.collection_radio_buttons :author_id, Author.all, :id, :name_with_initial %>
916
922
  # <%= f.submit %>
917
923
  # <% end %>
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "cgi"
3
+ require "cgi/escape"
4
+ require "cgi/util" if RUBY_VERSION < "3.5"
4
5
  require "action_view/helpers/content_exfiltration_prevention_helper"
5
6
  require "action_view/helpers/url_helper"
6
7
  require "action_view/helpers/text_helper"
@@ -393,24 +394,24 @@ module ActionView
393
394
  # * Any other key creates standard HTML attributes for the tag.
394
395
  #
395
396
  # ==== Examples
396
- # text_area_tag 'post'
397
+ # textarea_tag 'post'
397
398
  # # => <textarea id="post" name="post"></textarea>
398
399
  #
399
- # text_area_tag 'bio', @user.bio
400
+ # textarea_tag 'bio', @user.bio
400
401
  # # => <textarea id="bio" name="bio">This is my biography.</textarea>
401
402
  #
402
- # text_area_tag 'body', nil, rows: 10, cols: 25
403
+ # textarea_tag 'body', nil, rows: 10, cols: 25
403
404
  # # => <textarea cols="25" id="body" name="body" rows="10"></textarea>
404
405
  #
405
- # text_area_tag 'body', nil, size: "25x10"
406
+ # textarea_tag 'body', nil, size: "25x10"
406
407
  # # => <textarea name="body" id="body" cols="25" rows="10"></textarea>
407
408
  #
408
- # text_area_tag 'description', "Description goes here.", disabled: true
409
+ # textarea_tag 'description', "Description goes here.", disabled: true
409
410
  # # => <textarea disabled="disabled" id="description" name="description">Description goes here.</textarea>
410
411
  #
411
- # text_area_tag 'comment', nil, class: 'comment_input'
412
+ # textarea_tag 'comment', nil, class: 'comment_input'
412
413
  # # => <textarea class="comment_input" id="comment" name="comment"></textarea>
413
- def text_area_tag(name, content = nil, options = {})
414
+ def textarea_tag(name, content = nil, options = {})
414
415
  options = options.stringify_keys
415
416
 
416
417
  if size = options.delete("size")
@@ -422,12 +423,13 @@ module ActionView
422
423
 
423
424
  content_tag :textarea, content.to_s.html_safe, { "name" => name, "id" => sanitize_to_id(name) }.update(options)
424
425
  end
426
+ alias_method :text_area_tag, :textarea_tag
425
427
 
426
428
  ##
427
429
  # :call-seq:
428
- # check_box_tag(name, options = {})
429
- # check_box_tag(name, value, options = {})
430
- # check_box_tag(name, value, checked, options = {})
430
+ # checkbox_tag(name, options = {})
431
+ # checkbox_tag(name, value, options = {})
432
+ # checkbox_tag(name, value, checked, options = {})
431
433
  #
432
434
  # Creates a check box form input tag.
433
435
  #
@@ -438,21 +440,21 @@ module ActionView
438
440
  # * Any other key creates standard HTML options for the tag.
439
441
  #
440
442
  # ==== Examples
441
- # check_box_tag 'accept'
443
+ # checkbox_tag 'accept'
442
444
  # # => <input id="accept" name="accept" type="checkbox" value="1" />
443
445
  #
444
- # check_box_tag 'rock', 'rock music'
446
+ # checkbox_tag 'rock', 'rock music'
445
447
  # # => <input id="rock" name="rock" type="checkbox" value="rock music" />
446
448
  #
447
- # check_box_tag 'receive_email', 'yes', true
449
+ # checkbox_tag 'receive_email', 'yes', true
448
450
  # # => <input checked="checked" id="receive_email" name="receive_email" type="checkbox" value="yes" />
449
451
  #
450
- # check_box_tag 'tos', 'yes', false, class: 'accept_tos'
452
+ # checkbox_tag 'tos', 'yes', false, class: 'accept_tos'
451
453
  # # => <input class="accept_tos" id="tos" name="tos" type="checkbox" value="yes" />
452
454
  #
453
- # check_box_tag 'eula', 'accepted', false, disabled: true
455
+ # checkbox_tag 'eula', 'accepted', false, disabled: true
454
456
  # # => <input disabled="disabled" id="eula" name="eula" type="checkbox" value="accepted" />
455
- def check_box_tag(name, *args)
457
+ def checkbox_tag(name, *args)
456
458
  if args.length >= 4
457
459
  raise ArgumentError, "wrong number of arguments (given #{args.length + 1}, expected 1..4)"
458
460
  end
@@ -462,6 +464,7 @@ module ActionView
462
464
  html_options["checked"] = "checked" if checked
463
465
  tag :input, html_options
464
466
  end
467
+ alias_method :check_box_tag, :checkbox_tag
465
468
 
466
469
  ##
467
470
  # :call-seq:
@@ -38,8 +38,7 @@ module ActionView # :nodoc:
38
38
 
39
39
  # Converts the array to a comma-separated sentence where the last element is
40
40
  # joined by the connector word. This is the html_safe-aware version of
41
- # ActiveSupport's {Array#to_sentence}[https://api.rubyonrails.org/classes/Array.html#method-i-to_sentence].
42
- #
41
+ # ActiveSupport's Array#to_sentence.
43
42
  def to_sentence(array, options = {})
44
43
  options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
45
44
 
@@ -1,32 +1,140 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ # :markup: markdown
4
+
3
5
  module ActionView
4
6
  module Helpers # :nodoc:
5
- # = Action View \Rendering \Helpers
7
+ # # Action View Rendering Helpers
6
8
  #
7
- # Implements methods that allow rendering from a view context.
8
- # In order to use this module, all you need is to implement
9
- # view_renderer that returns an ActionView::Renderer object.
9
+ # Implements methods that allow rendering from a view context. In order to use
10
+ # this module, all you need is to implement view_renderer that returns an
11
+ # ActionView::Renderer object.
10
12
  module RenderingHelper
11
- # Returns the result of a render that's dictated by the options hash. The primary options are:
13
+ # Renders a template and returns the result.
14
+ #
15
+ # Pass the template to render as the first argument. This is shorthand
16
+ # syntax for partial rendering, so the template filename should be
17
+ # prefixed with an underscore. The partial renderer looks for the partial
18
+ # template in the directory of the calling template first.
19
+ #
20
+ # <% # app/views/posts/new.html.erb %>
21
+ # <%= render "form" %>
22
+ # # => renders app/views/posts/_form.html.erb
23
+ #
24
+ # Use the complete view path to render a partial from another directory.
25
+ #
26
+ # <% # app/views/posts/show.html.erb %>
27
+ # <%= render "comments/form" %>
28
+ # # => renders app/views/comments/_form.html.erb
29
+ #
30
+ # Without the rendering mode, the second argument can be a Hash of local
31
+ # variable assignments for the template.
32
+ #
33
+ # <% # app/views/posts/new.html.erb %>
34
+ # <%= render "form", post: Post.new %>
35
+ # # => renders app/views/posts/_form.html.erb
36
+ #
37
+ # If the first argument responds to `render_in`, the template will be rendered
38
+ # by calling `render_in` with the current view context.
39
+ #
40
+ # class Greeting
41
+ # def render_in(view_context)
42
+ # view_context.render html: "<h1>Hello, World</h1>"
43
+ # end
44
+ #
45
+ # def format
46
+ # :html
47
+ # end
48
+ # end
49
+ #
50
+ # <%= render Greeting.new %>
51
+ # # => "<h1>Hello, World</h1>"
52
+ #
53
+ # #### Rendering Mode
54
+ #
55
+ # Pass the rendering mode as first argument to override it.
56
+ #
57
+ # `:partial`
58
+ # : See ActionView::PartialRenderer for details.
59
+ #
60
+ # <%= render partial: "form", locals: { post: Post.new } %>
61
+ # # => renders app/views/posts/_form.html.erb
62
+ #
63
+ # `:file`
64
+ # : Renders the contents of a file. This option should **not** be used with
65
+ # unsanitized user input.
66
+ #
67
+ # <%= render file: "/path/to/some/file" %>
68
+ # # => renders /path/to/some/file
69
+ #
70
+ # `:inline`
71
+ # : Renders an ERB template string.
72
+ #
73
+ # <% name = "World" %>
74
+ # <%= render inline: "<h1>Hello, <%= name %>!</h1>" %>
75
+ # # => renders "<h1>Hello, World!</h1>"
76
+ #
77
+ # `:body`
78
+ # : Renders the provided text, and sets the format as `:text`.
79
+ #
80
+ # <%= render body: "Hello, World!" %>
81
+ # # => renders "Hello, World!"
82
+ #
83
+ # `:plain`
84
+ # : Renders the provided text, and sets the format as `:text`.
85
+ #
86
+ # <%= render plain: "Hello, World!" %>
87
+ # # => renders "Hello, World!"
88
+ #
89
+ # `:html`
90
+ # : Renders the provided HTML string, and sets the format as
91
+ # `:html`. If the string is not `html_safe?`, performs HTML escaping on
92
+ # the string before rendering.
93
+ #
94
+ # <%= render html: "<h1>Hello, World!</h1>".html_safe %>
95
+ # # => renders "<h1>Hello, World!</h1>"
96
+ #
97
+ # <%= render html: "<h1>Hello, World!</h1>" %>
98
+ # # => renders "&lt;h1&gt;Hello, World!&lt;/h1&gt;"
99
+ #
100
+ # `:renderable`
101
+ # : Renders the provided object by calling `render_in` with the current view
102
+ # context. The format is determined by calling `format` on the
103
+ # renderable if it responds to `format`, falling back to `:html` by
104
+ # default.
105
+ #
106
+ # <%= render renderable: Greeting.new %>
107
+ # # => renders "<h1>Hello, World</h1>"
108
+ #
109
+ #
110
+ # #### Options
111
+ #
112
+ # `:locals`
113
+ # : Hash of local variable assignments for the template.
114
+ #
115
+ # <%= render inline: "<h1>Hello, <%= name %>!</h1>", locals: { name: "World" } %>
116
+ # # => renders "<h1>Hello, World!</h1>"
117
+ #
118
+ # `:formats`
119
+ # : Override the current format to render a template for a different format.
120
+ #
121
+ # <% # app/views/posts/show.html.erb %>
122
+ # <%= render template: "posts/content", formats: [:text] %>
123
+ # # => renders app/views/posts/content.text.erb
12
124
  #
13
- # * <tt>:partial</tt> - See ActionView::PartialRenderer.
14
- # * <tt>:file</tt> - Renders an explicit template file (this used to be the old default), add +:locals+ to pass in those.
15
- # * <tt>:inline</tt> - Renders an inline template similar to how it's done in the controller.
16
- # * <tt>:plain</tt> - Renders the text passed in out. Setting the content
17
- # type as <tt>text/plain</tt>.
18
- # * <tt>:html</tt> - Renders the HTML safe string passed in out, otherwise
19
- # performs HTML escape on the string first. Setting the content type as
20
- # <tt>text/html</tt>.
21
- # * <tt>:body</tt> - Renders the text passed in, and inherits the content
22
- # type of <tt>text/plain</tt> from ActionDispatch::Response object.
125
+ # `:variants`
126
+ # : Render a template for a different variant.
23
127
  #
24
- # If no <tt>options</tt> hash is passed or if <tt>:update</tt> is specified, then:
128
+ # <% # app/views/posts/show.html.erb %>
129
+ # <%= render template: "posts/content", variants: [:tablet] %>
130
+ # # => renders app/views/posts/content.html+tablet.erb
25
131
  #
26
- # If an object responding to +render_in+ is passed, +render_in+ is called on the object,
27
- # passing in the current view context.
132
+ # `:handlers`
133
+ # : Render a template for a different handler.
28
134
  #
29
- # Otherwise, a partial is rendered using the second parameter as the locals hash.
135
+ # <% # app/views/posts/show.html.erb %>
136
+ # <%= render template: "posts/content", handlers: [:builder] %>
137
+ # # => renders app/views/posts/content.html.builder
30
138
  def render(options = {}, locals = {}, &block)
31
139
  case options
32
140
  when Hash
@@ -47,52 +155,54 @@ module ActionView
47
155
  end
48
156
 
49
157
  # Overrides _layout_for in the context object so it supports the case a block is
50
- # passed to a partial. Returns the contents that are yielded to a layout, given a
51
- # name or a block.
158
+ # passed to a partial. Returns the contents that are yielded to a layout, given
159
+ # a name or a block.
52
160
  #
53
- # You can think of a layout as a method that is called with a block. If the user calls
54
- # <tt>yield :some_name</tt>, the block, by default, returns <tt>content_for(:some_name)</tt>.
55
- # If the user calls simply +yield+, the default block returns <tt>content_for(:layout)</tt>.
161
+ # You can think of a layout as a method that is called with a block. If the user
162
+ # calls `yield :some_name`, the block, by default, returns
163
+ # `content_for(:some_name)`. If the user calls simply `yield`, the default block
164
+ # returns `content_for(:layout)`.
56
165
  #
57
166
  # The user can override this default by passing a block to the layout:
58
167
  #
59
- # # The template
60
- # <%= render layout: "my_layout" do %>
61
- # Content
62
- # <% end %>
168
+ # # The template
169
+ # <%= render layout: "my_layout" do %>
170
+ # Content
171
+ # <% end %>
63
172
  #
64
- # # The layout
65
- # <html>
66
- # <%= yield %>
67
- # </html>
173
+ # # The layout
174
+ # <html>
175
+ # <%= yield %>
176
+ # </html>
68
177
  #
69
- # In this case, instead of the default block, which would return <tt>content_for(:layout)</tt>,
70
- # this method returns the block that was passed in to <tt>render :layout</tt>, and the response
178
+ # In this case, instead of the default block, which would return `content_for(:layout)`,
179
+ # this method returns the block that was passed in to `render :layout`, and the response
71
180
  # would be
72
181
  #
73
- # <html>
74
- # Content
75
- # </html>
182
+ # <html>
183
+ # Content
184
+ # </html>
76
185
  #
77
- # Finally, the block can take block arguments, which can be passed in by +yield+:
186
+ # Finally, the block can take block arguments, which can be passed in by
187
+ # `yield`:
78
188
  #
79
- # # The template
80
- # <%= render layout: "my_layout" do |customer| %>
81
- # Hello <%= customer.name %>
82
- # <% end %>
189
+ # # The template
190
+ # <%= render layout: "my_layout" do |customer| %>
191
+ # Hello <%= customer.name %>
192
+ # <% end %>
83
193
  #
84
- # # The layout
85
- # <html>
86
- # <%= yield Struct.new(:name).new("David") %>
87
- # </html>
194
+ # # The layout
195
+ # <html>
196
+ # <%= yield Struct.new(:name).new("David") %>
197
+ # </html>
88
198
  #
89
- # In this case, the layout would receive the block passed into <tt>render :layout</tt>,
199
+ # In this case, the layout would receive the block passed into `render :layout`,
90
200
  # and the struct specified would be passed into the block as an argument. The result
91
201
  # would be
92
202
  #
93
- # <html>
94
- # Hello David
95
- # </html>
203
+ # <html>
204
+ # Hello David
205
+ # </html>
96
206
  #
97
207
  def _layout_for(*args, &block)
98
208
  name = args.first
@@ -24,6 +24,12 @@ module ActionView
24
24
  #
25
25
  # Custom sanitization rules can also be provided.
26
26
  #
27
+ # <b>Warning</b>: Adding disallowed tags or attributes to the allowlists may introduce
28
+ # vulnerabilities into your application. Please rely on the default allowlists whenever
29
+ # possible, because they are curated to maintain security and safety. If you think that the
30
+ # default allowlists should be expanded, please {open an issue on the rails-html-sanitizer
31
+ # project}[https://github.com/rails/rails-html-sanitizer/issues].
32
+ #
27
33
  # Please note that sanitizing user-provided text does not guarantee that the
28
34
  # resulting markup is valid or even well-formed.
29
35
  #
@@ -4,7 +4,6 @@ require "active_support/code_generator"
4
4
  require "active_support/core_ext/enumerable"
5
5
  require "active_support/core_ext/string/output_safety"
6
6
  require "active_support/core_ext/string/inflections"
7
- require "set"
8
7
  require "action_view/helpers/capture_helper"
9
8
  require "action_view/helpers/output_safety_helper"
10
9
 
@@ -48,48 +47,36 @@ module ActionView
48
47
  include CaptureHelper
49
48
  include OutputSafetyHelper
50
49
 
51
- def self.define_element(name, code_generator:, method_name: name.to_s.underscore)
52
- code_generator.define_cached_method(method_name, namespace: :tag_builder) do |batch|
53
- batch.push(<<~RUBY) unless instance_methods.include?(method_name.to_sym)
54
- def #{method_name}(content = nil, escape: true, **options, &block)
55
- tag_string("#{name}", content, options, escape: escape, &block)
56
- end
57
- RUBY
50
+ def self.define_element(name, code_generator:, method_name: name)
51
+ return if method_defined?(name)
52
+
53
+ code_generator.class_eval do |batch|
54
+ batch << "\n" <<
55
+ "def #{method_name}(content = nil, escape: true, **options, &block)" <<
56
+ " tag_string(#{name.inspect}, content, options, escape: escape, &block)" <<
57
+ "end"
58
58
  end
59
59
  end
60
60
 
61
- def self.define_void_element(name, code_generator:, method_name: name.to_s.underscore)
62
- code_generator.define_cached_method(method_name, namespace: :tag_builder) do |batch|
63
- batch.push(<<~RUBY)
64
- def #{method_name}(content = nil, escape: true, **options, &block)
65
- if content || block
66
- ActionView.deprecator.warn <<~TEXT
67
- Putting content inside a void element (#{name}) is invalid
68
- according to the HTML5 spec, and so it is being deprecated
69
- without replacement. In Rails 8.0, passing content as a
70
- positional argument will raise, and using a block will have
71
- no effect.
72
- TEXT
73
- tag_string("#{name}", content, options, escape: escape, &block)
74
- else
75
- self_closing_tag_string("#{name}", options, escape, ">")
76
- end
77
- end
78
- RUBY
61
+ def self.define_void_element(name, code_generator:, method_name: name)
62
+ code_generator.class_eval do |batch|
63
+ batch << "\n" <<
64
+ "def #{method_name}(escape: true, **options, &block)" <<
65
+ " self_closing_tag_string(#{name.inspect}, options, escape, '>')" <<
66
+ "end"
79
67
  end
80
68
  end
81
69
 
82
- def self.define_self_closing_element(name, code_generator:, method_name: name.to_s.underscore)
83
- code_generator.define_cached_method(method_name, namespace: :tag_builder) do |batch|
84
- batch.push(<<~RUBY)
85
- def #{method_name}(content = nil, escape: true, **options, &block)
86
- if content || block
87
- tag_string("#{name}", content, options, escape: escape, &block)
88
- else
89
- self_closing_tag_string("#{name}", options, escape)
90
- end
91
- end
92
- RUBY
70
+ def self.define_self_closing_element(name, code_generator:, method_name: name)
71
+ code_generator.class_eval do |batch|
72
+ batch << "\n" <<
73
+ "def #{method_name}(content = nil, escape: true, **options, &block)" <<
74
+ " if content || block" <<
75
+ " tag_string(#{name.inspect}, content, options, escape: escape, &block)" <<
76
+ " else" <<
77
+ " self_closing_tag_string(#{name.inspect}, options, escape)" <<
78
+ " end" <<
79
+ "end"
93
80
  end
94
81
  end
95
82
 
@@ -110,8 +97,8 @@ module ActionView
110
97
  define_void_element :wbr, code_generator: code_generator
111
98
 
112
99
  define_self_closing_element :animate, code_generator: code_generator
113
- define_self_closing_element :animateMotion, code_generator: code_generator
114
- define_self_closing_element :animateTransform, code_generator: code_generator
100
+ define_self_closing_element :animateMotion, code_generator: code_generator, method_name: :animate_motion
101
+ define_self_closing_element :animateTransform, code_generator: code_generator, method_name: :animate_transform
115
102
  define_self_closing_element :circle, code_generator: code_generator
116
103
  define_self_closing_element :ellipse, code_generator: code_generator
117
104
  define_self_closing_element :line, code_generator: code_generator