module Padrino
module Cache
##
# Helpers supporting page or fragment caching within a request route.
#
module Helpers
##
# Page caching is easy to integrate into your application. To turn it on, simply provide the
# :cache => true option on either a controller or one of its routes.
# By default, cached content is persisted with a "file store" --that is, in a
# subdirectory of your application root.
#
# @example
# # Setting content expiry time.
# class CachedApp < Padrino::Application
# enable :caching # turns on caching mechanism
#
# controller '/blog', :cache => true do
# expires 15
#
# get '/entries' do
# # expires 15 => can also be defined inside a single route
# 'Just broke up eating twinkies, lol'
# end
#
# get '/post/:id' do
# cache_key :my_name
# @post = Post.find(params[:id])
# end
# end
# end
#
# You can manually expire cache with CachedApp.cache.delete(:my_name)
#
# Note that the "latest" method call to expires determines its value: if
# called within a route, as opposed to a controller definition, the route's
# value will be assumed.
#
module Page
##
# This helper is used within a controller or route to indicate how often content
# should persist in the cache.
#
# After seconds seconds have passed, content previously cached will
# be discarded and re-rendered. Code associated with that route will not
# be executed; rather, its previous output will be sent to the client with a
# 200 OK status code.
#
# @param [Integer] time
# Time til expiration (seconds)
#
# @example
# controller '/blog', :cache => true do
# expires 15
#
# get '/entries' do
# 'Just broke up eating twinkies, lol'
# end
# end
#
# @api public
def expires(time)
@route.cache_expires = time
end
##
# This helper is used within a route or route to indicate the name in the cache.
#
# @param [Symbol] name
# cache key
# @param [Proc] block
# block to be evaluated to cache key
#
# @example
# controller '/blog', :cache => true do
#
# get '/post/:id' do
# cache_key :my_name
# @post = Post.find(params[:id])
# end
# end
#
# @example
# get '/foo', :cache => true do
# cache_key { param[:id] }
# "My id is #{param[:id}"
# end
# end
#
def cache_key(name = nil, &block)
fail "Can not provide both cache_key and a block" if name && block
@route.cache_key = name || block
end
CACHED_VERBS = { 'GET' => true, 'HEAD' => true }.freeze
def self.padrino_route_added(route, verb, *)
return unless route.cache && CACHED_VERBS[verb]
route.before_filters do
next unless settings.caching?
if cached_response = load_cached_response
content_type cached_response[:content_type]
halt 200, cached_response[:body]
end
end
route.after_filters do
save_cached_response(route.cache_expires) if settings.caching?
end
end
private
def load_cached_response
began_at = Time.now
route_cache_key = resolve_cache_key || env['PATH_INFO']
value = settings.cache[route_cache_key]
logger.debug "GET Cache", began_at, route_cache_key if defined?(logger) && value
value
end
def save_cached_response(cache_expires)
return unless @_response_buffer.kind_of?(String)
began_at = Time.now
route_cache_key = resolve_cache_key || env['PATH_INFO']
content = {
:body => @_response_buffer,
:content_type => @_content_type
}
settings.cache.store(route_cache_key, content, :expires => cache_expires)
logger.debug "SET Cache", began_at, route_cache_key if defined?(logger)
end
##
# Resolve the cache_key when it's a block in the correct context.
#
def resolve_cache_key
key = @route.cache_key
key.is_a?(Proc) ? instance_eval(&key) : key
end
module ClassMethods
##
# A method to set `expires` time inside `controller` blocks.
#
# @example
# controller :users do
# expires 15
#
# get :show do
# 'shown'
# end
# end
#
def expires(time)
@_expires = time
end
end
end
end
end
end