From 136b8141446d920ce1b5e5f48f5841a8affa9c71 Mon Sep 17 00:00:00 2001 From: Joel Hawksley Date: Thu, 16 Jul 2020 12:05:17 -0600 Subject: [PATCH] Include layout when rendering objects from controllers In https://github.com/rails/rails/pull/37919, support for rendering objects that respond_to render_in in controllers was added. However, the implementation did not support layouts. This change updates the implementation from #37919 to more closely match the rest of the ActionView::Template classes, enabling the use of layouts. Co-authored-by: Felipe Sateler --- .../controller/new_base/render_layout_test.rb | 23 +++++++++++++++++ actionpack/test/controller/renderer_test.rb | 6 ++--- .../{test_component.rb => test_renderable.rb} | 2 +- .../lib/action_view/renderer/renderer.rb | 3 --- .../action_view/renderer/template_renderer.rb | 2 ++ actionview/lib/action_view/rendering.rb | 2 +- actionview/lib/action_view/template.rb | 1 + .../lib/action_view/template/renderable.rb | 24 ++++++++++++++++++ actionview/test/fixtures/.DS_Store | Bin 0 -> 8196 bytes .../{test_component.rb => test_renderable.rb} | 2 +- actionview/test/template/render_test.rb | 8 +++--- guides/source/layouts_and_rendering.md | 2 +- 12 files changed, 61 insertions(+), 14 deletions(-) rename actionpack/test/lib/{test_component.rb => test_renderable.rb} (85%) create mode 100644 actionview/lib/action_view/template/renderable.rb create mode 100644 actionview/test/fixtures/.DS_Store rename actionview/test/lib/{test_component.rb => test_renderable.rb} (81%) diff --git a/actionpack/test/controller/new_base/render_layout_test.rb b/actionpack/test/controller/new_base/render_layout_test.rb index 806c6206dc..2e6f7efd3a 100644 --- a/actionpack/test/controller/new_base/render_layout_test.rb +++ b/actionpack/test/controller/new_base/render_layout_test.rb @@ -1,6 +1,7 @@ # frozen_string_literal: true require "abstract_unit" +require "test_renderable" module ControllerLayouts class ImplicitController < ::ApplicationController @@ -19,6 +20,10 @@ module ControllerLayouts render template: "basic", layout: "override" end + def override_renderable + render TestRenderable.new, layout: "override" + end + def layout_false render layout: false end @@ -36,6 +41,10 @@ module ControllerLayouts def index render template: "basic" end + + def renderable + render TestRenderable.new + end end class RenderLayoutTest < Rack::TestCase @@ -53,6 +62,20 @@ module ControllerLayouts assert_status 200 end + test "rendering a renderable object, using the implicit layout" do + get "/controller_layouts/implicit_name/renderable" + + assert_body "Implicit Hello, World! Layout" + assert_status 200 + end + + test "rendering a renderable object, using the override layout" do + get "/controller_layouts/implicit/override_renderable" + + assert_body "Override! Hello, World!" + assert_status 200 + end + test "overriding an implicit layout with render :layout option" do get "/controller_layouts/implicit/override" assert_body "Override! Hello world!" diff --git a/actionpack/test/controller/renderer_test.rb b/actionpack/test/controller/renderer_test.rb index 4d45c082d7..75a2767c0d 100644 --- a/actionpack/test/controller/renderer_test.rb +++ b/actionpack/test/controller/renderer_test.rb @@ -1,7 +1,7 @@ # frozen_string_literal: true require "abstract_unit" -require "test_component" +require "test_renderable" class RendererTest < ActiveSupport::TestCase test "action controller base has a renderer" do @@ -66,12 +66,12 @@ class RendererTest < ActiveSupport::TestCase assert_equal "The secret is foo\n", content end - def test_render_component + test "render a renderable object" do renderer = ApplicationController.renderer assert_equal( %(Hello, World!), - renderer.render(TestComponent.new) + renderer.render(TestRenderable.new) ) end diff --git a/actionpack/test/lib/test_component.rb b/actionpack/test/lib/test_renderable.rb similarity index 85% rename from actionpack/test/lib/test_component.rb rename to actionpack/test/lib/test_renderable.rb index 1207c35c0f..46e2f8983c 100644 --- a/actionpack/test/lib/test_component.rb +++ b/actionpack/test/lib/test_renderable.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class TestComponent +class TestRenderable def render_in(_view_context) "Hello, World!" end diff --git a/actionview/lib/action_view/renderer/renderer.rb b/actionview/lib/action_view/renderer/renderer.rb index b9278adafd..e7f4296229 100644 --- a/actionview/lib/action_view/renderer/renderer.rb +++ b/actionview/lib/action_view/renderer/renderer.rb @@ -25,9 +25,6 @@ module ActionView def render_to_object(context, options) # :nodoc: if options.key?(:partial) render_partial_to_object(context, options) - elsif options.key?(:object) - object = options[:object] - AbstractRenderer::RenderedTemplate.new(object.render_in(context), object) else render_template_to_object(context, options) end diff --git a/actionview/lib/action_view/renderer/template_renderer.rb b/actionview/lib/action_view/renderer/template_renderer.rb index f20e786b61..ee159c08aa 100644 --- a/actionview/lib/action_view/renderer/template_renderer.rb +++ b/actionview/lib/action_view/renderer/template_renderer.rb @@ -37,6 +37,8 @@ module ActionView @lookup_context.formats.first end Template::Inline.new(options[:inline], "inline template", handler, locals: keys, format: format) + elsif options.key?(:renderable) + Template::Renderable.new(options[:renderable]) elsif options.key?(:template) if options[:template].respond_to?(:render) options[:template] diff --git a/actionview/lib/action_view/rendering.rb b/actionview/lib/action_view/rendering.rb index 1ffd59599b..cecaee15d0 100644 --- a/actionview/lib/action_view/rendering.rb +++ b/actionview/lib/action_view/rendering.rb @@ -145,7 +145,7 @@ module ActionView if action.respond_to?(:permitted?) && action.permitted? options = action elsif action.respond_to?(:render_in) - options[:object] = action + options[:renderable] = action else options[:partial] = action end diff --git a/actionview/lib/action_view/template.rb b/actionview/lib/action_view/template.rb index 035aee2aa6..c45f9187f4 100644 --- a/actionview/lib/action_view/template.rb +++ b/actionview/lib/action_view/template.rb @@ -111,6 +111,7 @@ module ActionView eager_autoload do autoload :Error autoload :RawFile + autoload :Renderable autoload :Handlers autoload :HTML autoload :Inline diff --git a/actionview/lib/action_view/template/renderable.rb b/actionview/lib/action_view/template/renderable.rb new file mode 100644 index 0000000000..09e0fddd85 --- /dev/null +++ b/actionview/lib/action_view/template/renderable.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module ActionView + # = Action View Renderable Template for objects that respond to #render_in + class Template + class Renderable # :nodoc: + def initialize(renderable) + @renderable = renderable + end + + def identifier + @renderable.class.name + end + + def render(context, *args) + @renderable.render_in(context) + end + + def format + @renderable.format + end + end + end +end diff --git a/actionview/test/fixtures/.DS_Store b/actionview/test/fixtures/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d8a77e7468261d3b2caeffecfee28723e3d6d628 GIT binary patch literal 8196 zcmeHMTWl3Y82-OfV0Qt#+=Oj7c%Xn>N<9^j7O0$Fu#`(6_R6h?J!coXVY^$-?rDKi zQ{y$pXuKpofW{EtAn{3~#Kadb7>tRLc#Ha=FFqQLCO+vuJA1g5KA0FKVJDgSXa0Zw zo%z4{b|?QiM1+=-RZp~#h!n0u>NB{xMGO}&3unzkL2`ut-cqUj%ODmXUDR6%E4a8v;2`3 zzvNj#^qfv`l4dbB>Xkp~+a){l*;%7W$fO&x~^Po5e* zJ@&{M86o1Y1XbM>@!843b%Wtn8E(ZZ<#4|)hpTEcjE)^UwVXa@n4{P;6gs{a^w`CK zWlpaOIP#8y@9izx!roi(-O`{Jpp5z)yAT-mz;aeSJ7*fzEa*fUUI^d zat$NBL{;>+4qCj6-M%HBXhjyoNH15E147?gw7HdP%~-XX9n7wP1Btg;cb}^2LDw-p zUFoW=Td%0P)$N*j+l^HXjjY<)J1o(Rl}!&Sy!+a|fPB%6P&I!VvcbM!o& zp*QFPy-n}Y$8?E4p-<^^`j&pApXpcnjee&;=r1Txp6 z2^Dcjk|ZKenH=_8(gls=a9L_a zO0Qt1%i*fj8cmpE?NrdKkjrL{rZl+vd$d!@BW+p6g#-a2Vk zQ!6w@{pUe3z$EWcEUSv+b%$z)j^EWZDqs%=i zabYqB4tEwk*Y}RHz0BSlU6ax6h`mDcQp1KmQ>hx&M>/, e.message) end - def test_render_object + def test_render_renderable_object assert_equal "Hello: david", @view.render(partial: "test/customer", object: Customer.new("david")) assert_equal "FalseClass", @view.render(partial: "test/klass", object: false) assert_equal "NilClass", @view.render(partial: "test/klass", object: nil) @@ -697,10 +697,10 @@ module RenderTestCases assert_raises(ArgumentError) { ActionView::Template.register_template_handler CustomHandler } end - def test_render_component + def test_render_object assert_equal( %(Hello, World!), - @view.render(TestComponent.new) + @view.render(TestRenderable.new) ) end end diff --git a/guides/source/layouts_and_rendering.md b/guides/source/layouts_and_rendering.md index 62e8b9a73a..c36b681fdf 100644 --- a/guides/source/layouts_and_rendering.md +++ b/guides/source/layouts_and_rendering.md @@ -282,7 +282,7 @@ TIP: `send_file` is often a faster and better option if a layout isn't required. Rails can render objects responding to `:render_in`. ```ruby -render MyComponent.new +render MyRenderable.new ``` This calls `render_in` on the provided object with the current view context.