Skip to content

Engine: Optimize Rails-specific Partial Render Calls #654

Description

@marcoroth

Given a partial render call in Rails for an non-ambiguous partial file (which we want to encourage in the Linter through #160, #181), we could optimize these render calls, so we can avoid the slow partial file lookup at runtime.

Example:

app/views/posts/index.html

<h1>Posts</h1>

<% @posts.each do |post| %>
  <%= render partial: "posts/post", locals: { post: post } %>
<% end %>

app/views/posts/_post.html.erb

<article id="<%= dom_id(post) %>" class="post">
  <h1><%= post.title %></h1>

   <p><%= post.body %></p>
</article>

Option 1: Inline Partial

We could entirly eliminate the partial call by inlining the app/views/posts/_post.html.erb partial when we are compiling the app/views/posts/index.html view file. There are probably a few cases where this wouldn't work, like recursion, maybe deeply-nested partial calls, etc.

But effectively, before compiling app/views/posts/index.html we would transform it to this:

<h1>Posts</h1>

<% @posts.each do |post| %>
  <article id="<%= dom_id(post) %>" class="post">
    <h1><%= post.title %></h1>
  
     <p><%= post.body %></p>
  </article>
<% end %>

So when we compile it, it would result in:

__herb = ::Herb::Engine; _buf = ::String.new;
 _buf << '<h1>Posts</h1>

'.freeze; @posts.each do |post|; _buf << '
  <article id="'.freeze; _buf << ::Herb::Engine.attr((dom_id(post))); _buf << '" class="post">
    <h1>'.freeze; _buf << __herb.h((post.title)); _buf << '</h1>

     <p>'.freeze; _buf << __herb.h((post.body)); _buf << '</p>
  </article>
'.freeze; end; _buf << '
'.freeze;
_buf.to_s

And thus eliminating the slow partial file lookup at runtime.

Option 2: Rewriting the render call

Instead of inlining the partial, we could rewrite the render call to use the Action View-internal method directly. When Action View compiles the HTML+ERB files in app/views/** it defines methods for each template file it compiles.

So when we compile app/views/posts/_post.html.erb it will expose a method like _app_views_posts__post_html_erb_[digest]_[number]. Knowing this fact at compile time, we could rewrite the render partial call in app/views/posts/index.html to something like:

<h1>Posts</h1>

<% @posts.each do |post| %>
- <%= render partial: "posts/post", locals: { post: post } %>
+ <%= _app_views_posts__post_html_erb_[digest]_[number](post) %>
<% end %>

and with that, we avoid the slow partial file lookup at runtime which should improve the performance when rendering this view at runtime.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions