3

Rails 7 adds caching? and uncachable! helper

 2 years ago
source link: https://blog.saeloun.com/2021/12/08/rails-7-adds-cacheable-helper
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Rails 7 adds caching? and uncachable! helper

Dec 8, 2021 , by Murtaza Bagwala 1 minute read

We often hear Cache invalidation is a hard problem in computer science and could cause bugs but sometimes, caching something that should not be cached is a potential source of bugs and security vulnerabilities. Rails has a built-in mechanism for Fragment Caching, which stores part of the rendered view as a fragment. For subsequent requests, the pre-saved fragment is used instead of rendering it again.

But this could cause some serious bugs! For example, we could have an S3 URL helper which generates a unique presigned URL for each product or one could write a form helper that outputs a request-specific auth token. In such cases, it is better to avoid fragment caching.

Before

We can implement fragment caching using the cache helper.

views/products/index.html.erb

  <table>
    <thead>
        <tr>
        <th>Title</th>
        <th>Description</th>
        <th>Image</th>
        </tr>
    </thead>

    <tbody>
        <% @products.each do |product| %>
          <% cache(product) do %>
              <%= render product %>
          <% end %>
        <% end %>
    </tbody>
  </table>

views/products/_product.html.erb

  <tr>
    <td><%= product.title %></td>
    <td><%= product.description %></td>
    <td><%= image_tag(generate_presigned_url(product.image_url)) %></td>
  </tr>

But there is a bug because we get a cached version of the product each time we render despite generating a unique presigned URL each time. To resolve this, we need to include cacheable in the Product partial. If someone tries to cache the product partial, it will throw an ActionView::Template::Error error.

After

  <tr>
    <%= uncacheable! %>
    <td><%= product.title %></td>
    <td><%= product.description %></td>
    <td><%= image_tag(generate_presigned_url(product.image_url)) %></td>
  </tr>

which would result in,

```
  ActionView::Template::Error (can't be fragment cached):
    1: <tr>
    2:     <%= uncacheable! %>
    3:   <td><%= product.title %></td>
    4:   <td><%= product.description %></td>
    5:   <td><%= image_tag(generate_presigned_url(product.image_url)) %></td>

  app/views/products/_product.html.erb:2
```

We can also use the caching? helper to check whether the current code path is being cached or to enforce caching.

  <tr>
    <%= raise Exception.new "This partial needs to be cached" unless caching? %>
    <td><%= product.title %></td>
    <td><%= product.description %></td>
  </tr>

For more discussion related to this change, please refer to this PR.


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK