Cache it all

Posted by David N. Welton Fri, 13 Jun 2008 14:21:00 GMT

I recently redid my personal web site, at welton.it. Wanting to be quick about it, and make the look and feel a bit more uniform than it has been in the past, I hacked together some pages in Rails. Despite this being sort of a "killing a fly with a bazooka" situation, I've been doing lots with Rails, so it was quick to use. Here's the thing, though: Rails is definitely overkill, as the site is basically static. I don't need to calculate anything or fetch stuff from a database - I just wanted a reasonably good template system, and I am quite comfortable with Rails these days.

But the idea of leaving Rails running for a static site was of course no good: I basically need to cache the entire thing, so that Rails is simply not involved. How to do this as quick as possible (in between diaper changing and other baby duties!) ? Ideally, it would be possible to introspect Rails in order to know exactly which pages are present, then cache those, and avoid Rails on the server entirely (just generate them locally and put them in subversion), but that proved to be fairly hacky, so I settled for this code, which simply caches all pages which comes across, when caches_pages is placed in application.rb:

class CacheFileName
  include ActionController::Caching::Pages
  include ActionController::Caching::Pages::ClassMethods

  def cachedname(path)
    page_cache_file(path)
  end
end

def caches_pages()
  return unless perform_caching
  after_filter do |c|
    res = c.cache_page
    cfn = CacheFileName.new
    cf = Cachedfile.new :filename => cfn.cachedname(c.request.path)
    cf.save!
  end
end

It simply caches everything. To be able to easily clear out the cache if there are any changes to the site, we record the changes in the Cachedfile model, which is defined like this:

create_table "cachedfiles", :force => true do |t|
  t.string   "filename"
  t.datetime "created_at"
  t.datetime "updated_at"
end

with this model:

class Cachedfile < ActiveRecord::Base

  def Cachedfile.clean_cache
    Cachedfile.find(:all).each do |cf|
      begin
        fn = ActionController::Base.page_cache_directory + cf.filename
        File.delete fn
      rescue => e
        logger.error "Error deleting #{fn}: #{e.inspect}"
      ensure
        cf.destroy
      end
    end
  end

end

which has a class method to go through and clean out all the cached files. I call it manually from ./script/clean_cache:

#!/usr/bin/env ruby

ENV['RAILS_ENV'] = ARGV.first || ENV['RAILS_ENV'] || 'development'

require File.dirname(__FILE__) + '/../config/boot'
require "#{RAILS_ROOT}/config/environment"
require 'console_app'

Cachedfile.clean_cache

It's not a beautiful system, but it gets the job done.

5 comments | Tags , | atom

Trackbacks

Use the following link to trackback from your own site:
http://journal.dedasys.com/trackbacks?article_id=cache-it-all&day=13&month=06&year=2008

Comments

Leave a response

  1. carmen
    about 5 hours later:

    any reason youre creating a CachedFile model with its own filenames, atime, mtime, inside a web framework inside an ORM inside Ruby via an SQL daemon, when theres OS calls that can support this natively, and blinding fast ruby bindings to said functions?

    just curious..

  2. David Welton
    about 11 hours later:

    Because it "just doesn't matter". Saving a few microseconds every month is less important than writing the code quickly.

    In any case, now I have a system that caches everything so that Rails, which is fairly heavy duty, doesn't have to run. Rails is quite useful, on the other hand, as a quick and dirty way to get templates working the way I want.

  3. Stuart Ellis
    1 day later:

    FWIW, Nanoc is a static Website builder that is written in Ruby and uses templates, and generally has a slightly Railsish flavour.

  4. Ben Marini
    2 days later:

    Why not do something simpler? In your application controller you could just put before_filter {|c| c.cache_page}

    And to easily wipe the cache, change the cache directory.

    That way you could have a rake task that just deletes everything in public/cache.

  5. Stephen Duncan Jr
    2 days later:

    Similar to the suggestion to use nanoc, I've enjoyed using Webby: http://webby.rubyforge.org/

Leave a comment