require 'fileutils'
module Sinatra
# Sinatra Caching module
#
# TODO:: Need to write documentation here
#
module Cache
VERSION = 'Sinatra::Cache v0.2.0'
def self.version; VERSION; end
module Helpers
# Caches the given URI to a html file in /public
#
# Usage:
# >> cache( erb(:contact, :layout => :layout))
# => returns the HTML output written to /public//contact.html
#
# Also accepts an Options Hash, with the following options:
# * :extension => in case you need to change the file extension
#
# TODO:: implement the opts={} hash functionality. What other options are needed?
#
def cache(content, opts={})
return content unless settings.cache_enabled
unless content.nil?
content = "#{content}\n#{page_cached_timestamp}\n"
path = cache_page_path(request.path_info,opts)
FileUtils.makedirs(File.dirname(path))
open(path, 'wb+') { |f| f << content }
log("Cached Page: [#{path}]",:info)
content
end
end
# Expires the cached URI (as .html file) in /public
#
# Usage:
# >> cache_expire('/contact')
# => deletes the /public/contact.html page
#
# get '/contact' do
# cache_expire # deletes the /public/contact.html page as well
# end
#
# TODO:: implement the options={} hash functionality. What options are really needed ?
def cache_expire(path = nil, opts={})
return unless settings.cache_enabled
path = (path.nil?) ? cache_page_path(request.path_info) : cache_page_path(path)
if File.exist?(path)
File.delete(path)
log("Expired Page deleted at: [#{path}]",:info)
else
log("No Expired Page was found at the path: [#{path}]",:info)
end
end
# Prints a basic HTML comment with a timestamp in it, so that you can see when a file was cached last.
#
# *NB!* IE6 does NOT like this to be the first line of a HTML document, so output
# inside the tag. Many hours wasted on that lesson ;-)
#
# Usage:
# >> <%= page_cached_timestamp %>
# =>
#
def page_cached_timestamp
"\n" if settings.cache_enabled
end
private
# Establishes the file name of the cached file from the path given
#
# TODO:: implement the opts={} functionality, and support for custom extensions on a per request basis.
#
def cache_file_name(path,opts={})
name = (path.empty? || path == "/") ? "index" : Rack::Utils.unescape(path.sub(/^(\/)/,'').chomp('/'))
name << settings.cache_page_extension unless (name.split('/').last || name).include? '.'
return name
end
# Sets the full path to the cached page/file
# Dependent upon Sinatra.settings .public and .cache_dir variables being present and set.
#
def cache_page_path(path,opts={})
# test if given a full path rather than relative path, otherwise join the public path to cache_dir
# and ensure it is a full path
cache_dir = (settings.cache_dir == File.expand_path(settings.cache_dir)) ?
settings.cache_dir : File.expand_path("#{settings.public}/#{settings.cache_dir}")
cache_dir = cache_dir[0..-2] if cache_dir[-1,1] == '/'
"#{cache_dir}/#{cache_file_name(path,opts)}"
end
# TODO:: this implementation really stinks, how do I incorporate Sinatra's logger??
def log(msg,scope=:debug)
if settings.cache_logging
"Log: msg=[#{msg}]" if scope == settings.cache_logging_level
else
# just ignore the stuff...
# puts "just ignoring msg=[#{msg}] since cache_logging => [#{settings.cache_logging.to_s}]"
end
end
end #/module Helpers
# Sets the default settings:
#
# * +:cache_enabled+ => toggle for the cache functionality. Default is: +true+
# * +:cache_page_extension+ => sets the default extension for cached files. Default is: +.html+
# * +:cache_dir+ => sets cache directory where cached files are stored. Default is: ''(empty) == root of /public.
# set to empty, since the ideal 'system/cache/' does not work with Passenger & mod_rewrite :(
# * +cache_logging+ => toggle for logging the cache calls. Default is: +true+
# * +cache_logging_level+ => sets the level of the cache logger. Default is: :info.
# Options:(unused atm) [:info, :warn, :debug]
#
def self.registered(app)
app.helpers(Cache::Helpers)
app.set :cache_enabled, true
app.set :cache_page_extension, '.html'
app.set :cache_dir, ''
app.set :cache_logging, true
app.set :cache_logging_level, :info
end
end #/module Cache
register(Sinatra::Cache)
end #/module Sinatra