actionview 4.1.13 → 6.1.3.1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of actionview might be problematic. Click here for more details.

Files changed (124) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +181 -359
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +12 -6
  5. data/lib/action_view/base.rb +115 -43
  6. data/lib/action_view/buffers.rb +22 -4
  7. data/lib/action_view/cache_expiry.rb +52 -0
  8. data/lib/action_view/context.rb +8 -12
  9. data/lib/action_view/dependency_tracker.rb +61 -21
  10. data/lib/action_view/digestor.rb +89 -84
  11. data/lib/action_view/flows.rb +12 -13
  12. data/lib/action_view/gem_version.rb +6 -4
  13. data/lib/action_view/helpers/active_model_helper.rb +16 -11
  14. data/lib/action_view/helpers/asset_tag_helper.rb +311 -105
  15. data/lib/action_view/helpers/asset_url_helper.rb +197 -80
  16. data/lib/action_view/helpers/atom_feed_helper.rb +20 -17
  17. data/lib/action_view/helpers/cache_helper.rb +109 -45
  18. data/lib/action_view/helpers/capture_helper.rb +20 -22
  19. data/lib/action_view/helpers/controller_helper.rb +15 -4
  20. data/lib/action_view/helpers/csp_helper.rb +26 -0
  21. data/lib/action_view/helpers/csrf_helper.rb +8 -6
  22. data/lib/action_view/helpers/date_helper.rb +245 -140
  23. data/lib/action_view/helpers/debug_helper.rb +14 -17
  24. data/lib/action_view/helpers/form_helper.rb +875 -148
  25. data/lib/action_view/helpers/form_options_helper.rb +128 -82
  26. data/lib/action_view/helpers/form_tag_helper.rb +253 -91
  27. data/lib/action_view/helpers/javascript_helper.rb +37 -15
  28. data/lib/action_view/helpers/number_helper.rb +100 -77
  29. data/lib/action_view/helpers/output_safety_helper.rb +42 -10
  30. data/lib/action_view/helpers/rendering_helper.rb +26 -15
  31. data/lib/action_view/helpers/sanitize_helper.rb +79 -164
  32. data/lib/action_view/helpers/tag_helper.rb +277 -64
  33. data/lib/action_view/helpers/tags/base.rb +143 -92
  34. data/lib/action_view/helpers/tags/check_box.rb +20 -19
  35. data/lib/action_view/helpers/tags/checkable.rb +4 -2
  36. data/lib/action_view/helpers/tags/collection_check_boxes.rb +12 -30
  37. data/lib/action_view/helpers/tags/collection_helpers.rb +69 -36
  38. data/lib/action_view/helpers/tags/collection_radio_buttons.rb +6 -12
  39. data/lib/action_view/helpers/tags/collection_select.rb +4 -2
  40. data/lib/action_view/helpers/tags/color_field.rb +4 -3
  41. data/lib/action_view/helpers/tags/date_field.rb +3 -2
  42. data/lib/action_view/helpers/tags/date_select.rb +38 -37
  43. data/lib/action_view/helpers/tags/datetime_field.rb +14 -5
  44. data/lib/action_view/helpers/tags/datetime_local_field.rb +3 -2
  45. data/lib/action_view/helpers/tags/datetime_select.rb +2 -0
  46. data/lib/action_view/helpers/tags/email_field.rb +2 -0
  47. data/lib/action_view/helpers/tags/file_field.rb +2 -0
  48. data/lib/action_view/helpers/tags/grouped_collection_select.rb +4 -2
  49. data/lib/action_view/helpers/tags/hidden_field.rb +2 -0
  50. data/lib/action_view/helpers/tags/label.rb +41 -22
  51. data/lib/action_view/helpers/tags/month_field.rb +3 -2
  52. data/lib/action_view/helpers/tags/number_field.rb +2 -0
  53. data/lib/action_view/helpers/tags/password_field.rb +3 -1
  54. data/lib/action_view/helpers/tags/placeholderable.rb +24 -0
  55. data/lib/action_view/helpers/tags/radio_button.rb +7 -6
  56. data/lib/action_view/helpers/tags/range_field.rb +2 -0
  57. data/lib/action_view/helpers/tags/search_field.rb +3 -0
  58. data/lib/action_view/helpers/tags/select.rb +11 -10
  59. data/lib/action_view/helpers/tags/tel_field.rb +2 -0
  60. data/lib/action_view/helpers/tags/text_area.rb +7 -1
  61. data/lib/action_view/helpers/tags/text_field.rb +11 -7
  62. data/lib/action_view/helpers/tags/time_field.rb +3 -2
  63. data/lib/action_view/helpers/tags/time_select.rb +2 -0
  64. data/lib/action_view/helpers/tags/time_zone_select.rb +3 -1
  65. data/lib/action_view/helpers/tags/translator.rb +39 -0
  66. data/lib/action_view/helpers/tags/url_field.rb +2 -0
  67. data/lib/action_view/helpers/tags/week_field.rb +3 -2
  68. data/lib/action_view/helpers/tags.rb +4 -1
  69. data/lib/action_view/helpers/text_helper.rb +80 -45
  70. data/lib/action_view/helpers/translation_helper.rb +148 -67
  71. data/lib/action_view/helpers/url_helper.rb +289 -147
  72. data/lib/action_view/helpers.rb +5 -3
  73. data/lib/action_view/layouts.rb +68 -63
  74. data/lib/action_view/log_subscriber.rb +80 -13
  75. data/lib/action_view/lookup_context.rb +137 -92
  76. data/lib/action_view/model_naming.rb +4 -2
  77. data/lib/action_view/path_set.rb +30 -16
  78. data/lib/action_view/railtie.rb +62 -13
  79. data/lib/action_view/record_identifier.rb +53 -26
  80. data/lib/action_view/renderer/abstract_renderer.rb +152 -13
  81. data/lib/action_view/renderer/collection_renderer.rb +196 -0
  82. data/lib/action_view/renderer/object_renderer.rb +34 -0
  83. data/lib/action_view/renderer/partial_renderer/collection_caching.rb +102 -0
  84. data/lib/action_view/renderer/partial_renderer.rb +61 -261
  85. data/lib/action_view/renderer/renderer.rb +67 -6
  86. data/lib/action_view/renderer/streaming_template_renderer.rb +58 -54
  87. data/lib/action_view/renderer/template_renderer.rb +83 -75
  88. data/lib/action_view/rendering.rb +73 -46
  89. data/lib/action_view/routing_url_for.rb +54 -17
  90. data/lib/action_view/tasks/cache_digests.rake +25 -0
  91. data/lib/action_view/template/error.rb +44 -29
  92. data/lib/action_view/template/handlers/builder.rb +12 -13
  93. data/lib/action_view/template/handlers/erb/erubi.rb +89 -0
  94. data/lib/action_view/template/handlers/erb.rb +23 -89
  95. data/lib/action_view/template/handlers/html.rb +11 -0
  96. data/lib/action_view/template/handlers/raw.rb +4 -4
  97. data/lib/action_view/template/handlers.rb +22 -9
  98. data/lib/action_view/template/html.rb +10 -11
  99. data/lib/action_view/template/inline.rb +22 -0
  100. data/lib/action_view/template/raw_file.rb +25 -0
  101. data/lib/action_view/template/renderable.rb +24 -0
  102. data/lib/action_view/template/resolver.rb +267 -181
  103. data/lib/action_view/template/sources/file.rb +17 -0
  104. data/lib/action_view/template/sources.rb +13 -0
  105. data/lib/action_view/template/text.rb +8 -10
  106. data/lib/action_view/template/types.rb +18 -18
  107. data/lib/action_view/template.rb +109 -99
  108. data/lib/action_view/test_case.rb +73 -53
  109. data/lib/action_view/testing/resolvers.rb +24 -33
  110. data/lib/action_view/unbound_template.rb +31 -0
  111. data/lib/action_view/version.rb +3 -1
  112. data/lib/action_view/view_paths.rb +74 -44
  113. data/lib/action_view.rb +14 -9
  114. data/lib/assets/compiled/rails-ujs.js +746 -0
  115. metadata +71 -26
  116. data/lib/action_view/helpers/record_tag_helper.rb +0 -108
  117. data/lib/action_view/tasks/dependencies.rake +0 -23
  118. data/lib/action_view/vendor/html-scanner/html/document.rb +0 -68
  119. data/lib/action_view/vendor/html-scanner/html/node.rb +0 -532
  120. data/lib/action_view/vendor/html-scanner/html/sanitizer.rb +0 -188
  121. data/lib/action_view/vendor/html-scanner/html/selector.rb +0 -830
  122. data/lib/action_view/vendor/html-scanner/html/tokenizer.rb +0 -107
  123. data/lib/action_view/vendor/html-scanner/html/version.rb +0 -11
  124. data/lib/action_view/vendor/html-scanner.rb +0 -20
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActionView
2
4
  # This is the main entry point for rendering. It basically delegates
