рельсы пропускают некоторые части из кэширования фрагментов

У меня есть приложение rails 4, использующее драгоценный камень pundit для авторизации. Если я сделаю кеширование фрагмента матрешки, как в коде ниже, условный оператор, используемый для авторизации, также будет закэширован, что нехорошо, поскольку кнопки редактирования/удаления должны быть доступны только для post.user.

Каков хороший способ обойти это? Должен ли я разделить кэш на более мелкие части или есть способ исключить некоторые части кэширования? Каково соглашение рельсов в этом случае?

index.html.erb

<% cache ["posts-index", @posts.map(&:id), @posts.map(&:updated_at).max, @posts.map {|post| post.user.profile.updated_at}.max] do %>
  <%= render @posts %>
<% end %>

_post.html.erb

<% cache ['post', post, post.user.profile ] do %>

  <div class="row>
    <div class="col-md-2">
      <%= link_to user_path(post.user) do %>
        <%= image_tag post.user.avatar.url(:base_thumb), class: 'post-avatar' %>
      <% end %>
    </div>

    <div class="col-md-8">
      <span class="post-user-name"><%= post.user.full_name %></span>
      <span class="post-updated"><%= local_time_ago(post.updated_at) %></span>
      <div class="post-body">
        <%= post.body %>
      </div>

    <div class="col-md-2" style="text-align:right;">

      <!--############### THIS IS THE PART THAT SHOULD NOT BE CACHED #############-->

      <% if policy(post).edit? && policy(post).delete? %> 
        <li class="dropdown">
          <ul class = "dropdown-menu dropdown-menu-right">
            <li>
              <%= link_to "Edit Post", edit_post_path(post), remote: true, type: "button", 'data-toggle' => "modal", 'data-target' => "#updatepost_#{post.id}" %>
            </li>   
            <li>
              <a href="#" data-toggle="modal" role="button" data-target="#deletepost_<%= post.id %>">Delete Post</a>
            </li>
          </ul>
        </li>
      <% end %>

      <!--########################## UNTIL HERE ############################-->

    </div>
  </div>

  <div class = "row comment-top-row" style="padding-bottom:10px;">
    <div class="col-md-12 post-comment-form">
      <%= render partial: 'posts/post_comments/post_comment_form', locals: { post: post } %>
    </div>
  </div>

  <div class = "row">
    <div class="col-md-12 post-comment-insert-<%= post.id%>">
      <%= render partial: 'posts/post_comments/post_comment', collection: post.post_comments.ordered.included, as: :post_comment, locals: {post: post} %>
    </div>
  </div>

  <% if policy(post).edit? %>
    <div class="modal fade updatepost" id="updatepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
       <!-- FORM GETS RENDERED HERE VIA JS -->
    </div>
  <% end %>

  <% if policy(post).delete? %>
    <div class="modal fade" id="deletepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
      ......
    </div>
  <% end %>

<% end %>

person Sean Magyar    schedule 06.04.2016    source источник


Ответы (1)


Кэширование Русской Куклы — это простой, но удобный способ кэширования, здесь нет сложных опций или условностей для исключения из него части фрагмента. Кроме того, это больше относится к стратегиям кэширования. Вот две стратегии для этой конкретной ситуации пользователя:

  1. Переупорядочивать по отдельности и кэшировать фрагменты вручную, чего я не рекомендую. Потому что он более сложный и не использует преимущества кэширования русских кукол. Не такой ремонтопригодный. Вот пример:

index.html.erb

<% # pull out cache %>
<%= render @posts %>

_post.html.erb

<% cache post %>
  <%= # first part %>
<% end %>

<% # without cache %>
<%= # user specific part %>

<% cache post %>
  <%= # third part %>
<% end %>
  1. Предпочтительный способ: добавьте current_user как часть cache_key, что означает, что у вас будет столько кешей фрагментов, сколько примерно ваших пользователей, и фрагменты будут автоматически аннулироваться всякий раз, когда сообщение или пользователь меняют свой отпечаток пальца. Это более элегантно и ремонтопригодно. Вот пример:

index.html.erb

<% cache ["posts-index", @posts.map(&:id), @posts.map(&:updated_at).max, @posts.map {|post| post.user.profile.updated_at}.max] do %>
  <%= render @posts %>
<% end %>

_post.html.erb

<% cache ['post', post, post.user.profile, current_user ] do %>
  <div class="row>
    <div class="col-md-2">
      <%= link_to user_path(post.user) do %>
        <%= image_tag post.user.avatar.url(:base_thumb), class: 'post-avatar' %>
      <% end %>
    </div>

    <div class="col-md-8">
      <span class="post-user-name"><%= post.user.full_name %></span>
      <span class="post-updated"><%= local_time_ago(post.updated_at) %></span>
      <div class="post-body">
        <%= post.body %>
      </div>

    <div class="col-md-2" style="text-align:right;">
      <% if policy(post).edit? && policy(post).delete? %> 
        <li class="dropdown">
          <ul class = "dropdown-menu dropdown-menu-right">
            <li>
              <%= link_to "Edit Post", edit_post_path(post), remote: true, type: "button", 'data-toggle' => "modal", 'data-target' => "#updatepost_#{post.id}" %>
            </li>   
            <li>
              <a href="#" data-toggle="modal" role="button" data-target="#deletepost_<%= post.id %>">Delete Post</a>
            </li>
          </ul>
        </li>
      <% end %>
    </div>
  </div>

  <div class = "row comment-top-row" style="padding-bottom:10px;">
    <div class="col-md-12 post-comment-form">
      <%= render partial: 'posts/post_comments/post_comment_form', locals: { post: post } %>
    </div>
  </div>

  <div class = "row">
    <div class="col-md-12 post-comment-insert-<%= post.id%>">
      <%= render partial: 'posts/post_comments/post_comment', collection: post.post_comments.ordered.included, as: :post_comment, locals: {post: post} %>
    </div>
  </div>

  <% if policy(post).edit? %>
    <div class="modal fade updatepost" id="updatepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
       <!-- FORM GETS RENDERED HERE VIA JS -->
    </div>
  <% end %>

  <% if policy(post).delete? %>
    <div class="modal fade" id="deletepost_<%= post.id %>" tabindex="-1" role="dialog" aria-labelledby="myModalLabel">
      ......
    </div>
  <% end %>
<% end %>
person abookyun    schedule 11.04.2016
comment
abookyun, второй способ достаточно хорош? Не потеряет ли кеширование свою цель? Я имею в виду, как вы упомянули, фрагмент будет переписан для каждого отдельного пользователя. Кроме того, не будет ли он потреблять слишком много памяти в долгосрочной перспективе? - person Sean Magyar; 18.04.2016
comment
Пожалуйста, прочитайте также мой предыдущий комментарий. Я забыл упомянуть (может быть, вы видели), что у меня post_comment вложено в post, а post_comment_reply вложено в post_comment. Они также должны быть доступны для редактирования только current_user, поэтому в них также есть некоторые части, видимые только current_user. Должен ли я менять такие, или touch позаботится об этом? - person Sean Magyar; 18.04.2016
comment
@SzilardMagyar ИМХО: как я уже сказал, это проблема стратегии, только если есть возможность сохранять тяжелые запросы, такие как вложенные сообщения и запросы комментариев, я думаю, что этого достаточно, напротив, если шаблон часто меняется, вам нужна другая структура кэширования шаблонов. Вы можете прочитать эту статью, чтобы понять, как кеширование влияет на скорость вашего приложения signalvnoise.com/posts/ - person abookyun; 19.04.2016