Skip to content

Commit

Permalink
Action View: Fallback to existing partial when possible
Browse files Browse the repository at this point in the history
Closes [rails#50844][]

Motivation / Background
---

A controller declared in the top-level module can render a top-level
Active Model instance whose partial is declared in the root view
directory (like `articles/_article.html.erb`).

A controller scoped within a module can render an Active Model instance
whose partial is similarly scoped within view directory (like
`scoped/articles/_article.html.erb`).

A controller scoped within a module cannot render an Active Model
instance whose partial is declared in the root view directory (like
`articles/_article.html.erb`), despite the absence of a similarly scoped
partial.

This is intended behavior that's powered by
[`config.action_view.prefix_partial_path_with_controller_namespace =
true`][prefix_partial_path_with_controller_namespace] (`true` by
default).

This change was introduced in March of 2012 as part of [rails#5625][].

Detail
---

As a consumer of Action View, my intuition is that the lookup would
fallback, in the same way that a controller that inherits from
`ApplicationController` could define its own view, then rely on fallback
to render an `app/views/application` partial.

This commit modifies the behavior to gracefully fall back to the
root-level view partial.

Checklist
---

Before submitting the PR make sure the following are checked:

* [x] This Pull Request is related to one change. Changes that are unrelated should be opened in separate PRs.
* [x] Commit message has a detailed description of what changed and why. If this PR fixes a related issue include it in the commit message. Ex: `[Fix #issue-number]`
* [x] Tests are added or updated if you fix a bug or add a feature.
* [x] CHANGELOG files are updated for the changed libraries if there is a behavior change or additional feature. Minor bug fixes and documentation changes should not be included.

[#59844]: rails#50844
[prefix_partial_path_with_controller_namespace]: https://guides.rubyonrails.org/configuring.html#config-action-view-prefix-partial-path-with-controller-namespace
[rails#5625]: rails#5625
  • Loading branch information
seanpdoyle committed Mar 2, 2024
1 parent 5cedb87 commit 901bd68
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 1 deletion.
31 changes: 31 additions & 0 deletions actionview/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
* Fallback to rendering partial without controller namespace prefix when available

When `config.action_view.prefix_partial_path_with_controller_namespace =
true` and a root-level partial exists, fallback to rendering the partial:

```erb
<%= # app/views/articles/_article.html.erb %>
Rendered
<% end %>
```

```ruby
# app/controllers/articles_controller.rb
class ArticlesController < ApplicationController
def show
render partial: Article.find(params[:id])
# => "Rendered"
end
end

# app/controllers/scoped/articles_controller.rb
class Scoped::ArticlesController < ApplicationController
def show
render partial: Article.find(params[:id])
# => "Rendered"
end
end
```

*Sean Doyle*

* Raise `ArgumentError` if `:renderable` object does not respond to `#render_in`

*Sean Doyle*
Expand Down
13 changes: 12 additions & 1 deletion actionview/lib/action_view/renderer/object_renderer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,18 @@ def initialize(lookup_context, options)
def render_object_with_partial(object, partial, context, block)
@object = object
@local_name = local_variable(partial)
render(partial, context, block)

begin
render(partial, context, block)
rescue MissingTemplate
_, *parts = partial.split("/")

if parts.many?
render_object_with_partial(@object, parts.join("/"), context, block)
else
raise
end
end
end

def render_object_derive_partial(object, context, block)
Expand Down
21 changes: 21 additions & 0 deletions actionview/test/actionpack/controller/render_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1072,6 +1072,27 @@ def test_render_to_string_inline
assert_equal "Hello world!", @response.body
end

def test_unnested_rendering_with_fallback
@controller = Fun::GamesController.new
def @controller.hello_world
render partial: ::Customer.new("Rendered"), locals: { greeting: "Hello" }
end

get :hello_world
assert_equal "Hello: Rendered", @response.body
end

def test_unnested_rendering_without_fallback
@controller = Fun::GamesController.new
def @controller.hello_world
render partial: Post.new
end

assert_raises ActionView::MissingTemplate, match: "Missing partial posts/_post" do
get :hello_world
end
end

# :ported:
def test_nested_rendering
@controller = Fun::GamesController.new
Expand Down

0 comments on commit 901bd68

Please sign in to comment.