3
5
  # to other objects like TemplateRenderer and PartialRenderer which
@@ -15,12 +17,16 @@ module ActionView
15
17
  @lookup_context = lookup_context
16
18
  end
17
19
 
18
- # Main render entry point shared by AV and AC.
20
+ # Main render entry point shared by Action View and Action Controller.
19
21
  def render(context, options)
22
+ render_to_object(context, options).body
23
+ end
24
+
25
+ def render_to_object(context, options) # :nodoc:
20
26
  if options.key?(:partial)
21
- render_partial(context, options)
27
+ render_partial_to_object(context, options)
22
28
  else
23
- render_template(context, options)
29
+ render_template_to_object(context, options)
24
30
  end
25
31
  end
26
32
 
@@ -37,14 +43,69 @@ module ActionView
37
43
  end
38
44
  end
39
45
 
40
- # Direct accessor to template rendering.
46
+ # Direct access to template rendering.
41
47
  def render_template(context, options) #:nodoc:
42
- TemplateRenderer.new(@lookup_context).render(context, options)
48
+ render_template_to_object(context, options).body
43
49
  end
44
50
 
45
51
  # Direct access to partial rendering.
46
52
  def render_partial(context, options, &block) #:nodoc:
47
- PartialRenderer.new(@lookup_context).render(context, options, block)
53
+ render_partial_to_object(context, options, &block).body
54
+ end
55
+
56
+ def cache_hits # :nodoc:
57
+ @cache_hits ||= {}
48
58
  end
