Skip to content

Commit 1fcb83a

Browse files
committed
Add support for implicitly rendering renderables
In rails#36388, we added support for rendering objects that respond to for controller actions. This PR adds support for implicitly rendering renderables for controller actions as one can for regular templates today. @bradgessler's post on doing this for Phlex: https://fly.io/ruby-dispatch/hacking-rails-implicit-rendering-for-view-components/ ViewComponent thread discussing the topic: ViewComponent/view_component#618
1 parent 0e65871 commit 1fcb83a

File tree

6 files changed

+49
-1
lines changed

6 files changed

+49
-1
lines changed

actionpack/lib/action_controller/metal/implicit_render.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ module ImplicitRender
3535
include BasicImplicitRender
3636

3737
def default_render
38-
if template_exists?(action_name.to_s, _prefixes, variants: request.variant)
38+
if renderable = find_renderable(action_name.to_s, _prefixes)
39+
render(renderable.new)
40+
elsif template_exists?(action_name.to_s, _prefixes, variants: request.variant)
3941
render
4042
elsif any_templates?(action_name.to_s, _prefixes)
4143
message = "#{self.class.name}\##{action_name} is missing a template " \
@@ -60,6 +62,10 @@ def method_for_action(action_name)
6062
end
6163

6264
private
65+
def find_renderable(action_name, prefixes)
66+
ActionView::RenderableRegistry.get_renderables(self)[[prefixes, action_name].join("/")]
67+
end
68+
6369
def interactive_browser_request?
6470
request.get? && request.format == Mime[:html] && !request.xhr?
6571
end

actionview/CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
* Add support for implicitly rendering renderables in controllers.
2+
3+
*Joel Hawksley*
4+
15
* Rename `check_box*` methods into `checkbox*`.
26

37
Old names are still available as aliases.

actionview/lib/action_view.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ module ActionView
4444
autoload :PathRegistry
4545
autoload :PathSet
4646
autoload :RecordIdentifier
47+
autoload :RenderableRegistry
4748
autoload :Rendering
4849
autoload :RoutingUrlFor
4950
autoload :Template
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# frozen_string_literal: true
2+
3+
module ActionView # :nodoc:
4+
module RenderableRegistry # :nodoc:
5+
@renderables_by_class = Hash.new({})
6+
7+
def self.get_renderables(klass)
8+
@renderables_by_class[klass] || get_renderables(klass.superclass)
9+
end
10+
11+
def self.set_renderable(klass, path, renderable_klass)
12+
@renderables_by_class[klass][path] = renderable_klass
13+
end
14+
end
15+
end

actionview/lib/action_view/view_paths.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@ def view_paths=(paths)
6969
self._view_paths = _build_view_paths(paths)
7070
end
7171

72+
def register_renderable(path, renderable_klass)
73+
ActionView::RenderableRegistry.set_renderable(self, path, renderable_klass)
74+
end
75+
7276
private
7377
# Override this method in your controller if you want to change paths prefixes for finding views.
7478
# Prefixes defined here will still be added to parents' <tt>._prefixes</tt>.

actionview/test/actionpack/controller/render_test.rb

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ class ValidatingPost < Post
3939
validates :title, presence: true
4040
end
4141

42+
class ImplicitRenderable
43+
def render_in(_)
44+
"implicit renderable"
45+
end
46+
end
47+
4248
class TestController < ActionController::Base
4349
protect_from_forgery
4450

@@ -287,6 +293,9 @@ def formatted_html_erb
287293
def formatted_xml_erb
288294
end
289295

296+
def implicit_renderable
297+
end
298+
290299
def render_to_string_test
291300
@foo = render_to_string inline: "this is a test"
292301
end
@@ -662,6 +671,7 @@ class RenderTest < ActionController::TestCase
662671
get :empty_partial_collection, to: "test#empty_partial_collection"
663672
get :formatted_html_erb, to: "test#formatted_html_erb"
664673
get :formatted_xml_erb, to: "test#formatted_xml_erb"
674+
get :implicit_renderable, to: "test#implicit_renderable"
665675
get :greeting, to: "test#greeting"
666676
get :hello_in_a_string, to: "test#hello_in_a_string"
667677
get :hello_world, to: "fun/games#hello_world"
@@ -772,6 +782,9 @@ def setup
772782

773783
@request.host = "www.nextangle.com"
774784

785+
# This would likely be handled at by the view object framework
786+
ActionController::Base.register_renderable("test/implicit_renderable", ImplicitRenderable)
787+
775788
@old_view_paths = ActionController::Base.view_paths
776789
ActionController::Base.view_paths = File.join(FIXTURE_LOAD_PATH, "actionpack")
777790
end
@@ -1122,6 +1135,11 @@ def test_should_render_formatted_html_erb_template_with_faulty_accepts_header
11221135
assert_equal "<test>passed formatted HTML erb</test>", @response.body
11231136
end
11241137

1138+
def test_should_render_implicit_renderable
1139+
get :implicit_renderable
1140+
assert_equal "implicit renderable", @response.body
1141+
end
1142+
11251143
def test_layout_test_with_different_layout
11261144
get :layout_test_with_different_layout
11271145
assert_equal "<html>Hello world!</html>", @response.body

0 commit comments

Comments
 (0)