Fix MissingTemplate on Rails >= 8.0 with prepended controller modules - #201
Closed
giant-saimiri wants to merge 1 commit into
Closed
Fix MissingTemplate on Rails >= 8.0 with prepended controller modules#201giant-saimiri wants to merge 1 commit into
giant-saimiri wants to merge 1 commit into
Conversation
Rails 8.0 moved render option normalization out of _normalize_render
(which ran before custom renderers) into render_to_body (which runs
after). Until then the renderer's own template/prefixes defaulting was
dead code: Rails had already set options[:template] to the action name
and options[:prefixes] to the controller's _prefixes by the time the
:xlsx renderer block executed.
On Rails 8.0 that defaulting is exercised for the first time, and
computing prefixes via
self.class.ancestors.take_while { |a| a.respond_to?(:controller_path) }
returns [] whenever a module is prepended to the controller (memo_wise,
instrumentation gems, ...): the prepended module is the first ancestor
and does not respond to controller_path, so take_while stops at once.
The bare template name is then looked up at the view-paths root and
raises ActionView::MissingTemplate (e.g. "Missing template /show").
Use the controller's _prefixes instead - it is built from the
superclass chain (local_prefixes + superclass._prefixes), immune to
prepended modules, and exactly the value Rails itself injected before
8.0 and still uses for a bare render :action.
Author
|
Superseded by #202 — same change, moved to our organization's fork. Sorry for the noise! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fix
MissingTemplateon Rails >= 8.0 when a module is prepended to the controllerSymptom
On Rails 8.0+,
render xlsx: "show"raises:— note the bare
/show: the template is looked up at the view-paths root with no prefixes, instead of under the controller's view directory. This happens for any controller that has a module prepended to it (memo_wise, instrumentation/APM gems, etc.). On Rails <= 7.2 the same code works fine.Root cause
Two things combine:
Rails 8.0 reordered render option normalization. Through Rails 7.2,
_normalize_renderran before custom renderers, sooptions[:template]was already defaulted to the action name andoptions[:prefixes]to the controller's_prefixesby the time this gem's:xlsxrenderer block executed — the gem's ownif options[:template].nil?defaulting was dead code. Rails 8.0 moved that normalization intorender_to_body(_normalize_optionsbecame_process_render_template_optionsinActionView::Rendering), which runs after the renderer. So on 8.0 the gem's defaulting branch is exercised for the first time.The gem's prefix computation breaks on prepended modules.
A prepended module is the first entry in
ancestorsand does not respond tocontroller_path, sotake_whilestops immediately and returns[]. With empty prefixes, the bare template name is searched at the view-paths root →MissingTemplate.Fix
Use the controller's
_prefixesinstead. It is built from the superclass chain (local_prefixes + superclass._prefixes, seeAbstractController::ViewPaths), so it is immune to prepended modules — and it is exactly the value Rails itself injected before 8.0 and still uses for a barerender :action, so behavior is identical for vanilla controllers.Test
Adds
Examples::PrependedModuleController(prepends an empty module to mirror what memo_wise & co. do) with a request spec. Without the fix it fails on Rails 8.0/8.1 with the exact error above; it passes on <= 7.2 either way, since there the normalization-order made the branch dead code. Suite verified green locally on Rails 7.1, 7.2, 8.0 and 8.1.🤖 Generated with Claude Code