59
+
60
+ def render_template_to_object(context, options) #:nodoc:
61
+ TemplateRenderer.new(@lookup_context).render(context, options)
62
+ end
63
+
64
+ def render_partial_to_object(context, options, &block) #:nodoc:
65
+ partial = options[:partial]
66
+ if String === partial
67
+ collection = collection_from_options(options)
68
+
69
+ if collection
70
+ # Collection + Partial
71
+ renderer = CollectionRenderer.new(@lookup_context, options)
72
+ renderer.render_collection_with_partial(collection, partial, context, block)
73
+ else
74
+ if options.key?(:object)
75
+ # Object + Partial
76
+ renderer = ObjectRenderer.new(@lookup_context, options)
77
+ renderer.render_object_with_partial(options[:object], partial, context, block)
78
+ else
79
+ # Partial
80
+ renderer = PartialRenderer.new(@lookup_context, options)
81
+ renderer.render(partial, context, block)
82
+ end
83
+ end
84
+ else
85
+ collection = collection_from_object(partial) || collection_from_options(options)
86
+
87
+ if collection
88
+ # Collection + Derived Partial
89
+ renderer = CollectionRenderer.new(@lookup_context, options)
90
+ renderer.render_collection_derive_partial(collection, context, block)
91
+ else
92
+ # Object + Derived Partial
93
+ renderer = ObjectRenderer.new(@lookup_context, options)
94
+ renderer.render_object_derive_partial(partial, context, block)
95
+ end
96
+ end
97
+ end
98
+
99
+ private
100
+ def collection_from_options(options)
101
+ if options.key?(:collection)
102
+ collection = options[:collection]
103
+ collection || []
104
+ end
105
+ end
106
+
107
+ def collection_from_object(object)
108
+ object if object.respond_to?(:to_ary)
109
+ end
49
110
  end
