diff --git a/lib/sinatra/cache/helpers.rb b/lib/sinatra/cache/helpers.rb
index 3bdcc95..794019f 100644
--- a/lib/sinatra/cache/helpers.rb
+++ b/lib/sinatra/cache/helpers.rb
@@ -1,6 +1,6 @@
-module Sinatra
-
+module Sinatra
+
# = Sinatra::Cache
#
# A Sinatra Extension that makes Page and Fragment Caching easy wthin your Sinatra apps.
@@ -325,9 +325,9 @@ module Sinatra
#
#
module Cache
-
- module Helpers
-
+
+ module Helpers
+
##
# This method either caches the code fragment and then renders it,
# or locates the cached fragement and renders that.
@@ -387,10 +387,10 @@ module Helpers
# ... would use the same cached fragment.
#
# @api public
- def cache_fragment(fragment_name, shared = nil, &block)
+ def cache_fragment(fragment_name, shared = nil, &block)
# 1. check for a block, there must always be a block
raise ArgumentError, "Missing block" unless block_given?
-
+
# 2. get the fragment path, by combining the PATH_INFO of the request, and the fragment_name
dir_structure = request.path_info.empty? ? '' : request.path_info.gsub(/^\//,'').gsub(/\/$/,'')
# if we are sharing this fragment with other URLs (as in the all the articles in a category of a blog)
@@ -402,7 +402,7 @@ def cache_fragment(fragment_name, shared = nil, &block)
cf = "#{settings.cache_fragments_output_dir}/#{dir_structure}/#{fragment_name}.html"
# 3. ensure the fragment cache directory exists for this fragment
FileUtils.mkdir_p(File.dirname(cf)) rescue "ERROR: could NOT create the cache directory: [ #{File.dirname(cf)} ]"
-
+
# 3. check if the fragment is already cached ?
if test(?f, cf)
# 4. yes. cached, so load it up into the ERB buffer . Sorry, don't know how to do this for Haml or any others.
@@ -426,19 +426,41 @@ def cache_fragment(fragment_name, shared = nil, &block)
end
# for future versions once old habits are gone
# alias_method :cache, :cache_fragment
-
-
+
+ ##
+ # Cache some raw data
+ # @api public
+ def cache_raw(options = {}, &block)
+
+ # 1. check for a block, there must always be a block
+ raise ArgumentError, "Missing block" unless block_given?
+
+ if cache_enabled?(options)
+ file_path = cache_file_path
+ if test(?f, file_path)
+ IO.read(file_path)
+ else
+ content = block.call
+ cache_write_file(file_path, content)
+ content
+ end
+ else
+ block.call
+ end
+ end
+
+
##
# NB!! Deprecated method.
#
# Just returns the content after throwing out a warning.
#
- def cache(content, opts={})
+ def cache(content, opts={})
warn("Deprecated method, caching is now happening by default if the :cache_enabled option is true")
content
end
-
-
+
+
##
# Expires the cached file (page) or fragment.
#
@@ -455,18 +477,18 @@ def cache(content, opts={})
#
#
# @api public
- def cache_expire(path, options={})
+ def cache_expire(path, options={})
# 1. bail quickly if we don't have caching enabled
return unless settings.cache_enabled
options = { :fragment => false }.merge(options)
-
+
if options[:fragment] # dealing with a fragment
dir_structure = path.gsub(/^\//,'').gsub(/\/$/,'')
file_path = "#{settings.cache_fragments_output_dir}/#{dir_structure}/#{options[:fragment]}.html"
else
file_path = cache_file_path(path)
end
-
+
if test(?f, file_path)
File.delete(file_path)
log(:info,"Expired [#{file_path.sub(settings.root,'')}] successfully")
@@ -474,8 +496,8 @@ def cache_expire(path, options={})
log(:warn,"The cached file [#{file_path}] could NOT be expired as it was NOT found")
end
end
-
-
+
+
##
# Prints a basic HTML comment with a timestamp in it, so that you can see when a file was cached last.
#
@@ -487,7 +509,7 @@ def cache_expire(path, options={})
# <%= cache_timestamp %> # =>
#
# @api public
- def cache_timestamp
+ def cache_timestamp
if settings.cache_enabled && settings.cache_environment == settings.environment
"\n"
end
@@ -495,44 +517,43 @@ def cache_timestamp
# backwards compat and syntactic sugar for others
alias_method :cache_page_timestamp, :cache_timestamp
alias_method :page_cached_at, :cache_timestamp
-
-
+
+
## PRIVATE METHODS
private
-
+
##
# Converts the PATH_INFO path into the full cached file path.
- #
+ #
# ==== GOTCHA:
- #
- # NB! completely ignores the URL query params passed such as
+ #
+ # NB! completely ignores the URL query params passed such as
# in this example:
- #
- # /products?page=2
- #
+ #
+ # /products?page=2
+ #
# To capture and cache those query strings, please do as follows:
- #
+ #
# get 'products/page/:page' { ... } # in your Sinatra app
- #
+ #
# /products/page/2 => .../public/cache/products/page/2.html
- #
- #
+ #
+ #
# ==== Examples
- #
+ #
# / => .../public/cache/index.html
- #
+ #
# /contact => .../public/cache/contact.html
- #
+ #
# /contact/ => .../public/cache/contact/index.html
- #
- #
+ #
+ #
# @api public
- def cache_file_path(in_path = nil)
+ def cache_file_path(in_path = nil)
path = settings.send(:cache_output_dir).dup
-
-
+
path_info = in_path.nil? ? request.path_info : in_path
- if (path_info.empty? || path_info == "/" )
+ if (path_info.empty? || path_info == "/" )
path << "/index"
elsif ( path_info[-1, 1] == '/' )
path << ::Rack::Utils.unescape(path_info.chomp('/') << '/index')
@@ -540,73 +561,91 @@ def cache_file_path(in_path = nil)
path << ::Rack::Utils.unescape(path_info.chomp('/'))
end
path << settings.cache_page_extension if File.extname(path) == ''
- return path
+ path
end
-
+
##
- # Writes the cached file to disk, only during GET requests,
+ # Writes the cached file to disk, only during GET requests,
# and then returns the content.
- #
+ #
# ==== Examples
- #
- #
+ #
+ #
# @api private
- def cache_write_file(cache_file, content)
+ def cache_write_file(cache_file, content)
# only cache GET request [http://rack.rubyforge.org/doc/classes/Rack/Request.html#M000239]
if request.get?
FileUtils.mkdir_p(File.dirname(cache_file)) rescue "ERROR: could NOT create the cache directory: [ #{File.dirname(cache_file)} ]"
File.open(cache_file, 'wb'){ |f| f << content}
end
- return content
+ content
end
-
+
##
# Establishes the file name of the cached file from the path given
- #
+ #
# @api private
- def cache_file_name(path, options={})
+ def cache_file_name(path, options={})
name = (path.empty? || path == "/") ? "index" : Rack::Utils.unescape(path.sub(/^(\/)/,'').chomp('/'))
name << settings.cache_page_extension unless (name.split('/').last || name).include? '.'
- return name
+ name
end
-
+
##
# Sets the full path to the cached page/file
# Dependent upon Sinatra.options .public and .cache_dir variables being present and set.
- #
- #
+ #
+ #
# @api private
- def cache_page_path(path, options={})
- # test if given a full path rather than relative path, otherwise join the public path to cache_dir
+ def cache_page_path(path, options={})
+ # 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_output_dir == File.expand_path(settings.cache_output_dir)) ?
+ cache_dir = (settings.cache_output_dir == File.expand_path(settings.cache_output_dir)) ?
settings.cache_output_dir : File.expand_path("#{settings.public}/#{settings.cache_output_dir}")
cache_dir = cache_output_dir[0..-2] if cache_dir[-1,1] == '/'
"#{cache_dir}/#{cache_file_name(path, options)}"
end
-
+
+ ##
+ # Calculate if the cache is enabled
+ #
+ # @api private
+ def cache_enabled? options
+ (settings.respond_to?(:cache_enabled) ? settings.send(:cache_enabled) : false) &&
+ (options[:cache] || true) &&
+ settings.send(:environment) == settings.cache_environment
+ end
+
+ ##
+ # Extract content type from options
+ #
+ # @api private
+ def extract_content_type options
+ options.delete(:content_type) || options.delete(:default_content_type)
+ end
+
##
# Convenience method that handles logging of Cache related stuff.
- #
+ #
# Uses Sinatra::Logger's #logger method if available, otherwise just
# puts out the log message.
- #
- def log(scope, msg)
+ #
+ def log(scope, msg)
if settings.cache_logging
if scope.to_sym == settings.cache_logging_level.to_sym
if self.respond_to?(:logger)
- logger.send(scope, msg)
+ logger.send(scope, msg)
else
puts "#{scope.to_s.upcase}: #{msg}"
end
end
end
end
-
-
+
+
end #/ Helpers
-
-
+
+
##
# The default options:
#
@@ -635,7 +674,7 @@ def log(scope, msg)
def self.registered(app)
app.register(Sinatra::OutputBuffer)
app.helpers Cache::Helpers
-
+
## CONFIGURATIONS::
app.set :cache_enabled, false
app.set :cache_environment, :production
@@ -643,11 +682,11 @@ def self.registered(app)
app.set :cache_output_dir, lambda { app.public }
app.set :cache_fragments_output_dir, lambda { "#{app.root}/tmp/cache_fragments" }
app.set :cache_fragments_wrap_with_html_comments, true
-
+
app.set :cache_logging, true
app.set :cache_logging_level, :info
-
-
+
+
## add the extension specific options to those inspectable by :settings_inspect method
if app.respond_to?(:sinatra_settings_for_inspection)
%w( cache_enabled cache_environment cache_page_extension cache_output_dir
@@ -657,11 +696,11 @@ def self.registered(app)
app.sinatra_settings_for_inspection << m
end
end
-
+
end #/ self.registered
-
+
end #/ Cache
-
+
register(Sinatra::Cache) # support classic apps
-
+
end #/ Sinatra
\ No newline at end of file
diff --git a/lib/sinatra/templates.rb b/lib/sinatra/templates.rb
index 865f045..a3eaebc 100644
--- a/lib/sinatra/templates.rb
+++ b/lib/sinatra/templates.rb
@@ -23,15 +23,7 @@ def render(engine, data, options={}, locals={}, &block)
@default_layout = :layout if @default_layout.nil?
layout = options.delete(:layout)
layout = @default_layout if layout.nil? or layout == true
- content_type = options.delete(:content_type) || options.delete(:default_content_type)
-
- # set the cache related options
- cache_enabled = settings.respond_to?(:cache_enabled) ? settings.send(:cache_enabled) : false
- cache_output_dir = settings.send(:cache_output_dir) if settings.respond_to?(:cache_output_dir)
- # raise Exception, "The Sinatra::Cache cache_output_dir variable is pointing to a non-existant directory cache_output_dir=[#{cache_output_dir}]" unless test(?d, cache_output_dir)
- cache_option = options[:cache]
- cache_option = true if cache_option.nil?
-
+
# compile and render template
layout_was = @default_layout
@default_layout = false
@@ -45,17 +37,16 @@ def render(engine, data, options={}, locals={}, &block)
options = options.merge(:views => views, :layout => false)
output = render(engine, layout, options, locals) { output }
# Cache the content or just return it
- (cache_enabled && cache_option && settings.send(:environment) == settings.cache_environment) ?
- cache_write_file(cache_file_path, output.gsub(/\n\r?$/,"")) : output
+ cache_enabled?(options) ? cache_write_file(cache_file_path, output.gsub(/\n\r?$/,"")) : output
rescue Errno::ENOENT
end
end
# rendering without a layout
- (cache_enabled && cache_option && settings.send(:environment) == settings.cache_environment) ?
- cache_write_file(cache_file_path, output.gsub(/\n\r?$/,"") ) : output
-
- output.extend(ContentTyped).content_type = content_type if content_type
+ cache_enabled?(options) ? cache_write_file(cache_file_path, output.gsub(/\n\r?$/,"") ) : output
+ if content_type = extract_content_type(options)
+ output.extend(ContentTyped).content_type = content_type
+ end
output
end