benschwarz-merb-cache 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/LICENSE +20 -0
  2. data/README +224 -0
  3. data/Rakefile +17 -0
  4. data/lib/merb-cache.rb +15 -0
  5. data/lib/merb-cache/cache.rb +91 -0
  6. data/lib/merb-cache/cache_request.rb +48 -0
  7. data/lib/merb-cache/core_ext/enumerable.rb +9 -0
  8. data/lib/merb-cache/core_ext/hash.rb +21 -0
  9. data/lib/merb-cache/merb_ext/controller/class_methods.rb +244 -0
  10. data/lib/merb-cache/merb_ext/controller/instance_methods.rb +163 -0
  11. data/lib/merb-cache/stores/fundamental/abstract_store.rb +101 -0
  12. data/lib/merb-cache/stores/fundamental/file_store.rb +113 -0
  13. data/lib/merb-cache/stores/fundamental/memcached_store.rb +110 -0
  14. data/lib/merb-cache/stores/strategy/abstract_strategy_store.rb +119 -0
  15. data/lib/merb-cache/stores/strategy/action_store.rb +61 -0
  16. data/lib/merb-cache/stores/strategy/adhoc_store.rb +69 -0
  17. data/lib/merb-cache/stores/strategy/gzip_store.rb +63 -0
  18. data/lib/merb-cache/stores/strategy/mintcache_store.rb +75 -0
  19. data/lib/merb-cache/stores/strategy/page_store.rb +68 -0
  20. data/lib/merb-cache/stores/strategy/sha1_store.rb +62 -0
  21. data/spec/merb-cache/cache_request_spec.rb +78 -0
  22. data/spec/merb-cache/cache_spec.rb +88 -0
  23. data/spec/merb-cache/core_ext/enumerable_spec.rb +26 -0
  24. data/spec/merb-cache/core_ext/hash_spec.rb +51 -0
  25. data/spec/merb-cache/merb_ext/controller_spec.rb +5 -0
  26. data/spec/merb-cache/stores/fundamental/abstract_store_spec.rb +118 -0
  27. data/spec/merb-cache/stores/fundamental/file_store_spec.rb +205 -0
  28. data/spec/merb-cache/stores/fundamental/memcached_store_spec.rb +258 -0
  29. data/spec/merb-cache/stores/strategy/abstract_strategy_store_spec.rb +78 -0
  30. data/spec/merb-cache/stores/strategy/action_store_spec.rb +208 -0
  31. data/spec/merb-cache/stores/strategy/adhoc_store_spec.rb +227 -0
  32. data/spec/merb-cache/stores/strategy/gzip_store_spec.rb +68 -0
  33. data/spec/merb-cache/stores/strategy/mintcache_store_spec.rb +59 -0
  34. data/spec/merb-cache/stores/strategy/page_store_spec.rb +146 -0
  35. data/spec/merb-cache/stores/strategy/sha1_store_spec.rb +84 -0
  36. data/spec/spec_helper.rb +95 -0
  37. metadata +112 -0
