Skip to content

Commit

Permalink
first stab at caching
Browse files Browse the repository at this point in the history
  • Loading branch information
joshbuddy committed Sep 8, 2010
1 parent ea729ed commit f6dafa7
Show file tree
Hide file tree
Showing 11 changed files with 237 additions and 9 deletions.
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ padrino_gems = [
"padrino-helpers",
"padrino-mailer",
"padrino-admin",
"padrino-cache",
"padrino"
]

Expand Down
33 changes: 33 additions & 0 deletions padrino-cache/lib/padrino-cache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require 'fileutils'
require 'padrino-core'
require 'padrino-helpers'
FileSet.glob_require('padrino-cache/{helpers}/*.rb', __FILE__)

module Padrino
module Cache
##
# Register these helpers:
#
# Padrino::Cache::FragmentHelpers
# Padrino::Cache::PageHelpers
#
# for Padrino::Application
#

autoload :Store, 'padrino-cache/store'

class << self
def registered(app)
app.helpers Padrino::Cache::Helpers::CacheStore
app.helpers Padrino::Cache::Helpers::Fragment
app.helpers
app.set :cache_store, Padrino::Cache::Store::File.new(File.join(app.root, 'tmp', 'cache'))
end
alias :included :registered

def padrino_route_added(route, verb, path, args, options, block)
Padrino::Cache::Helpers::Page.padrino_route_added(route, verb, path, args, options, block)
end
end
end # Helpers
end # Padrino
11 changes: 11 additions & 0 deletions padrino-cache/lib/padrino-cache/helpers/cache_store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Padrino
module Cache
module Helpers
module CacheStore
def expire(key)
self.class.cache_store.delete(key)
end
end
end
end
end
19 changes: 19 additions & 0 deletions padrino-cache/lib/padrino-cache/helpers/fragment.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
module Padrino
module Cache
module Helpers
module Fragment
include Padrino::Helpers::OutputHelpers

def cache(key, &block)
if value = self.class.cache_store.get(key)
concat_content(value)
else
value = capture_html(&block)
self.class.cache_store.set(key, value)
concat_content(value)
end
end
end
end
end
end
20 changes: 20 additions & 0 deletions padrino-cache/lib/padrino-cache/helpers/page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Padrino
module Cache
module Helpers
module Page
def self.padrino_route_added(route, verb, path, args, options, block)
if route.cache
raise "Cachable routes must be GET or HEAD" unless %w(GET HEAD).include?(verb)
route.add_before_filter(Proc.new {
value = self.class.cache_store.get(env['PATH_INFO'])
halt 200, value if value
})
route.add_after_filter(Proc.new { |something|
self.class.cache_store.set(request.env['PATH_INFO'], @_response_buffer)
})
end
end
end
end
end
end
8 changes: 8 additions & 0 deletions padrino-cache/lib/padrino-cache/store.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
module Padrino
module Cache
module Store
autoload :File, 'padrino-cache/store/file'
autoload :Memcache, 'padrino-cache/store/memcache'
end
end
end
39 changes: 39 additions & 0 deletions padrino-cache/lib/padrino-cache/store/file.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
module Padrino
module Cache
module Store
class File
def initialize(root)
@root = root
end

def get(key)
init
::File.exist?(path_for_key(key)) ? ::File.read(path_for_key(key)) : nil
end

def set(key, value)
init
::File.open(path_for_key(key), 'w') { |f| f << value.to_s } if value
end

def delete(key)
init
FileUtils.rm_rf(path_for_key(key))
end

private
def path_for_key(key)
::File.join(@root, Rack::Utils.escape(key.to_s))
end

def init
unless @init
FileUtils.rm_rf(@root)
FileUtils.mkdir_p(@root)
@init = true
end
end
end
end
end
end
31 changes: 31 additions & 0 deletions padrino-cache/lib/padrino-cache/store/memcache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
begin
require 'memcached'
rescue LoadError
raise "You must install memecached to use the Memecache cache store backend"
end

module Padrino
module Cache
module Store
class File
def initialize(*args)
@backend = Memcached.new(*args)
end

def get(key)
@backend.get(key)
rescue Memcached::NotFound
nil
end

def set(key, value)
@backend.set(key, value)
end

def delete(key)
@backend.delete(key)
end
end
end
end
end
4 changes: 3 additions & 1 deletion padrino-cache/test/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,4 +63,6 @@ def logger # :nodoc:
@logger = nil
end
end
end
end

require File.join(File.dirname(__FILE__), '..', '..', 'padrino-core', 'test', 'helper')
45 changes: 44 additions & 1 deletion padrino-cache/test/test_padrino_cache.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
require File.expand_path(File.dirname(__FILE__) + '/helper')

