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.
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