@@ -0,0 +1,51 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Hash do
4
+
5
+ describe "to_sha1" do
6
+ before(:each) do
7
+ @params = {:id => 1, :string => "string", :symbol => :symbol}
8
+ end
9
+
10
+ it{@params.should respond_to(:to_sha2)}
11
+
12
+ # This sec only has meaning in ruby 1.9 that uses an ordered hash
13
+ it "should encode the hash by alphabetic key" do
14
+ x = @params.to_sha2
15
+ y = {:id => 1, :symbol => :symbol, :string => "string"}.to_sha2
16
+ x.should == y
17
+ end
18
+
19
+ it "should not have any collisions between different values (using arrays)" do
20
+ x = {'test' => ['at','vc'], 'test2' => 'b'}.to_sha2
21
+ y = {'test' => ['atv', 'c'], 'test2' => 'b'}.to_sha2
22
+ puts x != y
23
+ x.should_not == y
24
+ end
25
+
26
+ it "should encode the keys" do
27
+ x = {'key' => [1,2,3]}.to_sha2
28
+ y = {'test' => [1,2,3]}.to_sha2
29
+ x.should_not == y
30
+ end
31
+
32
+ it "should not make a distinction between symbol and strings for keys" do
33
+ x = {:key => 1}.to_sha2
34
+ y = {'key' => 1}.to_sha2
35
+ x.should == y
36
+ end
37
+
38
+ it "should not have any collisions between keys and values" do
39
+ x = {'key' => 'ab'}.to_sha2
40
+ y = {'keya' => 'b'}.to_sha2
41
+ x.should_not == y
42
+ end
43
+
44
+ it "should not have any collisions between the values of two different keys" do
45
+ x = {'a' => 'abc', 'b' => 'def'}.to_sha2
46
+ y = {'a' => 'abcd', 'b' => 'ef'}.to_sha2
47
+ x.should_not == y
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,5 @@
1
+ require File.dirname(__FILE__) + '/../../spec_helper'
2
+
3
+ describe Merb::Cache::Controller do
4
+ # Oh this hurts
5
+ end
@@ -0,0 +1,118 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ describe 'all stores', :shared => true do
4
+ describe "#writable?" do
5
+ it "should not raise a NotImplementedError error" do
6
+ lambda { @store.writable?(:foo) }.should_not raise_error(NotImplementedError)
7
+ end
8
+
9
+ it "should accept a conditions hash" do
10
+ @store.writable?('key', :conditions => :hash)
11
+ end
12
+ end
13
+
14
+ describe "#read" do
15
+ it "should not raise a NotImplementedError error" do
16
+ lambda { @store.read('foo') }.should_not raise_error(NotImplementedError)
17
+ end
18
+
19
+ it "should accept a parameters hash" do
20
+ @store.read('foo', :params => :hash)
21
+ end
22
+ end
23
+
24
+ describe "#write" do
25
+ it "should not raise a NotImplementedError error" do
26
+ lambda { @store.write('foo', 'bar') }.should_not raise_error(NotImplementedError)
27
+ end
28
+ end
29
+
30
+ describe "#fetch" do
31
+ it "should not raise a NotImplementedError error" do
32
+ lambda { @store.fetch('foo') {'bar'} }.should_not raise_error(NotImplementedError)
33
+ end
34
+
35
+ it "should accept parameters and conditions" do
36
+ @store.fetch('foo', {:params => :hash}, :conditions => :hash) { 'bar' }
37
+ end
38
+
39
+ it "should accept a value generating block" do
40
+ @store.fetch('foo') {'bar'}
41
+ end
42
+
43
+ it "should return the value of the block if it is called" do
44
+ @store.fetch(:boo) { "bar" }.should == "bar" if @store.writable?(:boo)
45
+ end
46
+ end
47
+
48
+ describe "#exists?" do
49
+ it "should not raise a NotImplementedError error" do
50
+ lambda { @store.exists?('foo') }.should_not raise_error(NotImplementedError)
51
+ end
52
+ end
53
+
54
+ describe "#delete" do
55
+ it "should not raise a NotImplementedError error" do
56
+ lambda { @store.delete('foo') }.should_not raise_error(NotImplementedError)
57
+ end
58
+
59
+ it "should accept a parameters hash" do
60
+ @store.delete('foo', :params => :hash)
61
+ end
62
+ end
63
+
64
+ describe "#delete_all!" do
65
+ it "should not raise a NotImplementedError error" do
66
+ lambda { @store.delete_all! }.should_not raise_error(NotImplementedError)
67
+ end
68
+ end
69
+ end
70
+
71
+
72
+ describe Merb::Cache::AbstractStore do
73
+ before(:each) do
74
+ @store = Merb::Cache::AbstractStore.new
75
+ end
76
+
77
+ describe "#writable?" do
78
+ it "should raise a NotImplementedError error" do
79
+ lambda { @store.writable?('foo') }.should raise_error(NotImplementedError)
80
+ end
81
+ end
82
+
83
+ describe "#read" do
84
+ it "should raise a NotImplementedError" do
85
+ lambda { @store.read('foo') }.should raise_error(NotImplementedError)
86
+ end
87
+ end
88
+
89
+ describe "#write" do
90
+ it "should raise a NotImplementedError" do
91
+ lambda { @store.write('foo', 'bar') }.should raise_error(NotImplementedError)
92
+ end
93
+ end
94
+
95
+ describe "#fetch" do
96
+ it "should raise a NotImplementedError" do
97
+ lambda { @store.fetch('foo') {'bar'} }.should raise_error(NotImplementedError)
98
+ end
99
+ end
100
+
101
+ describe "#exists?" do
102
+ it "should raise a NotImplementedError" do
103
+ lambda { @store.exists?('foo') }.should raise_error(NotImplementedError)
104
+ end
105
+ end
106
+
107
+ describe "#delete" do
108
+ it "should raise a NotImplementedError" do
109
+ lambda { @store.delete('foo') }.should raise_error(NotImplementedError)
110
+ end
111
+ end
112
+
113
+ describe "#delete_all!" do
114
+ it "should raise a NotImplementedError" do
115
+ lambda { @store.delete_all! }.should raise_error(NotImplementedError)
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,205 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+ # has 'all stores' shared group
3
+ require File.dirname(__FILE__) + '/abstract_store_spec'
4
+
5
+ describe Merb::Cache::FileStore do
6
+ it_should_behave_like 'all stores'
7
+
8
+ before(:each) do
9
+ @store = Merb::Cache::FileStore.new(:dir => File.dirname(Tempfile.new("").path))
10
+ end
11
+
12
+ #
13
+ # ==== #writable
14
+ #
15
+
16
+ describe "#writable?" do
17
+ describe "when conditions hash is empty" do
18
+ it "returns true" do
19
+ @store.writable?('foo').should be_true
20
+ end
21
+ end
22
+
23
+ describe "when given expire_in option" do
24
+ it "returns false" do
25
+ @store.writable?('foo', {}, :expire_in => 10).should be_false
26
+ end
27
+ end
28
+
29
+ it "should return a boolean" do
30
+ @store.writable?(:foo, {}).should be_true
31
+ end
32
+ end
33
+
34
+ #
35
+ # ==== #read
36
+ #
37
+
38
+ describe "#read" do
39
+ describe "when cache file does not exist" do
40
+ it "should return nil" do
41
+ key = "body.txt"
42
+
43
+ FileUtils.rm(@store.pathify(key)) if File.exists?(@store.pathify(key))
44
+ @store.read(key).should be_nil
45
+ end
46
+ end
47
+
48
+ describe "when cache file exists" do
49
+ it "reads the contents of the file" do
50
+ key = "tmp.txt"
51
+ body = "body of the file"
52
+
53
+ File.open(@store.pathify(key), "w+") do |file|
54
+ file << body
55
+ end
56
+
57
+ @store.read(key).should == body
58
+ end
59
+ end
60
+ end
61
+
62
+ #
63
+ # ==== #write
64
+ #
65
+
66
+ describe "#write" do
67
+ it "should write" do
68
+ @store.write('foo', 'bar', {:params => :hash}, :conditions => :hash).should be_true
69
+ end
70
+
71
+ describe "if it does not exist" do
72
+ it "create the file" do
73
+ key = "body.txt"
74
+
75
+ FileUtils.rm(@store.pathify(key)) if File.exists?(@store.pathify(key))
76
+
77
+ File.should_not be_exist(@store.pathify(key))
78
+
79
+ @store.write(key, "")
80
+
81
+ File.should be_exist(@store.pathify(key))
82
+ end
83
+ end
84
+
85
+ describe "when file already exists" do
86
+ it "overwrites the file" do
87
+ key = "tmp.txt"
88
+ old_body, new_body = "old body", "new body"
89
+
90
+ File.open(@store.pathify(key), "w+") do |file|
91
+ file << old_body
92
+ end
93
+
94
+ File.open(@store.pathify(key), "r") do |file|
95
+ file.read.should == old_body
96
+ end
97
+
98
+ @store.write(key, new_body)
99
+
100
+ File.open(@store.pathify(key), "r") do |file|
101
+ file.read.should == new_body
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ #
108
+ # ==== #fetch
109
+ #
110
+
111
+ describe "#fetch" do
112
+ describe "when the entry can be read" do
113
+ it "does not call the block" do
114
+ key, body = "tmp.txt", "body"
115
+ called = false
116
+ proc = lambda { called = true }
117
+
118
+ File.open(@store.pathify(key), "w+") do |file|
119
+ file << body
120
+ end
121
+
122
+ @store.fetch(key, &proc)
123
+ called.should be_false
124
+ end
125
+ end
126
+
127
+ describe "when entry cannot be read" do
128
+ it "calls the block" do
129
+ key = "tmp.txt"
130
+ called = false
131
+ proc = lambda { called = true }
132
+
133
+ FileUtils.rm(@store.pathify(key)) if File.exists?(@store.pathify(key))
134
+
135
+ @store.fetch(key, &proc)
136
+ called.should be_true
137
+ end
138
+ end
139
+ end
140
+
141
+ #
142
+ # ==== # exists?
143
+ #
144
+
145
+ describe "#exists?" do
146
+ it "should return a boolean" do
147
+ @store.write('foo', 'bar')
148
+ @store.exists?('foo').should be_true
149
+ end
150
+ end
151
+
152
+ #
153
+ # ==== #delete
154
+ #
155
+
156
+ describe "#delete" do
157
+ describe "when file exists" do
158
+ it "deletes the file" do
159
+ key, body = "tmp.txt", "body of the file"
160
+
161
+ File.open(@store.pathify(key), "w+") do |file|
162
+ file << body
163
+ end
164
+
165
+ File.exists?(@store.pathify(key)).should be_true
166
+ @store.delete(key)
167
+ File.exists?(@store.pathify(key)).should be_false
168
+ end
169
+ end
170
+
171
+ describe "when file does not exist" do
172
+ it "does not raise" do
173
+ @store.delete("#{rand}-#{rand}-#{Time.now.to_i}.txt")
174
+ end
175
+ end
176
+ end
177
+
178
+ #
179
+ # ==== #delete_all
180
+ #
181
+
182
+ describe "#delete_all" do
183
+ it "is not supported" do
184
+ lambda { @store.delete_all }.should raise_error(Merb::Cache::NotSupportedError)
185
+ end
186
+ end
187
+
188
+ #
189
+ # ==== #pathify
190
+ #
191
+
192
+ describe "#pathify" do
193
+ it "should begin with the cache dir" do
194
+ @store.pathify("tmp.txt").should include(@store.dir)
195
+ end
196
+
197
+ it "should add any parameters to the end of the filename" do
198
+ @store.pathify("index.html", :page => 3, :lang => :en).should =~ %r[--#{{:page => 3, :lang => :en}.to_sha2}$]
199
+ end
200
+
201
+ it "should separate the parameters from the key by a '?'" do
202
+ @store.pathify("index.html", :page => 3, :lang => :en).should =~ %r[--#{{:page => 3, :lang => :en}.to_sha2}$]
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,258 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+ require File.dirname(__FILE__) + '/abstract_store_spec'
3
+
4
+ begin
5
+ require 'memcached'
6
+ servers = '127.0.0.1:11211'
7
+ namespace = 'memcached_test_namespace'
8
+
9
+ options = {
10
+ :namespace => @namespace,
11
+ :hash => :default,
12
+ :distribution => :modula
13
+ }
14
+ cache = Memcached.new(servers, options)
15
+ key, value = Time.now.to_i.to_s, Time.now.to_s
16
+ cache.set(key, value)
17
+ raise Exception unless cache.get(key) == value
18
+ rescue Exception => e
19
+ puts e.message
20
+ puts "Memcached connection failed. Try starting memcached on the default port (11211)"
21
+ else
22
+
23
+ describe Merb::Cache::MemcachedStore do
24
+ it_should_behave_like 'all stores'
25
+
26
+ before(:each) do
27
+ @store = Merb::Cache::MemcachedStore.new(:namespace => "specs", :servers => ["127.0.0.1:11211"])
28
+ @memcached = @store.memcached
29
+ @memcached.flush
30
+ end
31
+
32
+ after(:each) do
33
+ @memcached.flush
34
+ end
35
+
36
+
37
+ it "has accessor for namespace" do
38
+ @store.namespace.should == "specs"
39
+ end
40
+
41
+ it "has accessor for servers" do
42
+ @store.servers.should == ["127.0.0.1:11211"]
43
+ end
44
+
45
+ it "has accessor for memcached connector" do
46
+ @store.memcached.should == @memcached
47
+ end
48
+
49
+
50
+ #
51
+ # ==== #writable?
52
+ #
53
+
54
+ describe "#writable?" do
55
+ describe "when conditions hash is empty" do
56
+ it "returns true" do
57
+ @store.writable?('foo').should be_true
58
+ end
59
+ end
60
+ end
61
+
62
+ #
63
+ # ==== #read
64
+ #
65
+
66
+ describe "#read" do
67
+ describe "when cache has NO entry matching key" do
68
+ it "returns nil" do
69
+ key = "foo"
70
+
71
+ @memcached.delete(key) rescue nil
72
+ @store.read(key).should be_nil
73
+ end
74
+ end
75
+
76
+ describe "when cache has entry matching key" do
77
+ it "returns the entry matching the key" do
78
+ key, data = "foo", "bar"
79
+
80
+ @memcached.set(key, data)
81
+
82
+ @store.read(key).should == data
83
+ end
84
+ end
85
+ end
86
+
87
+ #
88
+ # ==== #write
89
+ #
90
+
91
+ describe "#write" do
92
+ it "should write" do
93
+ @store.write('foo', 'bar', {:params => :hash}, :conditions => :hash).should be_true
94
+ end
95
+
96
+ describe "when entry with the same key does not exist" do
97
+ it "create a new entry" do
98
+ key, data = "foo", "bar"
99
+
100
+ @memcached.delete(key) rescue nil
101
+ lambda { @memcached.get(key) }.should raise_error(Memcached::NotFound)
102
+
103
+ @store.write(key, data)
104
+ @memcached.get(key).should == data
105
+ end
106
+ end
107
+
108
+ describe "when entry with the same key already exists" do
109
+ it "overwrites the entry in the cache" do
110
+ key, data = "foo", "bar"
111
+
112
+ @memcached.set(key, "baz")
113
+ @memcached.get(key).should == "baz"
114
+
115
+ @store.write(key, data)
116
+ @memcached.get(key).should == data
117
+ end
118
+ end
119
+ end
120
+
121
+ #
122
+ # ==== #exists?
123
+ #
124
+ describe "#exists?" do
125
+ it "should return a boolean" do
126
+ @store.write('foo', 'bar')
127
+ @store.exists?('foo').should be_true
128
+ end
129
+ end
130
+
131
+ #
132
+ # ==== #fetch
133
+ #
134
+
135
+ describe "#fetch" do
136
+ describe "when the entry exists in the cache" do
137
+ it "does NOT call the block" do
138
+ key, data = "foo", "bar"
139
+ called = false
140
+ proc = lambda { called = true }
141
+
142
+ @memcached.set(key, data)
143
+ @store.fetch(key, &proc)
144
+
145
+ called.should be_false
146
+ end
147
+ end
148
+
149
+ describe "when the entry does not exist in the cache" do
150
+ it "calls the block" do
151
+ key, data = "foo", "bar"
152
+ called = false
153
+ proc = lambda { called = true }
154
+
155
+ @memcached.delete(key) rescue nil
156
+ @store.fetch(key, &proc)
157
+
158
+ called.should be_true
159
+ end
160
+ end
161
+ end
162
+
163
+ #
164
+ # ==== #delete
165
+ #
166
+
167
+ describe "#delete" do
168
+ describe "when the entry exists in the cache" do
169
+ it "deletes the entry" do
170
+ key, data = "foo", "bar"
171
+
172
+ @memcached.set(key, data)
173
+ @memcached.get(key).should == data
174
+
175
+ @store.delete(key)
176
+ lambda { @memcached.get(key) }.should raise_error(Memcached::NotFound)
177
+ end
178
+ end
179
+
180
+ describe "when the entry does not exist in the cache" do
181
+ it "raises Memcached::NotFound" do
182
+ lambda { @memcached.delete("#{rand}-#{rand}-#{Time.now.to_i}") }.
183
+ should raise_error(Memcached::NotFound)
184
+ end
185
+ end
186
+ end
187
+
188
+ #
189
+ # ==== #delete_all
190
+ #
191
+
192
+ describe "#delete_all" do
193
+ it "flushes Memcached object" do
194
+ @memcached.set("ruby", "rb")
195
+ @memcached.set("python", "py")
196
+ @memcached.set("perl", "pl")
197
+
198
+ @store.delete_all
199
+
200
+ @store.exists?("ruby").should be_false
201
+ @store.exists?("python").should be_false
202
+ @store.exists?("perl").should be_false
203
+ end
204
+ end
205
+
206
+ #
207
+ # ==== #clone
208
+ #
209
+
210
+ describe "#clone" do
211
+ it "clones Memcached instance" do
212
+ clone = @store.clone
213
+
214
+ clone.memcached.object_id.should_not == @store.memcached.object_id
215
+ end
216
+ end
217
+
218
+ #
219
+ # ==== #normalize
220
+ #
221
+
222
+ describe "#normalize" do
223
+ it "should begin with the key" do
224
+ @store.normalize("this/is/the/key").should =~ /^this\/is\/the\/key/
225
+ end
226
+
227
+ it "should not add the '?' if there are no parameters" do
228
+ @store.normalize("this/is/the/key").should_not =~ /\?/
229
+ end
230
+
231
+ it "should seperate the parameters from the key by a '?'" do
232
+ @store.normalize("this/is/the/key", :page => 3, :lang => :en).
233
+ should =~ %r!this\/is\/the\/key--#{{:page => 3, :lang => :en}.to_sha2}$!
234
+ end
235
+ end
236
+
237
+ #
238
+ # ==== #expire_time
239
+ #
240
+
241
+ describe "#expire_time" do
242
+ describe "when there is NO :expire_in parameter" do
243
+ it "returns 0" do
244
+ @store.expire_time.should == 0
245
+ end
246
+ end
247
+
248
+ describe "when there is :expire_in parameter" do
249
+ it "returns Time.now + the :expire_in parameter" do
250
+ now = Time.now
251
+ Time.should_receive(:now).and_return now
252
+
253
+ @store.expire_time(:expire_in => 100).should == now + 100
254
+ end
255
+ end
256
+ end
257
+ end
258
+ end