Expiring Rails File Cache by Time

» 13 Jul 2009

One of the major weaknesses of the Rails cache file store is that cached pages cannot be expired by time. This poses a problem for keeping caching as simple as possible. The solution I came up with stores cached content as a JSON file containing the content and the ttl. Expiration needs only be set when @cache@ is called. @read_fragment@ should know nothing about expiration.
  <% cache('home_most_popular', :expires_in => 1.day) do %>
    <%= render :partial => 'most_popular' %>
  <% end %>
  
The code that makes this work should go in @lib/hash_file_store.rb@
  require 'json'
  
  class HashFileStore < ActiveSupport::Cache::FileStore                                
    def write(name, value, options = nil)
      ttl = 0   
      if options.is_a?(Hash) && options.has_key?(:expires_in)                          
        ttl = options[:expires_in]                                                     
        #options.delete(:expires_in)                                                   
      end
      value = JSON.generate({'ttl' => ttl, 'value' => value.dup})                      
      super
    end 
  
    def read(name, options = nil)                                                      
      value = super
      return unless value                                                              

      #malformed JSON is the same as no JSON
      value = JSON.parse(value) rescue nil 

      fn = real_file_path(name)                                                        
      if value && (value['ttl'] == 0 || 
        (File.mtime(fn) > (Time.now - value['ttl'])))  
        
        value['value']                                                                 
      else
        nil
      end
    end
  end                    
  
Put the following line in @config/environment.rb@ and it should be good to go.
  ActionController::Base.cache_store = :hash_file_store, 
    "#{RAILS_ROOT}/tmp/cache"