No doubt, 5 minutes after posting this, someone will tell me of the built-in Rails way of doing this, but alas I could not find it.
Usage looks like this:
class PeopleController < ApplicationController caches_action :show, :for => 1.hour, :cache_path => Proc.new { |c| "people/#{c.params[:id]}_for_#{Person.logged_in.id}" } end
The ":for => 1.hour" part is where the magic happens.
Basically, this bit of code adds a before_filter that checks the last modified time of the cache entry, and expires it if it is older than the specified time period.
module ActionController module Caching module Fragments # expire a cache key only if the block returns true or # if the age of the fragment is more than the specified age argument. def expire_fragment_by_mtime(key, age=nil, &block) block = Proc.new { |m| m < age.ago } unless block_given? if (m = cache_store.mtime(fragment_cache_key(key))) and block.call(m) expire_fragment(key) end end end module Actions module ClassMethods # adds an option :for # caches_action :show, :for => 2.hours, :cache_path => ... # :cache_path is required, unfortunately def caches_action_with_for(*actions) original_actions = actions.clone options = actions.extract_options! if for_time = options.delete(:for) cache_path = options[:cache_path] before_filter do |controller| cache_path = cache_path.call(controller) if cache_path.respond_to?(:call) controller.expire_fragment_by_mtime(cache_path, for_time) end end caches_action_without_for(*original_actions) end alias_method_chain :caches_action, :for end end end end # Add a method to grab the last modified time of the cache key. # If you use a store other than the FileStore, you'll need to add # a method like this to your store. module ActiveSupport module Cache class FileStore def mtime(name) File.mtime(real_file_path(name)) rescue nil end end end end