Question

Since Rails 4 removed page caching and action caching, I'm wondering what is the Rails 4 way to cache an action that has no variables and has only html in the view? Should I fragment cache the static html in the view? How do I set an etag/fresh_when when there is no model to set it to expire with? I'm struggling to find an example or convention to caching what should be the easiest page to cache.

One note is that while the view is completely static, the page still has a dynamic navbar depending on whether the user is signed in or not. How would you handle a static page like this without resorting to action caching since its been removed and the convention has been set not to use the gem version?

Example:

class HomesController < ApplicationController    
  def index
  end
end

homes/index.html.erb

<div>A bunch of normal html tags with no erb</div>

Edit: Based on @severin's answer and my own research, here is what I have come up with so far.

class HomesController < ApplicationController    
  def index
    fresh_when(["some-identifier", current_user, flash])
  end
end

In addition, I'm using https://github.com/n8/bust_rails_etags to reset all etags after a deploy because the view may have changed between deploys. I think this covers the etag fairly well although I'm still curious whether fresh when will include some identifier about the view automatically and whether "some-idnetifier" is necessary? Is it going to be a problem that sometimes current_user and flash will be nil?

Now on the second point of fragment caching the static content. I'm assuming if I did:

cache "v1" do
  all my html
end

I'd have to remember to always change the cache identifier when the page is changed otherwise my app would serve stale content. Any way to automate this as well or is already handled by rails? It would be nice to just cache the last time the view was updated or something clever so I don't have to keep track on when my static content is changed.

Was it helpful?

Solution

You can set an etag/last modified at data without a model, check the documentation: http://api.rubyonrails.org/classes/ActionController/ConditionalGet.html#method-i-fresh_when

So you could do something like:

def index
  fresh_when(:etag => 'some_made_up_etag', :last_modified => a_long_time.ago, :public => true)
  render
end

Note: you don't need to provide an etag AND a last modified at timestamp, you could just provide an etag or only a last modified at timestamp.

In addition to this, I would also fragment cache the whole content of the view.

Or you could just continue using action-/page_caching using the official plugin/gem: https://github.com/rails/actionpack-page_caching


Some additions regarding the second part of your question:

Rails adds the content of the RAILS_CACHE_ID environment variable to all its cache keys (the etag and the fragment cache key in your example). The bust_rails_etags gem adds another environment variable that affects only the etags... So in your case you could just remove the bust_rails_etags gem and update the RAILS_CACHE_ID environment variable on all your deploys.

You can even automate the updating of the RAILS_CACHE_ID environment variable by adding something like this in config/environment.rb:

code_revision = # ... some piece of code that gets the current revision.
                # I'm using git and I use the following (crude) piece of
                # to get the current revision:
                # code_revision = `git log --pretty=format:%h -n1`.strip
ENV['RAILS_CACHE_ID'] = code_revision

This way, the current code revision is always added to all cache keys.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top