Automatic Expiration of Rails Action Caching
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:
1 2 class PeopleController < ApplicationController 3 caches_action :show, :for => 1.hour, :cache_path => Proc.new { |c| "people/#{c.params[:id]}_for_#{Person.logged_in.id}" } 4 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.
1 2 module ActionController 3 module Caching 4 module Fragments 5 # expire a cache key only if the block returns true or 6 # if the age of the fragment is more than the specified age argument. 7 def expire_fragment_by_mtime(key, age=nil, &block) 8 block = Proc.new { |m| m < age.ago } unless block_given? 9 if (m = cache_store.mtime(fragment_cache_key(key))) and block.call(m) 10 expire_fragment(key) 11 end 12 end 13 end 14 module Actions 15 module ClassMethods 16 # adds an option :for 17 # caches_action :show, :for => 2.hours, :cache_path => ... 18 # :cache_path is required, unfortunately 19 def caches_action_with_for(*actions) 20 original_actions = actions.clone 21 options = actions.extract_options! 22 if for_time = options.delete(:for) 23 cache_path = options[:cache_path] 24 before_filter do |controller| 25 cache_path = cache_path.call(controller) if cache_path.respond_to?(:call) 26 controller.expire_fragment_by_mtime(cache_path, for_time) 27 end 28 end 29 caches_action_without_for(*original_actions) 30 end 31 alias_method_chain :caches_action, :for 32 end 33 end 34 end 35 end 36 37 # Add a method to grab the last modified time of the cache key. 38 # If you use a store other than the FileStore, you'll need to add 39 # a method like this to your store. 40 module ActiveSupport 41 module Cache 42 class FileStore 43 def mtime(name) 44 File.mtime(real_file_path(name)) rescue nil 45 end 46 end 47 end 48 end