50
111
  end
@@ -1,10 +1,11 @@
1
- require 'fiber'
1
+ # frozen_string_literal: true
2
+
3
+ require "fiber"
2
4
 
3
5
  module ActionView
4
6
  # == TODO
5
7
  #
6
8
  # * Support streaming from child templates, partials and so on.
7
- # * Integrate exceptions with exceptron
8
9
  # * Rack::Cache needs to support streaming bodies
9
10
  class StreamingTemplateRenderer < TemplateRenderer #:nodoc:
10
11
  # A valid Rack::Body (i.e. it responds to each).
@@ -26,78 +27,81 @@ module ActionView
26
27
  end
27
28
 
28
29
  private
29
-
30
- # This is the same logging logic as in ShowExceptions middleware.
31
- # TODO Once "exceptron" is in, refactor this piece to simply re-use exceptron.
32
- def log_error(exception) #:nodoc:
33
- logger = ActionView::Base.logger
34
- return unless logger
35
-
36
- message = "\n#{exception.class} (#{exception.message}):\n"
37
- message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code)
38
- message << " " << exception.backtrace.join("\n ")
39
- logger.fatal("#{message}\n\n")
40
- end
30
+ # This is the same logging logic as in ShowExceptions middleware.
31
+ def log_error(exception)
32
+ logger = ActionView::Base.logger
33
+ return unless logger
34
+
35
+ message = +"\n#{exception.class} (#{exception.message}):\n"
36
+ message << exception.annotated_source_code.to_s if exception.respond_to?(:annotated_source_code)
37
+ message << " " << exception.backtrace.join("\n ")
38
+ logger.fatal("#{message}\n\n")
39
+ end
41
40
  end
42
41
 
43
42
  # For streaming, instead of rendering a given a template, we return a Body
44
43
  # object that responds to each. This object is initialized with a block
45
44
  # that knows how to render the template.
46
- def render_template(template, layout_name = nil, locals = {}) #:nodoc:
47
- return [super] unless layout_name && template.supports_streaming?
45
+ def render_template(view, template, layout_name = nil, locals = {}) #:nodoc:
46
+ return [super.body] unless layout_name && template.supports_streaming?
48
47
 
49
48
  locals ||= {}
50
- layout = layout_name && find_layout(layout_name, locals.keys)
49
+ layout = layout_name && find_layout(layout_name, locals.keys, [formats.first])
51
50
 
52
51
  Body.new do |buffer|
53
- delayed_render(buffer, template, layout, @view, locals)
52
+ delayed_render(buffer, template, layout, view, locals)
54
53
  end
55
54
  end
56
55
 
57
56
  private
58
-
59
- def delayed_render(buffer, template, layout, view, locals)
60
- # Wrap the given buffer in the StreamingBuffer and pass it to the
61
- # underlying template handler. Now, every time something is concatenated
62
- # to the buffer, it is not appended to an array, but streamed straight
63
- # to the client.
64
- output = ActionView::StreamingBuffer.new(buffer)
65
- yielder = lambda { |*name| view._layout_for(*name) }
66
-
67
- instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
68
- fiber = Fiber.new do
69
- if layout
70
- layout.render(view, locals, output, &yielder)
71
- else
72
- # If you don't have a layout, just render the thing
73
- # and concatenate the final result. This is the same
74
- # as a layout with just <%= yield %>
75
- output.safe_concat view._layout_for
57
+ def delayed_render(buffer, template, layout, view, locals)
58
+ # Wrap the given buffer in the StreamingBuffer and pass it to the
59
+ # underlying template handler. Now, every time something is concatenated
60
+ # to the buffer, it is not appended to an array, but streamed straight
61
+ # to the client.
62
+ output = ActionView::StreamingBuffer.new(buffer)
63
+ yielder = lambda { |*name| view._layout_for(*name) }
64
+
65
+ ActiveSupport::Notifications.instrument(
66
+ "render_template.action_view",
67
+ identifier: template.identifier,
68
+ layout: layout && layout.virtual_path
69
+ ) do
70
+ outer_config = I18n.config
71
+ fiber = Fiber.new do
72
+ I18n.config = outer_config
73
+ if layout
74
+ layout.render(view, locals, output, &yielder)
75
+ else
76
+ # If you don't have a layout, just render the thing
77
+ # and concatenate the final result. This is the same
78
+ # as a layout with just <%= yield %>
79
+ output.safe_concat view._layout_for
80
+ end
76
81
  end
