omg-actionview 8.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +25 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.rdoc +40 -0
  5. data/app/assets/javascripts/rails-ujs.esm.js +686 -0
  6. data/app/assets/javascripts/rails-ujs.js +630 -0
  7. data/lib/action_view/base.rb +316 -0
  8. data/lib/action_view/buffers.rb +165 -0
  9. data/lib/action_view/cache_expiry.rb +69 -0
  10. data/lib/action_view/context.rb +32 -0
  11. data/lib/action_view/dependency_tracker/erb_tracker.rb +159 -0
  12. data/lib/action_view/dependency_tracker/ruby_tracker.rb +43 -0
  13. data/lib/action_view/dependency_tracker/wildcard_resolver.rb +32 -0
  14. data/lib/action_view/dependency_tracker.rb +41 -0
  15. data/lib/action_view/deprecator.rb +7 -0
  16. data/lib/action_view/digestor.rb +130 -0
  17. data/lib/action_view/flows.rb +75 -0
  18. data/lib/action_view/gem_version.rb +17 -0
  19. data/lib/action_view/helpers/active_model_helper.rb +54 -0
  20. data/lib/action_view/helpers/asset_tag_helper.rb +680 -0
  21. data/lib/action_view/helpers/asset_url_helper.rb +473 -0
  22. data/lib/action_view/helpers/atom_feed_helper.rb +205 -0
  23. data/lib/action_view/helpers/cache_helper.rb +315 -0
  24. data/lib/action_view/helpers/capture_helper.rb +236 -0
  25. data/lib/action_view/helpers/content_exfiltration_prevention_helper.rb +70 -0
  26. data/lib/action_view/helpers/controller_helper.rb +42 -0
  27. data/lib/action_view/helpers/csp_helper.rb +26 -0
  28. data/lib/action_view/helpers/csrf_helper.rb +35 -0
  29. data/lib/action_view/helpers/date_helper.rb +1266 -0
  30. data/lib/action_view/helpers/debug_helper.rb +38 -0
  31. data/lib/action_view/helpers/form_helper.rb +2765 -0
  32. data/lib/action_view/helpers/form_options_helper.rb +927 -0
  33. data/lib/action_view/helpers/form_tag_helper.rb +1088 -0
  34. data/lib/action_view/helpers/javascript_helper.rb +96 -0
  35. data/lib/action_view/helpers/number_helper.rb +165 -0
  36. data/lib/action_view/helpers/output_safety_helper.rb +70 -0
  37. data/lib/action_view/helpers/rendering_helper.rb +218 -0
  38. data/lib/action_view/helpers/sanitize_helper.rb +201 -0
  39. data/lib/action_view/helpers/tag_helper.rb +621 -0
  40. data/lib/action_view/helpers/tags/base.rb +138 -0
  41. data/lib/action_view/helpers/tags/check_box.rb +65 -0
  42. data/lib/action_view/helpers/tags/checkable.rb +18 -0
  43. data/lib/action_view/helpers/tags/collection_check_boxes.rb +37 -0
  44. data/lib/action_view/helpers/tags/collection_helpers.rb +118 -0
  45. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +31 -0
  46. data/lib/action_view/helpers/tags/collection_select.rb +33 -0
  47. data/lib/action_view/helpers/tags/color_field.rb +26 -0
  48. data/lib/action_view/helpers/tags/date_field.rb +14 -0
  49. data/lib/action_view/helpers/tags/date_select.rb +75 -0
  50. data/lib/action_view/helpers/tags/datetime_field.rb +39 -0
  51. data/lib/action_view/helpers/tags/datetime_local_field.rb +29 -0
  52. data/lib/action_view/helpers/tags/datetime_select.rb +10 -0
  53. data/lib/action_view/helpers/tags/email_field.rb +10 -0
  54. data/lib/action_view/helpers/tags/file_field.rb +26 -0
  55. data/lib/action_view/helpers/tags/grouped_collection_select.rb +34 -0
  56. data/lib/action_view/helpers/tags/hidden_field.rb +14 -0
  57. data/lib/action_view/helpers/tags/label.rb +84 -0
  58. data/lib/action_view/helpers/tags/month_field.rb +14 -0
  59. data/lib/action_view/helpers/tags/number_field.rb +20 -0
  60. data/lib/action_view/helpers/tags/password_field.rb +14 -0
  61. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  62. data/lib/action_view/helpers/tags/radio_button.rb +32 -0
  63. data/lib/action_view/helpers/tags/range_field.rb +10 -0
  64. data/lib/action_view/helpers/tags/search_field.rb +27 -0
  65. data/lib/action_view/helpers/tags/select.rb +45 -0
  66. data/lib/action_view/helpers/tags/select_renderer.rb +56 -0
  67. data/lib/action_view/helpers/tags/tel_field.rb +10 -0
  68. data/lib/action_view/helpers/tags/text_area.rb +24 -0
  69. data/lib/action_view/helpers/tags/text_field.rb +33 -0
  70. data/lib/action_view/helpers/tags/time_field.rb +23 -0
  71. data/lib/action_view/helpers/tags/time_select.rb +10 -0
  72. data/lib/action_view/helpers/tags/time_zone_select.rb +25 -0
  73. data/lib/action_view/helpers/tags/translator.rb +39 -0
  74. data/lib/action_view/helpers/tags/url_field.rb +10 -0
  75. data/lib/action_view/helpers/tags/week_field.rb +14 -0
  76. data/lib/action_view/helpers/tags/weekday_select.rb +31 -0
  77. data/lib/action_view/helpers/tags.rb +47 -0
  78. data/lib/action_view/helpers/text_helper.rb +568 -0
  79. data/lib/action_view/helpers/translation_helper.rb +161 -0
  80. data/lib/action_view/helpers/url_helper.rb +812 -0
  81. data/lib/action_view/helpers.rb +68 -0
  82. data/lib/action_view/layouts.rb +434 -0
  83. data/lib/action_view/locale/en.yml +56 -0
  84. data/lib/action_view/log_subscriber.rb +132 -0
  85. data/lib/action_view/lookup_context.rb +299 -0
  86. data/lib/action_view/model_naming.rb +14 -0
  87. data/lib/action_view/path_registry.rb +57 -0
  88. data/lib/action_view/path_set.rb +84 -0
  89. data/lib/action_view/railtie.rb +132 -0
  90. data/lib/action_view/record_identifier.rb +118 -0
  91. data/lib/action_view/render_parser/prism_render_parser.rb +139 -0
  92. data/lib/action_view/render_parser/ripper_render_parser.rb +350 -0
  93. data/lib/action_view/render_parser.rb +40 -0
  94. data/lib/action_view/renderer/abstract_renderer.rb +186 -0
  95. data/lib/action_view/renderer/collection_renderer.rb +204 -0
  96. data/lib/action_view/renderer/object_renderer.rb +34 -0
  97. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +120 -0
  98. data/lib/action_view/renderer/partial_renderer.rb +267 -0
  99. data/lib/action_view/renderer/renderer.rb +107 -0
  100. data/lib/action_view/renderer/streaming_template_renderer.rb +107 -0
  101. data/lib/action_view/renderer/template_renderer.rb +115 -0
  102. data/lib/action_view/rendering.rb +190 -0
  103. data/lib/action_view/routing_url_for.rb +149 -0
  104. data/lib/action_view/tasks/cache_digests.rake +25 -0
  105. data/lib/action_view/template/error.rb +264 -0
  106. data/lib/action_view/template/handlers/builder.rb +25 -0
  107. data/lib/action_view/template/handlers/erb/erubi.rb +85 -0
  108. data/lib/action_view/template/handlers/erb.rb +157 -0
  109. data/lib/action_view/template/handlers/html.rb +11 -0
  110. data/lib/action_view/template/handlers/raw.rb +11 -0
  111. data/lib/action_view/template/handlers.rb +66 -0
  112. data/lib/action_view/template/html.rb +33 -0
  113. data/lib/action_view/template/inline.rb +22 -0
  114. data/lib/action_view/template/raw_file.rb +25 -0
  115. data/lib/action_view/template/renderable.rb +30 -0
  116. data/lib/action_view/template/resolver.rb +212 -0
  117. data/lib/action_view/template/sources/file.rb +17 -0
  118. data/lib/action_view/template/sources.rb +13 -0
  119. data/lib/action_view/template/text.rb +32 -0
  120. data/lib/action_view/template/types.rb +50 -0
  121. data/lib/action_view/template.rb +580 -0
  122. data/lib/action_view/template_details.rb +66 -0
  123. data/lib/action_view/template_path.rb +66 -0
  124. data/lib/action_view/test_case.rb +449 -0
  125. data/lib/action_view/testing/resolvers.rb +44 -0
  126. data/lib/action_view/unbound_template.rb +67 -0
  127. data/lib/action_view/version.rb +10 -0
  128. data/lib/action_view/view_paths.rb +117 -0
  129. data/lib/action_view.rb +104 -0
  130. metadata +275 -0
