-
Notifications
You must be signed in to change notification settings - Fork 445
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Refactor compiler to use ActionView's partial sorting algorithm #2158
base: v4
Are you sure you want to change the base?
Conversation
4b3679e
to
5e13764
Compare
5e13764
to
2f943a3
Compare
@joelhawksley great to see you've merged my previous PR. I've updated this one, and I think it's ready for review. I discovered the reason why I was seeing more allocated objects and I've fixed the issue (I wasn't using the |
# | ||
# @private | ||
def format | ||
@__vc_variant if defined?(@__vc_variant) | ||
nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've spent some time investigating why this code returned the variant and I still don't really understand why. It was added during 1.x and it's been refactored numerous times. I don't think this has any meaningful contribution to caching anymore as Rails cache already uses the LookupContext@details
ivar as an entry point to caching. Any cache generated from a ViewComponent render would already be keyed by the specific LookupContext details including locale, variants, formats, and handlers.
@@ -328,7 +330,7 @@ def content_evaluated? | |||
end | |||
|
|||
def maybe_escape_html(text) | |||
return text if __vc_request && !__vc_request.format.html? | |||
return text if @current_template && !@current_template.html? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that current_template is set by render_template_for
now, so this logic takes into account the format picked by the renderer (e.g. if the chosen template is json
then the before/after strings won't be HTML sanitised).
@joelhawksley how does this look for v4 |
We deployed this branch to production yesterday. No perceivable difference in memory utilisation or CPU since deployment. I'll continue monitoring and report any issues we encounter (@joelhawksley as discussed). |
@joelhawksley any further reservationd or ideas to move this forward |
@joelhawksley could you please give this PR a review? |
What are you trying to accomplish?
Rails uses
ActionView::TemplateDetails::Requested
to sort available templates to find the best match for a givenLookupContext
, taking in to account the acceptable content types, variants, locale, and handlers available. ViewComponent implements a subset of this logic that leaves some gaps, i.e. #2154 #2128This PR aims to bring the ViewComponent template selection logic in line with how Rails chooses a partial, and ensure that it won't drift in the future by using Rails' internal structures for the calculation.
What approach did you choose and why?
refine
to provide access to Rails' internal@details
ivar fromLookupContext
that memoizes details about the rendering context for use in partial/template resolution. Using the Rails internal logic for template resolution ensures that ViewComponents will resolve templates in a consistent way and remain consistent in the future.render_template_for
. This approach allows closing over the templates available to the component, which would not be possible with a compiled method body as a string. The benefit of this approach is that the code is easy to read and maintain, and it's equivalent or slightly faster to render components. An alternative approach would be to generate constants that hold the data required for the template selection and refer to them in the method body, but I think the code would be harder to read and maintain.I have run the included benchmarks and the CPU performance is marginally better vs v4. See attached.
v4.benchmark.txt
2128-variant.benchmark.txt
The number of allocated objects per render is slightly better than v4 because it's using the LookupContext cached resolve structures instead of storing its own format and variants. There is some additional memory retained per component as the
Template
objects remain in scope after compilation, but the Template objects are small and the overhead is proportional to the number of component templates, it won't grow during application lifecycle.My team has added this code to one of our larger projects and it will go to production in the next launch. I'll provide feedback, if any, once we have it.
Anything you want to highlight for special attention from reviewers?
I intend that this change has minimal impact on the observable API of ViewComponents. The benefits would be better alignment with Rails' behaviour, in line with the goal of seamless integration.
I've discussed this approach with @joelhawksley but I'm keen for feedback from other members of the community.