77
- end
78
82
 
79
- # Set the view flow to support streaming. It will be aware
80
- # when to stop rendering the layout because it needs to search
81
- # something in the template and vice-versa.
82
- view.view_flow = StreamingFlow.new(view, fiber)
83
+ # Set the view flow to support streaming. It will be aware
84
+ # when to stop rendering the layout because it needs to search
85
+ # something in the template and vice-versa.
86
+ view.view_flow = StreamingFlow.new(view, fiber)
83
87
 
84
- # Yo! Start the fiber!
85
- fiber.resume
88
+ # Yo! Start the fiber!
89
+ fiber.resume
86
90
 
87
- # If the fiber is still alive, it means we need something
88
- # from the template, so start rendering it. If not, it means
89
- # the layout exited without requiring anything from the template.
90
- if fiber.alive?
91
- content = template.render(view, locals, &yielder)
91
+ # If the fiber is still alive, it means we need something
92
+ # from the template, so start rendering it. If not, it means
93
+ # the layout exited without requiring anything from the template.
94
+ if fiber.alive?
95
+ content = template.render(view, locals, &yielder)
92
96
 
93
- # Once rendering the template is done, sets its content in the :layout key.
94
- view.view_flow.set(:layout, content)
97
+ # Once rendering the template is done, sets its content in the :layout key.
98
+ view.view_flow.set(:layout, content)
95
99
 
96
- # In case the layout continues yielding, we need to resume
97
- # the fiber until all yields are handled.
98
- fiber.resume while fiber.alive?
100
+ # In case the layout continues yielding, we need to resume
101
+ # the fiber until all yields are handled.
102
+ fiber.resume while fiber.alive?
103
+ end
99
104
  end
100
105
  end
101
- end
102
106
  end
103
107
  end
@@ -1,102 +1,110 @@
1
- require 'active_support/core_ext/object/try'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActionView
4
4
  class TemplateRenderer < AbstractRenderer #:nodoc:
5
5
  def render(context, options)
6
- @view = context
7
6
  @details = extract_details(options)
8
7
  template = determine_template(options)
9
- context = @lookup_context
10
8
 
11
- prepend_formats(template.formats)
9
+ prepend_formats(template.format)
12
10
 
13
- unless context.rendered_format
14
- context.rendered_format = template.formats.first || formats.first
15
- end
16
-
17
- render_template(template, options[:layout], options[:locals])
11
+ render_template(context, template, options[:layout], options[:locals] || {})
18
12
  end
19
13
 
20
- # Determine the template to be rendered using the given options.
21
- def determine_template(options) #:nodoc:
22
- keys = options.fetch(:locals, {}).keys
14
+ private
15
+ # Determine the template to be rendered using the given options.
16
+ def determine_template(options)
17
+ keys = options.has_key?(:locals) ? options[:locals].keys : []
23
18
 
