dee
dee

Reputation: 1898

How do I cache a personalised fragment in Rails 4?

My app (Rails 4) allows users to vote on posts. Is it possible to cache the post, but personalise the vote cache so it shows one personalised for current_user? For example, whether the user voted or not.

I would rather not change the html structure to achieve this.

# posts/_post.html.slim
- cache post do
  h1 = post.title
  = post.text
  = render 'votes/form', post: post

# votes/_form.html.slim
- if signed_in? && current_user.voted?(post)
  = form_for current_user.votes.find_by(post: post), method: :delete do |f|
    = f.submit
- else
  = form_for Vote.new do |f|
    = f.submit

Upvotes: 5

Views: 1011

Answers (2)

beydogan
beydogan

Reputation: 1110

Try the following, this will create 2 different fragments for both voted and not voted for each post. It will be read by according to its status.

# posts/_post.html.slim
- cache [post, current_user.votes.find_by(post: post)]do
  h1 = post.title
  = post.text
  = render 'votes/form', post: post

Upvotes: 1

jokklan
jokklan

Reputation: 3540

You have two options here:

Option 1: don't cache the votes

This is the simplest solution and the one i personally recommends. You just don't cache the dynamic user dependent part so you have something like this:

# posts/_post.html.slim
- cache post do
  h1 = post.title
  = post.text
= render 'votes/form', post: post # not cached

Option 2: use javascript

This solution is more complex, but is actually how basecamp do it (however mostly with more simple examples). You have both part rendered on the page but remove one of them with javascript. Here is a example using jQuery and CoffeeScript:

# posts/_post.html.slim
- cache post do
  h1 = post.title
  = post.text
  = render 'votes/form', post: post

# votes/_form.html.slim
div#votes{"data-id" => post.id}
  .not_voted
    = form_for current_user.votes.find_by(post: post), method: :delete do |f|
      = f.submit
  .voted
    = form_for Vote.new do |f|
      = f.submit

# css
.not_voted {
  display:none;
}

# javascript (coffeescript)
jQuery ->
  if $('#votes').length
    $.getScript('/posts/current/' + $('#votes').data('id'))

# posts_controller.b
def current
  @post = Post.find(params[:id])
end

# users/current.js.erb
<% signed_in? && current_user.voted?(@post) %>
  $('.voted').hide();
  $('.not_voted').show();
<% end %>

I would however properly change voted? method to accept a id, so you don't need to make a new query. You can learn more about this approach in this railscasts: http://railscasts.com/episodes/169-dynamic-page-caching-revised?view=asciicast

Upvotes: 5

Related Questions