class TestPadrinoCache < Test::Unit::TestCase
should "probably rename this file and start testing for real" do
should 'cache a fragment' do
called = false
mock_app do
register Padrino::Cache
get("/foo"){ cache(:test) { called ? halt(500) : (called = 'test fragment') } }
end
get "/foo"
assert_equal 200, status
assert_equal 'test fragment', body
get "/foo"
assert_equal 200, status
assert_equal 'test fragment', body
assert_not_equal called, false
end

should 'cache a page' do
called = false
mock_app do
register Padrino::Cache
get('/foo', :cache => true){ called ? halt(500) : (called = 'test fragment') }
end
get "/foo"
assert_equal 200, status
assert_equal 'test fragment', body
get "/foo"
assert_equal 200, status
assert_equal 'test fragment', body
assert_not_equal false, called
end

should 'delete from the cache' do
called = false
mock_app do
register Padrino::Cache
get('/foo', :cache => true){ called ? 'test fragment again' : (called = 'test fragment') }
get('/delete_foo'){ expire('/foo') }
end
get "/foo"
assert_equal 200, status
assert_equal 'test fragment', body
get "/delete_foo"
get "/foo"
assert_equal 200, status
assert_equal 'test fragment again', body
assert_not_equal false, called
end
end
35 changes: 28 additions & 7 deletions padrino-core/lib/padrino-core/application/routing.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,26 @@ module Routing
class ::HttpRouter #:nodoc:
attr_accessor :runner
class Route #:nodoc:
attr_accessor :custom_conditions, :before_filters, :after_filters, :use_layout, :controller
attr_reader :before_filters, :after_filters
attr_accessor :custom_conditions, :use_layout, :controller, :cache

def before_filters=(before_filters)
before_filters.each { |blk| arbitrary { |env| catch(:pass) { router.runner.instance_eval(&blk); true } == true } } if before_filters
@before_filters = before_filters
def add_before_filter(filter)
@before_filters ||= []
@before_filters << filter
arbitrary { |env| catch(:pass) { router.runner.instance_eval(&filter); true } == true }
end

def add_after_filter(filter)
@after_filters ||= []
@after_filters << filter
end

def before_filters=(filters)
filters.each { |filter| add_before_filter(filter) } if filters
end

def after_filters=(filters)
filters.each { |filter| add_after_filter(filter) } if filters
end

def custom_conditions=(custom_conditions)
Expand Down Expand Up @@ -127,6 +142,7 @@ def controller(*args, &block)
@_controller, original_controller = args, @_controller
@_parents, original_parent = options.delete(:parent), @_parents
@_provides, original_provides = options.delete(:provides), @_provides
@_cache, original_cache = options.delete(:cache), @_cache
@_map, original_map = options.delete(:map), @_map
@_defaults, original_defaults = options, @_defaults

Expand All @@ -143,7 +159,7 @@ def controller(*args, &block)
@layout = original_layout

# Controller defaults
@_controller, @_parents = original_controller, original_parent
@_controller, @_parents, @_cache = original_controller, original_parent, original_cache
@_defaults, @_provides, @_map = original_defaults, original_provides, original_map
else
include(*args) if extensions.any?
Expand Down Expand Up @@ -250,6 +266,7 @@ def route(verb, path, *args, &block)
# Do padrino parsing. We dup options so we can build HEAD request correctly
route_options = options.dup
route_options[:provides] = @_provides if @_provides
route_options[:cache] = @_cache if @_cache
path, name, options = *parse_route(path, route_options, verb)

# Sinatra defaults
Expand All @@ -272,6 +289,7 @@ def route(verb, path, *args, &block)
end

route.name(name) if name
route.cache = options.delete(:cache)
route.send(verb.downcase.to_sym)
route.host(options.delete(:host)) if options.key?(:host)
route.condition(:user_agent => options.delete(:agent)) if options.key?(:agent)
Expand All @@ -288,6 +306,8 @@ def route(verb, path, *args, &block)
conditions, @conditions = @conditions, []
route.custom_conditions = conditions

invoke_hook(:padrino_route_added, route, verb, path, args, options, block)

# Add Application defaults
if @_controller
route.before_filters = @before_filters
Expand Down Expand Up @@ -514,10 +534,11 @@ def route!(base=self.class, pass_block=nil)
# Now we can eval route, but because we have "throw halt" we need to be
# (en)sure to reset old layout and run controller after filters.
begin
route_eval(&match.destination)
@_response_buffer = catch(:halt) { route_eval(&match.destination) }
throw :halt, @_response_buffer
ensure
base.instance_variable_set(:@layout, parent_layout) if match.path.route.use_layout
match.path.route.after_filters.each { |aft| throw :pass if instance_eval(&aft) == false }
match.path.route.after_filters.each { |aft| throw :pass if instance_eval(&aft) == false } if match.path.route.after_filters
end
end
end
Expand Down

0 comments on commit f6dafa7

Please sign in to comment.