24
- if options.key?(:body)
25
- Template::Text.new(options[:body])
26
- elsif options.key?(:text)
27
- Template::Text.new(options[:text], formats.first)
28
- elsif options.key?(:plain)
29
- Template::Text.new(options[:plain])
30
- elsif options.key?(:html)
31
- Template::HTML.new(options[:html], formats.first)
32
- elsif options.key?(:file)
33
- with_fallbacks { find_template(options[:file], nil, false, keys, @details) }
34
- elsif options.key?(:inline)
35
- handler = Template.handler_for_extension(options[:type] || "erb")
36
- Template.new(options[:inline], "inline template", handler, :locals => keys)
37
- elsif options.key?(:template)
38
- if options[:template].respond_to?(:render)
39
- options[:template]
19
+ if options.key?(:body)
20
+ Template::Text.new(options[:body])
21
+ elsif options.key?(:plain)
22
+ Template::Text.new(options[:plain])
23
+ elsif options.key?(:html)
24
+ Template::HTML.new(options[:html], formats.first)
25
+ elsif options.key?(:file)
26
+ if File.exist?(options[:file])
27
+ Template::RawFile.new(options[:file])
28
+ else
29
+ raise ArgumentError, "`render file:` should be given the absolute path to a file. '#{options[:file]}' was given instead"
30
+ end
31
+ elsif options.key?(:inline)
32
+ handler = Template.handler_for_extension(options[:type] || "erb")
33
+ format = if handler.respond_to?(:default_format)
34
+ handler.default_format
35
+ else
36
+ @lookup_context.formats.first
37
+ end
38
+ Template::Inline.new(options[:inline], "inline template", handler, locals: keys, format: format)
39
+ elsif options.key?(:renderable)
40
+ Template::Renderable.new(options[:renderable])
41
+ elsif options.key?(:template)
42
+ if options[:template].respond_to?(:render)
43
+ options[:template]
44
+ else
45
+ @lookup_context.find_template(options[:template], options[:prefixes], false, keys, @details)
46
+ end
40
47
  else
41
- find_template(options[:template], options[:prefixes], false, keys, @details)
48
+ raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file, :plain, :html or :body option."
42
49
  end
43
- else
44
- raise ArgumentError, "You invoked render but did not give any of :partial, :template, :inline, :file, :plain, :text or :body option."
45
50
  end
46
- end
47
51
 
48
- # Renders the given template. A string representing the layout can be
49
- # supplied as well.
50
- def render_template(template, layout_name = nil, locals = nil) #:nodoc:
51
- view, locals = @view, locals || {}
52
-
53
- render_with_layout(layout_name, locals) do |layout|
54
- instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
55
- template.render(view, locals) { |*name| view._layout_for(*name) }
52
+ # Renders the given template. A string representing the layout can be
53
+ # supplied as well.
54
+ def render_template(view, template, layout_name, locals)
55
+ render_with_layout(view, template, layout_name, locals) do |layout|
56
+ ActiveSupport::Notifications.instrument(
57
+ "render_template.action_view",
58
+ identifier: template.identifier,
59
+ layout: layout && layout.virtual_path
60
+ ) do
61
+ template.render(view, locals) { |*name| view._layout_for(*name) }
62
+ end
56
63
  end
57
64
  end
58
- end
59
65
 
60
- def render_with_layout(path, locals) #:nodoc:
61
- layout = path && find_layout(path, locals.keys)
62
- content = yield(layout)
66
+ def render_with_layout(view, template, path, locals)
67
+ layout = path && find_layout(path, locals.keys, [formats.first])
63
68
 
64
- if layout
65
- view = @view
66
- view.view_flow.set(:layout, content)
67
- layout.render(view, locals){ |*name| view._layout_for(*name) }
68
- else
69
- content
69
+ body = if layout
70
+ ActiveSupport::Notifications.instrument("render_layout.action_view", identifier: layout.identifier) do
71
+ view.view_flow.set(:layout, yield(layout))
72
+ layout.render(view, locals) { |*name| view._layout_for(*name) }
73
+ end
74
+ else
75
+ yield
76
+ end
77
+ build_rendered_template(body, template)
70
78
  end
71
- end
72
79
 
73
- # This is the method which actually finds the layout using details in the lookup
74
- # context object. If no layout is found, it checks if at least a layout with
75
- # the given name exists across all details before raising the error.
76
- def find_layout(layout, keys)
77
- with_layout_format { resolve_layout(layout, keys) }
78
- end
80
+ # This is the method which actually finds the layout using details in the lookup
81
+ # context object. If no layout is found, it checks if at least a layout with
82
+ # the given name exists across all details before raising the error.
83
+ def find_layout(layout, keys, formats)
84
+ resolve_layout(layout, keys, formats)
85
+ end
79
86
 
