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
In order to use page and action caching you will need to add actionpack-page_caching and actionpack-action_caching to your Gemfile . By default, caching is only enabled in your production environment. You can play around with caching locally by running rails dev:cache , or by setting config. action_controller.
To use Redis as a Rails cache store, use a dedicated cache instance that's set up as an LRU (Last Recently Used) cache instead of pointing the store at your existing Redis server, to make sure entries are dropped from the store when it reaches its maximum size.
Cache sweeping is a mechanism which allows you to get around having a ton of expire_{page,action,fragment} calls in your code. It does this by moving all the work required to expire cached content into na ActionController::Caching::Sweeper class.
View caching in Ruby on Rails is taking the HTML that a view generates and storing it for later.
You have two options here:
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
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
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With