omg-actionview 8.0.0.alpha1

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 (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