Cache it all
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.
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
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..
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.
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.
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.
2 days later:
Similar to the suggestion to use nanoc, I've enjoyed using Webby: http://webby.rubyforge.org/