@@ -0,0 +1,96 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActionView
4
+ module Helpers # :nodoc:
5
+ # = Action View JavaScript \Helpers
6
+ module JavaScriptHelper
7
+ JS_ESCAPE_MAP = {
8
+ "\\" => "\\\\",
9
+ "</" => '<\/',
10
+ "\r\n" => '\n',
11
+ "\n" => '\n',
12
+ "\r" => '\n',
13
+ '"' => '\\"',
14
+ "'" => "\\'",
15
+ "`" => "\\`",
16
+ "$" => "\\$"
17
+ }
18
+
19
+ JS_ESCAPE_MAP[(+"\342\200\250").force_encoding(Encoding::UTF_8).encode!] = "&#x2028;"
20
+ JS_ESCAPE_MAP[(+"\342\200\251").force_encoding(Encoding::UTF_8).encode!] = "&#x2029;"
21
+
22
+ # Escapes carriage returns and single and double quotes for JavaScript segments.
23
+ #
24
+ # Also available through the alias j(). This is particularly helpful in JavaScript
25
+ # responses, like:
26
+ #
27
+ # $('some_element').replaceWith('<%= j render 'some/element_template' %>');
28
+ def escape_javascript(javascript)
29
+ javascript = javascript.to_s
30
+ if javascript.empty?
31
+ result = ""
32
+ else
33
+ result = javascript.gsub(/(\\|<\/|\r\n|\342\200\250|\342\200\251|[\n\r"']|[`]|[$])/u, JS_ESCAPE_MAP)
34
+ end
35
+ javascript.html_safe? ? result.html_safe : result
36
+ end
37
+
38
+ alias_method :j, :escape_javascript
39
+
40
+ # Returns a JavaScript tag with the +content+ inside. Example:
41
+ # javascript_tag "alert('All is good')"
42
+ #
43
+ # Returns:
44
+ # <script>
45
+ # //<![CDATA[
46
+ # alert('All is good')
47
+ # //]]>
48
+ # </script>
49
+ #
50
+ # +html_options+ may be a hash of attributes for the <tt>\<script></tt>
51
+ # tag.
52
+ #
53
+ # javascript_tag "alert('All is good')", type: 'application/javascript'
54
+ #
55
+ # Returns:
56
+ # <script type="application/javascript">
57
+ # //<![CDATA[
58
+ # alert('All is good')
59
+ # //]]>
60
+ # </script>
61
+ #
62
+ # Instead of passing the content as an argument, you can also use a block
63
+ # in which case, you pass your +html_options+ as the first parameter.
64
+ #
65
+ # <%= javascript_tag type: 'application/javascript' do -%>
66
+ # alert('All is good')
67
+ # <% end -%>
68
+ #
69
+ # If you have a content security policy enabled then you can add an automatic
70
+ # nonce value by passing <tt>nonce: true</tt> as part of +html_options+. Example:
71
+ #
72
+ # <%= javascript_tag nonce: true do -%>
73
+ # alert('All is good')
74
+ # <% end -%>
75
+ def javascript_tag(content_or_options_with_block = nil, html_options = {}, &block)
76
+ content =
77
+ if block_given?
78
+ html_options = content_or_options_with_block if content_or_options_with_block.is_a?(Hash)
79
+ capture(&block)
80
+ else
81
+ content_or_options_with_block
82
+ end
83
+
84
+ if html_options[:nonce] == true
85
+ html_options[:nonce] = content_security_policy_nonce
86
+ end
87
+
88
+ content_tag("script", javascript_cdata_section(content), html_options)
89
+ end
90
+
91
+ def javascript_cdata_section(content) # :nodoc:
92
+ "\n//#{cdata_section("\n#{content}\n//")}\n".html_safe
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/keys"
4
+ require "active_support/core_ext/string/output_safety"
5
+ require "active_support/number_helper"
6
+
7
+ module ActionView
8
+ module Helpers # :nodoc:
9
+ # = Action View Number \Helpers
10
+ #
11
+ # Provides methods for converting numbers into formatted strings.
12
+ # Methods are provided for phone numbers, currency, percentage,
13
+ # precision, positional notation, file size, and pretty printing.
14
+ #
15
+ # Most methods expect a +number+ argument, and will return it
16
+ # unchanged if can't be converted into a valid number.
17
+ module NumberHelper
18
+ # Raised when argument +number+ param given to the helpers is invalid and
19
+ # the option +:raise+ is set to +true+.
20
+ class InvalidNumberError < StandardError
21
+ attr_accessor :number
22
+ def initialize(number)
23
+ @number = number
24
+ end
25
+ end
26
+
27
+ # Delegates to ActiveSupport::NumberHelper#number_to_phone.
28
+ #
29
+ # Additionally, supports a +:raise+ option that will cause
30
+ # InvalidNumberError to be raised if +number+ is not a valid number:
31
+ #
32
+ # number_to_phone("12x34") # => "12x34"
33
+ # number_to_phone("12x34", raise: true) # => InvalidNumberError
34
+ #
35
+ def number_to_phone(number, options = {})
36
+ return unless number
37
+ options = options.symbolize_keys
38
+
39
+ parse_float(number, true) if options.delete(:raise)
40
+ ERB::Util.html_escape(ActiveSupport::NumberHelper.number_to_phone(number, options))
41
+ end
42
+
43
+ # Delegates to ActiveSupport::NumberHelper#number_to_currency.
44
+ #
45
+ # Additionally, supports a +:raise+ option that will cause
46
+ # InvalidNumberError to be raised if +number+ is not a valid number:
47
+ #
48
+ # number_to_currency("12x34") # => "$12x34"
49
+ # number_to_currency("12x34", raise: true) # => InvalidNumberError
50
+ #
51
+ def number_to_currency(number, options = {})
52
+ delegate_number_helper_method(:number_to_currency, number, options)
53
+ end
54
+
55
+ # Delegates to ActiveSupport::NumberHelper#number_to_percentage.
56
+ #
57
+ # Additionally, supports a +:raise+ option that will cause
58
+ # InvalidNumberError to be raised if +number+ is not a valid number:
59
+ #
60
+ # number_to_percentage("99x") # => "99x%"
61
+ # number_to_percentage("99x", raise: true) # => InvalidNumberError
62
+ #
63
+ def number_to_percentage(number, options = {})
64
+ delegate_number_helper_method(:number_to_percentage, number, options)
65
+ end
66
+
67
+ # Delegates to ActiveSupport::NumberHelper#number_to_delimited.
68
+ #
69
+ # Additionally, supports a +:raise+ option that will cause
70
+ # InvalidNumberError to be raised if +number+ is not a valid number:
71
+ #
72
+ # number_with_delimiter("12x34") # => "12x34"
73
+ # number_with_delimiter("12x34", raise: true) # => InvalidNumberError
74
+ #
75
+ def number_with_delimiter(number, options = {})
76
+ delegate_number_helper_method(:number_to_delimited, number, options)
77
+ end
78
+
79
+ # Delegates to ActiveSupport::NumberHelper#number_to_rounded.
80
+ #
81
+ # Additionally, supports a +:raise+ option that will cause
82
+ # InvalidNumberError to be raised if +number+ is not a valid number:
83
+ #
84
+ # number_with_precision("12x34") # => "12x34"
85
+ # number_with_precision("12x34", raise: true) # => InvalidNumberError
86
+ #
87
+ def number_with_precision(number, options = {})
88
+ delegate_number_helper_method(:number_to_rounded, number, options)
89
+ end
90
+
91
+ # Delegates to ActiveSupport::NumberHelper#number_to_human_size.
92
+ #
93
+ # Additionally, supports a +:raise+ option that will cause
94
+ # InvalidNumberError to be raised if +number+ is not a valid number:
95
+ #
96
+ # number_to_human_size("12x34") # => "12x34"
97
+ # number_to_human_size("12x34", raise: true) # => InvalidNumberError
98
+ #
99
+ def number_to_human_size(number, options = {})
100
+ delegate_number_helper_method(:number_to_human_size, number, options)
101
+ end
102
+
103
+ # Delegates to ActiveSupport::NumberHelper#number_to_human.
104
+ #
105
+ # Additionally, supports a +:raise+ option that will cause
106
+ # InvalidNumberError to be raised if +number+ is not a valid number:
107
+ #
108
+ # number_to_human("12x34") # => "12x34"
109
+ # number_to_human("12x34", raise: true) # => InvalidNumberError
110
+ #
111
+ def number_to_human(number, options = {})
112
+ delegate_number_helper_method(:number_to_human, number, options)
113
+ end
114
+
115
+ private
116
+ def delegate_number_helper_method(method, number, options)
117
+ return unless number
118
+ options = escape_unsafe_options(options.symbolize_keys)
119
+
120
+ wrap_with_output_safety_handling(number, options.delete(:raise)) {
121
+ ActiveSupport::NumberHelper.public_send(method, number, options)
122
+ }
123
+ end
124
+
125
+ def escape_unsafe_options(options)
126
+ options[:format] = ERB::Util.html_escape(options[:format]) if options[:format]
127
+ options[:negative_format] = ERB::Util.html_escape(options[:negative_format]) if options[:negative_format]
128
+ options[:separator] = ERB::Util.html_escape(options[:separator]) if options[:separator]
129
+ options[:delimiter] = ERB::Util.html_escape(options[:delimiter]) if options[:delimiter]
130
+ options[:unit] = ERB::Util.html_escape(options[:unit]) if options[:unit] && !options[:unit].html_safe?
131
+ options[:units] = escape_units(options[:units]) if options[:units] && Hash === options[:units]
132
+ options
133
+ end
134
+
135
+ def escape_units(units)
136
+ units.transform_values do |v|
137
+ ERB::Util.html_escape(v)
138
+ end
139
+ end
140
+
141
+ def wrap_with_output_safety_handling(number, raise_on_invalid, &block)
142
+ valid_float = valid_float?(number)
143
+ raise InvalidNumberError, number if raise_on_invalid && !valid_float
144
+
145
+ formatted_number = yield
146
+
147
+ if valid_float || number.html_safe?
148
+ formatted_number.html_safe
149
+ else
150
+ formatted_number
151
+ end
152
+ end
153
+
154
+ def valid_float?(number)
155
+ !parse_float(number, false).nil?
156
+ end
157
+
158
+ def parse_float(number, raise_error)
159
+ result = Float(number, exception: false)
160
+ raise InvalidNumberError, number if result.nil? && raise_error
161
+ result
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/output_safety"
4
+
5
+ module ActionView # :nodoc:
6
+ module Helpers # :nodoc:
7
+ # = Action View Raw Output \Helpers
8
+ module OutputSafetyHelper
9
+ # This method outputs without escaping a string. Since escaping tags is
10
+ # now default, this can be used when you don't want \Rails to automatically
11
+ # escape tags. This is not recommended if the data is coming from the user's
12
+ # input.
13
+ #
14
+ # For example:
15
+ #
16
+ # raw @user.name
17
+ # # => 'Jimmy <alert>Tables</alert>'
18
+ def raw(stringish)
19
+ stringish.to_s.html_safe
20
+ end
21
+
22
+ # This method returns an HTML safe string similar to what <tt>Array#join</tt>
23
+ # would return. The array is flattened, and all items, including
24
+ # the supplied separator, are HTML escaped unless they are HTML
25
+ # safe, and the returned string is marked as HTML safe.
26
+ #
27
+ # safe_join([tag.p("foo"), "<p>bar</p>"], "<br>")
28
+ # # => "<p>foo</p>&lt;br&gt;&lt;p&gt;bar&lt;/p&gt;"
29
+ #
30
+ # safe_join([tag.p("foo"), tag.p("bar")], tag.br)
31
+ # # => "<p>foo</p><br><p>bar</p>"
32
+ #
33
+ def safe_join(array, sep = $,)
34
+ sep = ERB::Util.unwrapped_html_escape(sep)
35
+
36
+ array.flatten.map! { |i| ERB::Util.unwrapped_html_escape(i) }.join(sep).html_safe
37
+ end
38
+
39
+ # Converts the array to a comma-separated sentence where the last element is
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
+ #
43
+ def to_sentence(array, options = {})
44
+ options.assert_valid_keys(:words_connector, :two_words_connector, :last_word_connector, :locale)
45
+
46
+ default_connectors = {
47
+ words_connector: ", ",
48
+ two_words_connector: " and ",
49
+ last_word_connector: ", and "
50
+ }
51
+ if defined?(I18n)
52
+ i18n_connectors = I18n.translate(:'support.array', locale: options[:locale], default: {})
53
+ default_connectors.merge!(i18n_connectors)
54
+ end
55
+ options = default_connectors.merge!(options)
56
+
57
+ case array.length
58
+ when 0
59
+ "".html_safe
60
+ when 1
61
+ ERB::Util.html_escape(array[0])
62
+ when 2
63
+ safe_join([array[0], array[1]], options[:two_words_connector])
64
+ else
65
+ safe_join([safe_join(array[0...-1], options[:words_connector]), options[:last_word_connector], array[-1]], nil)
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,218 @@
1
+ # frozen_string_literal: true
2
+
3
+ # :markup: markdown
4
+
5
+ module ActionView
6
+ module Helpers # :nodoc:
7
+ # # Action View Rendering Helpers
8
+ #
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.
12
+ module RenderingHelper
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
124
+ #
125
+ # `:variants`
126
+ # : Render a template for a different variant.
127
+ #
128
+ # <% # app/views/posts/show.html.erb %>
129
+ # <%= render template: "posts/content", variants: [:tablet] %>
130
+ # # => renders app/views/posts/content.html+tablet.erb
131
+ #
132
+ # `:handlers`
133
+ # : Render a template for a different handler.
134
+ #
135
+ # <% # app/views/posts/show.html.erb %>
136
+ # <%= render template: "posts/content", handlers: [:builder] %>
137
+ # # => renders app/views/posts/content.html.builder
138
+ def render(options = {}, locals = {}, &block)
139
+ case options
140
+ when Hash
141
+ in_rendering_context(options) do |renderer|
142
+ if block_given?
143
+ view_renderer.render_partial(self, options.merge(partial: options[:layout]), &block)
144
+ else
145
+ view_renderer.render(self, options)
146
+ end
147
+ end
148
+ else
149
+ if options.respond_to?(:render_in)
150
+ options.render_in(self, &block)
151
+ else
152
+ view_renderer.render_partial(self, partial: options, locals: locals, &block)
153
+ end
154
+ end
155
+ end
156
+
157
+ # Overrides _layout_for in the context object so it supports the case a block is
158
+ # passed to a partial. Returns the contents that are yielded to a layout, given
159
+ # a name or a block.
160
+ #
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)`.
165
+ #
166
+ # The user can override this default by passing a block to the layout:
167
+ #
168
+ # # The template
169
+ # <%= render layout: "my_layout" do %>
170
+ # Content
171
+ # <% end %>
172
+ #
173
+ # # The layout
174
+ # <html>
175
+ # <%= yield %>
176
+ # </html>
177
+ #
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
180
+ # would be
181
+ #
182
+ # <html>
183
+ # Content
184
+ # </html>
185
+ #
186
+ # Finally, the block can take block arguments, which can be passed in by
187
+ # `yield`:
188
+ #
189
+ # # The template
190
+ # <%= render layout: "my_layout" do |customer| %>
191
+ # Hello <%= customer.name %>
192
+ # <% end %>
193
+ #
194
+ # # The layout
195
+ # <html>
196
+ # <%= yield Struct.new(:name).new("David") %>
197
+ # </html>
198
+ #
199
+ # In this case, the layout would receive the block passed into `render :layout`,
200
+ # and the struct specified would be passed into the block as an argument. The result
201
+ # would be
202
+ #
203
+ # <html>
204
+ # Hello David
205
+ # </html>
206
+ #
207
+ def _layout_for(*args, &block)
208
+ name = args.first
209
+
210
+ if block && !name.is_a?(Symbol)
211
+ capture(*args, &block)
212
+ else
213
+ super
214
+ end
215
+ end
216
+ end
217
+ end
218
+ end