benschwarz-merb-cache 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +20 -0
- data/README +224 -0
- data/Rakefile +17 -0
- data/lib/merb-cache.rb +15 -0
- data/lib/merb-cache/cache.rb +91 -0
- data/lib/merb-cache/cache_request.rb +48 -0
- data/lib/merb-cache/core_ext/enumerable.rb +9 -0
- data/lib/merb-cache/core_ext/hash.rb +21 -0
- data/lib/merb-cache/merb_ext/controller/class_methods.rb +244 -0
- data/lib/merb-cache/merb_ext/controller/instance_methods.rb +163 -0
- data/lib/merb-cache/stores/fundamental/abstract_store.rb +101 -0
- data/lib/merb-cache/stores/fundamental/file_store.rb +113 -0
- data/lib/merb-cache/stores/fundamental/memcached_store.rb +110 -0
- data/lib/merb-cache/stores/strategy/abstract_strategy_store.rb +119 -0
- data/lib/merb-cache/stores/strategy/action_store.rb +61 -0
- data/lib/merb-cache/stores/strategy/adhoc_store.rb +69 -0
- data/lib/merb-cache/stores/strategy/gzip_store.rb +63 -0
- data/lib/merb-cache/stores/strategy/mintcache_store.rb +75 -0
- data/lib/merb-cache/stores/strategy/page_store.rb +68 -0
- data/lib/merb-cache/stores/strategy/sha1_store.rb +62 -0
- data/spec/merb-cache/cache_request_spec.rb +78 -0
- data/spec/merb-cache/cache_spec.rb +88 -0
- data/spec/merb-cache/core_ext/enumerable_spec.rb +26 -0
- data/spec/merb-cache/core_ext/hash_spec.rb +51 -0
- data/spec/merb-cache/merb_ext/controller_spec.rb +5 -0
- data/spec/merb-cache/stores/fundamental/abstract_store_spec.rb +118 -0
- data/spec/merb-cache/stores/fundamental/file_store_spec.rb +205 -0
- data/spec/merb-cache/stores/fundamental/memcached_store_spec.rb +258 -0
- data/spec/merb-cache/stores/strategy/abstract_strategy_store_spec.rb +78 -0
- data/spec/merb-cache/stores/strategy/action_store_spec.rb +208 -0
- data/spec/merb-cache/stores/strategy/adhoc_store_spec.rb +227 -0
- data/spec/merb-cache/stores/strategy/gzip_store_spec.rb +68 -0
- data/spec/merb-cache/stores/strategy/mintcache_store_spec.rb +59 -0
- data/spec/merb-cache/stores/strategy/page_store_spec.rb +146 -0
- data/spec/merb-cache/stores/strategy/sha1_store_spec.rb +84 -0
- data/spec/spec_helper.rb +95 -0
- metadata +112 -0
@@ -0,0 +1,68 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/abstract_strategy_store_spec'
|
3
|
+
|
4
|
+
describe Merb::Cache::GzipStore do
|
5
|
+
it_should_behave_like 'all strategy stores'
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@klass = Merb::Cache::GzipStore
|
9
|
+
@store = Merb::Cache::GzipStore[DummyStore].new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#writable?" do
|
13
|
+
it "should return true" do
|
14
|
+
@store.writable?(:foo).should be_true
|
15
|
+
@store.writable?('foo').should be_true
|
16
|
+
@store.writable?(123).should be_true
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should be false if none of the context caches are writable" do
|
20
|
+
@store.stores.each {|s| s.should_receive(:writable?).and_return false}
|
21
|
+
|
22
|
+
@store.writable?(:foo).should be_false
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe "#read" do
|
27
|
+
it "should return nil if hash does not exist as a key in any context store" do
|
28
|
+
@store.stores.each {|s| s.should_receive(:read).with(:foo, :bar => :baz).and_return nil}
|
29
|
+
|
30
|
+
@store.read(:foo, :bar => :baz).should be_nil
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should return the data from the context store when there is a hash key match" do
|
34
|
+
@store.stores.first.should_receive(:read).with(:foo, {}).and_return @store.compress("bar")
|
35
|
+
|
36
|
+
@store.read(:foo).should == "bar"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
describe "#exists?" do
|
41
|
+
it "should return a boolean" do
|
42
|
+
@store.write('foo', 'bar')
|
43
|
+
@store.exists?('foo').should be_true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "#write" do
|
48
|
+
it "should write" do
|
49
|
+
@store.write(:foo, "bar").should be_true
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should pass the hashed key to the context store" do
|
53
|
+
@store.stores.first.should_receive(:write).with(:foo, @store.compress('body'), {}, {}).and_return true
|
54
|
+
|
55
|
+
@store.write(:foo, 'body').should be_true
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should use the parameters to create the hashed key" do
|
59
|
+
@store.stores.first.should_receive(:write).with(:foo, @store.compress('body'), {:bar => :baz}, {}).and_return true
|
60
|
+
|
61
|
+
@store.write(:foo, 'body', :bar => :baz).should be_true
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "#fetch" do
|
66
|
+
# not sure how to spec this yet
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/abstract_strategy_store_spec'
|
3
|
+
|
4
|
+
describe Merb::Cache::MintCacheStore do
|
5
|
+
it_should_behave_like 'all strategy stores'
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@klass = Merb::Cache::MintCacheStore
|
9
|
+
@store = Merb::Cache::MintCacheStore[DummyStore].new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "reading caches" do
|
13
|
+
it "should return data after the first level cache has expired"
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "knowing if a cache exists" do
|
17
|
+
it "should return a boolean" do
|
18
|
+
@store.write('foo', 'bar')
|
19
|
+
@store.exists?('foo').should be_true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "when writing a cache" do
|
24
|
+
it "should write" do
|
25
|
+
@store.write('foo', 'bar').should be_true
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should write three keys" do
|
29
|
+
@store.write('foo', 'body')
|
30
|
+
%w(foo foo_validity foo_data).each do |key|
|
31
|
+
@store.read(key).should_not be_nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should write a validity key that is a time object" do
|
36
|
+
@store.write('foo', 'body')
|
37
|
+
@store.read('foo_validity').first.should be_a_kind_of Time
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should write a data key that is the same as a regular key" do
|
41
|
+
@store.write('foo', 'body')
|
42
|
+
@store.read('foo_data').first.should == @store.read('foo').first
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should write the additional keys with double expiry time" do
|
46
|
+
@store.write('foo', 'body', {}, :expire_in => 10)
|
47
|
+
@store.read('foo_data')[2][:expire_in].should eql 20
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should receive write_all three times (key, key_validity and key_data for write_all" do
|
51
|
+
@store.write_all('foo', 'body')
|
52
|
+
%w(foo foo_validity foo_data).each do |key|
|
53
|
+
@store.read(key).should_not be_nil
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,146 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/abstract_strategy_store_spec'
|
3
|
+
|
4
|
+
describe Merb::Cache::PageStore do
|
5
|
+
it_should_behave_like 'all strategy stores'
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
Merb::Cache.stores.clear
|
9
|
+
Thread.current[:'merb-cache'] = nil
|
10
|
+
|
11
|
+
@klass = Merb::Cache::PageStore[:dummy]
|
12
|
+
Merb::Cache.register(:dummy, DummyStore)
|
13
|
+
Merb::Cache.register(:default, @klass)
|
14
|
+
|
15
|
+
@store = Merb::Cache[:default]
|
16
|
+
@dummy = @store.stores.first
|
17
|
+
end
|
18
|
+
|
19
|
+
def dispatch(url, attrs = {})
|
20
|
+
Merb::Dispatcher.handle(request_for(url, attrs))
|
21
|
+
end
|
22
|
+
|
23
|
+
def request_for(url, attrs = {})
|
24
|
+
Merb::Request.new(Rack::MockRequest.env_for(url, attrs))
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "examples" do
|
28
|
+
|
29
|
+
class NhlScores < Merb::Controller
|
30
|
+
provides :xml, :json, :yaml
|
31
|
+
|
32
|
+
cache :index, :show
|
33
|
+
cache :overview
|
34
|
+
|
35
|
+
eager_cache :index, :overview, :uri => '/overview'
|
36
|
+
eager_cache :overview, :index
|
37
|
+
eager_cache(:overview, :show) {{ :uri => build_url(:show, :team => 1), :params => {:team => 1} }}
|
38
|
+
|
39
|
+
def index
|
40
|
+
"NHLScores index"
|
41
|
+
end
|
42
|
+
|
43
|
+
def show(team)
|
44
|
+
"NHLScores show(#{team})"
|
45
|
+
end
|
46
|
+
|
47
|
+
def overview
|
48
|
+
"NHLScores overview"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
before(:each) do
|
53
|
+
Merb::Router.prepare do |r|
|
54
|
+
r.match("/").to(:controller => "nhl_scores", :action => "index").name(:index)
|
55
|
+
r.match("/show/:team").to(:controller => "nhl_scores", :action => "show").name(:show)
|
56
|
+
r.match("/overview").to(:controller => "nhl_scores", :action => "overview").name(:overview)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# NOTE: the "REQUEST_URI" => ... stuff will be removed after has_query_params? is in core.
|
62
|
+
it "should cache the index action on the first request" do
|
63
|
+
dispatch(url(:index), "REQUEST_URI" => url(:index))
|
64
|
+
|
65
|
+
@dummy.data("/index.html").should == "NHLScores index"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should cache the yaml version of the index action request" do
|
69
|
+
dispatch(url(:index), "HTTP_ACCEPT" => "application/x-yaml", "REQUEST_URI" => url(:index))
|
70
|
+
|
71
|
+
@dummy.data("/index.yaml").should == "NHLScores index"
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should cache the show action when the team parameter is a route parameter" do
|
75
|
+
dispatch(url(:show, :team => 'redwings'), "REQUEST_URI" => url(:show, :team => 'redwings'))
|
76
|
+
|
77
|
+
@dummy.data("/show/redwings.html").should == "NHLScores show(redwings)"
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should cache the xml version of a request" do
|
81
|
+
dispatch(url(:show, :team => 'redwings'), "HTTP_ACCEPT" => "application/xml", "REQUEST_URI" => url(:show, :team => 'redwings'))
|
82
|
+
|
83
|
+
@dummy.data("/show/redwings.xml").should == "NHLScores show(redwings)"
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
it "should not cache the show action when the team parameter is not a route parameter" do
|
88
|
+
pending "the has_query_params? is implemented in merb-core"
|
89
|
+
dispatch_to(NhlScores, :show, :team => 'readwings', "REQUEST_URI" => url(:show, :team => 'redwings'))
|
90
|
+
|
91
|
+
@dummy.vault.should be_empty
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should not cache the action when a there is a query string parameter" do
|
95
|
+
dispatch(url(:index, :page => 2))
|
96
|
+
|
97
|
+
@dummy.data(url(:index)).should be_nil
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should not cache a POST request" do
|
101
|
+
dispatch(url(:index), "REQUEST_METHOD" => "POST")
|
102
|
+
|
103
|
+
@dummy.data('/index.html').should be_nil
|
104
|
+
end
|
105
|
+
|
106
|
+
it "should eager cache overview after a POST request to index" do
|
107
|
+
dispatch(url(:index), "REQUEST_METHOD" => "POST")
|
108
|
+
|
109
|
+
@dummy.data('/overview.html').should == "NHLScores overview"
|
110
|
+
end
|
111
|
+
|
112
|
+
it "should eager cache show after a request to overview" do
|
113
|
+
dispatch(url(:overview))
|
114
|
+
|
115
|
+
@dummy.data('/show/1.html').should == "NHLScores show(1)"
|
116
|
+
end
|
117
|
+
|
118
|
+
it "should not eager cache during an eager cache, causing an infinit loop of eagerness" do
|
119
|
+
dispatch(url(:overview), "REQUEST_URI" => url(:overview))
|
120
|
+
|
121
|
+
@dummy.data("/overview.html").should == "NHLScores overview"
|
122
|
+
end
|
123
|
+
|
124
|
+
describe "controller.caches?" do
|
125
|
+
it "should return true for a request to overview with the :get method" do
|
126
|
+
NhlScores.caches?(:overview, :uri => url(:overview)).should be_true
|
127
|
+
end
|
128
|
+
|
129
|
+
it "should return false for a request to overview with the :post method" do
|
130
|
+
NhlScores.caches?(:overview, :uri => url(:overview), :method => :post).should be_false
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should return false for a request to overview with query string parameters" do
|
134
|
+
NhlScores.caches?(:overview, :uri => url(:overview, :foo => 'baz')).should be_false
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should return true for a request to overview with the :get method when looking in the page store" do
|
138
|
+
NhlScores.caches?(:overview, :uri => url(:overview), :store => :default).should be_true
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should return false for a request to overview with the :get method when looking in the action store" do
|
142
|
+
pending "need to move all the specs for caches? over to a controller integration test that uses both controller stores"
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../../../spec_helper'
|
2
|
+
require File.dirname(__FILE__) + '/abstract_strategy_store_spec'
|
3
|
+
|
4
|
+
describe Merb::Cache::SHA1Store do
|
5
|
+
it_should_behave_like 'all strategy stores'
|
6
|
+
|
7
|
+
before(:each) do
|
8
|
+
@klass = Merb::Cache::SHA1Store
|
9
|
+
@store = Merb::Cache::SHA1Store[DummyStore].new
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#writable?" do
|
13
|
+
it "should return true" do
|
14
|
+
@store.writable?('foo').should be_true
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be false if none of the context caches are writable" do
|
18
|
+
@store.stores.each {|s| s.should_receive(:writable?).and_return false}
|
19
|
+
|
20
|
+
@store.writable?(:foo).should be_false
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#read" do
|
25
|
+
it "should return nil if hash does not exist as a key in any context store" do
|
26
|
+
@store.stores.each {|s| s.should_receive(:read).with(@store.digest(:foo, :bar => :baz)).and_return nil}
|
27
|
+
|
28
|
+
@store.read(:foo, :bar => :baz).should be_nil
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should return the data from the context store when there is a hash key match" do
|
32
|
+
@store.stores.first.should_receive(:read).with(@store.digest(:foo)).and_return :bar
|
33
|
+
|
34
|
+
@store.read(:foo).should == :bar
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#exists?" do
|
39
|
+
it "should return a boolean" do
|
40
|
+
@store.write('foo', 'bar')
|
41
|
+
@store.exists?('foo').should be_true
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
describe "#write" do
|
46
|
+
it "should write" do
|
47
|
+
@store.write('foo', 'bar').should be_true
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should pass the hashed key to the context store" do
|
51
|
+
@store.stores.first.should_receive(:write).with(@store.digest(:foo), 'body', {}, {}).and_return true
|
52
|
+
|
53
|
+
@store.write(:foo, 'body').should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should use the parameters to create the hashed key" do
|
57
|
+
@store.stores.first.should_receive(:write).with(@store.digest(:foo, :bar => :baz), 'body', {}, {}).and_return true
|
58
|
+
|
59
|
+
@store.write(:foo, 'body', :bar => :baz).should be_true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
describe "#fetch" do
|
64
|
+
it "should return nil if the arguments are not storable" do
|
65
|
+
@store.fetch(mock(:request)) {'body'}.should be_nil
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
describe "#digest" do
|
72
|
+
it "should produce the same digest for the exact same key and parameters" do
|
73
|
+
@store.digest(:foo, :bar => :baz).should == @store.digest(:foo, :bar => :baz)
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should use the string result of the parameter arguments to_param in the hash" do
|
77
|
+
@store.digest(:foo, :bar => :baz).should_not == @store.digest(:foo)
|
78
|
+
end
|
79
|
+
|
80
|
+
it "should use the key argument in the hash" do
|
81
|
+
@store.digest('', :bar => :baz).should_not == @store.digest('')
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,95 @@
|
|
1
|
+
$:.push File.join(File.dirname(__FILE__), '..', 'lib')
|
2
|
+
|
3
|
+
# Deps
|
4
|
+
require 'rubygems'
|
5
|
+
require 'merb-core'
|
6
|
+
require 'merb-action-args'
|
7
|
+
require File.join(File.dirname(__FILE__), '..', 'lib', 'merb-cache')
|
8
|
+
|
9
|
+
Merb.disable(:initfile)
|
10
|
+
|
11
|
+
Merb.start :environment => "test",
|
12
|
+
:adapter => "runner",
|
13
|
+
:log_file => File.join(File.dirname(__FILE__), '..', 'log', 'merb_test.log')
|
14
|
+
|
15
|
+
require "merb-core/test"
|
16
|
+
Spec::Runner.configure do |config|
|
17
|
+
config.include Merb::Test::Helpers
|
18
|
+
#config.include Merb::Test::ControllerHelper
|
19
|
+
config.include Merb::Test::RouteHelper
|
20
|
+
end
|
21
|
+
|
22
|
+
# Usage
|
23
|
+
# http://github.com/hassox/merb/tree/authz/merb-core/spec/public/authorization/base_spec.rb
|
24
|
+
class Viking
|
25
|
+
def self.captures
|
26
|
+
@captures ||= []
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.capture(obj)
|
30
|
+
captures << obj
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class DummyStore < Merb::Cache::AbstractStore
|
35
|
+
cattr_accessor :vault
|
36
|
+
attr_accessor :options
|
37
|
+
|
38
|
+
def initialize(config = {})
|
39
|
+
super(config)
|
40
|
+
@options = config
|
41
|
+
@@vault = {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def writable?(*args)
|
45
|
+
true
|
46
|
+
end
|
47
|
+
|
48
|
+
def read(key, parameters = {})
|
49
|
+
|
50
|
+
if @@vault.keys.include?(key)
|
51
|
+
@@vault[key].find {|data, timestamp, conditions, params| params == parameters}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def data(key, parameters = {})
|
56
|
+
read(key, parameters)[0] if read(key, parameters)
|
57
|
+
end
|
58
|
+
|
59
|
+
def time(key, parameters = {})
|
60
|
+
read(key, parameters)[1] if read(key, parameters)
|
61
|
+
end
|
62
|
+
|
63
|
+
def conditions(key, parameters = {})
|
64
|
+
read(key, parameters)[2] if read(key, parameters)
|
65
|
+
end
|
66
|
+
|
67
|
+
def write(key, data = nil, parameters = {}, conditions = {})
|
68
|
+
(@@vault[key] ||= []) << [data, Time.now, conditions, parameters]
|
69
|
+
true
|
70
|
+
end
|
71
|
+
|
72
|
+
def fetch(key, parameters = {}, conditions = {}, &blk)
|
73
|
+
@@vault[[key, parameters]] ||= blk.call
|
74
|
+
end
|
75
|
+
|
76
|
+
def exists?(key, parameters = {})
|
77
|
+
return true if @@vault.has_key?(key) and @@vault[key].find {|data, timestamp, conditions, params| params == parameters}
|
78
|
+
return false
|
79
|
+
end
|
80
|
+
|
81
|
+
def delete(key, parameters = {})
|
82
|
+
@@vault.delete([key, parameters]) unless @@vault[[key, parameters]].nil?
|
83
|
+
end
|
84
|
+
|
85
|
+
def delete_all
|
86
|
+
@@vault = {}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
#TODO change this to a work queue per class called in an after aspect
|
91
|
+
class Merb::Controller
|
92
|
+
def run_later
|
93
|
+
yield
|
94
|
+
end
|
95
|
+
end
|