80
- def resolve_layout(layout, keys)
81
- case layout
82
- when String
83
- begin
84
- if layout =~ /^\//
85
- with_fallbacks { find_template(layout, nil, false, keys, @details) }
86
- else
87
- find_template(layout, nil, false, keys, @details)
87
+ def resolve_layout(layout, keys, formats)
88
+ details = @details.dup
89
+ details[:formats] = formats
90
+
91
+ case layout
92
+ when String
93
+ begin
94
+ if layout.start_with?("/")
95
+ raise ArgumentError, "Rendering layouts from an absolute path is not supported."
96
+ else
97
+ @lookup_context.find_template(layout, nil, false, [], details)
98
+ end
99
+ rescue ActionView::MissingTemplate
100
+ all_details = @details.merge(formats: @lookup_context.default_formats)
101
+ raise unless template_exists?(layout, nil, false, [], **all_details)
88
102
  end
89
- rescue ActionView::MissingTemplate
90
- all_details = @details.merge(:formats => @lookup_context.default_formats)
91
- raise unless template_exists?(layout, nil, false, keys, all_details)
103
+ when Proc
104
+ resolve_layout(layout.call(@lookup_context, formats), keys, formats)
105
+ else
106
+ layout
92
107
  end
93
- when Proc
94
- resolve_layout(layout.call, keys)
95
- when FalseClass
96
- nil
97
- else
98
- layout
99
108
  end
100
- end
101
109
  end
102
110
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "action_view/view_paths"
2
4
 
3
5
  module ActionView
@@ -24,7 +26,14 @@ module ActionView
24
26
  extend ActiveSupport::Concern
25
27
  include ActionView::ViewPaths
26
28
 
27
- # Overwrite process to setup I18n proxy.
29
+ attr_reader :rendered_format
30
+
31
+ def initialize
32
+ @rendered_format = nil
33
+ super
34
+ end
35
+
36
+ # Overwrite process to set up I18n proxy.
28
37
  def process(*) #:nodoc:
29
38
  old_config, I18n.config = I18n.config, I18nProxy.new(I18n.config, lookup_context)
30
39
  super
@@ -33,47 +42,59 @@ module ActionView
33
42
  end
34
43
 
35
44
  module ClassMethods
36
- def view_context_class
37
- @view_context_class ||= begin
38
- routes = respond_to?(:_routes) && _routes
39
- helpers = respond_to?(:_helpers) && _helpers
40
-
41
- Class.new(ActionView::Base) do
42
- if routes
43
- include routes.url_helpers
44
- include routes.mounted_helpers
45
- end
46
-
47
- if helpers
48
- include helpers
49
- end
45
+ def _routes
46
+ end
47
+
48
+ def _helpers
49
+ end
50
+
51
+ def build_view_context_class(klass, supports_path, routes, helpers)
52
+ Class.new(klass) do
53
+ if routes
54
+ include routes.url_helpers(supports_path)
55
+ include routes.mounted_helpers
56
+ end
57
+
58
+ if helpers
59
+ include helpers
50
60
  end
51
61
  end
52
62
  end
53
- end
54
63
 
55
- attr_internal_writer :view_context_class
64
+ def view_context_class
65
+ klass = ActionView::LookupContext::DetailsKey.view_context_class(ActionView::Base)
66
+
67
+ @view_context_class ||= build_view_context_class(klass, supports_path?, _routes, _helpers)
68
+
69
+ if klass.changed?(@view_context_class)
70
+ @view_context_class = build_view_context_class(klass, supports_path?, _routes, _helpers)
71
+ end
72
+
73
+ @view_context_class
74
+ end
75
+ end
56
76
 
57
77
  def view_context_class
58
- @_view_context_class ||= self.class.view_context_class
78
+ self.class.view_context_class
59
79
  end
60
80
 
61
- # An instance of a view class. The default view class is ActionView::Base
81
+ # An instance of a view class. The default view class is ActionView::Base.
62
82
  #
63
83
  # The view class must have the following methods:
64
- # View.new[lookup_context, assigns, controller]
65
- # Create a new ActionView instance for a controller
66
- # View#render[options]
67
- # Returns String with the rendered template
84
+ #
85
+ # * <tt>View.new(lookup_context, assigns, controller)</tt> Create a new
86
+ # ActionView instance for a controller and we can also pass the arguments.
87
+ #
88
+ # * <tt>View#render(option)</tt> — Returns String with the rendered template.
68
89
  #
69
90
  # Override this method in a module to change the default behavior.
70
91
  def view_context
71
- view_context_class.new(view_renderer, view_assigns, self)
92
+ view_context_class.new(lookup_context, view_assigns, self)
72
93
  end
73
94
 
74
95
  # Returns an object that is able to render templates.
75
- # :api: private
76
- def view_renderer
96
+ def view_renderer # :nodoc:
97
+ # Lifespan: Per controller
77
98
  @_view_renderer ||= ActionView::Renderer.new(lookup_context)
78
99
  end
79
100
 
@@ -82,34 +103,35 @@ module ActionView
82
103
  _render_template(options)
83
104
  end
84
105
 
85
- def rendered_format
86
- Mime[lookup_context.rendered_format]
87
- end
88
-
89
106
  private
90
-
91
107
  # Find and render a template based on the options given.
92
- # :api: private
93
- def _render_template(options) #:nodoc:
94
- variant = options[:variant]
108
+ def _render_template(options)
109
+ variant = options.delete(:variant)
110
+ assigns = options.delete(:assigns)
111
+ context = view_context
95
112
 
96
- lookup_context.rendered_format = nil if options[:formats]
113
+ context.assign assigns if assigns
97
114
  lookup_context.variants = variant if variant
98
115
 
99
- view_renderer.render(view_context, options)
116
+ rendered_template = context.in_rendering_context(options) do |renderer|
117
+ renderer.render_to_object(context, options)
118
+ end
119
+
120
+ rendered_format = rendered_template.format || lookup_context.formats.first
121
+ @rendered_format = Template::Types[rendered_format]
122
+
123
+ rendered_template.body
100
124
  end
101
125
 
102
- # Assign the rendered format to lookup context.
103
- def _process_format(format, options = {}) #:nodoc:
126
+ # Assign the rendered format to look up context.
127
+ def _process_format(format)
104
128
  super
105
- lookup_context.formats = [format.to_sym]
106
- lookup_context.rendered_format = lookup_context.formats.first
129
+ lookup_context.formats = [format.to_sym] if format.to_sym
107
130
  end
108
131
 
109
132
  # Normalize args by converting render "foo" to render :action => "foo" and
110
- # render "foo/bar" to render :file => "foo/bar".
111
- # :api: private
112
- def _normalize_args(action=nil, options={})
133
+ # render "foo/bar" to render :template => "foo/bar".
134
+ def _normalize_args(action = nil, options = {})
113
135
  options = super(action, options)
114
136
  case action
115
137
  when NilClass
@@ -117,17 +139,22 @@ module ActionView
117
139
  options = action
118
140
  when String, Symbol
119
141
  action = action.to_s
120
- key = action.include?(?/) ? :file : :action
142
+ key = action.include?(?/) ? :template : :action
121
143
  options[key] = action
122
144
  else
123
- options[:partial] = action
145
+ if action.respond_to?(:permitted?) && action.permitted?
146
+ options = action
147
+ elsif action.respond_to?(:render_in)
148
+ options[:renderable] = action
149
+ else
150
+ options[:partial] = action
151
+ end
124
152
  end
125
153
 
126
154
  options
127
155
  end
128
156
 
129
157
  # Normalize options.
130
- # :api: private
131
158
  def _normalize_options(options)
132
159
  options = super(options)
133
160
  if options[:partial] == true